From e1f26613037f570245bb861bfabdd5960816c12a Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 21 Mar 2023 17:00:35 +0500 Subject: [PATCH 001/145] feat: initial project --- .editorconfig | 25 + .gitignore | 38 + README.md | 0 backend/go.mod | 176 + backend/go.sum | 1007 +++ backend/pkg/bootstrap.go | 38 + backend/pkg/cache/cache.go | 54 + backend/pkg/cache/memory.go | 17 + backend/pkg/cache/redis.go | 20 + backend/pkg/config/cache.go | 62 + backend/pkg/config/cors.go | 53 + backend/pkg/config/credentials.go | 67 + backend/pkg/config/error.go | 12 + backend/pkg/config/logger.go | 75 + backend/pkg/config/messaging.go | 59 + backend/pkg/config/persistence.go | 49 + backend/pkg/config/registry.go | 57 + backend/pkg/config/resilience.go | 66 + backend/pkg/config/server.go | 84 + backend/pkg/config/trace.go | 52 + backend/pkg/config/worker.go | 62 + backend/pkg/log/error.go | 23 + backend/pkg/log/hook/elastic.go | 280 + backend/pkg/log/logger.go | 124 + backend/pkg/log/logrus.go | 170 + backend/pkg/messaging/broker.go | 59 + backend/pkg/middleware/cache.go | 17 + backend/pkg/middleware/cors.go | 17 + backend/pkg/middleware/log.go | 19 + backend/pkg/middleware/rate.go | 36 + backend/pkg/middleware/secure.go | 18 + backend/pkg/middleware/timeout.go | 12 + backend/pkg/middleware/trace.go | 38 + backend/pkg/middleware/version.go | 15 + backend/pkg/middleware/wrapper/trace.go | 35 + backend/pkg/registry/registry.go | 40 + backend/pkg/resilience/circuit.go | 31 + backend/pkg/service/http/service.go | 131 + backend/pkg/service/repl/service.go | 52 + backend/pkg/service/rpc/service.go | 136 + backend/pkg/shared/env.go | 19 + backend/pkg/trace/error.go | 8 + backend/pkg/trace/tracer.go | 56 + backend/pkg/trace/zipkin.go | 17 + backend/pkg/worker/asynq.go | 124 + backend/pkg/worker/enqueuer.go | 22 + backend/pkg/worker/option.go | 54 + backend/pkg/worker/worker.go | 22 + backend/services/auth/cmd/root.go | 30 + backend/services/auth/cmd/server.go | 66 + .../services/auth/config/config.example.yml | 34 + backend/services/auth/deployment/.keep | 0 backend/services/auth/docs/.keep | 0 backend/services/auth/main.go | 13 + backend/services/auth/script/.keep | 0 .../services/auth/web/core/adapter/memory.go | 69 + .../auth/web/core/adapter/memory_test.go | 45 + .../services/auth/web/core/adapter/mongo.go | 129 + .../auth/web/core/adapter/mongo_test.go | 107 + .../services/auth/web/core/domain/error.go | 13 + backend/services/auth/web/core/domain/user.go | 88 + backend/services/auth/web/core/port/input.go | 14 + backend/services/auth/web/core/port/output.go | 14 + .../services/auth/web/core/service/error.go | 12 + .../services/auth/web/core/service/user.go | 229 + .../auth/web/core/service/user_test.go | 106 + backend/services/auth/web/handler/delete.go | 32 + backend/services/auth/web/handler/select.go | 84 + .../services/auth/web/handler/select_test.go | 48 + backend/services/auth/web/message/error.go | 5 + backend/services/auth/web/message/insert.go | 30 + backend/services/auth/web/server.go | 55 + backend/services/builder/cmd/root.go | 30 + backend/services/builder/cmd/server.go | 68 + .../builder/config/config.example.yml | 35 + backend/services/builder/deployment/.keep | 0 backend/services/builder/docs/.keep | 0 backend/services/builder/main.go | 13 + backend/services/builder/script/.keep | 0 backend/services/builder/web/handler/build.go | 210 + backend/services/builder/web/handler/error.go | 7 + backend/services/builder/web/server.go | 45 + backend/services/callback/cmd/root.go | 30 + backend/services/callback/cmd/server.go | 68 + .../callback/config/config.example.yml | 37 + backend/services/callback/deployment/.keep | 0 backend/services/callback/docs/README.md | 0 backend/services/callback/main.go | 13 + backend/services/callback/scripts/Makefile | 0 .../callback/web/controller/callback.go | 155 + backend/services/callback/web/server.go | 84 + .../services/callback/web/worker/callback.go | 132 + backend/services/gateway/cmd/root.go | 30 + backend/services/gateway/cmd/server.go | 68 + .../gateway/config/config.example.yml | 35 + backend/services/gateway/deployment/.keep | 0 backend/services/gateway/docs/README.md | 0 backend/services/gateway/main.go | 13 + backend/services/gateway/scripts/Makefile | 0 .../services/gateway/web/controller/api.go | 379 + .../services/gateway/web/controller/auth.go | 134 + .../services/gateway/web/middleware/auth.go | 37 + .../gateway/web/middleware/context.go | 37 + backend/services/gateway/web/server.go | 109 + backend/services/settings/cmd/root.go | 30 + backend/services/settings/cmd/server.go | 66 + .../settings/config/config.example.yml | 31 + backend/services/settings/deployment/.keep | 0 backend/services/settings/docs/.keep | 0 backend/services/settings/main.go | 13 + backend/services/settings/script/.keep | 0 .../settings/web/core/adapter/memory.go | 71 + .../settings/web/core/adapter/memory_test.go | 46 + .../settings/web/core/adapter/mongo.go | 114 + .../settings/web/core/adapter/mongo_test.go | 92 + .../settings/web/core/domain/error.go | 13 + .../settings/web/core/domain/settings.go | 63 + .../services/settings/web/core/port/input.go | 14 + .../services/settings/web/core/port/output.go | 14 + .../settings/web/core/service/error.go | 12 + .../settings/web/core/service/settings.go | 122 + .../services/settings/web/handler/select.go | 54 + .../settings/web/handler/select_test.go | 43 + .../services/settings/web/message/delete.go | 27 + .../services/settings/web/message/error.go | 5 + .../services/settings/web/message/insert.go | 36 + backend/services/settings/web/server.go | 58 + backend/services/shared/client/api.go | 159 + backend/services/shared/client/auth.go | 105 + backend/services/shared/client/command.go | 76 + backend/services/shared/client/error.go | 17 + .../services/shared/client/model/access.go | 7 + backend/services/shared/client/model/error.go | 5 + backend/services/shared/client/model/lang.go | 18 + backend/services/shared/client/model/token.go | 46 + backend/services/shared/client/model/user.go | 27 + backend/services/shared/config.go | 73 + backend/services/shared/constants/file.go | 88 + backend/services/shared/constants/session.go | 7 + backend/services/shared/crypto/aes.go | 77 + backend/services/shared/crypto/aes_test.go | 49 + backend/services/shared/crypto/jwt.go | 75 + backend/services/shared/error.go | 14 + backend/services/shared/message/job.go | 16 + backend/services/shared/request/callback.go | 65 + backend/services/shared/request/command.go | 26 + backend/services/shared/request/config.go | 18 + backend/services/shared/request/session.go | 13 + backend/services/shared/request/settings.go | 42 + backend/services/shared/request/token.go | 20 + backend/services/shared/request/uninstall.go | 15 + backend/services/shared/response/callback.go | 12 + backend/services/shared/response/command.go | 5 + backend/services/shared/response/config.go | 67 + backend/services/shared/response/error.go | 15 + backend/services/shared/response/generic.go | 13 + backend/services/shared/response/settings.go | 13 + backend/services/shared/response/user.go | 29 + frontend/.eslintignore | 9 + frontend/.eslintrc.js | 66 + frontend/.vscode/settings.json | 8 + frontend/jest.config.js | 25 + frontend/package.json | 102 + frontend/postcss.config.js | 3 + frontend/src/App.tsx | 76 + frontend/src/__mocks__/fileMock.ts | 1 + frontend/src/__mocks__/styleMock.ts | 1 + frontend/src/__tests__/__snapshots__/.keep | 0 frontend/src/assets/arrow-down.svg | 3 + frontend/src/assets/background-error.svg | 5 + frontend/src/assets/docx.svg | 10 + frontend/src/assets/favicon.ico | 0 frontend/src/assets/import.svg | 3 + frontend/src/assets/index.css | 76 + frontend/src/assets/nofile copy.svg | 37 + frontend/src/assets/nofile.svg | 37 + frontend/src/assets/pencil.svg | 3 + frontend/src/assets/pptx.svg | 15 + frontend/src/assets/shape.svg | 3 + frontend/src/assets/trash.svg | 3 + frontend/src/assets/unsupported.svg | 14 + frontend/src/assets/xlsx.svg | 7 + frontend/src/components/button/Button.tsx | 38 + frontend/src/components/button/index.ts | 1 + frontend/src/components/divider/Divider.tsx | 13 + frontend/src/components/divider/index.ts | 1 + frontend/src/components/drop/Drop.tsx | 152 + frontend/src/components/drop/index.ts | 1 + frontend/src/components/error/Error.tsx | 11 + frontend/src/components/error/index.ts | 1 + frontend/src/components/file/File.tsx | 64 + frontend/src/components/file/index.ts | 1 + frontend/src/components/header/Header.tsx | 22 + frontend/src/components/header/index.ts | 1 + frontend/src/components/info/Info.tsx | 28 + frontend/src/components/info/index.ts | 1 + frontend/src/components/input/Input.tsx | 64 + frontend/src/components/input/index.ts | 1 + frontend/src/components/nofile/NoFile.tsx | 16 + frontend/src/components/nofile/index.ts | 1 + frontend/src/components/search/Search.tsx | 46 + frontend/src/components/search/index.ts | 1 + frontend/src/components/spinner/Spinner.tsx | 3 + frontend/src/components/spinner/index.ts | 1 + frontend/src/components/tile/Tile.tsx | 56 + frontend/src/components/tile/index.ts | 1 + frontend/src/components/title/Subtitle.tsx | 20 + frontend/src/components/title/Title.tsx | 20 + frontend/src/components/title/index.ts | 2 + frontend/src/context/PipedriveContext.tsx | 4 + frontend/src/context/TokenContext.tsx | 42 + frontend/src/custom.d.ts | 4 + frontend/src/hooks/useBuildConfig.tsx | 24 + frontend/src/hooks/useDeleteFile.tsx | 14 + frontend/src/hooks/useFileSearch.tsx | 39 + frontend/src/i18n.ts | 16 + frontend/src/index.tsx | 25 + frontend/src/layout/ErrorBackground.tsx | 36 + frontend/src/lib/.keep | 0 frontend/src/pages/Creation/Creation.tsx | 121 + frontend/src/pages/Creation/Upload.tsx | 54 + frontend/src/pages/Creation/index.tsx | 63 + frontend/src/pages/Editor/index.tsx | 141 + frontend/src/pages/Main/Actions.tsx | 80 + frontend/src/pages/Main/Main.tsx | 128 + frontend/src/pages/Main/index.tsx | 32 + frontend/src/pages/Settings/index.tsx | 140 + frontend/src/public/index.html | 16 + frontend/src/services/config.ts | 37 + frontend/src/services/file.ts | 68 + frontend/src/services/me.ts | 36 + frontend/src/services/settings.ts | 52 + frontend/src/setupTests.ts | 1 + frontend/src/types/config.ts | 52 + frontend/src/types/file.ts | 26 + frontend/src/types/settings.ts | 4 + frontend/src/types/user.ts | 19 + frontend/src/utils/file.ts | 139 + frontend/src/utils/url.ts | 9 + frontend/tailwind.config.js | 67 + frontend/tsconfig.json | 29 + frontend/tsconfig.paths.json | 15 + frontend/webpack.common.js | 59 + frontend/webpack.development.js | 27 + frontend/webpack.production.js | 14 + frontend/yarn.lock | 7757 +++++++++++++++++ 246 files changed, 19240 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 README.md create mode 100644 backend/go.mod create mode 100644 backend/go.sum create mode 100644 backend/pkg/bootstrap.go create mode 100644 backend/pkg/cache/cache.go create mode 100644 backend/pkg/cache/memory.go create mode 100644 backend/pkg/cache/redis.go create mode 100644 backend/pkg/config/cache.go create mode 100644 backend/pkg/config/cors.go create mode 100644 backend/pkg/config/credentials.go create mode 100644 backend/pkg/config/error.go create mode 100644 backend/pkg/config/logger.go create mode 100644 backend/pkg/config/messaging.go create mode 100644 backend/pkg/config/persistence.go create mode 100644 backend/pkg/config/registry.go create mode 100644 backend/pkg/config/resilience.go create mode 100644 backend/pkg/config/server.go create mode 100644 backend/pkg/config/trace.go create mode 100644 backend/pkg/config/worker.go create mode 100644 backend/pkg/log/error.go create mode 100644 backend/pkg/log/hook/elastic.go create mode 100644 backend/pkg/log/logger.go create mode 100644 backend/pkg/log/logrus.go create mode 100644 backend/pkg/messaging/broker.go create mode 100644 backend/pkg/middleware/cache.go create mode 100644 backend/pkg/middleware/cors.go create mode 100644 backend/pkg/middleware/log.go create mode 100644 backend/pkg/middleware/rate.go create mode 100644 backend/pkg/middleware/secure.go create mode 100644 backend/pkg/middleware/timeout.go create mode 100644 backend/pkg/middleware/trace.go create mode 100644 backend/pkg/middleware/version.go create mode 100644 backend/pkg/middleware/wrapper/trace.go create mode 100644 backend/pkg/registry/registry.go create mode 100644 backend/pkg/resilience/circuit.go create mode 100644 backend/pkg/service/http/service.go create mode 100644 backend/pkg/service/repl/service.go create mode 100644 backend/pkg/service/rpc/service.go create mode 100644 backend/pkg/shared/env.go create mode 100644 backend/pkg/trace/error.go create mode 100644 backend/pkg/trace/tracer.go create mode 100644 backend/pkg/trace/zipkin.go create mode 100644 backend/pkg/worker/asynq.go create mode 100644 backend/pkg/worker/enqueuer.go create mode 100644 backend/pkg/worker/option.go create mode 100644 backend/pkg/worker/worker.go create mode 100644 backend/services/auth/cmd/root.go create mode 100644 backend/services/auth/cmd/server.go create mode 100644 backend/services/auth/config/config.example.yml create mode 100644 backend/services/auth/deployment/.keep create mode 100644 backend/services/auth/docs/.keep create mode 100644 backend/services/auth/main.go create mode 100644 backend/services/auth/script/.keep create mode 100644 backend/services/auth/web/core/adapter/memory.go create mode 100644 backend/services/auth/web/core/adapter/memory_test.go create mode 100644 backend/services/auth/web/core/adapter/mongo.go create mode 100644 backend/services/auth/web/core/adapter/mongo_test.go create mode 100644 backend/services/auth/web/core/domain/error.go create mode 100644 backend/services/auth/web/core/domain/user.go create mode 100644 backend/services/auth/web/core/port/input.go create mode 100644 backend/services/auth/web/core/port/output.go create mode 100644 backend/services/auth/web/core/service/error.go create mode 100644 backend/services/auth/web/core/service/user.go create mode 100644 backend/services/auth/web/core/service/user_test.go create mode 100644 backend/services/auth/web/handler/delete.go create mode 100644 backend/services/auth/web/handler/select.go create mode 100644 backend/services/auth/web/handler/select_test.go create mode 100644 backend/services/auth/web/message/error.go create mode 100644 backend/services/auth/web/message/insert.go create mode 100644 backend/services/auth/web/server.go create mode 100644 backend/services/builder/cmd/root.go create mode 100644 backend/services/builder/cmd/server.go create mode 100644 backend/services/builder/config/config.example.yml create mode 100644 backend/services/builder/deployment/.keep create mode 100644 backend/services/builder/docs/.keep create mode 100644 backend/services/builder/main.go create mode 100644 backend/services/builder/script/.keep create mode 100644 backend/services/builder/web/handler/build.go create mode 100644 backend/services/builder/web/handler/error.go create mode 100644 backend/services/builder/web/server.go create mode 100644 backend/services/callback/cmd/root.go create mode 100644 backend/services/callback/cmd/server.go create mode 100644 backend/services/callback/config/config.example.yml create mode 100644 backend/services/callback/deployment/.keep create mode 100644 backend/services/callback/docs/README.md create mode 100644 backend/services/callback/main.go create mode 100644 backend/services/callback/scripts/Makefile create mode 100644 backend/services/callback/web/controller/callback.go create mode 100644 backend/services/callback/web/server.go create mode 100644 backend/services/callback/web/worker/callback.go create mode 100644 backend/services/gateway/cmd/root.go create mode 100644 backend/services/gateway/cmd/server.go create mode 100644 backend/services/gateway/config/config.example.yml create mode 100644 backend/services/gateway/deployment/.keep create mode 100644 backend/services/gateway/docs/README.md create mode 100644 backend/services/gateway/main.go create mode 100644 backend/services/gateway/scripts/Makefile create mode 100644 backend/services/gateway/web/controller/api.go create mode 100644 backend/services/gateway/web/controller/auth.go create mode 100644 backend/services/gateway/web/middleware/auth.go create mode 100644 backend/services/gateway/web/middleware/context.go create mode 100644 backend/services/gateway/web/server.go create mode 100644 backend/services/settings/cmd/root.go create mode 100644 backend/services/settings/cmd/server.go create mode 100644 backend/services/settings/config/config.example.yml create mode 100644 backend/services/settings/deployment/.keep create mode 100644 backend/services/settings/docs/.keep create mode 100644 backend/services/settings/main.go create mode 100644 backend/services/settings/script/.keep create mode 100644 backend/services/settings/web/core/adapter/memory.go create mode 100644 backend/services/settings/web/core/adapter/memory_test.go create mode 100644 backend/services/settings/web/core/adapter/mongo.go create mode 100644 backend/services/settings/web/core/adapter/mongo_test.go create mode 100644 backend/services/settings/web/core/domain/error.go create mode 100644 backend/services/settings/web/core/domain/settings.go create mode 100644 backend/services/settings/web/core/port/input.go create mode 100644 backend/services/settings/web/core/port/output.go create mode 100644 backend/services/settings/web/core/service/error.go create mode 100644 backend/services/settings/web/core/service/settings.go create mode 100644 backend/services/settings/web/handler/select.go create mode 100644 backend/services/settings/web/handler/select_test.go create mode 100644 backend/services/settings/web/message/delete.go create mode 100644 backend/services/settings/web/message/error.go create mode 100644 backend/services/settings/web/message/insert.go create mode 100644 backend/services/settings/web/server.go create mode 100644 backend/services/shared/client/api.go create mode 100644 backend/services/shared/client/auth.go create mode 100644 backend/services/shared/client/command.go create mode 100644 backend/services/shared/client/error.go create mode 100644 backend/services/shared/client/model/access.go create mode 100644 backend/services/shared/client/model/error.go create mode 100644 backend/services/shared/client/model/lang.go create mode 100644 backend/services/shared/client/model/token.go create mode 100644 backend/services/shared/client/model/user.go create mode 100644 backend/services/shared/config.go create mode 100644 backend/services/shared/constants/file.go create mode 100644 backend/services/shared/constants/session.go create mode 100644 backend/services/shared/crypto/aes.go create mode 100644 backend/services/shared/crypto/aes_test.go create mode 100644 backend/services/shared/crypto/jwt.go create mode 100644 backend/services/shared/error.go create mode 100644 backend/services/shared/message/job.go create mode 100644 backend/services/shared/request/callback.go create mode 100644 backend/services/shared/request/command.go create mode 100644 backend/services/shared/request/config.go create mode 100644 backend/services/shared/request/session.go create mode 100644 backend/services/shared/request/settings.go create mode 100644 backend/services/shared/request/token.go create mode 100644 backend/services/shared/request/uninstall.go create mode 100644 backend/services/shared/response/callback.go create mode 100644 backend/services/shared/response/command.go create mode 100644 backend/services/shared/response/config.go create mode 100644 backend/services/shared/response/error.go create mode 100644 backend/services/shared/response/generic.go create mode 100644 backend/services/shared/response/settings.go create mode 100644 backend/services/shared/response/user.go create mode 100644 frontend/.eslintignore create mode 100644 frontend/.eslintrc.js create mode 100644 frontend/.vscode/settings.json create mode 100644 frontend/jest.config.js create mode 100644 frontend/package.json create mode 100644 frontend/postcss.config.js create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/__mocks__/fileMock.ts create mode 100644 frontend/src/__mocks__/styleMock.ts create mode 100644 frontend/src/__tests__/__snapshots__/.keep create mode 100644 frontend/src/assets/arrow-down.svg create mode 100644 frontend/src/assets/background-error.svg create mode 100644 frontend/src/assets/docx.svg create mode 100644 frontend/src/assets/favicon.ico create mode 100644 frontend/src/assets/import.svg create mode 100644 frontend/src/assets/index.css create mode 100644 frontend/src/assets/nofile copy.svg create mode 100644 frontend/src/assets/nofile.svg create mode 100644 frontend/src/assets/pencil.svg create mode 100644 frontend/src/assets/pptx.svg create mode 100644 frontend/src/assets/shape.svg create mode 100644 frontend/src/assets/trash.svg create mode 100644 frontend/src/assets/unsupported.svg create mode 100644 frontend/src/assets/xlsx.svg create mode 100644 frontend/src/components/button/Button.tsx create mode 100644 frontend/src/components/button/index.ts create mode 100644 frontend/src/components/divider/Divider.tsx create mode 100644 frontend/src/components/divider/index.ts create mode 100644 frontend/src/components/drop/Drop.tsx create mode 100644 frontend/src/components/drop/index.ts create mode 100644 frontend/src/components/error/Error.tsx create mode 100644 frontend/src/components/error/index.ts create mode 100644 frontend/src/components/file/File.tsx create mode 100644 frontend/src/components/file/index.ts create mode 100644 frontend/src/components/header/Header.tsx create mode 100644 frontend/src/components/header/index.ts create mode 100644 frontend/src/components/info/Info.tsx create mode 100644 frontend/src/components/info/index.ts create mode 100644 frontend/src/components/input/Input.tsx create mode 100644 frontend/src/components/input/index.ts create mode 100644 frontend/src/components/nofile/NoFile.tsx create mode 100644 frontend/src/components/nofile/index.ts create mode 100644 frontend/src/components/search/Search.tsx create mode 100644 frontend/src/components/search/index.ts create mode 100644 frontend/src/components/spinner/Spinner.tsx create mode 100644 frontend/src/components/spinner/index.ts create mode 100644 frontend/src/components/tile/Tile.tsx create mode 100644 frontend/src/components/tile/index.ts create mode 100644 frontend/src/components/title/Subtitle.tsx create mode 100644 frontend/src/components/title/Title.tsx create mode 100644 frontend/src/components/title/index.ts create mode 100644 frontend/src/context/PipedriveContext.tsx create mode 100644 frontend/src/context/TokenContext.tsx create mode 100644 frontend/src/custom.d.ts create mode 100644 frontend/src/hooks/useBuildConfig.tsx create mode 100644 frontend/src/hooks/useDeleteFile.tsx create mode 100644 frontend/src/hooks/useFileSearch.tsx create mode 100644 frontend/src/i18n.ts create mode 100644 frontend/src/index.tsx create mode 100644 frontend/src/layout/ErrorBackground.tsx create mode 100644 frontend/src/lib/.keep create mode 100644 frontend/src/pages/Creation/Creation.tsx create mode 100644 frontend/src/pages/Creation/Upload.tsx create mode 100644 frontend/src/pages/Creation/index.tsx create mode 100644 frontend/src/pages/Editor/index.tsx create mode 100644 frontend/src/pages/Main/Actions.tsx create mode 100644 frontend/src/pages/Main/Main.tsx create mode 100644 frontend/src/pages/Main/index.tsx create mode 100644 frontend/src/pages/Settings/index.tsx create mode 100644 frontend/src/public/index.html create mode 100644 frontend/src/services/config.ts create mode 100644 frontend/src/services/file.ts create mode 100644 frontend/src/services/me.ts create mode 100644 frontend/src/services/settings.ts create mode 100644 frontend/src/setupTests.ts create mode 100644 frontend/src/types/config.ts create mode 100644 frontend/src/types/file.ts create mode 100644 frontend/src/types/settings.ts create mode 100644 frontend/src/types/user.ts create mode 100644 frontend/src/utils/file.ts create mode 100644 frontend/src/utils/url.ts create mode 100644 frontend/tailwind.config.js create mode 100644 frontend/tsconfig.json create mode 100644 frontend/tsconfig.paths.json create mode 100644 frontend/webpack.common.js create mode 100644 frontend/webpack.development.js create mode 100644 frontend/webpack.production.js create mode 100644 frontend/yarn.lock diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a8b35fc --- /dev/null +++ b/.editorconfig @@ -0,0 +1,25 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true + +[*.go] +indent_style = tab + +[*.{js, ts, json, html}] +indent_style = space +indent_size = 4 + +[frontend/package.json] +indent_size = 2 + +[{Makefile, *.mk}] +indent_style = tab + +[*.md] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e30f156 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# Logs +_log +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Dependency directories +node_modules/ +bower_components/ +jspm_packages/ + +# Coverage directories +coverage/ + +# Build output +build/ +dist/ + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# Miscellaneous +.DS_Store + +# Env +.env +.env.production + +# Backend config +config.yml diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/backend/go.mod b/backend/go.mod new file mode 100644 index 0000000..921cc6f --- /dev/null +++ b/backend/go.mod @@ -0,0 +1,176 @@ +module github.com/ONLYOFFICE/onlyoffice-pipedrive + +go 1.19 + +require ( + github.com/gin-gonic/gin v1.8.2 + github.com/go-chi/chi/v5 v5.0.8 + github.com/go-chi/cors v1.2.1 + github.com/go-micro/plugins/v4/broker/nats v1.2.0 + github.com/go-micro/plugins/v4/broker/rabbitmq v1.2.1 + github.com/go-micro/plugins/v4/registry/consul v1.2.0 + github.com/go-micro/plugins/v4/registry/etcd v1.2.0 + github.com/go-micro/plugins/v4/registry/kubernetes v1.1.1 + github.com/go-micro/plugins/v4/registry/mdns v1.2.0 + github.com/go-micro/plugins/v4/server/http v1.2.1 + github.com/go-micro/plugins/v4/wrapper/breaker/hystrix v1.2.0 + github.com/go-micro/plugins/v4/wrapper/ratelimiter/uber v1.2.0 + github.com/go-micro/plugins/v4/wrapper/select/roundrobin v1.2.0 + github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0 + github.com/google/uuid v1.3.0 + github.com/gorilla/sessions v1.2.1 + github.com/hellofresh/health-go/v5 v5.0.0 + github.com/justinas/alice v1.2.0 + github.com/mitchellh/mapstructure v1.3.3 + github.com/olivere/elastic/v7 v7.0.32 + github.com/prometheus/client_golang v1.14.0 + github.com/sethvargo/go-envconfig v0.8.3 + github.com/sethvargo/go-limiter v0.7.2 + github.com/stretchr/testify v1.8.1 + github.com/urfave/cli/v2 v2.17.1 + go-micro.dev/v4 v4.9.0 + go.mongodb.org/mongo-driver v1.9.1 + go.opentelemetry.io/otel v1.12.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.12.0 + go.opentelemetry.io/otel/exporters/zipkin v1.12.0 + go.opentelemetry.io/otel/sdk v1.12.0 + go.uber.org/fx v1.19.2 + go.uber.org/ratelimit v0.2.0 + golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/Microsoft/go-winio v0.6.0 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad // indirect + github.com/acomagu/bufpipe v1.0.3 // indirect + github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 // indirect + github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect + github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bitly/go-simplejson v0.5.0 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cloudflare/circl v1.2.0 // indirect + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.3.2 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/fatih/color v1.9.0 // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-git/gcfg v1.5.0 // indirect + github.com/go-git/go-billy/v5 v5.3.1 // indirect + github.com/go-git/go-git/v5 v5.4.2 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/go-playground/validator/v10 v10.11.1 // indirect + github.com/go-stack/stack v1.8.0 // indirect + github.com/goccy/go-json v0.9.11 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/gopherjs/gopherjs v1.17.2 // indirect + github.com/gorilla/securecookie v1.1.1 // indirect + github.com/hashicorp/consul/api v1.9.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.1 // indirect + github.com/hashicorp/go-hclog v0.12.0 // indirect + github.com/hashicorp/go-immutable-radix v1.0.0 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/golang-lru v0.5.1 // indirect + github.com/hashicorp/serf v0.9.5 // indirect + github.com/imdario/mergo v0.3.13 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/jtolds/gls v4.20.0+incompatible // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/klauspost/compress v1.15.11 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/miekg/dns v1.1.50 // indirect + github.com/minio/highwayhash v1.0.2 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/hashstructure v1.1.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/nats-io/jwt/v2 v2.3.0 // indirect + github.com/nats-io/nats.go v1.16.0 // indirect + github.com/nats-io/nkeys v0.3.0 // indirect + github.com/nats-io/nuid v1.0.1 // indirect + github.com/nxadm/tail v1.4.8 // indirect + github.com/onsi/gomega v1.20.0 // indirect + github.com/openzipkin/zipkin-go v0.4.1 // indirect + github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sergi/go-diff v1.2.0 // indirect + github.com/spf13/cast v1.3.1 // indirect + github.com/streadway/amqp v1.0.0 // indirect + github.com/ugorji/go/codec v1.2.7 // indirect + github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect + github.com/xanzy/ssh-agent v0.3.2 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.0.2 // indirect + github.com/xdg-go/stringprep v1.0.2 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect + go.etcd.io/etcd/api/v3 v3.5.2 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.2 // indirect + go.etcd.io/etcd/client/v3 v3.5.2 // indirect + go.opentelemetry.io/otel/metric v0.35.0 // indirect + go.opentelemetry.io/otel/trace v1.12.0 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/dig v1.16.1 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.23.0 // indirect + golang.org/x/crypto v0.6.0 // indirect + golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 // indirect + golang.org/x/mod v0.6.0 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.2.0 // indirect + google.golang.org/appengine v1.6.6 // indirect + google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e // indirect + google.golang.org/grpc v1.50.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect + gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +require ( + github.com/coocood/freecache v1.2.3 + github.com/eko/gocache/lib/v4 v4.1.3 + github.com/eko/gocache/store/freecache/v4 v4.1.2 + github.com/eko/gocache/store/redis/v4 v4.1.2 + github.com/go-micro/plugins/v4/broker/memory v1.2.0 + github.com/go-redis/redis/v8 v8.11.5 + github.com/go-resty/resty/v2 v2.7.0 + github.com/golang-jwt/jwt v3.2.2+incompatible + github.com/hibiken/asynq v0.24.0 + github.com/josharian/intern v1.0.0 // indirect + github.com/kamva/mgm/v3 v3.5.0 + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mileusna/useragent v1.2.1 + github.com/natefinch/lumberjack v2.0.0+incompatible + github.com/pkg/errors v0.9.1 // indirect + github.com/sirupsen/logrus v1.9.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.38.0 +) diff --git a/backend/go.sum b/backend/go.sum new file mode 100644 index 0000000..484b35d --- /dev/null +++ b/backend/go.sum @@ -0,0 +1,1007 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= +github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad h1:QeeqI2zxxgZVe11UrYFXXx6gVxPVF40ygekjBzEg4XY= +github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= +github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= +github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= +github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cloudflare/circl v1.2.0 h1:NheeISPSUcYftKlfrLuOo4T62FkmD4t4jviLfFFYaec= +github.com/cloudflare/circl v1.2.0/go.mod h1:Ch2UgYr6ti2KTtlejELlROl0YIYj7SLjAC8M+INXlMk= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/coocood/freecache v1.2.3 h1:lcBwpZrwBZRZyLk/8EMyQVXRiFl663cCuMOrjCALeto= +github.com/coocood/freecache v1.2.3/go.mod h1:RBUWa/Cy+OHdfTGFEhEuE1pMCMX51Ncizj7rthiQ3vk= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eko/gocache/lib/v4 v4.1.3 h1:i7xVxWWhAESLAcHgp73piJtST63TS7ThFD7LE78vmXc= +github.com/eko/gocache/lib/v4 v4.1.3/go.mod h1:7uSUlaDeRgr17IYCV8kSluhRDXq4SGutWWxTpxpwW3w= +github.com/eko/gocache/store/freecache/v4 v4.1.2 h1:lWSmt0EVtCx6GY+i4oRYuPjgpRaH5RZgBykl1czaogU= +github.com/eko/gocache/store/freecache/v4 v4.1.2/go.mod h1:UFvuSJgLkftoBEqOc7g/8UoDeW+3aQj6IQvvG+Cl5Js= +github.com/eko/gocache/store/redis/v4 v4.1.2 h1:PtpjQu8Q698kpC3H06As5As15s+hJofV8NtkddVX5aQ= +github.com/eko/gocache/store/redis/v4 v4.1.2/go.mod h1:P2HeqvNwqkdwajYro+qwBWquhdYpN0LgfH2rJ3jK7yg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch/v5 v5.5.0 h1:bAmFiUJ+o0o2B4OiTFeE3MqCOtyo+jjPP9iZ0VRxYUc= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.8.2 h1:UzKToD9/PoFj/V4rvlKqTRKnQYyz8Sc1MJlv4JHPtvY= +github.com/gin-gonic/gin v1.8.2/go.mod h1:qw5AYuDrzRTnhvusDsrov+fDIxp9Dleuu12h8nfB398= +github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-acme/lego/v4 v4.4.0 h1:uHhU5LpOYQOdp3aDU+XY2bajseu8fuExphTL1Ss6/Fc= +github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= +github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= +github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= +github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34= +github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8= +github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= +github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4= +github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-micro/plugins/v4/broker/memory v1.2.0 h1:T4jWRCSyiPzMF3ek2+3FY6yJTSLjoIvE9S9H61OKs1o= +github.com/go-micro/plugins/v4/broker/memory v1.2.0/go.mod h1:xjWD6VTJUgDOF8d1lmjUmT8RuybEwLqr3rbUm8S4ARQ= +github.com/go-micro/plugins/v4/broker/nats v1.2.0 h1:uI9ksw/YrG1dJWMHnG/1bqodqUr3OvsISyjWGjiBE6I= +github.com/go-micro/plugins/v4/broker/nats v1.2.0/go.mod h1:HKZKcWPJQPCA2CA/WvDtko0aXz2U5xywZYzCgGn8hiY= +github.com/go-micro/plugins/v4/broker/rabbitmq v1.2.1 h1:w5JMsPcy5GYQ6H7mS29M1aaA0zE3VtBCuYGBk7t92+s= +github.com/go-micro/plugins/v4/broker/rabbitmq v1.2.1/go.mod h1:nPBTTlkdxUGd5zTcwOzeLc4hda1LGty87zBZTBjEmgM= +github.com/go-micro/plugins/v4/registry/consul v1.2.0 h1:nqrTzfWUTWKAy+M+i2FazbHnQn4m77EOtoch57kbCH4= +github.com/go-micro/plugins/v4/registry/consul v1.2.0/go.mod h1:wTat7/K9XQ+i64VbbcMYFcEwipYfSgJM51HcA/sgsM4= +github.com/go-micro/plugins/v4/registry/etcd v1.2.0 h1:tcHlU1GzvX3oZa8WQH8ylMCGie5qD5g98YWTESJjeqQ= +github.com/go-micro/plugins/v4/registry/etcd v1.2.0/go.mod h1:CQeTHkjN3xMtIQsynaTTquMz2sHEdsTfRIfFzrX7aug= +github.com/go-micro/plugins/v4/registry/kubernetes v1.1.1 h1:nzwyyK2JKJtmJ8w0CRn9zsn4+kwJitx8CYfAWH99eiI= +github.com/go-micro/plugins/v4/registry/kubernetes v1.1.1/go.mod h1:u78+qWLUq8jxu/CF4UW+1UUtNgBz67x27ar2kV5Dd/o= +github.com/go-micro/plugins/v4/registry/mdns v1.2.0 h1:BsGnco+PgycvSX+HS0XbeUQEPoPT3a+dDiHWV6dbVDs= +github.com/go-micro/plugins/v4/registry/mdns v1.2.0/go.mod h1:re0JvO5F56n59WEDaAKj2jtboKa2dklAd6iWyz5xa54= +github.com/go-micro/plugins/v4/server/http v1.2.1 h1:Cia924J90rgFT/4qWWvyLvN+XqEm5T9tiQyQ+GU4bOQ= +github.com/go-micro/plugins/v4/server/http v1.2.1/go.mod h1:YuAjaSPxcn3LI8j2FUsqx0Rxunrj4YwDV41Ax76rLl0= +github.com/go-micro/plugins/v4/wrapper/breaker/hystrix v1.2.0 h1:0GYwyoHJ3kfJHYEuzSUH+033Kg924Y6aBSJYbpHAKNg= +github.com/go-micro/plugins/v4/wrapper/breaker/hystrix v1.2.0/go.mod h1:Ii9bwBo2ADZfQJ8zGMQGh6nNN+8OEBU4VvDbrJjwbsE= +github.com/go-micro/plugins/v4/wrapper/ratelimiter/uber v1.2.0 h1:VaiAfA2F0tS0ORn0CVSY3UxWH7jjUkWiL7T7KBa7JoM= +github.com/go-micro/plugins/v4/wrapper/ratelimiter/uber v1.2.0/go.mod h1:66L6PV2/J/qh+YNgOIpmduyuOZCvYwEKDrSknsJMzDI= +github.com/go-micro/plugins/v4/wrapper/select/roundrobin v1.2.0 h1:/CsAjj+LkOb4vj88W9akq5C8NCB5UEsOZ6Eefogk7uc= +github.com/go-micro/plugins/v4/wrapper/select/roundrobin v1.2.0/go.mod h1:t2+wVxyngajrYziHkqX2CLycp1ge9el8mRphRHcg3+w= +github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0 h1:e2hgtWMNqJ3DmbMt9ZxzmH/BkVAw9Xg23l6CHrXQfKw= +github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0/go.mod h1:BBqL7ckGNb7rFfk3vU2Yj/CILVsz/WF19CkAyveQl8A= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-redis/redis/v8 v8.11.2/go.mod h1:DLomh7y2e3ggQXQLd1YgmvIfecPJoFl7WU5SOQ/r06M= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs= +github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.9.0 h1:T6dKIWcaihG2c21YUi0BMAHbJanVXiYuz+mPgqxY3N4= +github.com/hashicorp/consul/api v1.9.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= +github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU= +github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.12.0 h1:d4QkX8FRTYaKaCZBoXYY8zJX2BXjWxurN/GA2tkrmZM= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= +github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g= +github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM= +github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hellofresh/health-go/v5 v5.0.0 h1:jxjllHekqEU4VYIajKJtFoOxDp1YaaygNWwAoZwWFh0= +github.com/hellofresh/health-go/v5 v5.0.0/go.mod h1:9hFVIBdKkxrg1bJurUPlw1D/0FWhl47IVfGYPy4Op9o= +github.com/hibiken/asynq v0.24.0 h1:r1CiSVYCy1vGq9REKGI/wdB2D5n/QmtzihYHHXOuBUs= +github.com/hibiken/asynq v0.24.0/go.mod h1:FVnRfUTm6gcoDkM/EjF4OIh5/06ergCPUO6pS2B2y+w= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= +github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= +github.com/kamva/mgm/v3 v3.5.0 h1:/2mNshpqwAC9spdzJZ0VR/UZ/SY/PsNTrMjT111KQjM= +github.com/kamva/mgm/v3 v3.5.0/go.mod h1:F4J1hZnXQMkqL3DZgR7Z7BOuiTqQG/JTic3YzliG4jk= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= +github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/mileusna/useragent v1.2.1 h1:p3RJWhi3LfuI6BHdddojREyK3p6qX67vIfOVMnUIVr0= +github.com/mileusna/useragent v1.2.1/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0= +github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI= +github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= +github.com/nats-io/nats-server/v2 v2.3.1 h1:tR8rp+ohGbqPGHROuzlvNzvw4G5TTeA5jUkxnSTEYPE= +github.com/nats-io/nats.go v1.16.0 h1:zvLE7fGBQYW6MWaFaRdsgm9qT39PJDQoju+DS8KsO1g= +github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= +github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E= +github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= +github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= +github.com/onsi/gomega v1.20.0/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/openzipkin/zipkin-go v0.4.1 h1:kNd/ST2yLLWhaWrkgchya40TJabe8Hioj9udfPcEO5A= +github.com/openzipkin/zipkin-go v0.4.1/go.mod h1:qY0VqDSN1pOBN94dBc6w2GJlWLiovAyg7Qt6/I9HecM= +github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= +github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/sethvargo/go-envconfig v0.8.3 h1:dXyUrDCJvCm3ybP7yNpiux93qoSORvuH23bdsgFfiJ0= +github.com/sethvargo/go-envconfig v0.8.3/go.mod h1:Iz1Gy1Sf3T64TQlJSvee81qDhf7YIlt8GMUX6yyNFs0= +github.com/sethvargo/go-limiter v0.7.2 h1:FgC4N7RMpV5gMrUdda15FaFTkQ/L4fEqM7seXMs4oO8= +github.com/sethvargo/go-limiter v0.7.2/go.mod h1:C0kbSFbiriE5k2FFOe18M1YZbAR2Fiwf72uGu0CXCcU= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfItdAd1r3cck= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= +github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/urfave/cli/v2 v2.17.1 h1:UzjDEw2dJQUE3iRaiNQ1VrVFbyAtKGH3VdkMoHA58V0= +github.com/urfave/cli/v2 v2.17.1/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= +github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= +github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/xanzy/ssh-agent v0.3.2 h1:eKj4SX2Fe7mui28ZgnFW5fmTz1EIr7ugo5s6wDxdHBM= +github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go-micro.dev/v4 v4.9.0 h1:pd1CpqMT9hA47jSmX8mfdGK865PkMh95Rwj5RdfqPqE= +go-micro.dev/v4 v4.9.0/go.mod h1:Ju8HrZ5hQSF+QguZ2QUs9Kbe42MHP1tJa/fpP5g07Cs= +go.etcd.io/etcd/api/v3 v3.5.2 h1:tXok5yLlKyuQ/SXSjtqHc4uzNaMqZi2XsoSPr/LlJXI= +go.etcd.io/etcd/api/v3 v3.5.2/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= +go.etcd.io/etcd/client/pkg/v3 v3.5.2 h1:4hzqQ6hIb3blLyQ8usCU4h3NghkqcsohEQ3o3VetYxE= +go.etcd.io/etcd/client/pkg/v3 v3.5.2/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v3 v3.5.2 h1:WdnejrUtQC4nCxK0/dLTMqKOB+U5TP/2Ya0BJL+1otA= +go.etcd.io/etcd/client/v3 v3.5.2/go.mod h1:kOOaWFFgHygyT0WlSmL8TJiXmMysO/nNUlEsSsN6W4o= +go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= +go.mongodb.org/mongo-driver v1.9.1 h1:m078y9v7sBItkt1aaoe2YlvWEXcD263e1a4E1fBrJ1c= +go.mongodb.org/mongo-driver v1.9.1/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.38.0 h1:rTxmym+VN9f6ajzNtITVgyvZrNbpLt3NHr3suLLHLEQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.38.0/go.mod h1:w6xNm+kC506KNs5cknSHal6dtdRnc4uema0uN9GSQc0= +go.opentelemetry.io/otel v1.12.0 h1:IgfC7kqQrRccIKuB7Cl+SRUmsKbEwSGPr0Eu+/ht1SQ= +go.opentelemetry.io/otel v1.12.0/go.mod h1:geaoz0L0r1BEOR81k7/n9W4TCXYCJ7bPO7K374jQHG0= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.12.0 h1:FXwvCIXsrMas/reQkSUTPZVCqud1yDy441acn4Fdu6w= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.12.0/go.mod h1:TTDkohPFSX4LMcbmNMYDh0hMXV8YigEjw5WwD+nDn6U= +go.opentelemetry.io/otel/exporters/zipkin v1.12.0 h1:0Pvqi4MAvQ28rGUFoH8uIaNKNCGVLNpn7TbRCkB1gP8= +go.opentelemetry.io/otel/exporters/zipkin v1.12.0/go.mod h1:zrxX/glbLVD/EFi01JCn2W1KdWNrJb3FkBHF+oRPlXs= +go.opentelemetry.io/otel/metric v0.35.0 h1:aPT5jk/w7F9zW51L7WgRqNKDElBdyRLGuBtI5MX34e8= +go.opentelemetry.io/otel/metric v0.35.0/go.mod h1:qAcbhaTRFU6uG8QM7dDo7XvFsWcugziq/5YI065TokQ= +go.opentelemetry.io/otel/sdk v1.12.0 h1:8npliVYV7qc0t1FKdpU08eMnOjgPFMnriPhn0HH4q3o= +go.opentelemetry.io/otel/sdk v1.12.0/go.mod h1:WYcvtgquYvgODEvxOry5owO2y9MyciW7JqMz6cpXShE= +go.opentelemetry.io/otel/trace v1.12.0 h1:p28in++7Kd0r2d8gSt931O57fdjUyWxkVbESuILAeUc= +go.opentelemetry.io/otel/trace v1.12.0/go.mod h1:pHlgBynn6s25qJ2szD+Bv+iwKJttjHSI3lUAyf0GNuQ= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/dig v1.16.1 h1:+alNIBsl0qfY0j6epRubp/9obgtrObRAc5aD+6jbWY8= +go.uber.org/dig v1.16.1/go.mod h1:557JTAUZT5bUK0SvCwikmLPPtdQhfvLYtO5tJgQSbnk= +go.uber.org/fx v1.19.2 h1:SyFgYQFr1Wl0AYstE8vyYIzP4bFz2URrScjwC4cwUvY= +go.uber.org/fx v1.19.2/go.mod h1:43G1VcqSzbIv77y00p1DRAsyZS8WdzuYdhZXmEUkMyQ= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA= +go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 h1:yZNXmy+j/JpX19vZkVktWqAo7Gny4PBWYYK3zskGpx4= +golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 h1:cu5kTvlzcw1Q5S9f5ip1/cpiB4nXvw1XYzFPGgzLUOY= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e h1:halCgTFuLWDRD61piiNSxPsARANGD3Xl16hPrLgLiIg= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.50.0 h1:fPVVDxY9w++VjTZsYvXWqEf9Rqar/e+9zYfxKK+W+YU= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/backend/pkg/bootstrap.go b/backend/pkg/bootstrap.go new file mode 100644 index 0000000..963d306 --- /dev/null +++ b/backend/pkg/bootstrap.go @@ -0,0 +1,38 @@ +package pkg + +import ( + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/cache" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/messaging" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/registry" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/repl" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/trace" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/worker" + "go.uber.org/fx" +) + +func Bootstrap(path string) fx.Option { + return fx.Module( + "pkg", + fx.Provide(config.BuildNewCacheConfig(path)), + fx.Provide(config.BuildNewCorsConfig(path)), + fx.Provide(config.BuildNewCredentialsConfig(path)), + fx.Provide(config.BuildNewLoggerConfig(path)), + fx.Provide(config.BuildNewMessagingConfig(path)), + fx.Provide(config.BuildNewPersistenceConfig(path)), + fx.Provide(config.BuildNewRegistryConfig(path)), + fx.Provide(config.BuildNewResilienceConfig(path)), + fx.Provide(config.BuildNewServerConfig(path)), + fx.Provide(config.BuildNewTracerConfig(path)), + fx.Provide(config.BuildNewWorkerConfig(path)), + fx.Provide(cache.NewCache), + fx.Provide(log.NewLogrusLogger), + fx.Provide(registry.NewRegistry), + fx.Provide(messaging.NewBroker), + fx.Provide(trace.NewTracer), + fx.Provide(worker.NewBackgroundWorker), + fx.Provide(worker.NewBackgroundEnqueuer), + fx.Provide(repl.NewService), + ) +} diff --git a/backend/pkg/cache/cache.go b/backend/pkg/cache/cache.go new file mode 100644 index 0000000..4f87f1f --- /dev/null +++ b/backend/pkg/cache/cache.go @@ -0,0 +1,54 @@ +package cache + +import ( + "context" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + "github.com/eko/gocache/lib/v4/marshaler" + "github.com/eko/gocache/lib/v4/store" + "go-micro.dev/v4/cache" +) + +type CustomCache struct { + store *marshaler.Marshaler + name string +} + +func (c *CustomCache) Get(ctx context.Context, key string) (interface{}, time.Time, error) { + var result interface{} + _, err := c.store.Get(ctx, key, &result) + return result, time.Now(), err +} + +func (c *CustomCache) Put(ctx context.Context, key string, val interface{}, d time.Duration) error { + return c.store.Set(ctx, key, val, store.WithExpiration(d)) +} + +func (c *CustomCache) Delete(ctx context.Context, key string) error { + return c.store.Delete(ctx, key) +} + +func (c *CustomCache) String() string { + return c.name +} + +func NewCache(config *config.CacheConfig) cache.Cache { + switch config.Cache.Type { + case 1: + return &CustomCache{ + store: newMemory(config.Cache.Size), + name: "Freecache", + } + case 2: + return &CustomCache{ + store: newRedis(config.Cache.Address, config.Cache.Password, 0), + name: "Redis", + } + default: + return &CustomCache{ + store: newMemory(config.Cache.Size), + name: "Freecache", + } + } +} diff --git a/backend/pkg/cache/memory.go b/backend/pkg/cache/memory.go new file mode 100644 index 0000000..0132163 --- /dev/null +++ b/backend/pkg/cache/memory.go @@ -0,0 +1,17 @@ +package cache + +import ( + "time" + + "github.com/coocood/freecache" + "github.com/eko/gocache/lib/v4/cache" + "github.com/eko/gocache/lib/v4/marshaler" + "github.com/eko/gocache/lib/v4/store" + freecache_store "github.com/eko/gocache/store/freecache/v4" +) + +func newMemory(size int) *marshaler.Marshaler { + freecacheStore := freecache_store.NewFreecache(freecache.NewCache(size*1024*1024), store.WithExpiration(10*time.Second)) + cacheManage := cache.New[[]byte](freecacheStore) + return marshaler.New(cacheManage.GetCodec().GetStore()) +} diff --git a/backend/pkg/cache/redis.go b/backend/pkg/cache/redis.go new file mode 100644 index 0000000..191da94 --- /dev/null +++ b/backend/pkg/cache/redis.go @@ -0,0 +1,20 @@ +package cache + +import ( + "github.com/eko/gocache/lib/v4/cache" + "github.com/eko/gocache/lib/v4/marshaler" + redis_store "github.com/eko/gocache/store/redis/v4" + "github.com/go-redis/redis/v8" +) + +func newRedis(address, password string, db int) *marshaler.Marshaler { + redisClient := redis.NewClient(&redis.Options{ + Addr: address, + Password: password, + DB: db, + }) + redisStore := redis_store.NewRedis(redisClient) + cacheManager := cache.New[string](redisStore) + marshaller := marshaler.New(cacheManager.GetCodec().GetStore()) + return marshaller +} diff --git a/backend/pkg/config/cache.go b/backend/pkg/config/cache.go new file mode 100644 index 0000000..f1c96e3 --- /dev/null +++ b/backend/pkg/config/cache.go @@ -0,0 +1,62 @@ +package config + +import ( + "context" + "os" + "time" + + "github.com/sethvargo/go-envconfig" + "gopkg.in/yaml.v2" +) + +type CacheConfig struct { + Cache struct { + Type int `yaml:"type" env:"CACHE_TYPE,overwrite"` + Size int `yaml:"size" env:"CACHE_SIZE,overwrite"` + Address string `yaml:"address" env:"CACHE_ADDRESS,overwrite"` + Password string `yaml:"password" env:"CACHE_PASSWORD,overwrite"` + } `yaml:"cache"` +} + +func (b *CacheConfig) Validate() error { + switch b.Cache.Type { + case 2: + if b.Cache.Address == "" { + return &InvalidConfigurationParameterError{ + Parameter: "Address", + Reason: "Redis cache must have a valid address", + } + } + return nil + default: + return nil + } +} + +func BuildNewCacheConfig(path string) func() (*CacheConfig, error) { + return func() (*CacheConfig, error) { + var config CacheConfig + config.Cache.Size = 10 + if path != "" { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + decoder := yaml.NewDecoder(file) + + if err := decoder.Decode(&config); err != nil { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) + defer cancel() + if err := envconfig.Process(ctx, &config); err != nil { + return nil, err + } + + return &config, config.Validate() + } +} diff --git a/backend/pkg/config/cors.go b/backend/pkg/config/cors.go new file mode 100644 index 0000000..6771418 --- /dev/null +++ b/backend/pkg/config/cors.go @@ -0,0 +1,53 @@ +package config + +import ( + "context" + "os" + "time" + + "github.com/sethvargo/go-envconfig" + "gopkg.in/yaml.v2" +) + +type CORSConfig struct { + CORS struct { + AllowedOrigins []string `yaml:"origins" env:"ALLOWED_ORIGINS,overwrite"` + AllowedMethods []string `yaml:"methods" env:"ALLOWED_METHODS,overwrite"` + AllowedHeaders []string `yaml:"headers" env:"ALLOWED_HEADERS,overwrite"` + AllowCredentials bool `yaml:"credentials" env:"ALLOW_CREDENTIALS,overwrite"` + } `yaml:"cors"` +} + +func (cc *CORSConfig) Validate() error { + return nil +} + +func BuildNewCorsConfig(path string) func() (*CORSConfig, error) { + return func() (*CORSConfig, error) { + var config CORSConfig + config.CORS.AllowedOrigins = []string{"*"} + config.CORS.AllowedMethods = []string{"*"} + config.CORS.AllowedHeaders = []string{"*"} + if path != "" { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + decoder := yaml.NewDecoder(file) + + if err := decoder.Decode(&config); err != nil { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) + defer cancel() + if err := envconfig.Process(ctx, &config); err != nil { + return nil, err + } + + return &config, config.Validate() + } +} diff --git a/backend/pkg/config/credentials.go b/backend/pkg/config/credentials.go new file mode 100644 index 0000000..6fde7fb --- /dev/null +++ b/backend/pkg/config/credentials.go @@ -0,0 +1,67 @@ +package config + +import ( + "context" + "os" + "strings" + "time" + + "github.com/sethvargo/go-envconfig" + "gopkg.in/yaml.v2" +) + +type OAuthCredentialsConfig struct { + Credentials struct { + ClientID string `yaml:"client_id" env:"CLIENT_ID,overwrite"` + ClientSecret string `yaml:"client_secret" env:"CLIENT_SECRET,overwrite"` + RedirectURI string `yaml:"redirect_uri" env:"REDIRECT_URI,overwrite"` + } `yaml:"oauth"` +} + +func (zc *OAuthCredentialsConfig) Validate() error { + zc.Credentials.ClientID = strings.TrimSpace(zc.Credentials.ClientID) + zc.Credentials.ClientSecret = strings.TrimSpace(zc.Credentials.ClientSecret) + + if zc.Credentials.ClientID == "" { + return &InvalidConfigurationParameterError{ + Parameter: "ClientID", + Reason: "Should not be empty", + } + } + + if zc.Credentials.ClientSecret == "" { + return &InvalidConfigurationParameterError{ + Parameter: "ClientSecret", + Reason: "Should not be empty", + } + } + + return nil +} + +func BuildNewCredentialsConfig(path string) func() (*OAuthCredentialsConfig, error) { + return func() (*OAuthCredentialsConfig, error) { + var config OAuthCredentialsConfig + if path != "" { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + decoder := yaml.NewDecoder(file) + + if err := decoder.Decode(&config); err != nil { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) + defer cancel() + if err := envconfig.Process(ctx, &config); err != nil { + return nil, err + } + + return &config, config.Validate() + } +} diff --git a/backend/pkg/config/error.go b/backend/pkg/config/error.go new file mode 100644 index 0000000..350ba39 --- /dev/null +++ b/backend/pkg/config/error.go @@ -0,0 +1,12 @@ +package config + +import "fmt" + +type InvalidConfigurationParameterError struct { + Parameter string + Reason string +} + +func (e *InvalidConfigurationParameterError) Error() string { + return fmt.Sprintf("invald configuration [%s] parameter. Reason: %s", e.Parameter, e.Reason) +} diff --git a/backend/pkg/config/logger.go b/backend/pkg/config/logger.go new file mode 100644 index 0000000..5334a10 --- /dev/null +++ b/backend/pkg/config/logger.go @@ -0,0 +1,75 @@ +package config + +import ( + "context" + "os" + "time" + + "github.com/sethvargo/go-envconfig" + "gopkg.in/yaml.v2" +) + +type LoggerConfig struct { + Logger struct { + Name string `yaml:"name" env:"LOGGER_NAME,overwrite"` + Level int `yaml:"level" env:"LOGGER_LEVEL,overwrite"` + Pretty bool `yaml:"pretty" env:"LOGGER_PRETTY,overwrite"` + Color bool `yaml:"color" env:"LOGGER_COLOR,overwrite"` + File FileLogConfig `yaml:"file"` + Elastic ElasticLogConfig `yaml:"elastic"` + } `yaml:"logger"` +} + +type ElasticLogConfig struct { + Address string `yaml:"address" env:"ELASTIC_ADDRESS,overwrite"` + Index string `yaml:"index" env:"ELASTIC_INDEX,overwrite"` + Level int `yaml:"level" env:"ELASTIC_LEVEL,overwrite"` + Bulk bool `yaml:"bulk" env:"ELASTIC_BULK,overwrite"` + Async bool `yaml:"async" env:"ELASTIC_ASYNC,overwrite"` + HealthcheckEnabled bool `yaml:"healthcheck" env:"ELASTIC_HEALTHCHECK,overwrite"` + BasicAuthUsername string `yaml:"username" env:"ELASTIC_AUTH_USERNAME,overwrite"` + BasicAuthPassword string `yaml:"password" env:"ELASTIC_AUTH_PASSWORD,overwrite"` + GzipEnabled bool `yaml:"gzip" env:"ELASTIC_GZIP_ENABLED,overwrite"` +} + +type FileLogConfig struct { + Filename string `yaml:"filename" env:"FILELOG_NAME,overwrite"` + MaxSize int `yaml:"maxsize" env:"FILELOG_MAX_SIZE,overwrite"` + MaxAge int `yaml:"maxage" env:"FILELOG_MAX_AGE,overwrite"` + MaxBackups int `yaml:"maxbackups" env:"FILELOG_MAX_BACKUPS,overwrite"` + LocalTime bool `yaml:"localtime"` + Compress bool `yaml:"compress" env:"FILELOG_COMPRESS,overwrite"` +} + +func (lc *LoggerConfig) Validate() error { + return nil +} + +func BuildNewLoggerConfig(path string) func() (*LoggerConfig, error) { + return func() (*LoggerConfig, error) { + var config LoggerConfig + config.Logger.Name = "unknown" + config.Logger.Level = 4 + if path != "" { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + decoder := yaml.NewDecoder(file) + + if err := decoder.Decode(&config); err != nil { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) + defer cancel() + if err := envconfig.Process(ctx, &config); err != nil { + return nil, err + } + + return &config, config.Validate() + } +} diff --git a/backend/pkg/config/messaging.go b/backend/pkg/config/messaging.go new file mode 100644 index 0000000..6a7c7cc --- /dev/null +++ b/backend/pkg/config/messaging.go @@ -0,0 +1,59 @@ +package config + +import ( + "context" + "os" + "time" + + "github.com/sethvargo/go-envconfig" + "gopkg.in/yaml.v2" +) + +type BrokerConfig struct { + Messaging struct { + Addrs []string `yaml:"addresses" env:"BROKER_ADDRESSES,overwrite"` + Type int `yaml:"type" env:"BROKER_TYPE,overwrite"` + DisableAutoAck bool `yaml:"disable_auto_ack" env:"BROKER_DISABLE_AUTO_ACK,overwrite"` + Durable bool `yaml:"durable" env:"BROKER_DURABLE,overwrite"` + AckOnSuccess bool `yaml:"ack_on_success" env:"BROKER_ACK_ON_SUCCESS,overwrite"` + RequeueOnError bool `yaml:"requeue_on_error" env:"BROKER_REQUEUE_ON_ERROR,overwrite"` + } `yaml:"messaging"` +} + +func (b *BrokerConfig) Validate() error { + if len(b.Messaging.Addrs) == 0 { + return &InvalidConfigurationParameterError{ + Parameter: "Addrs", + Reason: "Invalid number of addresses", + } + } + + return nil +} + +func BuildNewMessagingConfig(path string) func() (*BrokerConfig, error) { + return func() (*BrokerConfig, error) { + var config BrokerConfig + if path != "" { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + decoder := yaml.NewDecoder(file) + + if err := decoder.Decode(&config); err != nil { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) + defer cancel() + if err := envconfig.Process(ctx, &config); err != nil { + return nil, err + } + + return &config, config.Validate() + } +} diff --git a/backend/pkg/config/persistence.go b/backend/pkg/config/persistence.go new file mode 100644 index 0000000..58b6aae --- /dev/null +++ b/backend/pkg/config/persistence.go @@ -0,0 +1,49 @@ +package config + +import ( + "context" + "os" + "strings" + "time" + + "github.com/sethvargo/go-envconfig" + "gopkg.in/yaml.v2" +) + +type PersistenceConfig struct { + Persistence struct { + URL string `yaml:"url" env:"PERSISTENCE_URL,overwrite"` + } `yaml:"persistence"` +} + +func (p *PersistenceConfig) Validate() error { + p.Persistence.URL = strings.TrimSpace(p.Persistence.URL) + return nil +} + +func BuildNewPersistenceConfig(path string) func() (*PersistenceConfig, error) { + return func() (*PersistenceConfig, error) { + var config PersistenceConfig + if path != "" { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + decoder := yaml.NewDecoder(file) + + if err := decoder.Decode(&config); err != nil { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) + defer cancel() + if err := envconfig.Process(ctx, &config); err != nil { + return nil, err + } + + return &config, config.Validate() + } +} diff --git a/backend/pkg/config/registry.go b/backend/pkg/config/registry.go new file mode 100644 index 0000000..fadd4c3 --- /dev/null +++ b/backend/pkg/config/registry.go @@ -0,0 +1,57 @@ +package config + +import ( + "context" + "os" + "time" + + "github.com/sethvargo/go-envconfig" + "gopkg.in/yaml.v2" +) + +type RegistryConfig struct { + Registry struct { + Addresses []string `yaml:"addresses" env:"REGISTRY_ADDRESSES,overwrite"` + CacheTTL time.Duration `yaml:"cache_duration" env:"REGISTRY_CACHE_DURATION,overwrite"` + RegistryType int `yaml:"type" env:"REGISTRY_TYPE,overwrite"` + } `yaml:"registry"` +} + +func (r *RegistryConfig) Validate() error { + if len(r.Registry.Addresses) <= 0 { + return &InvalidConfigurationParameterError{ + Parameter: "Addresses", + Reason: "Length should be greater than zero", + } + } + + return nil +} + +func BuildNewRegistryConfig(path string) func() (*RegistryConfig, error) { + return func() (*RegistryConfig, error) { + var config RegistryConfig + config.Registry.CacheTTL = 10 * time.Second + if path != "" { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + decoder := yaml.NewDecoder(file) + + if err := decoder.Decode(&config); err != nil { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) + defer cancel() + if err := envconfig.Process(ctx, &config); err != nil { + return nil, err + } + + return &config, config.Validate() + } +} diff --git a/backend/pkg/config/resilience.go b/backend/pkg/config/resilience.go new file mode 100644 index 0000000..d6cabdc --- /dev/null +++ b/backend/pkg/config/resilience.go @@ -0,0 +1,66 @@ +package config + +import ( + "context" + "os" + "time" + + "github.com/sethvargo/go-envconfig" + "gopkg.in/yaml.v2" +) + +type ResilienceConfig struct { + Resilience struct { + RateLimiter RateLimiterConfig `yaml:"rate_limiter"` + CircuitBreaker CircuitBreakerConfig `yaml:"circuit_breaker"` + } `yaml:"resilience"` +} + +func BuildNewResilienceConfig(path string) func() (*ResilienceConfig, error) { + return func() (*ResilienceConfig, error) { + var config ResilienceConfig + if path != "" { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + decoder := yaml.NewDecoder(file) + + if err := decoder.Decode(&config); err != nil { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) + defer cancel() + if err := envconfig.Process(ctx, &config); err != nil { + return nil, err + } + + return &config, config.Validate() + } +} + +type RateLimiterConfig struct { + Limit uint64 `yaml:"limit" env:"RATE_LIMIT,overwrite"` + IPLimit uint64 `yaml:"iplimit" env:"RATE_LIMIT_IP,overwrite"` +} + +type CircuitBreakerConfig struct { + // Timeout is how long to wait for command to complete, in milliseconds (default 1000) + Timeout int `yaml:"timeout" env:"CIRCUIT_TIMEOUT,overwrite"` + // MaxConcurrent is how many commands of the same type can run at the same time (default 10) + MaxConcurrent int `yaml:"max_concurrent" env:"CIRCUIT_MAX_CONCURRENT,overwrite"` + // VolumeThreshold is the minimum number of requests needed before a circuit can be tripped due to health (default 20) + VolumeThreshold int `yaml:"volume_threshold" env:"CIRCUIT_VOLUME_THRESHOLD,overwrite"` + // SleepWindow is how long, in milliseconds, to wait after a circuit opens before testing for recovery (default 5000) + SleepWindow int `yaml:"sleep_window" env:"CIRCUIT_SLEEP_WINDOW,overwrite"` + // ErrorPercentThreshold causes circuits to open once the rolling measure of errors exceeds this percent of requests (default 50) + ErrorPercentThreshold int `yaml:"error_percent_threshold" env:"CIRCUIT_ERROR_PERCENT_THRESHOLD,overwrite"` +} + +func (rc *ResilienceConfig) Validate() error { + return nil +} diff --git a/backend/pkg/config/server.go b/backend/pkg/config/server.go new file mode 100644 index 0000000..0b042b4 --- /dev/null +++ b/backend/pkg/config/server.go @@ -0,0 +1,84 @@ +package config + +import ( + "context" + "os" + "strings" + "time" + + "github.com/sethvargo/go-envconfig" + "gopkg.in/yaml.v2" +) + +type ServerConfig struct { + Namespace string `yaml:"namespace" env:"SERVER_NAMESPACE,overwrite"` + Name string `yaml:"name" env:"SERVER_NAME,overwrite"` + Version int `yaml:"version" env:"SERVER_VERSION,overwrite"` + Address string `yaml:"address" env:"SERVER_ADDRESS,overwrite"` + ReplAddress string `yaml:"repl_address" env:"REPL_ADDRESS,overwrite"` + Debug bool `yaml:"debug" env:"SERVER_DEBUG,overwrite"` +} + +func (hs *ServerConfig) Validate() error { + hs.Namespace = strings.TrimSpace(hs.Namespace) + hs.Name = strings.TrimSpace(hs.Name) + hs.Address = strings.TrimSpace(hs.Address) + hs.ReplAddress = strings.TrimSpace(hs.ReplAddress) + + if hs.Namespace == "" { + return &InvalidConfigurationParameterError{ + Parameter: "Namespace", + Reason: "Should not be empty", + } + } + + if hs.Name == "" { + return &InvalidConfigurationParameterError{ + Parameter: "Name", + Reason: "Should not be empty", + } + } + + if hs.Address == "" { + return &InvalidConfigurationParameterError{ + Parameter: "Address", + Reason: "Should not be empty", + } + } + + if hs.ReplAddress == "" { + return &InvalidConfigurationParameterError{ + Parameter: "Repl Address", + Reason: "Should not be empty", + } + } + + return nil +} + +func BuildNewServerConfig(path string) func() (*ServerConfig, error) { + return func() (*ServerConfig, error) { + var config ServerConfig + if path != "" { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + decoder := yaml.NewDecoder(file) + + if err := decoder.Decode(&config); err != nil { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) + defer cancel() + if err := envconfig.Process(ctx, &config); err != nil { + return nil, err + } + + return &config, config.Validate() + } +} diff --git a/backend/pkg/config/trace.go b/backend/pkg/config/trace.go new file mode 100644 index 0000000..2ea02dc --- /dev/null +++ b/backend/pkg/config/trace.go @@ -0,0 +1,52 @@ +package config + +import ( + "context" + "os" + "time" + + "github.com/sethvargo/go-envconfig" + "gopkg.in/yaml.v2" +) + +type TracerConfig struct { + Tracer struct { + Name string `yaml:"name" env:"TRACER_NAME,overwrite"` + Enable bool `yaml:"enable" env:"TRACER_ENABLE,overwrite"` + Address string `yaml:"address" env:"TRACER_ADDRESS,overwrite"` + TracerType int `yaml:"type" env:"TRACER_TYPE,overwrite"` + FractionRatio float64 `yaml:"fraction" env:"TRACER_FRACTION_RATIO,overwrite"` + } `yaml:"tracer"` +} + +func (tc *TracerConfig) Validate() error { + return nil +} + +func BuildNewTracerConfig(path string) func() (*TracerConfig, error) { + return func() (*TracerConfig, error) { + var config TracerConfig + config.Tracer.FractionRatio = 1 + if path != "" { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + decoder := yaml.NewDecoder(file) + + if err := decoder.Decode(&config); err != nil { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) + defer cancel() + if err := envconfig.Process(ctx, &config); err != nil { + return nil, err + } + + return &config, config.Validate() + } +} diff --git a/backend/pkg/config/worker.go b/backend/pkg/config/worker.go new file mode 100644 index 0000000..083d877 --- /dev/null +++ b/backend/pkg/config/worker.go @@ -0,0 +1,62 @@ +package config + +import ( + "context" + "os" + "time" + + "github.com/sethvargo/go-envconfig" + "gopkg.in/yaml.v2" +) + +// Worker configuration +type WorkerConfig struct { + Worker struct { + Enable bool `yaml:"enable" env:"WORKER_ENABLE,overwrite"` + Type int `yaml:"type" env:"WORKER_TYPE,overwrite"` + MaxConcurrency int `yaml:"max_concurrency" env:"WORKER_MAX_CONCURRENCY,overwrite"` + RedisAddresses []string `yaml:"addresses" env:"WORKER_ADDRESS,overwrite"` + RedisUsername string `yaml:"username" env:"WORKER_USERNAME,overwrite"` + RedisPassword string `yaml:"password" env:"WORKER_PASSWORD,overwrite"` + RedisDatabase int `yaml:"database" env:"WORKER_DATABASE,overwrite"` + } `yaml:"worker"` +} + +func (wc *WorkerConfig) Validate() error { + if wc.Worker.Enable && len(wc.Worker.RedisAddresses) < 1 { + return &InvalidConfigurationParameterError{ + Parameter: "Worker address", + Reason: "Should not be empty", + } + } + + return nil +} + +func BuildNewWorkerConfig(path string) func() (*WorkerConfig, error) { + return func() (*WorkerConfig, error) { + var config WorkerConfig + config.Worker.MaxConcurrency = 3 + if path != "" { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + decoder := yaml.NewDecoder(file) + + if err := decoder.Decode(&config); err != nil { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) + defer cancel() + if err := envconfig.Process(ctx, &config); err != nil { + return nil, err + } + + return &config, config.Validate() + } +} diff --git a/backend/pkg/log/error.go b/backend/pkg/log/error.go new file mode 100644 index 0000000..6f3daf7 --- /dev/null +++ b/backend/pkg/log/error.go @@ -0,0 +1,23 @@ +package log + +import "fmt" + +// LogFileInitializationError fires if Lumberjack throws an error. +type LogFileInitializationError struct { + Path string + Cause error +} + +func (e *LogFileInitializationError) Error() string { + return fmt.Sprintf("could not open/create a log file with path: %s. Cause: %s", e.Path, e.Cause.Error()) +} + +// LogElasticInitializationError fires when an elastic client throws an error. +type LogElasticInitializationError struct { + Address string + Cause error +} + +func (e *LogElasticInitializationError) Error() string { + return fmt.Sprintf("could not initialize an elastic client/hook with address: %s. Cause: %s", e.Address, e.Cause.Error()) +} diff --git a/backend/pkg/log/hook/elastic.go b/backend/pkg/log/hook/elastic.go new file mode 100644 index 0000000..2f9b2c6 --- /dev/null +++ b/backend/pkg/log/hook/elastic.go @@ -0,0 +1,280 @@ +/* +MIT License + +# Copyright (c) 2018 Radomír Sohlich + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +package hook + +import ( + "context" + "errors" + "strings" + "time" + + "github.com/google/uuid" + "github.com/olivere/elastic/v7" + "github.com/sirupsen/logrus" +) + +const mapping = ` +{ + "mappings":{ + "properties":{ + "Host":{ + "type":"text" + }, + "@timestamp":{ + "type":"text" + }, + "Message":{ + "type":"text" + }, + "Data":{ + "dynamic": true, + "properties": {} + }, + "Level":{ + "type":"text" + } + } + } +} +` + +var ( + // ErrCannotCreateIndex Fired if the index is not created + ErrCannotCreateIndex = errors.New("cannot create index") +) + +// IndexNameFunc get index name +type IndexNameFunc func() string + +type fireFunc func(entry *logrus.Entry, hook *ElasticHook) error + +// ElasticHook is a logrus +// hook for ElasticSearch +type ElasticHook struct { + client *elastic.Client + host string + index IndexNameFunc + levels []logrus.Level + ctx context.Context + ctxCancel context.CancelFunc + fireFunc fireFunc +} + +type message struct { + Host string `json:"Host,omitempty"` + Timestamp string `json:"@timestamp"` + File string `json:"File,omitempty"` + Func string `json:"Func,omitempty"` + Message string `json:"Message,omitempty"` + Data logrus.Fields + Level string `json:"Level,omitempty"` +} + +// NewElasticHook creates new hook. +// client - ElasticSearch client with specific es version (v5/v6/v7/...) +// host - host of system +// level - log level +// index - name of the index in ElasticSearch +func NewElasticHook(client *elastic.Client, host string, level logrus.Level, index string) (*ElasticHook, error) { + return NewElasticHookWithFunc(client, host, level, func() string { return index }) +} + +// NewAsyncElasticHook creates new hook with asynchronous log. +// client - ElasticSearch client with specific es version (v5/v6/v7/...) +// host - host of system +// level - log level +// index - name of the index in ElasticSearch +func NewAsyncElasticHook(client *elastic.Client, host string, level logrus.Level, index string) (*ElasticHook, error) { + return NewAsyncElasticHookWithFunc(client, host, level, func() string { return index }) +} + +// NewBulkProcessorElasticHook creates new hook that uses a bulk processor for indexing. +// client - ElasticSearch client with specific es version (v5/v6/v7/...) +// host - host of system +// level - log level +// index - name of the index in ElasticSearch +func NewBulkProcessorElasticHook(client *elastic.Client, host string, level logrus.Level, index string) (*ElasticHook, error) { + return NewBulkProcessorElasticHookWithFunc(client, host, level, func() string { return index }) +} + +// NewElasticHookWithFunc creates new hook with +// function that provides the index name. This is useful if the index name is +// somehow dynamic especially based on time. +// client - ElasticSearch client with specific es version (v5/v6/v7/...) +// host - host of system +// level - log level +// indexFunc - function providing the name of index +func NewElasticHookWithFunc(client *elastic.Client, host string, level logrus.Level, indexFunc IndexNameFunc) (*ElasticHook, error) { + return newHookFuncAndFireFunc(client, host, level, indexFunc, syncFireFunc) +} + +// NewAsyncElasticHookWithFunc creates new asynchronous hook with +// function that provides the index name. This is useful if the index name is +// somehow dynamic especially based on time. +// client - ElasticSearch client with specific es version (v5/v6/v7/...) +// host - host of system +// level - log level +// indexFunc - function providing the name of index +func NewAsyncElasticHookWithFunc(client *elastic.Client, host string, level logrus.Level, indexFunc IndexNameFunc) (*ElasticHook, error) { + return newHookFuncAndFireFunc(client, host, level, indexFunc, asyncFireFunc) +} + +// NewBulkProcessorElasticHookWithFunc creates new hook with +// function that provides the index name. This is useful if the index name is +// somehow dynamic especially based on time that uses a bulk processor for +// indexing. +// client - ElasticSearch client with specific es version (v5/v6/v7/...) +// host - host of system +// level - log level +// indexFunc - function providing the name of index +func NewBulkProcessorElasticHookWithFunc(client *elastic.Client, host string, level logrus.Level, indexFunc IndexNameFunc) (*ElasticHook, error) { + fireFunc, err := makeBulkFireFunc(client) + if err != nil { + return nil, err + } + return newHookFuncAndFireFunc(client, host, level, indexFunc, fireFunc) +} + +func newHookFuncAndFireFunc(client *elastic.Client, host string, level logrus.Level, indexFunc IndexNameFunc, fireFunc fireFunc) (*ElasticHook, error) { + var levels []logrus.Level + for _, l := range []logrus.Level{ + logrus.PanicLevel, + logrus.FatalLevel, + logrus.ErrorLevel, + logrus.WarnLevel, + logrus.InfoLevel, + logrus.DebugLevel, + logrus.TraceLevel, + } { + if l <= level { + levels = append(levels, l) + } + } + + ctx, cancel := context.WithCancel(context.TODO()) + + // Use the IndexExists service to check if a specified index exists. + exists, err := client.IndexExists(indexFunc()).Do(ctx) + if err != nil { + // Handle error + cancel() + return nil, err + } + if !exists { + createIndex, err := client.CreateIndex(indexFunc()).BodyString(mapping).Do(ctx) + if err != nil { + cancel() + return nil, err + } + if !createIndex.Acknowledged { + cancel() + return nil, ErrCannotCreateIndex + } + } + + return &ElasticHook{ + client: client, + host: host, + index: indexFunc, + levels: levels, + ctx: ctx, + ctxCancel: cancel, + fireFunc: fireFunc, + }, nil +} + +// Fire is required to implement +// Logrus hook +func (hook *ElasticHook) Fire(entry *logrus.Entry) error { + return hook.fireFunc(entry, hook) +} + +func asyncFireFunc(entry *logrus.Entry, hook *ElasticHook) error { + go syncFireFunc(entry, hook) + return nil +} + +func createMessage(entry *logrus.Entry, hook *ElasticHook) *message { + level := entry.Level.String() + + if e, ok := entry.Data[logrus.ErrorKey]; ok && e != nil { + if err, ok := e.(error); ok { + entry.Data[logrus.ErrorKey] = err.Error() + } + } + + var file string + var function string + if entry.HasCaller() { + file = entry.Caller.File + function = entry.Caller.Function + } + + return &message{ + hook.host, + entry.Time.UTC().Format(time.RFC3339Nano), + file, + function, + entry.Message, + entry.Data, + strings.ToUpper(level), + } +} + +func syncFireFunc(entry *logrus.Entry, hook *ElasticHook) error { + _, err := hook.client.Index(). + Index(hook.index()). + Id(uuid.NewString()). + BodyJson(*createMessage(entry, hook)). + Do(hook.ctx) + + return err +} + +// Create closure with bulk processor tied to fireFunc. +func makeBulkFireFunc(client *elastic.Client) (fireFunc, error) { + processor, err := client.BulkProcessor(). + Name("elogrus.v3.bulk.processor"). + Workers(2). + FlushInterval(time.Second). + Do(context.Background()) + + return func(entry *logrus.Entry, hook *ElasticHook) error { + r := elastic.NewBulkIndexRequest(). + Index(hook.index()). + Doc(*createMessage(entry, hook)) + processor.Add(r) + return nil + }, err +} + +// Levels Required for logrus hook implementation +func (hook *ElasticHook) Levels() []logrus.Level { + return hook.levels +} + +// Cancel all calls to elastic +func (hook *ElasticHook) Cancel() { + hook.ctxCancel() +} diff --git a/backend/pkg/log/logger.go b/backend/pkg/log/logger.go new file mode 100644 index 0000000..2d10611 --- /dev/null +++ b/backend/pkg/log/logger.go @@ -0,0 +1,124 @@ +package log + +import ( + "fmt" + "log" + "os" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" +) + +// Logger is a generic logger interface. +type Logger interface { + Debugf(format string, args ...interface{}) + Infof(format string, args ...interface{}) + Warnf(format string, args ...interface{}) + Errorf(format string, args ...interface{}) + Fatalf(format string, args ...interface{}) + Debug(args ...interface{}) + Info(args ...interface{}) + Warn(args ...interface{}) + Error(args ...interface{}) + Fatal(args ...interface{}) +} + +// EmptyLogger is an empty Logger implementation. +type EmptyLogger struct{} + +// NewEmptyLogger is an empty logger constructor. +func NewEmptyLogger() Logger { + return EmptyLogger{} +} + +func (l EmptyLogger) Debugf(format string, args ...interface{}) {} +func (l EmptyLogger) Infof(format string, args ...interface{}) {} +func (l EmptyLogger) Warnf(format string, args ...interface{}) {} +func (l EmptyLogger) Errorf(format string, args ...interface{}) {} +func (l EmptyLogger) Fatalf(format string, args ...interface{}) {} +func (l EmptyLogger) Debug(args ...interface{}) {} +func (l EmptyLogger) Info(args ...interface{}) {} +func (l EmptyLogger) Warn(args ...interface{}) {} +func (l EmptyLogger) Error(args ...interface{}) {} +func (l EmptyLogger) Fatal(args ...interface{}) {} + +// DefaultLogger is a golang log package Logger implementation. +type DefaultLogger struct { + debugL *log.Logger + infoL *log.Logger + warnL *log.Logger + errorL *log.Logger + fatalL *log.Logger + config config.LoggerConfig +} + +// NewDefaultLogger is a golang log package Logger constructor. +func NewDefaultLogger(config *config.LoggerConfig) Logger { + return DefaultLogger{ + debugL: log.New(os.Stdout, fmt.Sprintf("[DEBUG - Default %s]: ", config.Logger.Name), log.Ldate|log.Ltime|log.Llongfile), + infoL: log.New(os.Stdout, fmt.Sprintf("[INFO - Default %s]: ", config.Logger.Name), log.Ldate|log.Ltime|log.Lshortfile), + warnL: log.New(os.Stdout, fmt.Sprintf("[WARN - Default %s]: ", config.Logger.Name), log.Ldate|log.Ltime|log.Lshortfile), + errorL: log.New(os.Stdout, fmt.Sprintf("[ERROR - Default %s]: ", config.Logger.Name), log.Ldate|log.Ltime|log.Lshortfile), + fatalL: log.New(os.Stderr, fmt.Sprintf("[FATAL - Default %s]: ", config.Logger.Name), log.Ldate|log.Ltime|log.Llongfile), + config: *config, + } +} + +func (l DefaultLogger) Debugf(format string, args ...interface{}) { + if l.config.Logger.Level <= 2 { + l.debugL.Printf(format+"\n", args...) + } +} + +func (l DefaultLogger) Infof(format string, args ...interface{}) { + if l.config.Logger.Level <= 3 { + l.infoL.Printf(format+"\n", args...) + } +} + +func (l DefaultLogger) Warnf(format string, args ...interface{}) { + if l.config.Logger.Level <= 4 { + l.warnL.Printf(format+"\n", args...) + } +} + +func (l DefaultLogger) Errorf(format string, args ...interface{}) { + if l.config.Logger.Level <= 5 { + l.errorL.Printf(format+"\n", args...) + } +} + +func (l DefaultLogger) Fatalf(format string, args ...interface{}) { + if l.config.Logger.Level <= 6 { + l.fatalL.Fatalf(format+"\n", args...) + } +} + +func (l DefaultLogger) Debug(args ...interface{}) { + if l.config.Logger.Level <= 2 { + l.debugL.Println(args...) + } +} + +func (l DefaultLogger) Info(args ...interface{}) { + if l.config.Logger.Level <= 3 { + l.infoL.Println(args...) + } +} + +func (l DefaultLogger) Warn(args ...interface{}) { + if l.config.Logger.Level <= 4 { + l.warnL.Println(args...) + } +} + +func (l DefaultLogger) Error(args ...interface{}) { + if l.config.Logger.Level <= 5 { + l.errorL.Println(args...) + } +} + +func (l DefaultLogger) Fatal(args ...interface{}) { + if l.config.Logger.Level <= 6 { + l.fatalL.Fatalln(args...) + } +} diff --git a/backend/pkg/log/logrus.go b/backend/pkg/log/logrus.go new file mode 100644 index 0000000..b405107 --- /dev/null +++ b/backend/pkg/log/logrus.go @@ -0,0 +1,170 @@ +package log + +import ( + "os" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log/hook" + "github.com/natefinch/lumberjack" + elastic "github.com/olivere/elastic/v7" + "github.com/sirupsen/logrus" +) + +type LogLevel int + +const ( + LEVEL_TRACE LogLevel = 1 + LEVEL_DEBUG LogLevel = 2 + LEVEL_INFO LogLevel = 3 + LEVEL_WARNING LogLevel = 4 + LEVEL_ERROR LogLevel = 5 + LEVEL_FATAL LogLevel = 6 +) + +var levels = map[LogLevel]logrus.Level{ + LEVEL_TRACE: logrus.TraceLevel, + LEVEL_DEBUG: logrus.DebugLevel, + LEVEL_INFO: logrus.InfoLevel, + LEVEL_WARNING: logrus.WarnLevel, + LEVEL_ERROR: logrus.ErrorLevel, + LEVEL_FATAL: logrus.FatalLevel, +} + +// LogrusLogger is a logrus logger wrapper. +type LogrusLogger struct { + logger *logrus.Logger + config config.LoggerConfig +} + +// createElasticHook opens a new elastic client and generates an elastic hook. +func createElasticHook(config config.ElasticLogConfig) (*hook.ElasticHook, error) { + client, err := elastic.NewClient( + elastic.SetURL(config.Address), + elastic.SetSniff(false), + elastic.SetBasicAuth(config.BasicAuthUsername, config.BasicAuthPassword), + elastic.SetHealthcheck(config.HealthcheckEnabled), + elastic.SetGzip(config.GzipEnabled), + ) + + if err != nil { + return nil, &LogElasticInitializationError{ + Address: config.Address, + Cause: err, + } + } + + if config.Bulk { + return hook.NewBulkProcessorElasticHook(client, config.Address, levels[LogLevel(config.Level)], config.Index) + } + + if config.Async { + return hook.NewAsyncElasticHook(client, config.Address, levels[LogLevel(config.Level)], config.Index) + } + + return hook.NewElasticHook(client, config.Address, levels[LogLevel(config.Level)], config.Index) +} + +// NewLogrusLogger creates a new logger compliant with the Logger interface. +func NewLogrusLogger(config *config.LoggerConfig) (Logger, error) { + log := logrus.New() + log.SetFormatter(&logrus.TextFormatter{ + DisableColors: !config.Logger.Color, + FullTimestamp: true, + }) + + if lvl, ok := levels[LogLevel(config.Logger.Level)]; ok { + log.SetLevel(lvl) + } + + log.SetReportCaller(true) + log.SetOutput(os.Stdout) + + if config.Logger.File.Filename != "" { + log.SetOutput(&lumberjack.Logger{ + Filename: config.Logger.File.Filename, + MaxSize: config.Logger.File.MaxSize, + MaxBackups: config.Logger.File.MaxBackups, + MaxAge: config.Logger.File.MaxAge, + LocalTime: config.Logger.File.LocalTime, + Compress: config.Logger.File.Compress, + }) + } + + if config.Logger.File.Filename == "" && config.Logger.Elastic.Address != "" && config.Logger.Elastic.Index != "" { + hook, err := createElasticHook(config.Logger.Elastic) + + if err != nil { + return nil, &LogElasticInitializationError{ + Address: config.Logger.Elastic.Address, + Cause: err, + } + } + + log.AddHook(hook) + } + + return LogrusLogger{ + logger: log, + config: *config, + }, nil +} + +func (l LogrusLogger) Debugf(format string, args ...interface{}) { + l.logger.WithFields(logrus.Fields{ + "name": l.config.Logger.Name, + }).Debugf(format, args...) +} + +func (l LogrusLogger) Infof(format string, args ...interface{}) { + l.logger.WithFields(logrus.Fields{ + "name": l.config.Logger.Name, + }).Infof(format, args...) +} + +func (l LogrusLogger) Warnf(format string, args ...interface{}) { + l.logger.WithFields(logrus.Fields{ + "name": l.config.Logger.Name, + }).Warnf(format, args...) +} + +func (l LogrusLogger) Errorf(format string, args ...interface{}) { + l.logger.WithFields(logrus.Fields{ + "name": l.config.Logger.Name, + }).Errorf(format, args...) +} + +func (l LogrusLogger) Fatalf(format string, args ...interface{}) { + l.logger.WithFields(logrus.Fields{ + "name": l.config.Logger.Name, + }).Fatalf(format, args...) +} + +func (l LogrusLogger) Debug(args ...interface{}) { + l.logger.WithFields(logrus.Fields{ + "name": l.config.Logger.Name, + }).Debug(args...) +} + +func (l LogrusLogger) Info(args ...interface{}) { + l.logger.WithFields(logrus.Fields{ + "name": l.config.Logger.Name, + }).Info(args...) +} + +func (l LogrusLogger) Warn(args ...interface{}) { + l.logger.WithFields(logrus.Fields{ + "name": l.config.Logger.Name, + }).Warn(args...) +} + +func (l LogrusLogger) Error(args ...interface{}) { + l.logger.WithFields(logrus.Fields{ + "name": l.config.Logger.Name, + }).Error(args...) +} + +func (l LogrusLogger) Fatal(args ...interface{}) { + l.logger.WithFields(logrus.Fields{ + "name": l.config.Logger.Name, + }).Fatal(args...) +} diff --git a/backend/pkg/messaging/broker.go b/backend/pkg/messaging/broker.go new file mode 100644 index 0000000..724db3d --- /dev/null +++ b/backend/pkg/messaging/broker.go @@ -0,0 +1,59 @@ +package messaging + +import ( + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + "github.com/go-micro/plugins/v4/broker/memory" + "github.com/go-micro/plugins/v4/broker/nats" + "github.com/go-micro/plugins/v4/broker/rabbitmq" + "go-micro.dev/v4/broker" + "go-micro.dev/v4/registry" +) + +type BrokerWithOptions struct { + Broker broker.Broker + SubOptions broker.SubscribeOptions +} + +// NewBroker create a broker instance based on BrokerType value +func NewBroker(registry registry.Registry, config *config.BrokerConfig) BrokerWithOptions { + bo := []broker.Option{ + broker.Addrs(config.Messaging.Addrs...), + broker.Registry(registry), + } + + var b broker.Broker + var subOpts broker.SubscribeOptions + + switch config.Messaging.Type { + case 1: + b = rabbitmq.NewBroker(bo...) + + opts := []broker.SubscribeOption{} + if config.Messaging.DisableAutoAck { + opts = append(opts, broker.DisableAutoAck()) + } + + if config.Messaging.AckOnSuccess { + opts = append(opts, rabbitmq.AckOnSuccess()) + } + + if config.Messaging.Durable { + opts = append(opts, rabbitmq.DurableQueue()) + } + + if config.Messaging.RequeueOnError { + opts = append(opts, rabbitmq.RequeueOnError()) + } + + subOpts = broker.NewSubscribeOptions(opts...) + case 2: + b = nats.NewBroker(bo...) + default: + b = memory.NewBroker(bo...) + } + + return BrokerWithOptions{ + Broker: b, + SubOptions: subOpts, + } +} diff --git a/backend/pkg/middleware/cache.go b/backend/pkg/middleware/cache.go new file mode 100644 index 0000000..dd3790b --- /dev/null +++ b/backend/pkg/middleware/cache.go @@ -0,0 +1,17 @@ +package middleware + +import ( + "net/http" + "time" +) + +// NoCache sets no-cache headers. +func NoCache(next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.Header().Set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value") + rw.Header().Set("Expires", "Thu, 01 Jan 1970 00:00:00 GMT") + rw.Header().Set("Last-Modified", time.Now().UTC().Format(http.TimeFormat)) + + next.ServeHTTP(rw, r) + }) +} diff --git a/backend/pkg/middleware/cors.go b/backend/pkg/middleware/cors.go new file mode 100644 index 0000000..0580e67 --- /dev/null +++ b/backend/pkg/middleware/cors.go @@ -0,0 +1,17 @@ +package middleware + +import ( + "net/http" + + corsmiddleware "github.com/go-chi/cors" +) + +// Cors creates a new CORS middleware. +func Cors(AllowedOrigins, AllowedMethods, AllowedHeaders []string, AllowCredentials bool) func(http.Handler) http.Handler { + return corsmiddleware.Handler(corsmiddleware.Options{ + AllowedOrigins: AllowedOrigins, + AllowedMethods: AllowedMethods, + AllowedHeaders: AllowedHeaders, + AllowCredentials: AllowCredentials, + }) +} diff --git a/backend/pkg/middleware/log.go b/backend/pkg/middleware/log.go new file mode 100644 index 0000000..aca4fde --- /dev/null +++ b/backend/pkg/middleware/log.go @@ -0,0 +1,19 @@ +package middleware + +import ( + "net/http" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" +) + +// Log creates a new debug logging middleware. +func Log(logger log.Logger) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if logger != nil { + logger.Debugf("calling [%s] route %s", r.Method, r.URL.Path) + } + next.ServeHTTP(w, r) + }) + } +} diff --git a/backend/pkg/middleware/rate.go b/backend/pkg/middleware/rate.go new file mode 100644 index 0000000..4d80a09 --- /dev/null +++ b/backend/pkg/middleware/rate.go @@ -0,0 +1,36 @@ +package middleware + +import ( + "net/http" + "time" + + "github.com/sethvargo/go-limiter/httplimit" + "github.com/sethvargo/go-limiter/memorystore" +) + +// Option defines a single option. +type Option func() httplimit.KeyFunc + +const _AllRequests = "ALL" + +// WithKeyFuncIP sets ratelimiter based on IP. +func WithKeyFuncIP() httplimit.KeyFunc { + return httplimit.IPKeyFunc("RemoteAddr", "X-Forwarded-For", "X-Real-IP") +} + +// WithKeyFuncAll sets global ratelimiter. +func WithKeyFuncAll() httplimit.KeyFunc { + return func(r *http.Request) (string, error) { + return _AllRequests, nil + } +} + +// NewRateLimiter creates a ratelimiter middleware. +func NewRateLimiter(limit uint64, exp time.Duration, keyFunc Option) func(next http.Handler) http.Handler { + store, _ := memorystore.New(&memorystore.Config{ + Tokens: limit, + Interval: exp, + }) + limiter, _ := httplimit.NewMiddleware(store, keyFunc()) + return limiter.Handle +} diff --git a/backend/pkg/middleware/secure.go b/backend/pkg/middleware/secure.go new file mode 100644 index 0000000..9d705e2 --- /dev/null +++ b/backend/pkg/middleware/secure.go @@ -0,0 +1,18 @@ +package middleware + +import ( + "net/http" +) + +// Secure creates a new CSP, X-Frame, X-Content-Type headers middleware. +func Secure(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains") + w.Header().Set("X-Frame-Options", "DENY") + w.Header().Set("Referrer-Policy", "same-origin") + w.Header().Set("Content-Security-Policy", "frame-ancestors 'none'") + w.Header().Set("X-Content-Type-Options", "nosniff") + w.Header().Set("Content-Type", "Desired Content Type; charset=utf-8") + next.ServeHTTP(w, r) + }) +} diff --git a/backend/pkg/middleware/timeout.go b/backend/pkg/middleware/timeout.go new file mode 100644 index 0000000..7d54c83 --- /dev/null +++ b/backend/pkg/middleware/timeout.go @@ -0,0 +1,12 @@ +package middleware + +import ( + "net/http" + "time" +) + +func Timeout(timeout time.Duration) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.TimeoutHandler(http.HandlerFunc(next.ServeHTTP), timeout, "request timeout") + } +} diff --git a/backend/pkg/middleware/trace.go b/backend/pkg/middleware/trace.go new file mode 100644 index 0000000..3b17406 --- /dev/null +++ b/backend/pkg/middleware/trace.go @@ -0,0 +1,38 @@ +package middleware + +import ( + "net/http" + + "go-micro.dev/v4/metadata" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/propagation" +) + +const ( + InstrumentationName = "github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry" +) + +// TracePropagationMiddleware inject previous context into a new request +func TracePropagationMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + carrier := propagation.MapCarrier{} + propagator := otel.GetTextMapPropagator() + ctx := propagator.Extract(r.Context(), carrier) + + propagator.Inject(ctx, carrier) + next.ServeHTTP(w, r.WithContext(metadata.NewContext(ctx, metadata.Metadata(carrier)))) + }) +} + +// LogTraceMiddleware starts tracing with spans +func LogTraceMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + carrier := propagation.MapCarrier{} + + ctx, span := otel.GetTracerProvider().Tracer(InstrumentationName).Start(r.Context(), r.URL.Path) + defer span.End() + + otel.GetTextMapPropagator().Inject(ctx, carrier) + next.ServeHTTP(w, r.WithContext(metadata.NewContext(ctx, metadata.Metadata(carrier)))) + }) +} diff --git a/backend/pkg/middleware/version.go b/backend/pkg/middleware/version.go new file mode 100644 index 0000000..8fb11c5 --- /dev/null +++ b/backend/pkg/middleware/version.go @@ -0,0 +1,15 @@ +package middleware + +import ( + "net/http" +) + +// Version creates a new X-Version header middleware. +func Version(version string) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.Header().Set("X-Api-Version", version) + next.ServeHTTP(rw, r) + }) + } +} diff --git a/backend/pkg/middleware/wrapper/trace.go b/backend/pkg/middleware/wrapper/trace.go new file mode 100644 index 0000000..4ea4ffd --- /dev/null +++ b/backend/pkg/middleware/wrapper/trace.go @@ -0,0 +1,35 @@ +package wrapper + +import ( + "context" + "strings" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/middleware" + "go-micro.dev/v4/metadata" + "go-micro.dev/v4/server" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/propagation" +) + +// TracePropagationHandlerWrapper wraps RPC handlers to trace rpc calls +func TracePropagationHandlerWrapper(hf server.HandlerFunc) server.HandlerFunc { + return func(ctx context.Context, req server.Request, rsp interface{}) error { + meta, _ := metadata.FromContext(ctx) + converted := make(map[string]string) + + for k, v := range meta { + converted[strings.ToLower(k)] = v + } + + ctx = otel.GetTextMapPropagator().Extract(ctx, propagation.MapCarrier(converted)) + + ctx, span := otel.GetTracerProvider().Tracer(middleware.InstrumentationName).Start(ctx, req.Endpoint()) + defer span.End() + + if err := hf(ctx, req, rsp); err != nil { + return err + } + + return nil + } +} diff --git a/backend/pkg/registry/registry.go b/backend/pkg/registry/registry.go new file mode 100644 index 0000000..77809bf --- /dev/null +++ b/backend/pkg/registry/registry.go @@ -0,0 +1,40 @@ +package registry + +import ( + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + "github.com/go-micro/plugins/v4/registry/consul" + "github.com/go-micro/plugins/v4/registry/etcd" + "github.com/go-micro/plugins/v4/registry/kubernetes" + "github.com/go-micro/plugins/v4/registry/mdns" + "go-micro.dev/v4/registry" + "go-micro.dev/v4/registry/cache" +) + +// NewRegistry looks up envs and configures respective registries based on those variables. Defaults to memory +func NewRegistry(config *config.RegistryConfig) registry.Registry { + var r registry.Registry + switch config.Registry.RegistryType { + case 1: + r = kubernetes.NewRegistry( + registry.Addrs(config.Registry.Addresses...), + ) + case 2: + r = consul.NewRegistry( + registry.Addrs(config.Registry.Addresses...), + ) + case 3: + r = etcd.NewRegistry( + registry.Addrs(config.Registry.Addresses...), + ) + case 4: + r = mdns.NewRegistry( + registry.Addrs(config.Registry.Addresses...), + ) + default: + r = mdns.NewRegistry( + registry.Addrs(config.Registry.Addresses...), + ) + } + + return cache.New(r, cache.WithTTL(config.Registry.CacheTTL)) +} diff --git a/backend/pkg/resilience/circuit.go b/backend/pkg/resilience/circuit.go new file mode 100644 index 0000000..880b203 --- /dev/null +++ b/backend/pkg/resilience/circuit.go @@ -0,0 +1,31 @@ +package resilience + +import ( + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + "github.com/go-micro/plugins/v4/wrapper/breaker/hystrix" +) + +func BuildHystrixCommandConfig(resilienceConfig *config.ResilienceConfig) hystrix.CommandConfig { + var config hystrix.CommandConfig + if resilienceConfig.Resilience.CircuitBreaker.Timeout > 0 { + config.Timeout = resilienceConfig.Resilience.CircuitBreaker.Timeout + } + + if resilienceConfig.Resilience.CircuitBreaker.MaxConcurrent > 0 { + config.MaxConcurrentRequests = resilienceConfig.Resilience.CircuitBreaker.MaxConcurrent + } + + if resilienceConfig.Resilience.CircuitBreaker.VolumeThreshold > 0 { + config.RequestVolumeThreshold = resilienceConfig.Resilience.CircuitBreaker.VolumeThreshold + } + + if resilienceConfig.Resilience.CircuitBreaker.SleepWindow > 0 { + config.SleepWindow = resilienceConfig.Resilience.CircuitBreaker.SleepWindow + } + + if resilienceConfig.Resilience.CircuitBreaker.ErrorPercentThreshold > 0 { + config.ErrorPercentThreshold = resilienceConfig.Resilience.CircuitBreaker.ErrorPercentThreshold + } + + return config +} diff --git a/backend/pkg/service/http/service.go b/backend/pkg/service/http/service.go new file mode 100644 index 0000000..6185f03 --- /dev/null +++ b/backend/pkg/service/http/service.go @@ -0,0 +1,131 @@ +package http + +import ( + "context" + "log" + "net/http" + "strconv" + "strings" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/messaging" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/middleware" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/resilience" + chimiddleware "github.com/go-chi/chi/v5/middleware" + mserver "github.com/go-micro/plugins/v4/server/http" + "github.com/go-micro/plugins/v4/wrapper/breaker/hystrix" + "github.com/go-micro/plugins/v4/wrapper/select/roundrobin" + "github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry" + "go-micro.dev/v4" + "go-micro.dev/v4/cache" + "go-micro.dev/v4/client" + "go-micro.dev/v4/registry" + "go-micro.dev/v4/server" + "go.opentelemetry.io/otel" + oteltrace "go.opentelemetry.io/otel/sdk/trace" +) + +type ServerEngine interface { + ApplyMiddleware(middlewares ...func(http.Handler) http.Handler) + NewHandler(client client.Client, c cache.Cache) interface { + ServeHTTP(w http.ResponseWriter, req *http.Request) + } +} + +// NewService Initializes an http service. +func NewService( + engine ServerEngine, + registry registry.Registry, + broker messaging.BrokerWithOptions, + cache cache.Cache, + tracer *oteltrace.TracerProvider, + logger plog.Logger, + serverConfig *config.ServerConfig, + resilienceConfig *config.ResilienceConfig, + corsConfig *config.CORSConfig, + tracerConfig *config.TracerConfig, +) micro.Service { + if err := broker.Broker.Init(); err != nil { + log.Fatalf("could not initialize a new broker instance: %s", err.Error()) + } + + if err := broker.Broker.Connect(); err != nil { + log.Fatalf("broker connection error: %s", err.Error()) + } + + hystrix.ConfigureDefault(resilience.BuildHystrixCommandConfig(resilienceConfig)) + + service := micro.NewService( + micro.Name(strings.Join([]string{serverConfig.Namespace, serverConfig.Name}, ":")), + micro.Version(strconv.Itoa(serverConfig.Version)), + micro.Context(context.Background()), + micro.Server(mserver.NewServer( + server.Name(strings.Join([]string{serverConfig.Namespace, serverConfig.Name}, ":")), + server.Address(serverConfig.Address), + )), + micro.Cache(cache), + micro.Registry(registry), + micro.Broker(broker.Broker), + micro.Client(client.NewClient( + client.Registry(registry), + client.Broker(broker.Broker), + )), + micro.WrapClient( + roundrobin.NewClientWrapper(), + hystrix.NewClientWrapper(), + ), + micro.WrapCall(opentelemetry.NewCallWrapper(opentelemetry.WithTraceProvider(otel.GetTracerProvider()))), + micro.RegisterTTL(30*time.Second), + micro.RegisterInterval(10*time.Second), + micro.AfterStop(func() error { + if tracer != nil { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + if err := tracer.Shutdown(ctx); err != nil { + return err + } + + return nil + } + + return nil + }), + ) + + if resilienceConfig.Resilience.RateLimiter.IPLimit > 0 { + engine.ApplyMiddleware(middleware.NewRateLimiter(resilienceConfig.Resilience.RateLimiter.IPLimit, 1*time.Second, middleware.WithKeyFuncIP)) + } + + if resilienceConfig.Resilience.RateLimiter.Limit > 0 { + engine.ApplyMiddleware(middleware.NewRateLimiter(resilienceConfig.Resilience.RateLimiter.Limit, 1*time.Second, middleware.WithKeyFuncAll)) + } + + engine.ApplyMiddleware( + middleware.Log(logger), + chimiddleware.RealIP, + chimiddleware.RequestID, + chimiddleware.StripSlashes, + middleware.Secure, + middleware.Version(strconv.Itoa(serverConfig.Version)), + middleware.Cors(corsConfig.CORS.AllowedOrigins, corsConfig.CORS.AllowedMethods, corsConfig.CORS.AllowedHeaders, corsConfig.CORS.AllowCredentials), + ) + + if tracerConfig.Tracer.Enable { + engine.ApplyMiddleware( + middleware.TracePropagationMiddleware, + middleware.LogTraceMiddleware, + ) + } + + if err := micro.RegisterHandler( + service.Server(), + engine.NewHandler(service.Options().Client, service.Options().Cache), + ); err != nil { + log.Fatal("could not register http handler: ", err) + } + + return service +} diff --git a/backend/pkg/service/repl/service.go b/backend/pkg/service/repl/service.go new file mode 100644 index 0000000..7abfb54 --- /dev/null +++ b/backend/pkg/service/repl/service.go @@ -0,0 +1,52 @@ +package repl + +import ( + "fmt" + "net/http" + "net/http/pprof" + "strconv" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/middleware" + chimiddleware "github.com/go-chi/chi/v5/middleware" + "github.com/hellofresh/health-go/v5" + "github.com/justinas/alice" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +// NewService Initializes repl service with options. +func NewService( + replConfig *config.ServerConfig, + corsConfig *config.CORSConfig, +) *http.Server { + mux := http.NewServeMux() + h, _ := health.New(health.WithComponent(health.Component{ + Name: fmt.Sprintf("%s:%s", replConfig.Namespace, replConfig.Name), + Version: fmt.Sprintf("v%d", replConfig.Version), + })) + + mux.Handle("/metrics", promhttp.Handler()) + mux.Handle("/health", h.Handler()) + + if replConfig.Debug { + mux.HandleFunc("/debug/pprof/", pprof.Index) + mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) + mux.HandleFunc("/debug/pprof/profile", pprof.Profile) + mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) + mux.HandleFunc("/debug/pprof/trace", pprof.Trace) + } + + return &http.Server{ + Addr: replConfig.ReplAddress, + Handler: alice.New( + chimiddleware.RealIP, + middleware.NewRateLimiter(1000, 1*time.Second, middleware.WithKeyFuncAll), + chimiddleware.RequestID, + middleware.Cors(corsConfig.CORS.AllowedOrigins, corsConfig.CORS.AllowedMethods, corsConfig.CORS.AllowedHeaders, corsConfig.CORS.AllowCredentials), + middleware.Secure, + middleware.NoCache, + middleware.Version(strconv.Itoa(replConfig.Version)), + ).Then(mux), + } +} diff --git a/backend/pkg/service/rpc/service.go b/backend/pkg/service/rpc/service.go new file mode 100644 index 0000000..e72d006 --- /dev/null +++ b/backend/pkg/service/rpc/service.go @@ -0,0 +1,136 @@ +package rpc + +import ( + "context" + "log" + "strconv" + "strings" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/messaging" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/middleware/wrapper" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/resilience" + "github.com/go-micro/plugins/v4/wrapper/breaker/hystrix" + rlimiter "github.com/go-micro/plugins/v4/wrapper/ratelimiter/uber" + "github.com/go-micro/plugins/v4/wrapper/select/roundrobin" + "github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry" + "go-micro.dev/v4" + "go-micro.dev/v4/cache" + gcache "go-micro.dev/v4/cache" + "go-micro.dev/v4/client" + "go-micro.dev/v4/registry" + "go-micro.dev/v4/server" + "go.opentelemetry.io/otel" + oteltrace "go.opentelemetry.io/otel/sdk/trace" + uber "go.uber.org/ratelimit" +) + +type RPCMessageHandler struct { + Topic string + Queue string + Handler interface{} +} + +type RPCEngine interface { + BuildMessageHandlers() []RPCMessageHandler + BuildHandlers(client client.Client, cache gcache.Cache) []interface{} +} + +// NewService Initializes an http service with options. +func NewService( + engine RPCEngine, + registry registry.Registry, + broker messaging.BrokerWithOptions, + cache cache.Cache, + tracer *oteltrace.TracerProvider, + rpcConfig *config.ServerConfig, + resilienceConfig *config.ResilienceConfig, + tracerConfig *config.TracerConfig, +) micro.Service { + var wrappers []server.HandlerWrapper = make([]server.HandlerWrapper, 0, 2) + + if err := broker.Broker.Init(); err != nil { + log.Fatalf("could not initialize a new broker instance: %s", err.Error()) + } + + if err := broker.Broker.Connect(); err != nil { + log.Fatalf("broker connection error: %s", err.Error()) + } + + if resilienceConfig.Resilience.RateLimiter.Limit > 0 { + wrappers = append(wrappers, rlimiter.NewHandlerWrapper(int(resilienceConfig.Resilience.RateLimiter.Limit), uber.Per(1*time.Second))) + } + + if tracerConfig.Tracer.Enable { + wrappers = append(wrappers, wrapper.TracePropagationHandlerWrapper) + } + + hystrix.ConfigureDefault(resilience.BuildHystrixCommandConfig(resilienceConfig)) + + service := micro.NewService( + micro.Name(strings.Join([]string{rpcConfig.Namespace, rpcConfig.Name}, ":")), + micro.Version(strconv.Itoa(rpcConfig.Version)), + micro.Context(context.Background()), + micro.Server(server.NewServer( + server.Name(strings.Join([]string{rpcConfig.Namespace, rpcConfig.Name}, ":")), + server.Address(rpcConfig.Address), + )), + micro.Cache(cache), + micro.Registry(registry), + micro.Broker(broker.Broker), + micro.Client(client.NewClient( + client.Registry(registry), + client.Broker(broker.Broker), + )), + micro.WrapClient( + roundrobin.NewClientWrapper(), + hystrix.NewClientWrapper(), + opentelemetry.NewClientWrapper(opentelemetry.WithTraceProvider(otel.GetTracerProvider())), + ), + micro.WrapSubscriber(opentelemetry.NewSubscriberWrapper(opentelemetry.WithTraceProvider(otel.GetTracerProvider()))), + micro.WrapHandler(wrappers...), + micro.RegisterTTL(30*time.Second), + micro.RegisterInterval(10*time.Second), + micro.AfterStop(func() error { + if tracer != nil { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + if err := tracer.Shutdown(ctx); err != nil { + return err + } + + return nil + } + + return nil + }), + ) + + if len(engine.BuildMessageHandlers()) > 0 { + for _, entry := range engine.BuildMessageHandlers() { + if entry.Handler != nil && entry.Topic != "" { + if entry.Queue != "" { + if err := micro.RegisterSubscriber( + entry.Topic, service.Server(), entry.Handler, server.SubscriberContext(broker.SubOptions.Context), server.SubscriberQueue(entry.Queue), + ); err != nil { + log.Fatalf("could not register a new subscriber with topic %s. Reason: %s", entry.Topic, err.Error()) + } + } else { + if err := micro.RegisterSubscriber(entry.Topic, service.Server(), entry.Handler, server.SubscriberContext(broker.SubOptions.Context)); err != nil { + log.Fatalf("could not register a new subscriber with topic %s. Reason: %s", entry.Topic, err.Error()) + } + } + } + } + } + + for _, handler := range engine.BuildHandlers(service.Client(), service.Options().Cache) { + if err := micro.RegisterHandler(service.Server(), handler); err != nil { + log.Fatalf("could not initialize rpc handlers: %s", err.Error()) + } + } + + return service +} diff --git a/backend/pkg/shared/env.go b/backend/pkg/shared/env.go new file mode 100644 index 0000000..c0c202d --- /dev/null +++ b/backend/pkg/shared/env.go @@ -0,0 +1,19 @@ +package shared + +const ( + DEV = "development" + TEST = "testing" + PROD = "production" +) + +var SUPPORTED_ENVIRONMENTS = map[string]string{ + "development": DEV, + "testing": TEST, + "production": PROD, + "dev": DEV, + "test": TEST, + "prod": PROD, + "d": DEV, + "t": TEST, + "p": PROD, +} diff --git a/backend/pkg/trace/error.go b/backend/pkg/trace/error.go new file mode 100644 index 0000000..53dce64 --- /dev/null +++ b/backend/pkg/trace/error.go @@ -0,0 +1,8 @@ +package trace + +import ( + "errors" +) + +var ErrTracerInvalidNameInitialization = errors.New("could not initialize a tracer with an empty name") +var ErrTracerInvalidAddressInitialization = errors.New("could not initialize a tracer without a URL") diff --git a/backend/pkg/trace/tracer.go b/backend/pkg/trace/tracer.go new file mode 100644 index 0000000..542a2b7 --- /dev/null +++ b/backend/pkg/trace/tracer.go @@ -0,0 +1,56 @@ +package trace + +import ( + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" +) + +type TracerType int + +var ( + Default TracerType = 0 + Zipkin TracerType = 1 +) + +// NewTracer initializes a new tracer. +func NewTracer(config *config.TracerConfig) (*trace.TracerProvider, error) { + var exporter trace.SpanExporter + + if config.Tracer.Name == "" { + config.Tracer.Name = "default-tracer" + } + + switch config.Tracer.TracerType { + case 1: + if config.Tracer.Address == "" { + return nil, ErrTracerInvalidAddressInitialization + } + exporter = NewZipkinExporter(config.Tracer.Address) + default: + exporter, _ = stdouttrace.New() + } + + provider := trace.NewTracerProvider( + trace.WithSampler(trace.ParentBased(trace.TraceIDRatioBased(config.Tracer.FractionRatio))), + trace.WithBatcher(exporter), + trace.WithResource(resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String(config.Tracer.Name), + )), + ) + + otel.SetTracerProvider(provider) + otel.SetTextMapPropagator( + propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, + propagation.Baggage{}, + ), + ) + + return provider, nil +} diff --git a/backend/pkg/trace/zipkin.go b/backend/pkg/trace/zipkin.go new file mode 100644 index 0000000..ef64da1 --- /dev/null +++ b/backend/pkg/trace/zipkin.go @@ -0,0 +1,17 @@ +package trace + +import ( + "log" + + "go.opentelemetry.io/otel/exporters/zipkin" + "go.opentelemetry.io/otel/sdk/trace" +) + +// NewZipkingExporter creates a new zipkin exporter. +func NewZipkinExporter(url string, opts ...zipkin.Option) trace.SpanExporter { + exporter, err := zipkin.New(url, opts...) + if err != nil { + log.Fatalln("could not initialize a new zipkin exporter: ", err) + } + return exporter +} diff --git a/backend/pkg/worker/asynq.go b/backend/pkg/worker/asynq.go new file mode 100644 index 0000000..d46ee1b --- /dev/null +++ b/backend/pkg/worker/asynq.go @@ -0,0 +1,124 @@ +package worker + +import ( + "context" + "log" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/hibiken/asynq" +) + +type asynqWorker struct { + enabled bool + srv *asynq.Server + mux *asynq.ServeMux +} + +type asynqEnqueuer struct { + enabled bool + client *asynq.Client +} + +func newAsynqWorker(config *config.WorkerConfig, logger plog.Logger) BackgroundWorker { + var workerOpts asynq.RedisConnOpt = asynq.RedisClientOpt{ + Addr: config.Worker.RedisAddresses[0], + Username: config.Worker.RedisUsername, + Password: config.Worker.RedisPassword, + ReadTimeout: 4 * time.Second, + WriteTimeout: 7 * time.Second, + } + if len(config.Worker.RedisAddresses) > 1 { + workerOpts = asynq.RedisClusterClientOpt{ + Addrs: config.Worker.RedisAddresses, + Username: config.Worker.RedisUsername, + Password: config.Worker.RedisPassword, + ReadTimeout: 4 * time.Second, + WriteTimeout: 7 * time.Second, + } + } + + return asynqWorker{ + enabled: config.Worker.Enable, + srv: asynq.NewServer(workerOpts, asynq.Config{ + Concurrency: config.Worker.MaxConcurrency, + Logger: logger, + }), + mux: asynq.NewServeMux(), + } +} + +func (w asynqWorker) Register(pattern string, handler func(ctx context.Context, payload []byte) error) { + if w.enabled { + w.mux.Handle(pattern, asynq.HandlerFunc(func(ctx context.Context, t *asynq.Task) error { + return handler(ctx, t.Payload()) + })) + } +} + +func (w asynqWorker) Run() { + if w.enabled { + go func() { + if err := w.srv.Run(w.mux); err != nil { + log.Fatal(err.Error()) + } + }() + } +} + +func newAsynqEnqueuer(config *config.WorkerConfig) BackgroundEnqueuer { + var enqOpts asynq.RedisConnOpt = asynq.RedisClientOpt{ + Addr: config.Worker.RedisAddresses[0], + Username: config.Worker.RedisUsername, + Password: config.Worker.RedisPassword, + ReadTimeout: 4 * time.Second, + WriteTimeout: 7 * time.Second, + } + if len(config.Worker.RedisAddresses) > 1 { + enqOpts = asynq.RedisClusterClientOpt{ + Addrs: config.Worker.RedisAddresses, + Username: config.Worker.RedisUsername, + Password: config.Worker.RedisPassword, + ReadTimeout: 4 * time.Second, + WriteTimeout: 7 * time.Second, + } + } + + return asynqEnqueuer{ + enabled: config.Worker.Enable, + client: asynq.NewClient(enqOpts), + } +} + +func (e asynqEnqueuer) Enqueue(pattern string, task []byte, opts ...EnqueuerOption) error { + if e.enabled { + options := NewEnqueuerOptions(opts...) + t := asynq.NewTask(pattern, task) + + _, err := e.client.Enqueue(t, asynq.MaxRetry(options.MaxRetry), asynq.Timeout(options.Timeout)) + return err + } + + return nil +} + +func (e asynqEnqueuer) EnqueueContext(ctx context.Context, pattern string, task []byte, opts ...EnqueuerOption) error { + if e.enabled { + options := NewEnqueuerOptions(opts...) + t := asynq.NewTask(pattern, task) + + _, err := e.client.EnqueueContext(ctx, t, asynq.MaxRetry(options.MaxRetry), asynq.Timeout(options.Timeout)) + return err + } + + return nil +} + +func (e asynqEnqueuer) Close() error { + if e.enabled { + return e.Close() + } + + return nil +} diff --git a/backend/pkg/worker/enqueuer.go b/backend/pkg/worker/enqueuer.go new file mode 100644 index 0000000..296fbf9 --- /dev/null +++ b/backend/pkg/worker/enqueuer.go @@ -0,0 +1,22 @@ +package worker + +import ( + "context" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" +) + +type BackgroundEnqueuer interface { + Enqueue(pattern string, task []byte, opts ...EnqueuerOption) error + EnqueueContext(ctx context.Context, pattern string, task []byte, opts ...EnqueuerOption) error + Close() error +} + +func NewBackgroundEnqueuer(config *config.WorkerConfig) BackgroundEnqueuer { + switch config.Worker.Type { + case 0: + return newAsynqEnqueuer(config) + default: + return newAsynqEnqueuer(config) + } +} diff --git a/backend/pkg/worker/option.go b/backend/pkg/worker/option.go new file mode 100644 index 0000000..e970f7d --- /dev/null +++ b/backend/pkg/worker/option.go @@ -0,0 +1,54 @@ +package worker + +import ( + "time" +) + +type WorkerType int + +var ( + Asynq WorkerType = 0 +) + +type WorkerRedisCredentials struct { + Addresses []string + Username string + Password string + Database int + ReadTimeout time.Duration + WriteTimeout time.Duration +} + +type EnqueuerOption func(*EnqueuerOptions) + +type EnqueuerOptions struct { + MaxRetry int + Timeout time.Duration +} + +func NewEnqueuerOptions(opts ...EnqueuerOption) EnqueuerOptions { + opt := EnqueuerOptions{ + MaxRetry: 3, + Timeout: 0 * time.Second, + } + + for _, o := range opts { + o(&opt) + } + + return opt +} + +func WithMaxRetry(val int) EnqueuerOption { + return func(eo *EnqueuerOptions) { + if val > 0 { + eo.MaxRetry = val + } + } +} + +func WithTimeout(val time.Duration) EnqueuerOption { + return func(eo *EnqueuerOptions) { + eo.Timeout = val + } +} diff --git a/backend/pkg/worker/worker.go b/backend/pkg/worker/worker.go new file mode 100644 index 0000000..b0eb244 --- /dev/null +++ b/backend/pkg/worker/worker.go @@ -0,0 +1,22 @@ +package worker + +import ( + "context" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" +) + +type BackgroundWorker interface { + Register(pattern string, handler func(ctx context.Context, payload []byte) error) + Run() +} + +func NewBackgroundWorker(config *config.WorkerConfig, logger log.Logger) BackgroundWorker { + switch config.Worker.Type { + case 0: + return newAsynqWorker(config, logger) + default: + return newAsynqWorker(config, logger) + } +} diff --git a/backend/services/auth/cmd/root.go b/backend/services/auth/cmd/root.go new file mode 100644 index 0000000..890313e --- /dev/null +++ b/backend/services/auth/cmd/root.go @@ -0,0 +1,30 @@ +package cmd + +import ( + "os" + + "github.com/urfave/cli/v2" +) + +func GetCommands() cli.Commands { + return []*cli.Command{ + Server(), + } +} + +func Run() error { + app := &cli.App{ + Name: "onlyoffice:auth", + Description: "Description", + Authors: []*cli.Author{ + { + Name: "Ascensio Systems SIA", + Email: "support@onlyoffice.com", + }, + }, + HideVersion: true, + Commands: GetCommands(), + } + + return app.Run(os.Args) +} diff --git a/backend/services/auth/cmd/server.go b/backend/services/auth/cmd/server.go new file mode 100644 index 0000000..fe4bda8 --- /dev/null +++ b/backend/services/auth/cmd/server.go @@ -0,0 +1,66 @@ +package cmd + +import ( + "context" + "net/http" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/rpc" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web" + "github.com/urfave/cli/v2" + "go-micro.dev/v4" + "go.uber.org/fx" + "golang.org/x/sync/errgroup" +) + +func Server() *cli.Command { + return &cli.Command{ + Name: "server", + Usage: "starts a new rpc server instance", + Category: "server", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config_path", + Usage: "sets custom configuration path", + Aliases: []string{"config", "conf", "c"}, + }, + &cli.StringFlag{ + Name: "environment", + Usage: "sets servers environment (development, testing, production)", + Aliases: []string{"env", "e"}, + }, + }, + Action: func(c *cli.Context) error { + var ( + CONFIG_PATH = c.String("config_path") + // ENVIRONMENT = c.String("environment") + ) + + fx.New( + pkg.Bootstrap(CONFIG_PATH), + fx.Provide(rpc.NewService), + fx.Provide(web.NewAuthRPCServer), + fx.Invoke(func(lifecycle fx.Lifecycle, service micro.Service, repl *http.Server, logger log.Logger) { + lifecycle.Append(fx.Hook{ + OnStart: func(ctx context.Context) error { + go repl.ListenAndServe() + go service.Run() + return nil + }, + OnStop: func(ctx context.Context) error { + g, gCtx := errgroup.WithContext(ctx) + g.Go(func() error { + return repl.Shutdown(gCtx) + }) + return g.Wait() + }, + }) + }), + fx.NopLogger, + ).Run() + + return nil + }, + } +} diff --git a/backend/services/auth/config/config.example.yml b/backend/services/auth/config/config.example.yml new file mode 100644 index 0000000..80f3944 --- /dev/null +++ b/backend/services/auth/config/config.example.yml @@ -0,0 +1,34 @@ +namespace: "pipedrive" +name: "auth" +version: 0 +address: ":5052" +repl_address: ":5053" +debug: false +persistence: + url: "" +registry: + addresses: ["127.0.0.1:8500"] + type: 2 +messaging: + type: 1 + addresses: ["0.0.0.0:5672"] + durable: true + ack_on_success: true + requeue_on_error: true + disable_auto_ack: true +tracer: + enable: false + address: "http://127.0.0.1:9411/api/v2/spans" + type: 1 +resilience: + rate_limiter: + limit: 500 + circuit_breaker: + timeout: 2500 +logger: + name: "auth-logger" + level: 1 + color: true +oauth: + client_id: "" + client_secret: "" diff --git a/backend/services/auth/deployment/.keep b/backend/services/auth/deployment/.keep new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/auth/docs/.keep b/backend/services/auth/docs/.keep new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/auth/main.go b/backend/services/auth/main.go new file mode 100644 index 0000000..06d8f11 --- /dev/null +++ b/backend/services/auth/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "log" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/cmd" +) + +func main() { + if err := cmd.Run(); err != nil { + log.Fatalln(err) + } +} diff --git a/backend/services/auth/script/.keep b/backend/services/auth/script/.keep new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/auth/web/core/adapter/memory.go b/backend/services/auth/web/core/adapter/memory.go new file mode 100644 index 0000000..9ded07c --- /dev/null +++ b/backend/services/auth/web/core/adapter/memory.go @@ -0,0 +1,69 @@ +package adapter + +import ( + "context" + "encoding/json" + "errors" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/port" +) + +type memoryUserAdapter struct { + kvs map[string][]byte +} + +func NewMemoryUserAdapter() port.UserAccessServiceAdapter { + return &memoryUserAdapter{ + kvs: make(map[string][]byte), + } +} + +func (m *memoryUserAdapter) save(user domain.UserAccess) error { + buffer, err := json.Marshal(user) + + if err != nil { + return err + } + + m.kvs[user.ID] = buffer + + return nil +} + +func (m *memoryUserAdapter) InsertUser(ctx context.Context, user domain.UserAccess) error { + return m.save(user) +} + +func (m *memoryUserAdapter) SelectUserByID(ctx context.Context, uid string) (domain.UserAccess, error) { + buffer, ok := m.kvs[uid] + var user domain.UserAccess + + if !ok { + return user, errors.New("user with this id doesn't exist") + } + + if err := json.Unmarshal(buffer, &user); err != nil { + return user, err + } + + return user, nil +} + +func (m *memoryUserAdapter) UpsertUser(ctx context.Context, user domain.UserAccess) (domain.UserAccess, error) { + if err := m.save(user); err != nil { + return domain.UserAccess{}, err + } + + return user, nil +} + +func (m *memoryUserAdapter) DeleteUserByID(ctx context.Context, uid string) error { + if _, ok := m.kvs[uid]; !ok { + return errors.New("user with this id doesn't exist") + } + + delete(m.kvs, uid) + + return nil +} diff --git a/backend/services/auth/web/core/adapter/memory_test.go b/backend/services/auth/web/core/adapter/memory_test.go new file mode 100644 index 0000000..0c8c8cb --- /dev/null +++ b/backend/services/auth/web/core/adapter/memory_test.go @@ -0,0 +1,45 @@ +package adapter + +import ( + "context" + "testing" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" + "github.com/stretchr/testify/assert" +) + +func TestMemoryAdapter(t *testing.T) { + adapter := NewMemoryUserAdapter() + + t.Run("save user", func(t *testing.T) { + assert.NoError(t, adapter.InsertUser(context.Background(), user)) + }) + + t.Run("save the same user", func(t *testing.T) { + assert.NoError(t, adapter.InsertUser(context.Background(), user)) + }) + + t.Run("get user by id", func(t *testing.T) { + u, err := adapter.SelectUserByID(context.Background(), "mock") + assert.NoError(t, err) + assert.Equal(t, user, u) + }) + + t.Run("update user by id", func(t *testing.T) { + u, err := adapter.UpsertUser(context.Background(), domain.UserAccess{ + ID: "mock", + AccessToken: "BRuh", + }) + assert.NoError(t, err) + assert.NotNil(t, u) + }) + + t.Run("delete user by id", func(t *testing.T) { + assert.NoError(t, adapter.DeleteUserByID(context.Background(), "mock")) + }) + + t.Run("get invalid user", func(t *testing.T) { + _, err := adapter.SelectUserByID(context.Background(), "mock") + assert.Error(t, err) + }) +} diff --git a/backend/services/auth/web/core/adapter/mongo.go b/backend/services/auth/web/core/adapter/mongo.go new file mode 100644 index 0000000..7029975 --- /dev/null +++ b/backend/services/auth/web/core/adapter/mongo.go @@ -0,0 +1,129 @@ +package adapter + +import ( + "context" + "errors" + "log" + "strings" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/port" + "github.com/kamva/mgm/v3" + "github.com/kamva/mgm/v3/operator" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +var _ErrInvalidUserId error = errors.New("invalid uid format") +var _ErrUserAlreadyExists error = errors.New("user already exists") + +type userAccessCollection struct { + mgm.DefaultModel `bson:",inline"` + UID string `json:"uid" bson:"uid"` + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + TokenType string `json:"token_type"` + Scope string `json:"scope"` + ExpiresAt int64 `json:"expires_at"` + ApiDomain string `json:"api_domain"` +} + +type mongoUserAdapter struct { +} + +func NewMongoUserAdapter(url string) port.UserAccessServiceAdapter { + if err := mgm.SetDefaultConfig( + &mgm.Config{CtxTimeout: 3 * time.Second}, "users", + options.Client().ApplyURI(url), + ); err != nil { + log.Fatalf("mongo initialization error: %s", err.Error()) + } + + return &mongoUserAdapter{} +} + +func (m *mongoUserAdapter) save(ctx context.Context, user domain.UserAccess) error { + return mgm.Transaction(func(session mongo.Session, sc mongo.SessionContext) error { + u := &userAccessCollection{} + collection := mgm.Coll(&userAccessCollection{}) + + if err := collection.FirstWithCtx(ctx, bson.M{"uid": user.ID}, u); err != nil { + if cerr := collection.CreateWithCtx(ctx, &userAccessCollection{ + UID: user.ID, + AccessToken: user.AccessToken, + RefreshToken: user.RefreshToken, + TokenType: user.TokenType, + Scope: user.Scope, + ExpiresAt: user.ExpiresAt, + ApiDomain: user.ApiDomain, + }); cerr != nil { + return cerr + } + + return session.CommitTransaction(sc) + } + + u.AccessToken = user.AccessToken + u.RefreshToken = user.RefreshToken + u.TokenType = user.TokenType + u.Scope = user.Scope + u.ExpiresAt = user.ExpiresAt + u.UpdatedAt = time.Now() + u.ApiDomain = user.ApiDomain + + if err := collection.UpdateWithCtx(ctx, u); err != nil { + return err + } + + return session.CommitTransaction(sc) + }) +} + +func (m *mongoUserAdapter) InsertUser(ctx context.Context, user domain.UserAccess) error { + if err := user.Validate(); err != nil { + return err + } + + return m.save(ctx, user) +} + +func (m *mongoUserAdapter) SelectUserByID(ctx context.Context, uid string) (domain.UserAccess, error) { + uid = strings.TrimSpace(uid) + + if uid == "" { + return domain.UserAccess{}, _ErrInvalidUserId + } + + user := &userAccessCollection{} + collection := mgm.Coll(user) + return domain.UserAccess{ + ID: user.UID, + AccessToken: user.AccessToken, + RefreshToken: user.RefreshToken, + TokenType: user.TokenType, + Scope: user.Scope, + ExpiresAt: user.ExpiresAt, + ApiDomain: user.ApiDomain, + }, collection.FirstWithCtx(ctx, bson.M{"uid": uid}, user) +} + +func (m *mongoUserAdapter) UpsertUser(ctx context.Context, user domain.UserAccess) (domain.UserAccess, error) { + if err := user.Validate(); err != nil { + return user, err + } + + return user, m.save(ctx, user) +} + +func (m *mongoUserAdapter) DeleteUserByID(ctx context.Context, uid string) error { + uid = strings.TrimSpace(uid) + + if uid == "" { + return _ErrInvalidUserId + } + + _, err := mgm.Coll(&userAccessCollection{}).DeleteMany(ctx, bson.M{"uid": bson.M{operator.Eq: uid}}) + return err +} diff --git a/backend/services/auth/web/core/adapter/mongo_test.go b/backend/services/auth/web/core/adapter/mongo_test.go new file mode 100644 index 0000000..c6baa2f --- /dev/null +++ b/backend/services/auth/web/core/adapter/mongo_test.go @@ -0,0 +1,107 @@ +package adapter + +import ( + "context" + "testing" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" + "github.com/stretchr/testify/assert" +) + +var user = domain.UserAccess{ + ID: "mock", + AccessToken: "mock", + RefreshToken: "mock", + TokenType: "mock", + Scope: "mock", + ExpiresAt: 123456, +} + +func TestMongoAdapter(t *testing.T) { + adapter := NewMongoUserAdapter("mongodb://localhost:27017") + + t.Run("save user with timeout", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 0*time.Second) + defer cancel() + assert.Error(t, adapter.InsertUser(ctx, user)) + }) + + t.Run("save user", func(t *testing.T) { + assert.NoError(t, adapter.InsertUser(context.Background(), user)) + }) + + t.Run("save the same user", func(t *testing.T) { + assert.NoError(t, adapter.InsertUser(context.Background(), user)) + }) + + t.Run("get user by id with timeout", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 0*time.Second) + defer cancel() + _, err := adapter.SelectUserByID(ctx, "mock") + assert.Error(t, err) + }) + + t.Run("get user by id", func(t *testing.T) { + u, err := adapter.SelectUserByID(context.Background(), "mock") + assert.NoError(t, err) + assert.Equal(t, user, u) + }) + + t.Run("delete user by id with timeout", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 0*time.Second) + defer cancel() + assert.Error(t, adapter.DeleteUserByID(ctx, "mock")) + }) + + t.Run("delete user by id", func(t *testing.T) { + assert.NoError(t, adapter.DeleteUserByID(context.Background(), "mock")) + }) + + t.Run("get invalid user", func(t *testing.T) { + _, err := adapter.SelectUserByID(context.Background(), "mock") + assert.Error(t, err) + }) + + t.Run("invald user update", func(t *testing.T) { + _, err := adapter.UpsertUser(context.Background(), domain.UserAccess{ + ID: "mock", + AccessToken: "BRuh", + }) + assert.Error(t, err) + }) + + t.Run("update user with timeout", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 0*time.Second) + defer cancel() + _, err := adapter.UpsertUser(ctx, domain.UserAccess{ + ID: "mock", + AccessToken: "BRuh", + RefreshToken: "BRUH", + TokenType: "mock", + Scope: "mock", + ExpiresAt: 123456, + }) + assert.Error(t, err) + }) + + t.Run("update user", func(t *testing.T) { + _, err := adapter.UpsertUser(context.Background(), domain.UserAccess{ + ID: "mock", + AccessToken: "BRuh", + RefreshToken: "BRUH", + TokenType: "mock", + Scope: "mock", + ExpiresAt: 123456, + }) + assert.NoError(t, err) + }) + + t.Run("get updated user", func(t *testing.T) { + u, err := adapter.SelectUserByID(context.Background(), "mock") + assert.NoError(t, err) + assert.Equal(t, "BRuh", u.AccessToken) + }) + + adapter.DeleteUserByID(context.Background(), "mock") +} diff --git a/backend/services/auth/web/core/domain/error.go b/backend/services/auth/web/core/domain/error.go new file mode 100644 index 0000000..b9a6aae --- /dev/null +++ b/backend/services/auth/web/core/domain/error.go @@ -0,0 +1,13 @@ +package domain + +import "fmt" + +type InvalidModelFieldError struct { + Model string + Field string + Reason string +} + +func (e *InvalidModelFieldError) Error() string { + return fmt.Sprintf("invald %s field %s. Reason: %s", e.Model, e.Field, e.Reason) +} diff --git a/backend/services/auth/web/core/domain/user.go b/backend/services/auth/web/core/domain/user.go new file mode 100644 index 0000000..f1f3c4f --- /dev/null +++ b/backend/services/auth/web/core/domain/user.go @@ -0,0 +1,88 @@ +package domain + +import ( + "encoding/json" + "strings" +) + +type UserAccess struct { + ID string `json:"id" mapstructure:"id"` + AccessToken string `json:"access_token" mapstructure:"access_token"` + RefreshToken string `json:"refresh_token" mapstructure:"refresh_token"` + TokenType string `json:"token_type" mapstructure:"token_type"` + Scope string `json:"scope" mapstructure:"scope"` + ExpiresAt int64 `json:"expires_at" mapstructure:"expires_at"` + ApiDomain string `json:"api_domain" mapstructure:"api_domain"` +} + +func (u UserAccess) ToJSON() []byte { + buf, _ := json.Marshal(u) + return buf +} + +func (u *UserAccess) Validate() error { + u.ID = strings.TrimSpace(u.ID) + u.AccessToken = strings.TrimSpace(u.AccessToken) + u.RefreshToken = strings.TrimSpace(u.RefreshToken) + u.TokenType = strings.TrimSpace(u.TokenType) + u.Scope = strings.TrimSpace(u.Scope) + u.ApiDomain = strings.TrimSpace(u.ApiDomain) + + if u.ID == "" { + return &InvalidModelFieldError{ + Model: "User", + Field: "ID", + Reason: "Should not be empty", + } + } + + if u.AccessToken == "" { + return &InvalidModelFieldError{ + Model: "User", + Field: "OAuth Access Token", + Reason: "Should not be empty", + } + } + + if u.RefreshToken == "" { + return &InvalidModelFieldError{ + Model: "User", + Field: "OAuth Refresh Token", + Reason: "Should not be empty", + } + } + + if u.TokenType == "" { + return &InvalidModelFieldError{ + Model: "User", + Field: "OAuth Token Type", + Reason: "Should not be empty", + } + } + + if u.Scope == "" { + return &InvalidModelFieldError{ + Model: "User", + Field: "OAuth Scope", + Reason: "Should not be empty", + } + } + + if u.ExpiresAt < 1 { + return &InvalidModelFieldError{ + Model: "User", + Field: "OAuth ExpiresAt", + Reason: "Invalid expiresAt value", + } + } + + if u.ApiDomain == "" { + return &InvalidModelFieldError{ + Model: "User", + Field: "OAuth Domain", + Reason: "Should not be empty", + } + } + + return nil +} diff --git a/backend/services/auth/web/core/port/input.go b/backend/services/auth/web/core/port/input.go new file mode 100644 index 0000000..2970a3c --- /dev/null +++ b/backend/services/auth/web/core/port/input.go @@ -0,0 +1,14 @@ +package port + +import ( + "context" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" +) + +type UserAccessService interface { + CreateUser(ctx context.Context, user domain.UserAccess) error + GetUser(ctx context.Context, uid string) (domain.UserAccess, error) + UpdateUser(ctx context.Context, user domain.UserAccess) (domain.UserAccess, error) + DeleteUser(ctx context.Context, uid string) error +} diff --git a/backend/services/auth/web/core/port/output.go b/backend/services/auth/web/core/port/output.go new file mode 100644 index 0000000..6be9054 --- /dev/null +++ b/backend/services/auth/web/core/port/output.go @@ -0,0 +1,14 @@ +package port + +import ( + "context" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" +) + +type UserAccessServiceAdapter interface { + InsertUser(ctx context.Context, user domain.UserAccess) error + SelectUserByID(ctx context.Context, uid string) (domain.UserAccess, error) + UpsertUser(ctx context.Context, user domain.UserAccess) (domain.UserAccess, error) + DeleteUserByID(ctx context.Context, uid string) error +} diff --git a/backend/services/auth/web/core/service/error.go b/backend/services/auth/web/core/service/error.go new file mode 100644 index 0000000..496ab29 --- /dev/null +++ b/backend/services/auth/web/core/service/error.go @@ -0,0 +1,12 @@ +package service + +import "fmt" + +type InvalidServiceParameterError struct { + Name string + Reason string +} + +func (e *InvalidServiceParameterError) Error() string { + return fmt.Sprintf("invald service parameter %s. Reason: %s", e.Name, e.Reason) +} diff --git a/backend/services/auth/web/core/service/user.go b/backend/services/auth/web/core/service/user.go new file mode 100644 index 0000000..fe4467f --- /dev/null +++ b/backend/services/auth/web/core/service/user.go @@ -0,0 +1,229 @@ +package service + +import ( + "context" + "errors" + "strings" + "sync" + + plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/port" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" +) + +var _ErrOperationTimeout = errors.New("operation timeout") + +type userService struct { + adapter port.UserAccessServiceAdapter + encryptor crypto.Encryptor + logger plog.Logger +} + +func NewUserService( + adapter port.UserAccessServiceAdapter, + encryptor crypto.Encryptor, + logger plog.Logger, +) port.UserAccessService { + return userService{ + adapter: adapter, + encryptor: encryptor, + logger: logger, + } +} + +func (s userService) CreateUser(ctx context.Context, user domain.UserAccess) error { + s.logger.Debugf("validating user %s to perform a persist action", user.ID) + if err := user.Validate(); err != nil { + return err + } + + var wg sync.WaitGroup + errChan := make(chan error, 2) + atokenChan := make(chan string, 1) + rtokenChan := make(chan string, 1) + + go func() { + wg.Add(1) + defer wg.Done() + aToken, err := s.encryptor.Encrypt(user.AccessToken) + if err != nil { + errChan <- err + return + } + atokenChan <- aToken + }() + + go func() { + wg.Add(1) + defer wg.Done() + rToken, err := s.encryptor.Encrypt(user.RefreshToken) + if err != nil { + errChan <- err + return + } + rtokenChan <- rToken + }() + + wg.Wait() + + select { + case err := <-errChan: + return err + case <-ctx.Done(): + return _ErrOperationTimeout + default: + } + + s.logger.Debugf("user %s is valid. Persisting to database: %s", user.ID, user.AccessToken) + if err := s.adapter.InsertUser(ctx, domain.UserAccess{ + ID: user.ID, + AccessToken: <-atokenChan, + RefreshToken: <-rtokenChan, + TokenType: user.TokenType, + Scope: user.Scope, + ExpiresAt: user.ExpiresAt, + ApiDomain: user.ApiDomain, + }); err != nil { + return err + } + + return nil +} + +func (s userService) GetUser(ctx context.Context, uid string) (domain.UserAccess, error) { + s.logger.Debugf("trying to select user with id: %s", uid) + id := strings.TrimSpace(uid) + + if id == "" { + return domain.UserAccess{}, &InvalidServiceParameterError{ + Name: "UID", + Reason: "Should not be blank", + } + } + + var wg sync.WaitGroup + errChan := make(chan error, 2) + atokenChan := make(chan string, 1) + rtokenChan := make(chan string, 1) + + user, err := s.adapter.SelectUserByID(ctx, id) + if err != nil { + return user, err + } + + s.logger.Debugf("found a user: %v", user) + + go func() { + wg.Add(1) + defer wg.Done() + aToken, err := s.encryptor.Decrypt(user.AccessToken) + if err != nil { + errChan <- err + return + } + atokenChan <- aToken + }() + + go func() { + wg.Add(1) + defer wg.Done() + rToken, err := s.encryptor.Decrypt(user.RefreshToken) + if err != nil { + errChan <- err + return + } + rtokenChan <- rToken + }() + + wg.Wait() + + select { + case err := <-errChan: + return domain.UserAccess{}, err + case <-ctx.Done(): + return domain.UserAccess{}, _ErrOperationTimeout + default: + return domain.UserAccess{ + ID: user.ID, + AccessToken: <-atokenChan, + RefreshToken: <-rtokenChan, + TokenType: user.TokenType, + Scope: user.Scope, + ExpiresAt: user.ExpiresAt, + ApiDomain: user.ApiDomain, + }, nil + } +} + +func (s userService) UpdateUser(ctx context.Context, user domain.UserAccess) (domain.UserAccess, error) { + s.logger.Debugf("validating user %s to perform an update action", user.ID) + if err := user.Validate(); err != nil { + return domain.UserAccess{}, err + } + + var wg sync.WaitGroup + errChan := make(chan error, 2) + atokenChan := make(chan string, 1) + rtokenChan := make(chan string, 1) + + go func() { + wg.Add(1) + defer wg.Done() + aToken, err := s.encryptor.Encrypt(user.AccessToken) + if err != nil { + errChan <- err + return + } + atokenChan <- aToken + }() + + go func() { + wg.Add(1) + defer wg.Done() + rToken, err := s.encryptor.Encrypt(user.RefreshToken) + if err != nil { + errChan <- err + return + } + rtokenChan <- rToken + }() + + select { + case err := <-errChan: + return user, err + case <-ctx.Done(): + return user, _ErrOperationTimeout + default: + } + + s.logger.Debugf("user %s is valid to perform an update action", user.ID) + if _, err := s.adapter.UpsertUser(ctx, domain.UserAccess{ + ID: user.ID, + AccessToken: <-atokenChan, + RefreshToken: <-rtokenChan, + TokenType: user.TokenType, + Scope: user.Scope, + ExpiresAt: user.ExpiresAt, + ApiDomain: user.ApiDomain, + }); err != nil { + return user, err + } + + return user, nil +} + +func (s userService) DeleteUser(ctx context.Context, uid string) error { + id := strings.TrimSpace(uid) + s.logger.Debugf("validating uid %s to perform a delete action", id) + + if id == "" { + return &InvalidServiceParameterError{ + Name: "UID", + Reason: "Should not be blank", + } + } + + s.logger.Debugf("uid %s is valid to perform a delete action", id) + return s.adapter.DeleteUserByID(ctx, uid) +} diff --git a/backend/services/auth/web/core/service/user_test.go b/backend/services/auth/web/core/service/user_test.go new file mode 100644 index 0000000..8ce9ee0 --- /dev/null +++ b/backend/services/auth/web/core/service/user_test.go @@ -0,0 +1,106 @@ +package service + +import ( + "context" + "testing" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" + "github.com/stretchr/testify/assert" +) + +type mockEncryptor struct{} + +func (e mockEncryptor) Encrypt(text string) (string, error) { + return string(text), nil +} + +func (e mockEncryptor) Decrypt(ciphertext string) (string, error) { + return string(ciphertext), nil +} + +type mockAdapter struct { +} + +var user = domain.UserAccess{ + ID: "mock", + AccessToken: "mock", + RefreshToken: "mock", + TokenType: "mock", + Scope: "mock", + ExpiresAt: 1000000, + ApiDomain: "pipedrive", +} + +func (m mockAdapter) InsertUser(ctx context.Context, user domain.UserAccess) error { + return nil +} + +func (m mockAdapter) SelectUserByID(ctx context.Context, uid string) (domain.UserAccess, error) { + return user, nil +} + +func (m mockAdapter) UpsertUser(ctx context.Context, user domain.UserAccess) (domain.UserAccess, error) { + return domain.UserAccess{ + ID: "mock", + AccessToken: "mock", + }, nil +} + +func (m mockAdapter) DeleteUserByID(ctx context.Context, uid string) error { + return nil +} + +func TestUserService(t *testing.T) { + service := NewUserService(mockAdapter{}, mockEncryptor{}, log.NewEmptyLogger()) + + t.Run("save user", func(t *testing.T) { + assert.NoError(t, service.CreateUser(context.Background(), user)) + }) + + t.Run("get user", func(t *testing.T) { + u, err := service.GetUser(context.Background(), "mock") + assert.NoError(t, err) + assert.Equal(t, user, u) + }) + + t.Run("get user with timeout", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 0*time.Second) + defer cancel() + _, err := service.GetUser(ctx, "mock") + assert.Error(t, err) + }) + + t.Run("update user token", func(t *testing.T) { + _, err := service.UpdateUser(context.Background(), domain.UserAccess{ + ID: "mock", + AccessToken: "mock", + RefreshToken: "mock", + TokenType: "mock", + Scope: "mock", + ExpiresAt: 100000, + ApiDomain: "pipedrive", + }) + assert.NoError(t, err) + }) + + t.Run("update user token with timeout", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 0*time.Second) + defer cancel() + _, err := service.UpdateUser(ctx, domain.UserAccess{ + ID: "mock", + AccessToken: "mock", + RefreshToken: "mock", + TokenType: "mock", + Scope: "mock", + ExpiresAt: 100000, + ApiDomain: "pipedrive", + }) + assert.Error(t, err) + }) + + t.Run("delete user", func(t *testing.T) { + assert.NoError(t, service.DeleteUser(context.Background(), "mock")) + }) +} diff --git a/backend/services/auth/web/handler/delete.go b/backend/services/auth/web/handler/delete.go new file mode 100644 index 0000000..a37b475 --- /dev/null +++ b/backend/services/auth/web/handler/delete.go @@ -0,0 +1,32 @@ +package handler + +import ( + "context" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/port" + "go-micro.dev/v4/client" +) + +type UserDeleteHandler struct { + service port.UserAccessService + client client.Client + logger log.Logger +} + +func NewUserDeleteHandler( + service port.UserAccessService, + client client.Client, + logger log.Logger, +) UserDeleteHandler { + return UserDeleteHandler{ + service: service, + client: client, + logger: logger, + } +} + +func (u UserDeleteHandler) DeleteUser(ctx context.Context, uid *string, res *interface{}) error { + u.logger.Debugf("removing user %s", *uid) + return u.service.DeleteUser(ctx, *uid) +} diff --git a/backend/services/auth/web/handler/select.go b/backend/services/auth/web/handler/select.go new file mode 100644 index 0000000..b90851c --- /dev/null +++ b/backend/services/auth/web/handler/select.go @@ -0,0 +1,84 @@ +package handler + +import ( + "context" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/port" + pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" + "go-micro.dev/v4/client" + "golang.org/x/sync/singleflight" +) + +var group singleflight.Group + +type UserSelectHandler struct { + service port.UserAccessService + client client.Client + pipedriveAuth pclient.PipedriveAuthClient + logger log.Logger +} + +func NewUserSelectHandler( + service port.UserAccessService, + client client.Client, + pipedriveAuth pclient.PipedriveAuthClient, + logger log.Logger, +) UserSelectHandler { + return UserSelectHandler{ + service: service, + client: client, + pipedriveAuth: pipedriveAuth, + logger: logger, + } +} + +func (u UserSelectHandler) GetUser(ctx context.Context, uid *string, res *domain.UserAccess) error { + user, err, _ := group.Do(*uid, func() (interface{}, error) { + user, err := u.service.GetUser(ctx, *uid) + if err != nil { + u.logger.Errorf("could not get user with id: %s. Reason: %s", *uid, err.Error()) + return nil, err + } + + if user.ExpiresAt <= time.Now().UnixMilli() { + u.logger.Debug("user token has expired. Trying to refresh!") + token, terr := u.pipedriveAuth.RefreshAccessToken(ctx, user.RefreshToken) + if terr != nil { + u.logger.Errorf("could not refresh user's %s token. Reason: %s", *uid, terr.Error()) + return nil, terr + } + + u.logger.Debugf("user's %s token has been refreshed", *uid) + access := domain.UserAccess{ + ID: user.ID, + AccessToken: token.AccessToken, + RefreshToken: token.RefreshToken, + TokenType: token.TokenType, + Scope: token.Scope, + ApiDomain: token.ApiDomain, + ExpiresAt: time.Now().Local().Add(time.Second * time.Duration(token.ExpiresIn-700)).UnixMilli(), + } + + _, err := u.service.UpdateUser(ctx, access) + if err != nil { + u.logger.Debugf("could not persist a new user's %s token. Reason: %s. Sending a fallback message!", *uid, err.Error()) + return nil, err + } + + u.logger.Debugf("user's %s token has been updated", *uid) + return access, nil + } + + return user, nil + }) + + if usr, ok := user.(domain.UserAccess); ok { + *res = usr + return nil + } + + return err +} diff --git a/backend/services/auth/web/handler/select_test.go b/backend/services/auth/web/handler/select_test.go new file mode 100644 index 0000000..5754c8a --- /dev/null +++ b/backend/services/auth/web/handler/select_test.go @@ -0,0 +1,48 @@ +package handler + +import ( + "context" + "testing" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/adapter" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/service" + pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" + "github.com/stretchr/testify/assert" +) + +type mockEncryptor struct{} + +func (e mockEncryptor) Encrypt(text string) (string, error) { + return string(text), nil +} + +func (e mockEncryptor) Decrypt(ciphertext string) (string, error) { + return string(ciphertext), nil +} + +func TestSelectCaching(t *testing.T) { + adapter := adapter.NewMemoryUserAdapter() + service := service.NewUserService(adapter, mockEncryptor{}, log.NewEmptyLogger()) + pclient := pclient.NewPipedriveAuthClient("clientID", "clientSecret") + + sel := NewUserSelectHandler(service, nil, pclient, log.NewEmptyLogger()) + + service.CreateUser(context.Background(), domain.UserAccess{ + ID: "mock", + AccessToken: "mock", + RefreshToken: "mock", + TokenType: "mock", + Scope: "mock", + ExpiresAt: time.Now().Add(24 * time.Hour).UnixMilli(), + }) + + t.Run("get user", func(t *testing.T) { + var res domain.UserAccess + id := "mock" + assert.NoError(t, sel.GetUser(context.Background(), &id, &res)) + assert.NotEmpty(t, res) + }) +} diff --git a/backend/services/auth/web/message/error.go b/backend/services/auth/web/message/error.go new file mode 100644 index 0000000..81e095e --- /dev/null +++ b/backend/services/auth/web/message/error.go @@ -0,0 +1,5 @@ +package message + +import "errors" + +var _ErrInvalidHandlerPayload = errors.New("invalid handler payload") diff --git a/backend/services/auth/web/message/insert.go b/backend/services/auth/web/message/insert.go new file mode 100644 index 0000000..91884a2 --- /dev/null +++ b/backend/services/auth/web/message/insert.go @@ -0,0 +1,30 @@ +package message + +import ( + "context" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/port" + "github.com/mitchellh/mapstructure" +) + +type InsertMessageHandler struct { + service port.UserAccessService +} + +func BuildInsertMessageHandler(service port.UserAccessService) InsertMessageHandler { + return InsertMessageHandler{ + service: service, + } +} + +func (i InsertMessageHandler) GetHandler() func(context.Context, interface{}) error { + return func(ctx context.Context, payload interface{}) error { + var user domain.UserAccess + if err := mapstructure.Decode(payload, &user); err != nil { + return err + } + _, err := i.service.UpdateUser(ctx, user) + return err + } +} diff --git a/backend/services/auth/web/server.go b/backend/services/auth/web/server.go new file mode 100644 index 0000000..a37fef5 --- /dev/null +++ b/backend/services/auth/web/server.go @@ -0,0 +1,55 @@ +package web + +import ( + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/rpc" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/adapter" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/port" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/service" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/handler" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/message" + pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" + "go-micro.dev/v4/cache" + mclient "go-micro.dev/v4/client" +) + +type AuthRPCServer struct { + service port.UserAccessService + pipedriveAuth pclient.PipedriveAuthClient + logger log.Logger +} + +func NewAuthRPCServer(persistenceConfig *config.PersistenceConfig, oauthConfig *config.OAuthCredentialsConfig, logger log.Logger) rpc.RPCEngine { + adptr := adapter.NewMemoryUserAdapter() + if persistenceConfig.Persistence.URL != "" { + adptr = adapter.NewMongoUserAdapter(persistenceConfig.Persistence.URL) + } + + service := service.NewUserService(adptr, crypto.NewAesEncryptor([]byte(oauthConfig.Credentials.ClientSecret)), logger) + return AuthRPCServer{ + service: service, + pipedriveAuth: pclient.NewPipedriveAuthClient( + oauthConfig.Credentials.ClientID, oauthConfig.Credentials.ClientSecret, + ), + logger: logger, + } +} + +func (a AuthRPCServer) BuildMessageHandlers() []rpc.RPCMessageHandler { + return []rpc.RPCMessageHandler{ + { + Topic: "insert-auth", + Queue: "pipedrive-auth", + Handler: message.BuildInsertMessageHandler(a.service).GetHandler(), + }, + } +} + +func (a AuthRPCServer) BuildHandlers(client mclient.Client, cache cache.Cache) []interface{} { + return []interface{}{ + handler.NewUserSelectHandler(a.service, client, a.pipedriveAuth, a.logger), + handler.NewUserDeleteHandler(a.service, client, a.logger), + } +} diff --git a/backend/services/builder/cmd/root.go b/backend/services/builder/cmd/root.go new file mode 100644 index 0000000..5b12c31 --- /dev/null +++ b/backend/services/builder/cmd/root.go @@ -0,0 +1,30 @@ +package cmd + +import ( + "os" + + "github.com/urfave/cli/v2" +) + +func GetCommands() cli.Commands { + return []*cli.Command{ + Server(), + } +} + +func Run() error { + app := &cli.App{ + Name: "onlyoffice:builder", + Description: "Description", + Authors: []*cli.Author{ + { + Name: "Ascensio Systems SIA", + Email: "support@onlyoffice.com", + }, + }, + HideVersion: true, + Commands: GetCommands(), + } + + return app.Run(os.Args) +} diff --git a/backend/services/builder/cmd/server.go b/backend/services/builder/cmd/server.go new file mode 100644 index 0000000..8018f2f --- /dev/null +++ b/backend/services/builder/cmd/server.go @@ -0,0 +1,68 @@ +package cmd + +import ( + "context" + "net/http" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/rpc" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/builder/web" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" + "github.com/urfave/cli/v2" + "go-micro.dev/v4" + "go.uber.org/fx" + "golang.org/x/sync/errgroup" +) + +func Server() *cli.Command { + return &cli.Command{ + Name: "server", + Usage: "starts a new rpc server instance", + Category: "server", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config_path", + Usage: "sets custom configuration path", + Aliases: []string{"config", "conf", "c"}, + }, + &cli.StringFlag{ + Name: "environment", + Usage: "sets servers environment (development, testing, production)", + Aliases: []string{"env", "e"}, + }, + }, + Action: func(c *cli.Context) error { + var ( + CONFIG_PATH = c.String("config_path") + // ENVIRONMENT = c.String("environment") + ) + + fx.New( + pkg.Bootstrap(CONFIG_PATH), + fx.Provide(shared.BuildNewOnlyofficeConfig(CONFIG_PATH)), + fx.Provide(rpc.NewService), + fx.Provide(web.NewConfigRPCServer), + fx.Invoke(func(lifecycle fx.Lifecycle, service micro.Service, repl *http.Server, logger log.Logger) { + lifecycle.Append(fx.Hook{ + OnStart: func(ctx context.Context) error { + go repl.ListenAndServe() + go service.Run() + return nil + }, + OnStop: func(ctx context.Context) error { + g, gCtx := errgroup.WithContext(ctx) + g.Go(func() error { + return repl.Shutdown(gCtx) + }) + return g.Wait() + }, + }) + }), + fx.NopLogger, + ).Run() + + return nil + }, + } +} diff --git a/backend/services/builder/config/config.example.yml b/backend/services/builder/config/config.example.yml new file mode 100644 index 0000000..51166da --- /dev/null +++ b/backend/services/builder/config/config.example.yml @@ -0,0 +1,35 @@ +namespace: "pipedrive" +name: "builder" +version: 0 +address: ":6060" +repl_address: ":7979" +debug: false +persistence: + url: "" +registry: + addresses: ["127.0.0.1:8500"] + type: 2 +messaging: + type: 1 + addresses: ["0.0.0.0:5672"] + durable: true +tracer: + enable: false + address: "http://127.0.0.1:9411/api/v2/spans" + type: 1 +resilience: + rate_limiter: + limit: 500 + circuit_breaker: + timeout: 2500 +logger: + name: "builder-logger" + level: 1 + color: true +oauth: + client_id: "" + client_secret: "" +onlyoffice: + builder: + callback_url: "" + gateway_url: "" diff --git a/backend/services/builder/deployment/.keep b/backend/services/builder/deployment/.keep new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/builder/docs/.keep b/backend/services/builder/docs/.keep new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/builder/main.go b/backend/services/builder/main.go new file mode 100644 index 0000000..9053b39 --- /dev/null +++ b/backend/services/builder/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "log" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/builder/cmd" +) + +func main() { + if err := cmd.Run(); err != nil { + log.Fatalln(err) + } +} diff --git a/backend/services/builder/script/.keep b/backend/services/builder/script/.keep new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/builder/web/handler/build.go b/backend/services/builder/web/handler/build.go new file mode 100644 index 0000000..57d1f0b --- /dev/null +++ b/backend/services/builder/web/handler/build.go @@ -0,0 +1,210 @@ +package handler + +import ( + "context" + "fmt" + "path/filepath" + "strings" + "sync" + "time" + + plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client/model" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/constants" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" + "github.com/mileusna/useragent" + "go-micro.dev/v4/client" + "golang.org/x/sync/singleflight" +) + +type ConfigHandler struct { + namespace string + logger plog.Logger + client client.Client + apiClient pclient.PipedriveApiClient + jwtManager crypto.JwtManager + gatewayURL string + group singleflight.Group +} + +func NewConfigHandler( + namespace string, + logger plog.Logger, + client client.Client, + jwtManager crypto.JwtManager, + gatewayURL string, +) ConfigHandler { + return ConfigHandler{ + namespace: namespace, + logger: logger, + client: client, + apiClient: pclient.NewPipedriveApiClient(), + jwtManager: jwtManager, + gatewayURL: gatewayURL, + } +} + +func (c ConfigHandler) processConfig(user response.UserResponse, req request.BuildConfigRequest, ctx context.Context) (response.BuildConfigResponse, error) { + var config response.BuildConfigResponse + var wg sync.WaitGroup + usrChan := make(chan model.User, 1) + settingsChan := make(chan response.DocSettingsResponse, 1) + errorsChan := make(chan error, 2) + + go func() { + wg.Add(1) + defer wg.Done() + u, err := c.apiClient.GetMe(ctx, model.Token{ + AccessToken: user.AccessToken, + RefreshToken: user.RefreshToken, + TokenType: user.TokenType, + Scope: user.Scope, + ApiDomain: user.ApiDomain, + }) + + if err != nil { + c.logger.Debugf("could not get pipedrive user: %s", err.Error()) + errorsChan <- err + return + } + + c.logger.Debugf("populating pipedrive user %d channel", u.ID) + usrChan <- u + c.logger.Debugf("successfully populated pipedrive channel") + }() + + go func() { + wg.Add(1) + defer wg.Done() + var docs response.DocSettingsResponse + if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:settings", c.namespace), "SettingsSelectHandler.GetSettings", fmt.Sprint(req.CID)), &docs); err != nil { + c.logger.Debugf("could not document server settings: %s", err.Error()) + errorsChan <- err + return + } + + c.logger.Debugf("populating document server %d settings channel", req.CID) + settingsChan <- docs + c.logger.Debugf("successfully populated document server settings channel") + }() + + c.logger.Debugf("waiting for goroutines to finish execution") + wg.Wait() + c.logger.Debugf("goroutines have finished the execution") + + select { + case err := <-errorsChan: + return config, err + default: + c.logger.Debugf("select default") + } + + usr := <-usrChan + settings := <-settingsChan + t := "desktop" + ua := useragent.Parse(req.UserAgent) + + if ua.Mobile || ua.Tablet { + t = "mobile" + } + + downloadToken := request.PipedriveTokenContext{ + UID: usr.ID, + CID: usr.CompanyID, + } + + downloadToken.IssuedAt = 0 + downloadToken.ExpiresAt = time.Now().Add(4 * time.Minute).UnixMilli() + tkn, _ := c.jwtManager.Sign(settings.DocSecret, downloadToken) + + config = response.BuildConfigResponse{ + Document: response.Document{ + Key: req.DocKey, + Title: req.Filename, + URL: fmt.Sprintf("%s/download?cid=%d&fid=%s&token=%s", c.gatewayURL, usr.CompanyID, req.FileID, tkn), + }, + EditorConfig: response.EditorConfig{ + User: response.User{ + ID: fmt.Sprint(usr.ID + usr.CompanyID), + Name: usr.Name, + }, + CallbackURL: fmt.Sprintf( + "%s/callback?cid=%d&did=%s&fid=%s&filename=%s", + c.gatewayURL, usr.CompanyID, req.Deal, req.FileID, req.Filename, + ), + Customization: response.Customization{ + Goback: response.Goback{ + RequestClose: true, + }, + Plugins: false, + HideRightMenu: true, + }, + Lang: usr.Language.Lang, + }, + Type: t, + ServerURL: settings.DocAddress, + } + + if strings.TrimSpace(req.Filename) != "" { + ext := strings.ReplaceAll(filepath.Ext(req.Filename), ".", "") + fileType, err := constants.GetFileType(ext) + if err != nil { + return config, err + } + config.Document.FileType = ext + config.Document.Permissions = response.Permissions{ + Edit: constants.IsExtensionEditable(ext), + Comment: true, + Download: true, + Print: false, + Review: false, + Copy: true, + ModifyContentControl: true, + ModifyFilter: true, + } + config.DocumentType = fileType + } + + token, err := c.jwtManager.Sign(settings.DocSecret, config) + if err != nil { + c.logger.Debugf("could not sign document server config: %s", err.Error()) + return config, err + } + + config.Token = token + return config, nil +} + +func (c ConfigHandler) BuildConfig(ctx context.Context, payload request.BuildConfigRequest, res *response.BuildConfigResponse) error { + c.logger.Debugf("processing a docs config: %s", payload.Filename) + + config, err, _ := c.group.Do(fmt.Sprint(payload.UID+payload.CID), func() (interface{}, error) { + req := c.client.NewRequest( + fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", + fmt.Sprint(payload.UID+payload.CID), + ) + + var ures response.UserResponse + if err := c.client.Call(ctx, req, &ures); err != nil { + c.logger.Debugf("could not get user %d access info: %s", payload.UID+payload.CID, err.Error()) + return nil, err + } + + config, err := c.processConfig(ures, payload, ctx) + if err != nil { + return nil, err + } + + return config, nil + }) + + if cfg, ok := config.(response.BuildConfigResponse); ok { + *res = cfg + return nil + } + + return err +} diff --git a/backend/services/builder/web/handler/error.go b/backend/services/builder/web/handler/error.go new file mode 100644 index 0000000..b313b08 --- /dev/null +++ b/backend/services/builder/web/handler/error.go @@ -0,0 +1,7 @@ +package handler + +import "errors" + +var ErrInvalidContextValue = errors.New("could not extract context value") +var ErrEmptyIdValue = errors.New("could not perform current action with an empty id") +var ErrUnauthorizedAccess = errors.New("unauthorized file access") diff --git a/backend/services/builder/web/server.go b/backend/services/builder/web/server.go new file mode 100644 index 0000000..39a0dfe --- /dev/null +++ b/backend/services/builder/web/server.go @@ -0,0 +1,45 @@ +package web + +import ( + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/rpc" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/builder/web/handler" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" + "go-micro.dev/v4/cache" + mclient "go-micro.dev/v4/client" +) + +type ConfigRPCServer struct { + namespace string + jwtManager crypto.JwtManager + logger plog.Logger + gatewayURL string +} + +func NewConfigRPCServer( + serverConfig *config.ServerConfig, + credentialsConfig *config.OAuthCredentialsConfig, + onlyofficeConfig *shared.OnlyofficeConfig, + logger log.Logger, +) rpc.RPCEngine { + jwtManager := crypto.NewOnlyofficeJwtManager() + return ConfigRPCServer{ + namespace: serverConfig.Namespace, + jwtManager: jwtManager, + logger: logger, + gatewayURL: onlyofficeConfig.Onlyoffice.Builder.GatewayURL, + } +} + +func (a ConfigRPCServer) BuildMessageHandlers() []rpc.RPCMessageHandler { + return nil +} + +func (a ConfigRPCServer) BuildHandlers(c mclient.Client, cache cache.Cache) []interface{} { + return []interface{}{ + handler.NewConfigHandler(a.namespace, a.logger, c, a.jwtManager, a.gatewayURL), + } +} diff --git a/backend/services/callback/cmd/root.go b/backend/services/callback/cmd/root.go new file mode 100644 index 0000000..91a9de5 --- /dev/null +++ b/backend/services/callback/cmd/root.go @@ -0,0 +1,30 @@ +package cmd + +import ( + "os" + + "github.com/urfave/cli/v2" +) + +func GetCommands() cli.Commands { + return []*cli.Command{ + Server(), + } +} + +func Run() error { + app := &cli.App{ + Name: "onlyoffice:callback", + Description: "Description", + Authors: []*cli.Author{ + { + Name: "Ascensio Systems SIA", + Email: "support@onlyoffice.com", + }, + }, + HideVersion: true, + Commands: GetCommands(), + } + + return app.Run(os.Args) +} diff --git a/backend/services/callback/cmd/server.go b/backend/services/callback/cmd/server.go new file mode 100644 index 0000000..6b197ac --- /dev/null +++ b/backend/services/callback/cmd/server.go @@ -0,0 +1,68 @@ +package cmd + +import ( + "context" + "net/http" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + chttp "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/http" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/callback/web" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" + "github.com/urfave/cli/v2" + "go-micro.dev/v4" + "go.uber.org/fx" + "golang.org/x/sync/errgroup" +) + +func Server() *cli.Command { + return &cli.Command{ + Name: "server", + Usage: "starts a new http server instance", + Category: "server", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config_path", + Usage: "sets custom configuration path", + Aliases: []string{"config", "conf", "c"}, + }, + &cli.StringFlag{ + Name: "environment", + Usage: "sets servers environment (development, testing, production)", + Aliases: []string{"env", "e"}, + }, + }, + Action: func(c *cli.Context) error { + var ( + CONFIG_PATH = c.String("config_path") + // ENVIRONMENT = c.String("environment") + ) + + fx.New( + pkg.Bootstrap(CONFIG_PATH), + fx.Provide(shared.BuildNewOnlyofficeConfig(CONFIG_PATH)), + fx.Provide(chttp.NewService), + fx.Provide(web.NewServer), + fx.Invoke(func(lifecycle fx.Lifecycle, service micro.Service, repl *http.Server, logger log.Logger) { + lifecycle.Append(fx.Hook{ + OnStart: func(ctx context.Context) error { + go repl.ListenAndServe() + go service.Run() + return nil + }, + OnStop: func(ctx context.Context) error { + g, gCtx := errgroup.WithContext(ctx) + g.Go(func() error { + return repl.Shutdown(gCtx) + }) + return g.Wait() + }, + }) + }), + fx.NopLogger, + ).Run() + + return nil + }, + } +} diff --git a/backend/services/callback/config/config.example.yml b/backend/services/callback/config/config.example.yml new file mode 100644 index 0000000..9d5e7de --- /dev/null +++ b/backend/services/callback/config/config.example.yml @@ -0,0 +1,37 @@ +namespace: "pipedrive" +name: "callback" +version: 0 +address: ":5044" +repl_address: ":3132" +debug: false +persistence: + url: "" +registry: + addresses: ["127.0.0.1:8500"] + type: 2 +messaging: + type: 1 + addresses: ["0.0.0.0:5672"] + durable: true +tracer: + enable: false + address: "http://127.0.0.1:9411/api/v2/spans" + type: 1 +resilience: + rate_limiter: + limit: 1000 + iplimit: 100 + circuit_breaker: + timeout: 2500 +cors: + origins: ["", ""] +logger: + name: "callback-logger" + level: 1 + color: true +worker: + addresses: ["0.0.0.0:6379"] +onlyoffice: + callback: + max_size: 210000000000 + upload_timeout: 120 diff --git a/backend/services/callback/deployment/.keep b/backend/services/callback/deployment/.keep new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/callback/docs/README.md b/backend/services/callback/docs/README.md new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/callback/main.go b/backend/services/callback/main.go new file mode 100644 index 0000000..2577b6d --- /dev/null +++ b/backend/services/callback/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "log" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/callback/cmd" +) + +func main() { + if err := cmd.Run(); err != nil { + log.Fatalln(err) + } +} diff --git a/backend/services/callback/scripts/Makefile b/backend/services/callback/scripts/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/callback/web/controller/callback.go b/backend/services/callback/web/controller/callback.go new file mode 100644 index 0000000..a9b1214 --- /dev/null +++ b/backend/services/callback/web/controller/callback.go @@ -0,0 +1,155 @@ +package controller + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strings" + "time" + + plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/worker" + pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/message" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" + "go-micro.dev/v4/client" +) + +type callbackController struct { + namespace string + maxSize int64 + logger plog.Logger + client client.Client + pipedriveAPI pclient.PipedriveApiClient + jwtManager crypto.JwtManager +} + +func NewCallbackController( + namespace string, + maxSize int64, + logger plog.Logger, + client client.Client, +) *callbackController { + return &callbackController{ + namespace: namespace, + maxSize: maxSize, + logger: logger, + client: client, + pipedriveAPI: pclient.NewPipedriveApiClient(), + jwtManager: crypto.NewOnlyofficeJwtManager(), + } +} + +func (c callbackController) BuildPostHandleCallback(enqueuer worker.BackgroundEnqueuer) http.HandlerFunc { + return func(rw http.ResponseWriter, r *http.Request) { + query := r.URL.Query() + cid, did, fid := strings.TrimSpace(query.Get("cid")), strings.TrimSpace(query.Get("did")), strings.TrimSpace(query.Get("fid")) + rw.Header().Set("Content-Type", "application/json") + + var body request.CallbackRequest + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + c.logger.Errorf("could not decode a callback body") + rw.WriteHeader(http.StatusBadRequest) + rw.Write(response.CallbackResponse{ + Error: 1, + }.ToJSON()) + return + } + + if body.Token == "" { + c.logger.Error("invalid callback body token") + rw.WriteHeader(http.StatusBadRequest) + rw.Write(response.CallbackResponse{ + Error: 1, + }.ToJSON()) + return + } + + if cid == "" || did == "" || fid == "" { + c.logger.Error("invalid query parameter") + rw.WriteHeader(http.StatusBadRequest) + rw.Write(response.CallbackResponse{ + Error: 1, + }.ToJSON()) + return + } + + req := c.client.NewRequest(fmt.Sprintf("%s:settings", c.namespace), "SettingsSelectHandler.GetSettings", cid) + var res response.DocSettingsResponse + if err := c.client.Call(r.Context(), req, &res); err != nil { + c.logger.Errorf("could not extract doc server settings %s", cid) + rw.WriteHeader(http.StatusBadRequest) + rw.Write(response.CallbackResponse{ + Error: 1, + }.ToJSON()) + return + } + + if err := c.jwtManager.Verify(res.DocSecret, body.Token, &body); err != nil { + c.logger.Errorf("could not verify callback jwt (%s). Reason: %s", body.Token, err.Error()) + rw.WriteHeader(http.StatusForbidden) + rw.Write(response.CallbackResponse{ + Error: 1, + }.ToJSON()) + return + } + + if err := body.Validate(); err != nil { + c.logger.Errorf("invalid callback body. Reason: %s", err.Error()) + rw.WriteHeader(http.StatusBadRequest) + rw.Write(response.CallbackResponse{ + Error: 1, + }.ToJSON()) + return + } + + if body.Status == 2 { + filename := strings.TrimSpace(r.URL.Query().Get("filename")) + if filename == "" { + rw.WriteHeader(http.StatusInternalServerError) + c.logger.Errorf("callback request %s does not contain a filename", body.Key) + rw.Write(response.CallbackResponse{ + Error: 1, + }.ToJSON()) + return + } + + ctx, cancel := context.WithTimeout(r.Context(), 7*time.Second) + defer cancel() + + usr := body.Users[0] + if usr != "" { + if err := c.pipedriveAPI.ValidateFileSize(ctx, c.maxSize, body.URL); err != nil { + c.logger.Errorf("could not validate file %s: %s", filename, err.Error()) + rw.WriteHeader(http.StatusBadRequest) + rw.Write(response.CallbackResponse{ + Error: 1, + }.ToJSON()) + return + } + if err := enqueuer.EnqueueContext(r.Context(), "pipedrive-callback-upload", message.JobMessage{ + UID: usr, + Deal: did, + FileID: fid, + Filename: filename, + Url: body.URL, + }.ToJSON(), worker.WithMaxRetry(3)); err != nil { + rw.WriteHeader(http.StatusInternalServerError) + c.logger.Errorf("could not enqueue a new job with key %s", body.Key) + rw.Write(response.CallbackResponse{ + Error: 1, + }.ToJSON()) + return + } + } + } + + rw.WriteHeader(http.StatusOK) + rw.Write(response.CallbackResponse{ + Error: 0, + }.ToJSON()) + } +} diff --git a/backend/services/callback/web/server.go b/backend/services/callback/web/server.go new file mode 100644 index 0000000..d8af4e2 --- /dev/null +++ b/backend/services/callback/web/server.go @@ -0,0 +1,84 @@ +package web + +import ( + "net/http" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + chttp "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/http" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/worker" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/callback/web/controller" + workerh "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/callback/web/worker" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" + "github.com/gin-gonic/gin" + "github.com/go-chi/chi/v5" + chimiddleware "github.com/go-chi/chi/v5/middleware" + "go-micro.dev/v4/cache" + "go-micro.dev/v4/client" +) + +type CallbackService struct { + namespace string + mux *chi.Mux + client client.Client + logger log.Logger + worker worker.BackgroundWorker + enqueuer worker.BackgroundEnqueuer + maxSize int64 + uploadTimeout int +} + +// ApplyMiddleware useed to apply http server middlewares. +func (s CallbackService) ApplyMiddleware(middlewares ...func(http.Handler) http.Handler) { + s.mux.Use(middlewares...) +} + +// NewService initializes http server with options. +func NewServer( + serverConfig *config.ServerConfig, + workerConfig *config.WorkerConfig, + onlyofficeConfig *shared.OnlyofficeConfig, + logger log.Logger, +) chttp.ServerEngine { + gin.SetMode(gin.ReleaseMode) + + service := CallbackService{ + namespace: serverConfig.Namespace, + mux: chi.NewRouter(), + logger: logger, + worker: worker.NewBackgroundWorker(workerConfig, logger), + enqueuer: worker.NewBackgroundEnqueuer(workerConfig), + maxSize: onlyofficeConfig.Onlyoffice.Callback.MaxSize, + uploadTimeout: onlyofficeConfig.Onlyoffice.Callback.UploadTimeout, + } + + return service +} + +// NewHandler returns http server engine. +func (s CallbackService) NewHandler(client client.Client, cache cache.Cache) interface { + ServeHTTP(w http.ResponseWriter, r *http.Request) +} { + return s.InitializeServer(client) +} + +// InitializeServer sets all injected dependencies. +func (s *CallbackService) InitializeServer(c client.Client) *chi.Mux { + s.client = c + s.worker.Register("pipedrive-callback-upload", workerh.NewCallbackWorker(s.namespace, c, s.uploadTimeout, s.logger).UploadFile) + s.InitializeRoutes() + s.worker.Run() + return s.mux +} + +// InitializeRoutes builds all http routes. +func (s *CallbackService) InitializeRoutes() { + callbackController := controller.NewCallbackController(s.namespace, s.maxSize, s.logger, s.client) + s.mux.Group(func(r chi.Router) { + r.Use(chimiddleware.Recoverer) + r.NotFound(func(rw http.ResponseWriter, r *http.Request) { + http.Redirect(rw, r.WithContext(r.Context()), "https://onlyoffice.com", http.StatusMovedPermanently) + }) + r.Post("/callback", callbackController.BuildPostHandleCallback(s.enqueuer)) + }) +} diff --git a/backend/services/callback/web/worker/callback.go b/backend/services/callback/web/worker/callback.go new file mode 100644 index 0000000..18478b8 --- /dev/null +++ b/backend/services/callback/web/worker/callback.go @@ -0,0 +1,132 @@ +package worker + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "sync" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client/model" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/message" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" + "go-micro.dev/v4/client" + "go-micro.dev/v4/logger" + "go-micro.dev/v4/util/backoff" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + "go.opentelemetry.io/otel" +) + +type workerContext struct{} + +type callbackWorker struct { + namespace string + client client.Client + pipedriveAPI pclient.PipedriveApiClient + uploadTimeout int + logger log.Logger +} + +func NewWorkerContext() workerContext { + return workerContext{} +} + +func NewCallbackWorker(namespace string, client client.Client, uploadTimeout int, logger log.Logger) callbackWorker { + return callbackWorker{ + namespace: namespace, + client: client, + pipedriveAPI: pclient.NewPipedriveApiClient(), + uploadTimeout: uploadTimeout, + logger: logger, + } +} +func (c callbackWorker) UploadFile(ctx context.Context, payload []byte) error { + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(c.uploadTimeout)*time.Second) + defer cancel() + + tracer := otel.GetTracerProvider().Tracer("pipedrive-onlyoffice/pool") + tctx, span := tracer.Start(ctx, "upload") + defer span.End() + + var msg message.JobMessage + if err := json.Unmarshal(payload, &msg); err != nil { + logger.Errorf("could not notify ws clients. Reason: %s", err.Error()) + return err + } + + c.logger.Debugf("got a new file %s upload job (%s)", msg.Filename, msg.UID) + + var wg sync.WaitGroup + userChan := make(chan response.UserResponse, 1) + sizeChan := make(chan int64, 1) + errChan := make(chan error, 2) + + go func() { + wg.Add(1) + defer wg.Done() + + c.logger.Debugf("trying to get an access token") + req := c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", msg.UID) + var ures response.UserResponse + if err := c.client.Call(tctx, req, &ures, client.WithRetries(3), client.WithBackoff(func(ctx context.Context, req client.Request, attempts int) (time.Duration, error) { + return backoff.Do(attempts), nil + })); err != nil { + errChan <- err + return + } + + c.logger.Debugf("populating user channel") + userChan <- ures + c.logger.Debugf("successfully populated user channel") + }() + + go func() { + wg.Add(1) + defer wg.Done() + + headResp, err := otelhttp.Head(tctx, msg.Url) + if err != nil { + errChan <- err + return + } + + size, err := strconv.ParseInt(headResp.Header.Get("Content-Length"), 10, 64) + if err != nil { + errChan <- err + return + } + + c.logger.Debugf("populating file size channel") + sizeChan <- size + c.logger.Debugf("successfully populated file size channel") + }() + + c.logger.Debugf("worker is waiting for waitgroup") + wg.Wait() + c.logger.Debugf("worker waitgroup ok") + + select { + case err := <-errChan: + c.logger.Debugf("an error from the channel: %s", err.Error()) + return err + default: + c.logger.Debugf("select default") + } + + ures := <-userChan + if err := c.pipedriveAPI.UploadFile(tctx, msg.Url, msg.Deal, msg.FileID, msg.Filename, <-sizeChan, model.Token{ + AccessToken: ures.AccessToken, + RefreshToken: ures.RefreshToken, + TokenType: ures.TokenType, + Scope: ures.Scope, + ApiDomain: ures.ApiDomain, + }); err != nil { + c.logger.Debugf("could not upload an onlyoffice file to zoom: %s", err.Error()) + return err + } + + return nil +} diff --git a/backend/services/gateway/cmd/root.go b/backend/services/gateway/cmd/root.go new file mode 100644 index 0000000..26039ad --- /dev/null +++ b/backend/services/gateway/cmd/root.go @@ -0,0 +1,30 @@ +package cmd + +import ( + "os" + + "github.com/urfave/cli/v2" +) + +func GetCommands() cli.Commands { + return []*cli.Command{ + Server(), + } +} + +func Run() error { + app := &cli.App{ + Name: "onlyoffice:gateway", + Description: "Description", + Authors: []*cli.Author{ + { + Name: "Ascensio Systems SIA", + Email: "support@onlyoffice.com", + }, + }, + HideVersion: true, + Commands: GetCommands(), + } + + return app.Run(os.Args) +} diff --git a/backend/services/gateway/cmd/server.go b/backend/services/gateway/cmd/server.go new file mode 100644 index 0000000..6acced4 --- /dev/null +++ b/backend/services/gateway/cmd/server.go @@ -0,0 +1,68 @@ +package cmd + +import ( + "context" + "net/http" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + chttp "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/http" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/gateway/web" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" + "github.com/urfave/cli/v2" + "go-micro.dev/v4" + "go.uber.org/fx" + "golang.org/x/sync/errgroup" +) + +func Server() *cli.Command { + return &cli.Command{ + Name: "server", + Usage: "starts a new http server instance", + Category: "server", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config_path", + Usage: "sets custom configuration path", + Aliases: []string{"config", "conf", "c"}, + }, + &cli.StringFlag{ + Name: "environment", + Usage: "sets servers environment (development, testing, production)", + Aliases: []string{"env", "e"}, + }, + }, + Action: func(c *cli.Context) error { + var ( + CONFIG_PATH = c.String("config_path") + // ENVIRONMENT = c.String("environment") + ) + + fx.New( + pkg.Bootstrap(CONFIG_PATH), + fx.Provide(shared.BuildNewOnlyofficeConfig(CONFIG_PATH)), + fx.Provide(chttp.NewService), + fx.Provide(web.NewServer), + fx.Invoke(func(lifecycle fx.Lifecycle, service micro.Service, repl *http.Server, logger log.Logger) { + lifecycle.Append(fx.Hook{ + OnStart: func(ctx context.Context) error { + go repl.ListenAndServe() + go service.Run() + return nil + }, + OnStop: func(ctx context.Context) error { + g, gCtx := errgroup.WithContext(ctx) + g.Go(func() error { + return repl.Shutdown(gCtx) + }) + return g.Wait() + }, + }) + }), + fx.NopLogger, + ).Run() + + return nil + }, + } +} diff --git a/backend/services/gateway/config/config.example.yml b/backend/services/gateway/config/config.example.yml new file mode 100644 index 0000000..f75d8a0 --- /dev/null +++ b/backend/services/gateway/config/config.example.yml @@ -0,0 +1,35 @@ +namespace: "pipedrive" +name: "gateway" +version: 0 +address: ":4044" +repl_address: ":9999" +debug: false +registry: + addresses: ["127.0.0.1:8500"] + type: 2 +messaging: + type: 1 + addresses: ["0.0.0.0:5672"] + durable: true +tracer: + enable: false + address: "http://127.0.0.1:9411/api/v2/spans" + type: 1 +resilience: + rate_limiter: + limit: 1000 + iplimit: 100 + circuit_breaker: + timeout: 15000 +cors: + origins: [] +logger: + name: "gateway-logger" + level: 1 + color: true +worker: + addresses: ["0.0.0.0:6379"] +oauth: + client_id: "" + client_secret: "" + redirect_uri: "https://" diff --git a/backend/services/gateway/deployment/.keep b/backend/services/gateway/deployment/.keep new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/gateway/docs/README.md b/backend/services/gateway/docs/README.md new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/gateway/main.go b/backend/services/gateway/main.go new file mode 100644 index 0000000..d59f4b7 --- /dev/null +++ b/backend/services/gateway/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "log" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/gateway/cmd" +) + +func main() { + if err := cmd.Run(); err != nil { + log.Fatalln(err) + } +} diff --git a/backend/services/gateway/scripts/Makefile b/backend/services/gateway/scripts/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/gateway/web/controller/api.go b/backend/services/gateway/web/controller/api.go new file mode 100644 index 0000000..44a839e --- /dev/null +++ b/backend/services/gateway/web/controller/api.go @@ -0,0 +1,379 @@ +package controller + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "strings" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client/model" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" + "go-micro.dev/v4/client" + "golang.org/x/sync/semaphore" +) + +type apiController struct { + namespace string + client client.Client + apiClient pclient.PipedriveApiClient + commandClient pclient.CommandClient + jwtManager crypto.JwtManager + logger log.Logger + allowedDownloads int +} + +func NewApiController( + namespace string, client client.Client, + jwtManager crypto.JwtManager, allowedDownloads int, logger log.Logger) apiController { + return apiController{ + namespace: namespace, + client: client, + apiClient: pclient.NewPipedriveApiClient(), + commandClient: pclient.NewCommandClient(jwtManager), + jwtManager: jwtManager, + logger: logger, + allowedDownloads: allowedDownloads, + } +} + +func (c *apiController) BuildGetMe() http.HandlerFunc { + return func(rw http.ResponseWriter, r *http.Request) { + pctx, ok := r.Context().Value(request.PipedriveTokenContext{}).(request.PipedriveTokenContext) + if !ok { + rw.WriteHeader(http.StatusForbidden) + c.logger.Error("could not extract pipedrive context from the context") + return + } + + ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) + defer cancel() + + var ures response.UserResponse + if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", fmt.Sprint(pctx.UID+pctx.CID)), &ures); err != nil { + c.logger.Errorf("could not get user access info: %s", err.Error()) + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + rw.WriteHeader(http.StatusRequestTimeout) + return + } + + microErr := response.MicroError{} + if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { + rw.WriteHeader(http.StatusUnauthorized) + return + } + + rw.WriteHeader(microErr.Code) + return + } + + rw.Write(response.UserTokenResponse{ + ID: ures.ID, + AccessToken: ures.AccessToken, + ExpiresAt: ures.ExpiresAt, + }.ToJSON()) + } +} + +func (c *apiController) BuildPostSettings() http.HandlerFunc { + return func(rw http.ResponseWriter, r *http.Request) { + pctx, ok := r.Context().Value(request.PipedriveTokenContext{}).(request.PipedriveTokenContext) + if !ok { + rw.WriteHeader(http.StatusForbidden) + c.logger.Error("could not extract pipedrive context from the context") + return + } + + var settings request.DocSettings + buf, _ := ioutil.ReadAll(r.Body) + if err := json.Unmarshal(buf, &settings); err != nil { + rw.WriteHeader(http.StatusBadRequest) + c.logger.Errorf(err.Error()) + return + } + + settings.CompanyID = pctx.CID + if err := settings.Validate(); err != nil { + rw.WriteHeader(http.StatusBadRequest) + c.logger.Errorf("invalid settings format: %s", err.Error()) + return + } + + ctx, cancel := context.WithTimeout(r.Context(), 4*time.Second) + defer cancel() + + if err := c.commandClient.License(ctx, settings.DocAddress, settings.DocSecret); err != nil { + c.logger.Errorf("could not validate ONLYOFFICE document server credentials: %s", err.Error()) + rw.WriteHeader(http.StatusForbidden) + return + } + + var ures response.UserResponse + if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", fmt.Sprint(pctx.UID+pctx.CID)), &ures); err != nil { + c.logger.Errorf("could not get user access info: %s", err.Error()) + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + rw.WriteHeader(http.StatusRequestTimeout) + return + } + + microErr := response.MicroError{} + if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { + rw.WriteHeader(http.StatusUnauthorized) + return + } + + rw.WriteHeader(microErr.Code) + return + } + + urs, err := c.apiClient.GetMe(ctx, model.Token{ + AccessToken: ures.AccessToken, + RefreshToken: ures.RefreshToken, + TokenType: ures.TokenType, + Scope: ures.Scope, + ApiDomain: ures.ApiDomain, + }) + + for _, access := range urs.Access { + if access.App == "global" && !access.Admin { + rw.WriteHeader(http.StatusForbidden) + return + } + } + + if err != nil { + c.logger.Errorf("could not get pipedrive user or no user has admin permissions") + rw.WriteHeader(http.StatusForbidden) + return + } + + msg := c.client.NewMessage("insert-settings", request.DocSettings{ + CompanyID: urs.CompanyID, + DocAddress: settings.DocAddress, + DocSecret: settings.DocSecret, + }) + + if err := c.client.Publish(ctx, msg); err != nil { + c.logger.Errorf("could not insert settings: %s", err.Error()) + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + rw.WriteHeader(http.StatusRequestTimeout) + return + } + + microErr := response.MicroError{} + if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { + rw.WriteHeader(http.StatusUnauthorized) + return + } + + rw.WriteHeader(microErr.Code) + return + } + + rw.WriteHeader(http.StatusCreated) + } +} + +func (c apiController) BuildGetSettings() http.HandlerFunc { + return func(rw http.ResponseWriter, r *http.Request) { + pctx, ok := r.Context().Value(request.PipedriveTokenContext{}).(request.PipedriveTokenContext) + if !ok { + rw.WriteHeader(http.StatusForbidden) + c.logger.Error("could not extract pipedrive context from the context") + return + } + + ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) + defer cancel() + + var ures response.UserResponse + if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", fmt.Sprint(pctx.UID+pctx.CID)), &ures); err != nil { + c.logger.Errorf("could not get user access info: %s", err.Error()) + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + rw.WriteHeader(http.StatusRequestTimeout) + return + } + + microErr := response.MicroError{} + if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { + rw.WriteHeader(http.StatusUnauthorized) + return + } + + rw.WriteHeader(microErr.Code) + return + } + + urs, _ := c.apiClient.GetMe(ctx, model.Token{ + AccessToken: ures.AccessToken, + RefreshToken: ures.RefreshToken, + TokenType: ures.TokenType, + Scope: ures.Scope, + ApiDomain: ures.ApiDomain, + }) + + for _, access := range urs.Access { + if access.App == "global" && !access.Admin { + rw.WriteHeader(http.StatusForbidden) + return + } + } + + var docs response.DocSettingsResponse + if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:settings", c.namespace), "SettingsSelectHandler.GetSettings", fmt.Sprint(pctx.CID)), &docs); err != nil { + c.logger.Errorf("could not get settings: %s", err.Error()) + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + rw.WriteHeader(http.StatusRequestTimeout) + return + } + + microErr := response.MicroError{} + if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { + rw.WriteHeader(http.StatusUnauthorized) + return + } + + rw.WriteHeader(microErr.Code) + return + } + + rw.Write(docs.ToJSON()) + } +} + +func (c apiController) BuildGetConfig() http.HandlerFunc { + return func(rw http.ResponseWriter, r *http.Request) { + rw.Header().Set("Content-Type", "application/json") + + query := r.URL.Query() + id, filename, key, dealID := strings.TrimSpace(query.Get("id")), strings.TrimSpace(query.Get("name")), strings.TrimSpace(query.Get("key")), strings.TrimSpace(query.Get("deal_id")) + + pctx, ok := r.Context().Value(request.PipedriveTokenContext{}).(request.PipedriveTokenContext) + if !ok { + rw.WriteHeader(http.StatusForbidden) + c.logger.Error("could not extract pipedrive context from the context") + return + } + + if filename == "" { + rw.WriteHeader(http.StatusBadRequest) + c.logger.Error("could not extract file name from URL Query") + return + } + + if key == "" { + rw.WriteHeader(http.StatusBadRequest) + c.logger.Error("could not extract doc key from URL Query") + return + } + + ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second) + defer cancel() + + var resp response.BuildConfigResponse + if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:builder", c.namespace), "ConfigHandler.BuildConfig", request.BuildConfigRequest{ + UID: pctx.UID, + CID: pctx.CID, + Deal: dealID, + UserAgent: r.UserAgent(), + Filename: filename, + FileID: id, + DocKey: key, + }), &resp); err != nil { + c.logger.Errorf("could not build onlyoffice config: %s", err.Error()) + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + rw.WriteHeader(http.StatusRequestTimeout) + return + } + + microErr := response.MicroError{} + if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { + rw.WriteHeader(http.StatusInternalServerError) + return + } + + c.logger.Errorf("build config micro error: %s", microErr.Detail) + rw.WriteHeader(microErr.Code) + return + } + + rw.WriteHeader(http.StatusOK) + rw.Write(resp.ToJSON()) + } +} + +func (c apiController) BuildGetFile() http.HandlerFunc { + sem := semaphore.NewWeighted(int64(c.allowedDownloads)) + return func(rw http.ResponseWriter, r *http.Request) { + if ok := sem.TryAcquire(1); !ok { + c.logger.Warn("too many download requests") + rw.WriteHeader(http.StatusTooManyRequests) + return + } + + defer sem.Release(1) + + fid, cid, token := strings.TrimSpace(r.URL.Query().Get("fid")), strings.TrimSpace(r.URL.Query().Get("cid")), strings.TrimSpace(r.URL.Query().Get("token")) + + var pctx request.PipedriveTokenContext + if token == "" { + c.logger.Errorf("unauthorized access to an api endpoint") + rw.WriteHeader(http.StatusUnauthorized) + return + } + + var docs response.DocSettingsResponse + if err := c.client.Call(r.Context(), c.client.NewRequest(fmt.Sprintf("%s:settings", c.namespace), "SettingsSelectHandler.GetSettings", cid), &docs); err != nil { + c.logger.Debugf("could not document server settings: %s", err.Error()) + rw.WriteHeader(http.StatusBadRequest) + return + } + + if err := c.jwtManager.Verify(docs.DocSecret, token, &pctx); err != nil { + c.logger.Errorf("could not verify X-Pipedrive-App-Context: %s", err.Error()) + rw.WriteHeader(http.StatusForbidden) + return + } + + ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) + defer cancel() + + var ures response.UserResponse + if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", fmt.Sprint(pctx.UID+pctx.CID)), &ures); err != nil { + c.logger.Errorf("could not get user access info: %s", err.Error()) + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + rw.WriteHeader(http.StatusRequestTimeout) + return + } + + microErr := response.MicroError{} + if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { + rw.WriteHeader(http.StatusUnauthorized) + return + } + + rw.WriteHeader(microErr.Code) + return + } + + req, _ := http.NewRequest("GET", fmt.Sprintf("%s/files/%s/download", ures.ApiDomain, fid), nil) + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", ures.AccessToken)) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + rw.WriteHeader(http.StatusBadRequest) + return + } + + defer resp.Body.Close() + io.Copy(rw, resp.Body) + } +} diff --git a/backend/services/gateway/web/controller/auth.go b/backend/services/gateway/web/controller/auth.go new file mode 100644 index 0000000..e6ae272 --- /dev/null +++ b/backend/services/gateway/web/controller/auth.go @@ -0,0 +1,134 @@ +package controller + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "strings" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" + "go-micro.dev/v4/client" + "golang.org/x/sync/singleflight" +) + +var group singleflight.Group + +type authController struct { + namespace string + redirectURI string + client client.Client + pipedriveAuth pclient.PipedriveAuthClient + pipedriveAPI pclient.PipedriveApiClient + logger log.Logger +} + +func NewAuthController( + namespace string, + redirectURI string, + client client.Client, + pipedriveAuth pclient.PipedriveAuthClient, + logger log.Logger, +) *authController { + return &authController{ + namespace: namespace, + redirectURI: redirectURI, + client: client, + pipedriveAuth: pipedriveAuth, + pipedriveAPI: pclient.NewPipedriveApiClient(), + logger: logger, + } +} + +func (c authController) BuildGetAuth() http.HandlerFunc { + return func(rw http.ResponseWriter, r *http.Request) { + c.logger.Debug("a new auth request") + code := strings.TrimSpace(r.URL.Query().Get("code")) + if code == "" { + rw.WriteHeader(http.StatusBadRequest) + c.logger.Debug("empty auth code parameter") + return + } + + group.Do(code, func() (interface{}, error) { + token, err := c.pipedriveAuth.GetAccessToken(r.Context(), code, c.redirectURI) + if err != nil { + rw.WriteHeader(http.StatusBadRequest) + c.logger.Errorf("could not get pipedrive access token: %s", err.Error()) + return nil, err + } + + usr, err := c.pipedriveAPI.GetMe(r.Context(), token) + if err != nil { + rw.WriteHeader(http.StatusBadRequest) + c.logger.Errorf("could not get pipedrive user: %s", err.Error()) + return nil, err + } + + if err := c.client.Publish(r.Context(), client.NewMessage("insert-auth", response.UserResponse{ + ID: fmt.Sprint(usr.ID + usr.CompanyID), + AccessToken: token.AccessToken, + RefreshToken: token.RefreshToken, + TokenType: token.TokenType, + Scope: token.Scope, + ApiDomain: token.ApiDomain, + ExpiresAt: time.Now().Local().Add(time.Second * time.Duration(token.ExpiresIn-700)).UnixMilli(), + })); err != nil { + rw.WriteHeader(http.StatusInternalServerError) + c.logger.Errorf("insert user error: %s", err.Error()) + return nil, err + } + + c.logger.Debugf("redirecting to api domain: %s", token.ApiDomain) + http.Redirect(rw, r, token.ApiDomain, http.StatusMovedPermanently) + return nil, nil + }) + } +} + +func (c authController) BuildDeleteAuth() http.HandlerFunc { + return func(rw http.ResponseWriter, r *http.Request) { + c.logger.Debug("a new uninstall request") + var ureq request.UninstallRequest + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + rw.WriteHeader(http.StatusBadRequest) + c.logger.Errorf("could not parse request body: %s", err.Error()) + return + } + + if err := json.Unmarshal(buf, &ureq); err != nil { + rw.WriteHeader(http.StatusInternalServerError) + c.logger.Errorf("could not unmarshal request body: %s", err.Error()) + return + } + + var res interface{} + if err := c.client.Call(r.Context(), c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserDeleteHandler.DeleteUser", fmt.Sprint(ureq.UserID+ureq.CompanyID)), &res); err != nil { + c.logger.Errorf("could not delete user: %s", err.Error()) + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + rw.WriteHeader(http.StatusRequestTimeout) + return + } + + microErr := response.MicroError{} + if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { + rw.WriteHeader(http.StatusUnauthorized) + return + } + + rw.WriteHeader(microErr.Code) + return + } + + c.logger.Debugf("successfully published delete-auth message for user %s", fmt.Sprint(ureq.UserID+ureq.CompanyID)) + rw.WriteHeader(http.StatusOK) + } +} diff --git a/backend/services/gateway/web/middleware/auth.go b/backend/services/gateway/web/middleware/auth.go new file mode 100644 index 0000000..80b7dc1 --- /dev/null +++ b/backend/services/gateway/web/middleware/auth.go @@ -0,0 +1,37 @@ +package middleware + +import ( + "encoding/base64" + "fmt" + "net/http" + "strings" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" +) + +func BuildHandleAuthMiddleware( + clientID, clientSecret string, + logger log.Logger, +) func(next http.Handler) http.HandlerFunc { + logger.Debugf("zoom event middleware has been built with client_id %s and client_secret %s", clientID, clientSecret) + return func(next http.Handler) http.HandlerFunc { + return func(rw http.ResponseWriter, r *http.Request) { + signature := strings.ReplaceAll(r.Header.Get("Authorization"), "Basic ", "") + if signature == "" { + logger.Errorf("an unauthorized access to deauth endpoint") + rw.WriteHeader(http.StatusUnauthorized) + return + } + + validSignature := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", clientID, clientSecret))) + if signature != validSignature { + logger.Errorf("invalid uninstall signature") + logger.Debugf("valid signature is %s whereas signature is %s", validSignature, signature) + rw.WriteHeader(http.StatusForbidden) + return + } + + next.ServeHTTP(rw, r) + } + } +} diff --git a/backend/services/gateway/web/middleware/context.go b/backend/services/gateway/web/middleware/context.go new file mode 100644 index 0000000..8ab6213 --- /dev/null +++ b/backend/services/gateway/web/middleware/context.go @@ -0,0 +1,37 @@ +package middleware + +import ( + "context" + "net/http" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" +) + +func BuildHandleContextMiddleware( + clientSecret string, + jwtManager crypto.JwtManager, + logger log.Logger, +) func(next http.Handler) http.HandlerFunc { + logger.Debugf("pipedrive context middleware has been built with client_secret %s", clientSecret) + return func(next http.Handler) http.HandlerFunc { + return func(rw http.ResponseWriter, r *http.Request) { + var ctx request.PipedriveTokenContext + token := r.Header.Get("X-Pipedrive-App-Context") + if token == "" { + logger.Errorf("unauthorized access to an api endpoint") + rw.WriteHeader(http.StatusUnauthorized) + return + } + + if err := jwtManager.Verify(clientSecret, token, &ctx); err != nil { + logger.Errorf("could not verify X-Pipedrive-App-Context: %s", err.Error()) + rw.WriteHeader(http.StatusForbidden) + return + } + + next.ServeHTTP(rw, r.WithContext(context.WithValue(r.Context(), request.PipedriveTokenContext{}, ctx))) + } + } +} diff --git a/backend/services/gateway/web/server.go b/backend/services/gateway/web/server.go new file mode 100644 index 0000000..0521401 --- /dev/null +++ b/backend/services/gateway/web/server.go @@ -0,0 +1,109 @@ +package web + +import ( + "net/http" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + shttp "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/http" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/gateway/web/controller" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/gateway/web/middleware" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" + pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" + "github.com/gin-gonic/gin" + "github.com/go-chi/chi/v5" + chimiddleware "github.com/go-chi/chi/v5/middleware" + "github.com/gorilla/sessions" + "go-micro.dev/v4/cache" + "go-micro.dev/v4/client" +) + +type PipedriveHTTPService struct { + namespace string + mux *chi.Mux + client client.Client + logger log.Logger + store sessions.Store + clientID string + clientSecret string + redirectURI string + allowedDownloads int +} + +// NewService initializes http server with options. +func NewServer( + serverConfig *config.ServerConfig, + credentialsConfig *config.OAuthCredentialsConfig, + onlyofficeConfig *shared.OnlyofficeConfig, + logger log.Logger, +) shttp.ServerEngine { + gin.SetMode(gin.ReleaseMode) + + service := PipedriveHTTPService{ + namespace: serverConfig.Namespace, + mux: chi.NewRouter(), + logger: logger, + clientID: credentialsConfig.Credentials.ClientID, + clientSecret: credentialsConfig.Credentials.ClientSecret, + redirectURI: credentialsConfig.Credentials.RedirectURI, + store: sessions.NewCookieStore([]byte(credentialsConfig.Credentials.ClientSecret)), + allowedDownloads: onlyofficeConfig.Onlyoffice.Builder.AllowedDownloads, + } + + return service +} + +// ApplyMiddleware useed to apply http server middlewares. +func (s PipedriveHTTPService) ApplyMiddleware(middlewares ...func(http.Handler) http.Handler) { + s.mux.Use(middlewares...) +} + +// NewHandler returns http server engine. +func (s PipedriveHTTPService) NewHandler(client client.Client, cache cache.Cache) interface { + ServeHTTP(w http.ResponseWriter, r *http.Request) +} { + return s.InitializeServer(client) +} + +// InitializeServer sets all injected dependencies. +func (s *PipedriveHTTPService) InitializeServer(c client.Client) *chi.Mux { + s.client = c + s.InitializeRoutes() + return s.mux +} + +// InitializeRoutes builds all http routes. +func (s *PipedriveHTTPService) InitializeRoutes() { + jwtManager := crypto.NewOnlyofficeJwtManager() + authMiddleware := middleware.BuildHandleAuthMiddleware(s.clientID, s.clientSecret, s.logger) + tokenMiddleware := middleware.BuildHandleContextMiddleware(s.clientSecret, jwtManager, s.logger) + + authController := controller.NewAuthController(s.namespace, s.redirectURI, s.client, pclient.NewPipedriveAuthClient(s.clientID, s.clientSecret), s.logger) + apiController := controller.NewApiController(s.namespace, s.client, jwtManager, s.allowedDownloads, s.logger) + + s.mux.Group(func(r chi.Router) { + r.Use(chimiddleware.Recoverer) + r.NotFound(func(rw http.ResponseWriter, cr *http.Request) { + http.Redirect(rw, cr.WithContext(cr.Context()), "https://onlyoffice.com", http.StatusMovedPermanently) + }) + + r.Route("/oauth", func(cr chi.Router) { + cr.Use(chimiddleware.NoCache) + cr.Get("/auth", authController.BuildGetAuth()) + cr.Delete("/auth", authMiddleware(authController.BuildDeleteAuth())) + }) + + r.Route("/api", func(cr chi.Router) { + cr.Use(func(h http.Handler) http.Handler { + return tokenMiddleware(h) + }) + cr.Get("/me", apiController.BuildGetMe()) + cr.Get("/config", apiController.BuildGetConfig()) + cr.Post("/settings", apiController.BuildPostSettings()) + cr.Get("/settings", apiController.BuildGetSettings()) + }) + + r.Get("/download", apiController.BuildGetFile()) + }) +} diff --git a/backend/services/settings/cmd/root.go b/backend/services/settings/cmd/root.go new file mode 100644 index 0000000..67fc366 --- /dev/null +++ b/backend/services/settings/cmd/root.go @@ -0,0 +1,30 @@ +package cmd + +import ( + "os" + + "github.com/urfave/cli/v2" +) + +func GetCommands() cli.Commands { + return []*cli.Command{ + Server(), + } +} + +func Run() error { + app := &cli.App{ + Name: "onlyoffice:docserver", + Description: "Description", + Authors: []*cli.Author{ + { + Name: "Ascensio Systems SIA", + Email: "support@onlyoffice.com", + }, + }, + HideVersion: true, + Commands: GetCommands(), + } + + return app.Run(os.Args) +} diff --git a/backend/services/settings/cmd/server.go b/backend/services/settings/cmd/server.go new file mode 100644 index 0000000..e7d190e --- /dev/null +++ b/backend/services/settings/cmd/server.go @@ -0,0 +1,66 @@ +package cmd + +import ( + "context" + "net/http" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/rpc" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web" + "github.com/urfave/cli/v2" + "go-micro.dev/v4" + "go.uber.org/fx" + "golang.org/x/sync/errgroup" +) + +func Server() *cli.Command { + return &cli.Command{ + Name: "server", + Usage: "starts a new rpc server instance", + Category: "server", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config_path", + Usage: "sets custom configuration path", + Aliases: []string{"config", "conf", "c"}, + }, + &cli.StringFlag{ + Name: "environment", + Usage: "sets servers environment (development, testing, production)", + Aliases: []string{"env", "e"}, + }, + }, + Action: func(c *cli.Context) error { + var ( + CONFIG_PATH = c.String("config_path") + // ENVIRONMENT = c.String("environment") + ) + + fx.New( + pkg.Bootstrap(CONFIG_PATH), + fx.Provide(rpc.NewService), + fx.Provide(web.NewDocserverRPCServer), + fx.Invoke(func(lifecycle fx.Lifecycle, service micro.Service, repl *http.Server, logger log.Logger) { + lifecycle.Append(fx.Hook{ + OnStart: func(ctx context.Context) error { + go repl.ListenAndServe() + go service.Run() + return nil + }, + OnStop: func(ctx context.Context) error { + g, gCtx := errgroup.WithContext(ctx) + g.Go(func() error { + return repl.Shutdown(gCtx) + }) + return g.Wait() + }, + }) + }), + fx.NopLogger, + ).Run() + + return nil + }, + } +} diff --git a/backend/services/settings/config/config.example.yml b/backend/services/settings/config/config.example.yml new file mode 100644 index 0000000..4710d56 --- /dev/null +++ b/backend/services/settings/config/config.example.yml @@ -0,0 +1,31 @@ +namespace: "pipedrive" +name: "settings" +version: 0 +address: ":5150" +repl_address: ":8889" +debug: false +# persistence: +# url: "" +registry: + addresses: ["127.0.0.1:8500"] + type: 2 +messaging: + type: 1 + addresses: ["0.0.0.0:5672"] + durable: true +tracer: + enable: false + address: "http://127.0.0.1:9411/api/v2/spans" + type: 1 +resilience: + rate_limiter: + limit: 500 + circuit_breaker: + timeout: 2500 +logger: + name: "settings-logger" + level: 1 + color: true +oauth: + client_id: "" + client_secret: "" diff --git a/backend/services/settings/deployment/.keep b/backend/services/settings/deployment/.keep new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/settings/docs/.keep b/backend/services/settings/docs/.keep new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/settings/main.go b/backend/services/settings/main.go new file mode 100644 index 0000000..99c0ec7 --- /dev/null +++ b/backend/services/settings/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "log" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/cmd" +) + +func main() { + if err := cmd.Run(); err != nil { + log.Fatalln(err) + } +} diff --git a/backend/services/settings/script/.keep b/backend/services/settings/script/.keep new file mode 100644 index 0000000..e69de29 diff --git a/backend/services/settings/web/core/adapter/memory.go b/backend/services/settings/web/core/adapter/memory.go new file mode 100644 index 0000000..db8afc6 --- /dev/null +++ b/backend/services/settings/web/core/adapter/memory.go @@ -0,0 +1,71 @@ +package adapter + +import ( + "context" + "encoding/json" + "errors" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/port" +) + +var _ErrNoCompanySettings = errors.New("no company settings") + +type memoryDocserverAdapter struct { + kvs map[string][]byte +} + +func NewMemoryDocserverAdapter() port.DocSettingsServiceAdapter { + return &memoryDocserverAdapter{ + kvs: make(map[string][]byte), + } +} + +func (m *memoryDocserverAdapter) save(settings domain.DocSettings) error { + buffer, err := json.Marshal(settings) + + if err != nil { + return err + } + + m.kvs[settings.CompanyID] = buffer + + return nil +} + +func (m *memoryDocserverAdapter) InsertSettings(ctx context.Context, settings domain.DocSettings) error { + return m.save(settings) +} + +func (m *memoryDocserverAdapter) SelectSettings(ctx context.Context, cid string) (domain.DocSettings, error) { + buffer, ok := m.kvs[cid] + var settings domain.DocSettings + + if !ok { + return settings, _ErrNoCompanySettings + } + + if err := json.Unmarshal(buffer, &settings); err != nil { + return settings, err + } + + return settings, nil +} + +func (m *memoryDocserverAdapter) UpsertSettings(ctx context.Context, settings domain.DocSettings) (domain.DocSettings, error) { + if err := m.save(settings); err != nil { + return domain.DocSettings{}, err + } + + return settings, nil +} + +func (m *memoryDocserverAdapter) DeleteSettings(ctx context.Context, cid string) error { + if _, ok := m.kvs[cid]; !ok { + return _ErrNoCompanySettings + } + + delete(m.kvs, cid) + + return nil +} diff --git a/backend/services/settings/web/core/adapter/memory_test.go b/backend/services/settings/web/core/adapter/memory_test.go new file mode 100644 index 0000000..f2a99d1 --- /dev/null +++ b/backend/services/settings/web/core/adapter/memory_test.go @@ -0,0 +1,46 @@ +package adapter + +import ( + "context" + "testing" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" + "github.com/stretchr/testify/assert" +) + +func TestMemoryAdapter(t *testing.T) { + adapter := NewMemoryDocserverAdapter() + + t.Run("save settings", func(t *testing.T) { + assert.NoError(t, adapter.InsertSettings(context.Background(), settings)) + }) + + t.Run("save the same settings object", func(t *testing.T) { + assert.NoError(t, adapter.InsertSettings(context.Background(), settings)) + }) + + t.Run("get settings by cid", func(t *testing.T) { + s, err := adapter.SelectSettings(context.Background(), "mock") + assert.NoError(t, err) + assert.Equal(t, settings, s) + }) + + t.Run("update settings by cid", func(t *testing.T) { + s, err := adapter.UpsertSettings(context.Background(), domain.DocSettings{ + CompanyID: "mock", + DocAddress: "mock", + DocSecret: "mock", + }) + assert.NoError(t, err) + assert.NotNil(t, s) + }) + + t.Run("delete settings by cid", func(t *testing.T) { + assert.NoError(t, adapter.DeleteSettings(context.Background(), "mock")) + }) + + t.Run("get invalid settings", func(t *testing.T) { + _, err := adapter.SelectSettings(context.Background(), "mock") + assert.Error(t, err) + }) +} diff --git a/backend/services/settings/web/core/adapter/mongo.go b/backend/services/settings/web/core/adapter/mongo.go new file mode 100644 index 0000000..b9c1779 --- /dev/null +++ b/backend/services/settings/web/core/adapter/mongo.go @@ -0,0 +1,114 @@ +package adapter + +import ( + "context" + "errors" + "log" + "strings" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/port" + "github.com/kamva/mgm/v3" + "github.com/kamva/mgm/v3/operator" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +var _ErrInvalidCompanyID error = errors.New("invalid cid format") +var _ErrUserAlreadyExists error = errors.New("user already exists") + +type docSettingsCollection struct { + mgm.DefaultModel `bson:",inline"` + CompanyID string `json:"company_id" bson:"company_id"` + DocAddress string `json:"doc_address" bson:"doc_address"` + DocSecret string `json:"doc_secret" bson:"doc_secret"` +} + +type mongoUserAdapter struct { +} + +func NewMongoDocserverAdapter(url string) port.DocSettingsServiceAdapter { + if err := mgm.SetDefaultConfig( + &mgm.Config{CtxTimeout: 3 * time.Second}, "settings", + options.Client().ApplyURI(url), + ); err != nil { + log.Fatalf("mongo initialization error: %s", err.Error()) + } + + return &mongoUserAdapter{} +} + +func (m *mongoUserAdapter) save(ctx context.Context, settings domain.DocSettings) error { + return mgm.Transaction(func(session mongo.Session, sc mongo.SessionContext) error { + u := &docSettingsCollection{} + collection := mgm.Coll(&docSettingsCollection{}) + + if err := collection.FirstWithCtx(ctx, bson.M{"company_id": settings.CompanyID}, u); err != nil { + if cerr := collection.CreateWithCtx(ctx, &docSettingsCollection{ + CompanyID: settings.CompanyID, + DocAddress: settings.DocAddress, + DocSecret: settings.DocSecret, + }); cerr != nil { + return cerr + } + + return session.CommitTransaction(sc) + } + + u.CompanyID = settings.CompanyID + u.DocAddress = settings.DocAddress + u.DocSecret = settings.DocSecret + u.UpdatedAt = time.Now() + + if err := collection.UpdateWithCtx(ctx, u); err != nil { + return err + } + + return session.CommitTransaction(sc) + }) +} + +func (m *mongoUserAdapter) InsertSettings(ctx context.Context, settings domain.DocSettings) error { + if err := settings.Validate(); err != nil { + return err + } + + return m.save(ctx, settings) +} + +func (m *mongoUserAdapter) SelectSettings(ctx context.Context, cid string) (domain.DocSettings, error) { + cid = strings.TrimSpace(cid) + + if cid == "" { + return domain.DocSettings{}, _ErrInvalidCompanyID + } + + settings := &docSettingsCollection{} + collection := mgm.Coll(settings) + return domain.DocSettings{ + CompanyID: cid, + DocAddress: settings.DocAddress, + DocSecret: settings.DocSecret, + }, collection.FirstWithCtx(ctx, bson.M{"company_id": cid}, settings) +} + +func (m *mongoUserAdapter) UpsertSettings(ctx context.Context, settings domain.DocSettings) (domain.DocSettings, error) { + if err := settings.Validate(); err != nil { + return settings, err + } + + return settings, m.save(ctx, settings) +} + +func (m *mongoUserAdapter) DeleteSettings(ctx context.Context, cid string) error { + cid = strings.TrimSpace(cid) + + if cid == "" { + return _ErrInvalidCompanyID + } + + _, err := mgm.Coll(&docSettingsCollection{}).DeleteMany(ctx, bson.M{"company_id": bson.M{operator.Eq: cid}}) + return err +} diff --git a/backend/services/settings/web/core/adapter/mongo_test.go b/backend/services/settings/web/core/adapter/mongo_test.go new file mode 100644 index 0000000..2dbb6cb --- /dev/null +++ b/backend/services/settings/web/core/adapter/mongo_test.go @@ -0,0 +1,92 @@ +package adapter + +import ( + "context" + "testing" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" + "github.com/stretchr/testify/assert" +) + +var settings = domain.DocSettings{ + CompanyID: "mock", + DocAddress: "mock", + DocSecret: "mock", +} + +func TestMongoAdapter(t *testing.T) { + adapter := NewMongoDocserverAdapter("mongodb://localhost:27017") + + t.Run("save settings with timeout", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 0*time.Second) + defer cancel() + assert.Error(t, adapter.InsertSettings(ctx, settings)) + }) + + t.Run("save settings", func(t *testing.T) { + assert.NoError(t, adapter.InsertSettings(context.Background(), settings)) + }) + + t.Run("save the same settings object", func(t *testing.T) { + assert.NoError(t, adapter.InsertSettings(context.Background(), settings)) + }) + + t.Run("get settings by id with timeout", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 0*time.Second) + defer cancel() + _, err := adapter.SelectSettings(ctx, "mock") + assert.Error(t, err) + }) + + t.Run("get settings by id", func(t *testing.T) { + s, err := adapter.SelectSettings(context.Background(), "mock") + assert.NoError(t, err) + assert.Equal(t, settings, s) + }) + + t.Run("delete settings by id with timeout", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 0*time.Second) + defer cancel() + assert.Error(t, adapter.DeleteSettings(ctx, "mock")) + }) + + t.Run("delete settings by id", func(t *testing.T) { + assert.NoError(t, adapter.DeleteSettings(context.Background(), "mock")) + }) + + t.Run("get invalid settings", func(t *testing.T) { + _, err := adapter.SelectSettings(context.Background(), "mock") + assert.Error(t, err) + }) + + t.Run("invald settings update", func(t *testing.T) { + _, err := adapter.UpsertSettings(context.Background(), domain.DocSettings{ + CompanyID: "mock", + DocAddress: "mock", + }) + assert.Error(t, err) + }) + + t.Run("update settings with timeout", func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 0*time.Second) + defer cancel() + _, err := adapter.UpsertSettings(ctx, domain.DocSettings{ + CompanyID: "mock", + DocAddress: "mock", + DocSecret: "mock", + }) + assert.Error(t, err) + }) + + t.Run("update settings", func(t *testing.T) { + _, err := adapter.UpsertSettings(context.Background(), domain.DocSettings{ + CompanyID: "mock", + DocAddress: "mock", + DocSecret: "mock", + }) + assert.NoError(t, err) + }) + + adapter.DeleteSettings(context.Background(), "mock") +} diff --git a/backend/services/settings/web/core/domain/error.go b/backend/services/settings/web/core/domain/error.go new file mode 100644 index 0000000..b9a6aae --- /dev/null +++ b/backend/services/settings/web/core/domain/error.go @@ -0,0 +1,13 @@ +package domain + +import "fmt" + +type InvalidModelFieldError struct { + Model string + Field string + Reason string +} + +func (e *InvalidModelFieldError) Error() string { + return fmt.Sprintf("invald %s field %s. Reason: %s", e.Model, e.Field, e.Reason) +} diff --git a/backend/services/settings/web/core/domain/settings.go b/backend/services/settings/web/core/domain/settings.go new file mode 100644 index 0000000..3938e2e --- /dev/null +++ b/backend/services/settings/web/core/domain/settings.go @@ -0,0 +1,63 @@ +package domain + +import ( + "encoding/json" + "fmt" + "net/url" + "strings" +) + +type DocSettings struct { + CompanyID string `json:"company_id" mapstructure:"company_id"` + DocAddress string `json:"doc_address" mapstructure:"doc_address"` + DocSecret string `json:"doc_secret" mapstructure:"doc_secret"` +} + +func (u DocSettings) ToJSON() []byte { + buf, _ := json.Marshal(u) + return buf +} + +func (u *DocSettings) Validate() error { + u.CompanyID = strings.TrimSpace(u.CompanyID) + u.DocAddress = strings.TrimSpace(u.DocAddress) + u.DocSecret = strings.TrimSpace(u.DocSecret) + + if u.CompanyID == "" { + return &InvalidModelFieldError{ + Model: "Docserver", + Field: "CompanyID", + Reason: "Should not be empty", + } + } + + url, err := url.Parse(u.DocAddress) + if err != nil { + return &InvalidModelFieldError{ + Model: "Docserver", + Field: "Document Address", + Reason: err.Error(), + } + } + + u.DocAddress = fmt.Sprintf("%s://%s/%s", url.Scheme, url.Host, url.Path) + for { + if strings.LastIndex(u.DocAddress, "/") == len(u.DocAddress)-1 { + u.DocAddress = u.DocAddress[:len(u.DocAddress)-1] + } else { + break + } + } + + u.DocAddress += "/" + + if u.DocSecret == "" { + return &InvalidModelFieldError{ + Model: "Docserver", + Field: "Document Secret", + Reason: "Should not be empty", + } + } + + return nil +} diff --git a/backend/services/settings/web/core/port/input.go b/backend/services/settings/web/core/port/input.go new file mode 100644 index 0000000..11d5403 --- /dev/null +++ b/backend/services/settings/web/core/port/input.go @@ -0,0 +1,14 @@ +package port + +import ( + "context" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" +) + +type DocSettingsService interface { + CreateSettings(ctx context.Context, settings domain.DocSettings) error + GetSettings(ctx context.Context, cid string) (domain.DocSettings, error) + UpdateSettings(ctx context.Context, settings domain.DocSettings) (domain.DocSettings, error) + DeleteSettings(ctx context.Context, cid string) error +} diff --git a/backend/services/settings/web/core/port/output.go b/backend/services/settings/web/core/port/output.go new file mode 100644 index 0000000..a0cd81a --- /dev/null +++ b/backend/services/settings/web/core/port/output.go @@ -0,0 +1,14 @@ +package port + +import ( + "context" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" +) + +type DocSettingsServiceAdapter interface { + InsertSettings(ctx context.Context, settings domain.DocSettings) error + SelectSettings(ctx context.Context, cid string) (domain.DocSettings, error) + UpsertSettings(ctx context.Context, settings domain.DocSettings) (domain.DocSettings, error) + DeleteSettings(ctx context.Context, cid string) error +} diff --git a/backend/services/settings/web/core/service/error.go b/backend/services/settings/web/core/service/error.go new file mode 100644 index 0000000..496ab29 --- /dev/null +++ b/backend/services/settings/web/core/service/error.go @@ -0,0 +1,12 @@ +package service + +import "fmt" + +type InvalidServiceParameterError struct { + Name string + Reason string +} + +func (e *InvalidServiceParameterError) Error() string { + return fmt.Sprintf("invald service parameter %s. Reason: %s", e.Name, e.Reason) +} diff --git a/backend/services/settings/web/core/service/settings.go b/backend/services/settings/web/core/service/settings.go new file mode 100644 index 0000000..bf5f1f7 --- /dev/null +++ b/backend/services/settings/web/core/service/settings.go @@ -0,0 +1,122 @@ +package service + +import ( + "context" + "errors" + "strings" + + plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/port" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" +) + +var _ErrOperationTimeout = errors.New("operation timeout") + +type settingsService struct { + adapter port.DocSettingsServiceAdapter + encryptor crypto.Encryptor + logger plog.Logger +} + +func NewSettingsService( + adapter port.DocSettingsServiceAdapter, + encryptor crypto.Encryptor, + logger plog.Logger, +) port.DocSettingsService { + return settingsService{ + adapter: adapter, + encryptor: encryptor, + logger: logger, + } +} + +func (s settingsService) CreateSettings(ctx context.Context, settings domain.DocSettings) error { + s.logger.Debugf("validating company %s settings to perform a persist action", settings.CompanyID) + if err := settings.Validate(); err != nil { + return err + } + + esecret, err := s.encryptor.Encrypt(settings.DocSecret) + if err != nil { + return err + } + + s.logger.Debugf("settings %s are valid. Persisting to database", settings.CompanyID) + if err := s.adapter.InsertSettings(ctx, domain.DocSettings{ + CompanyID: settings.CompanyID, + DocAddress: settings.DocAddress, + DocSecret: esecret, + }); err != nil { + return err + } + + return nil +} + +func (s settingsService) GetSettings(ctx context.Context, cid string) (domain.DocSettings, error) { + s.logger.Debugf("trying to select settings for company with id: %s", cid) + id := strings.TrimSpace(cid) + + if id == "" { + return domain.DocSettings{}, &InvalidServiceParameterError{ + Name: "CID", + Reason: "Should not be blank", + } + } + + settings, err := s.adapter.SelectSettings(ctx, id) + if err != nil { + return settings, err + } + + s.logger.Debugf("found settings: %v", settings) + dsecret, err := s.encryptor.Decrypt(settings.DocSecret) + if err != nil { + return settings, err + } + + return domain.DocSettings{ + CompanyID: cid, + DocAddress: settings.DocAddress, + DocSecret: dsecret, + }, nil +} + +func (s settingsService) UpdateSettings(ctx context.Context, settings domain.DocSettings) (domain.DocSettings, error) { + s.logger.Debugf("validating settings %s to perform an update action", settings.CompanyID) + if err := settings.Validate(); err != nil { + return settings, err + } + + esecret, err := s.encryptor.Encrypt(settings.DocSecret) + if err != nil { + return settings, err + } + + s.logger.Debugf("settings %s are valid to perform an update action", settings.CompanyID) + if _, err := s.adapter.UpsertSettings(ctx, domain.DocSettings{ + CompanyID: settings.CompanyID, + DocAddress: settings.DocAddress, + DocSecret: esecret, + }); err != nil { + return settings, err + } + + return settings, nil +} + +func (s settingsService) DeleteSettings(ctx context.Context, cid string) error { + id := strings.TrimSpace(cid) + s.logger.Debugf("validating cid %s to perform a delete action", id) + + if id == "" { + return &InvalidServiceParameterError{ + Name: "CID", + Reason: "Should not be blank", + } + } + + s.logger.Debugf("uid %s is valid to perform a delete action", id) + return s.adapter.DeleteSettings(ctx, id) +} diff --git a/backend/services/settings/web/handler/select.go b/backend/services/settings/web/handler/select.go new file mode 100644 index 0000000..f04e859 --- /dev/null +++ b/backend/services/settings/web/handler/select.go @@ -0,0 +1,54 @@ +package handler + +import ( + "context" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/port" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" + "go-micro.dev/v4/client" + "golang.org/x/sync/singleflight" +) + +var group singleflight.Group + +type SettingsSelectHandler struct { + service port.DocSettingsService + client client.Client + logger log.Logger +} + +func NewSettingsSelectHandler( + service port.DocSettingsService, + client client.Client, + logger log.Logger, +) SettingsSelectHandler { + return SettingsSelectHandler{ + service: service, + client: client, + logger: logger, + } +} + +func (u SettingsSelectHandler) GetSettings(ctx context.Context, cid *string, res *response.DocSettingsResponse) error { + settings, err, _ := group.Do(*cid, func() (interface{}, error) { + settings, err := u.service.GetSettings(ctx, *cid) + if err != nil { + u.logger.Warnf("could not get company %s settings. Reason: %s", *cid, err.Error()) + return settings, nil + } + + return settings, nil + }) + + if set, ok := settings.(domain.DocSettings); ok { + *res = response.DocSettingsResponse{ + DocAddress: set.DocAddress, + DocSecret: set.DocSecret, + } + return nil + } + + return err +} diff --git a/backend/services/settings/web/handler/select_test.go b/backend/services/settings/web/handler/select_test.go new file mode 100644 index 0000000..ddefcfe --- /dev/null +++ b/backend/services/settings/web/handler/select_test.go @@ -0,0 +1,43 @@ +package handler + +import ( + "context" + "testing" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/adapter" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/service" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" + "github.com/stretchr/testify/assert" +) + +type mockEncryptor struct{} + +func (e mockEncryptor) Encrypt(text string) (string, error) { + return string(text), nil +} + +func (e mockEncryptor) Decrypt(ciphertext string) (string, error) { + return string(ciphertext), nil +} + +func TestSelectCaching(t *testing.T) { + adapter := adapter.NewMemoryDocserverAdapter() + service := service.NewSettingsService(adapter, mockEncryptor{}, log.NewEmptyLogger()) + + sel := NewSettingsSelectHandler(service, nil, log.NewEmptyLogger()) + + service.CreateSettings(context.Background(), domain.DocSettings{ + CompanyID: "mock", + DocAddress: "mock", + DocSecret: "mock", + }) + + t.Run("get settings", func(t *testing.T) { + var res response.DocSettingsResponse + id := "mock" + assert.NoError(t, sel.GetSettings(context.Background(), &id, &res)) + assert.NotEmpty(t, res) + }) +} diff --git a/backend/services/settings/web/message/delete.go b/backend/services/settings/web/message/delete.go new file mode 100644 index 0000000..81723d9 --- /dev/null +++ b/backend/services/settings/web/message/delete.go @@ -0,0 +1,27 @@ +package message + +import ( + "context" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/port" +) + +type DeleteMessageHandler struct { + service port.DocSettingsService +} + +func BuildDeleteMessageHandler(service port.DocSettingsService) DeleteMessageHandler { + return DeleteMessageHandler{ + service: service, + } +} + +func (i DeleteMessageHandler) GetHandler() func(context.Context, interface{}) error { + return func(ctx context.Context, payload interface{}) error { + if cid, ok := payload.(string); !ok { + return _ErrInvalidMessagePayload + } else { + return i.service.DeleteSettings(ctx, cid) + } + } +} diff --git a/backend/services/settings/web/message/error.go b/backend/services/settings/web/message/error.go new file mode 100644 index 0000000..004042d --- /dev/null +++ b/backend/services/settings/web/message/error.go @@ -0,0 +1,5 @@ +package message + +import "errors" + +var _ErrInvalidMessagePayload = errors.New("invalid message payload") diff --git a/backend/services/settings/web/message/insert.go b/backend/services/settings/web/message/insert.go new file mode 100644 index 0000000..c0502c6 --- /dev/null +++ b/backend/services/settings/web/message/insert.go @@ -0,0 +1,36 @@ +package message + +import ( + "context" + "fmt" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/port" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" + "github.com/mitchellh/mapstructure" +) + +type InsertMessageHandler struct { + service port.DocSettingsService +} + +func BuildInsertMessageHandler(service port.DocSettingsService) InsertMessageHandler { + return InsertMessageHandler{ + service: service, + } +} + +func (i InsertMessageHandler) GetHandler() func(context.Context, interface{}) error { + return func(ctx context.Context, payload interface{}) error { + var settings request.DocSettings + if err := mapstructure.Decode(payload, &settings); err != nil { + return err + } + _, err := i.service.UpdateSettings(ctx, domain.DocSettings{ + CompanyID: fmt.Sprint(settings.CompanyID), + DocAddress: settings.DocAddress, + DocSecret: settings.DocSecret, + }) + return err + } +} diff --git a/backend/services/settings/web/server.go b/backend/services/settings/web/server.go new file mode 100644 index 0000000..dc2e2c7 --- /dev/null +++ b/backend/services/settings/web/server.go @@ -0,0 +1,58 @@ +package web + +import ( + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/rpc" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/adapter" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/port" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/service" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/handler" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/message" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" + "go-micro.dev/v4/cache" + mclient "go-micro.dev/v4/client" +) + +type DocserverRPCServer struct { + service port.DocSettingsService + logger log.Logger +} + +func NewDocserverRPCServer( + persistenceConfig *config.PersistenceConfig, + credentialsConfig *config.OAuthCredentialsConfig, + logger log.Logger, +) rpc.RPCEngine { + adptr := adapter.NewMemoryDocserverAdapter() + if persistenceConfig.Persistence.URL != "" { + adptr = adapter.NewMongoDocserverAdapter(persistenceConfig.Persistence.URL) + } + + service := service.NewSettingsService(adptr, crypto.NewAesEncryptor([]byte(credentialsConfig.Credentials.ClientSecret)), logger) + return DocserverRPCServer{ + service: service, + logger: logger, + } +} + +func (a DocserverRPCServer) BuildMessageHandlers() []rpc.RPCMessageHandler { + return []rpc.RPCMessageHandler{ + { + Topic: "insert-settings", + Queue: "pipedrive-docserver", + Handler: message.BuildInsertMessageHandler(a.service).GetHandler(), + }, + { + Topic: "delete-settings", + Queue: "pipedrive-docserver", + Handler: message.BuildDeleteMessageHandler(a.service).GetHandler(), + }, + } +} + +func (a DocserverRPCServer) BuildHandlers(client mclient.Client, cache cache.Cache) []interface{} { + return []interface{}{ + handler.NewSettingsSelectHandler(a.service, client, a.logger), + } +} diff --git a/backend/services/shared/client/api.go b/backend/services/shared/client/api.go new file mode 100644 index 0000000..d7ecea5 --- /dev/null +++ b/backend/services/shared/client/api.go @@ -0,0 +1,159 @@ +package client + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + "strconv" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client/model" + "github.com/go-resty/resty/v2" + "github.com/mitchellh/mapstructure" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" +) + +var _ErrInvalidContentLength = errors.New("could not perform api actions due to exceeding content-length") + +type PipedriveApiClient struct { + client *resty.Client +} + +func NewPipedriveApiClient() PipedriveApiClient { + otelClient := otelhttp.DefaultClient + otelClient.Transport = otelhttp.NewTransport(&http.Transport{ + Proxy: http.ProxyFromEnvironment, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 30 * time.Second, + ResponseHeaderTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + }) + return PipedriveApiClient{ + client: resty.NewWithClient(otelClient). + SetRetryCount(3). + SetRetryWaitTime(120 * time.Millisecond). + SetRetryMaxWaitTime(900 * time.Millisecond). + SetLogger(log.NewEmptyLogger()). + AddRetryCondition(func(r *resty.Response, err error) bool { + return r.StatusCode() == http.StatusTooManyRequests + }), + } +} + +func (p *PipedriveApiClient) GetMe(ctx context.Context, token model.Token) (model.User, error) { + var usr model.User + var resp interface{} + + res, err := p.client.R(). + SetContext(ctx). + SetAuthToken(token.AccessToken). + SetResult(&resp). + Get(fmt.Sprintf("%s/api/v1/users/me", token.ApiDomain)) + + if err != nil { + return usr, err + } + + if res.StatusCode() != http.StatusOK { + return usr, &UnexpectedStatusCodeError{ + Action: "get me", + Code: res.StatusCode(), + } + } + + m, ok := resp.(map[string]interface{}) + if !ok { + return usr, &UnexpectedStatusCodeError{ + Action: "get me", + Code: http.StatusInternalServerError, + } + } + + if err := mapstructure.Decode(m["data"], &usr); err != nil { + return usr, err + } + + return usr, nil +} + +func (p *PipedriveApiClient) UpdateFile(ctx context.Context, id, name string, token model.Token) error { + res, err := p.client.R(). + SetContext(ctx). + SetAuthToken(token.AccessToken). + SetFormData(map[string]string{ + "name": name, + }). + Put(fmt.Sprintf("%s/api/v1/files/%s", token.ApiDomain, id)) + + if err != nil { + return err + } + + if res.StatusCode() != http.StatusOK { + return &UnexpectedStatusCodeError{ + Action: "update file", + Code: res.StatusCode(), + } + } + + return nil +} + +func (c *PipedriveApiClient) ValidateFileSize(ctx context.Context, limit int64, url string) error { + headResp, err := c.client.R(). + SetContext(ctx). + Head(url) + + if err != nil { + return err + } + + if val, err := strconv.ParseInt(headResp.Header().Get("Content-Length"), 10, 0); val > limit || err != nil { + return _ErrInvalidContentLength + } + + return nil +} + +func (p PipedriveApiClient) getFile(ctx context.Context, url string) (io.ReadCloser, error) { + fileResp, err := p.client.R(). + SetContext(ctx). + SetDoNotParseResponse(true). + Get(url) + if err != nil { + return nil, err + } + + return fileResp.RawBody(), nil +} + +func (p *PipedriveApiClient) UploadFile(ctx context.Context, url, deal, fileID, filename string, size int64, token model.Token) error { + if err := p.UpdateFile(ctx, fileID, filename, token); err != nil { + return err + } + + file, err := p.getFile(ctx, url) + if err != nil { + return err + } + defer file.Close() + + _, err = p.client.R(). + SetContext(ctx). + SetAuthToken(token.AccessToken). + SetFileReader("file", filename, file). + SetFormData(map[string]string{ + "deal_id": deal, + }). + Post(fmt.Sprintf("%s/api/v1/files", token.ApiDomain)) + + if err != nil { + return err + } + + return nil +} diff --git a/backend/services/shared/client/auth.go b/backend/services/shared/client/auth.go new file mode 100644 index 0000000..a8976c1 --- /dev/null +++ b/backend/services/shared/client/auth.go @@ -0,0 +1,105 @@ +package client + +import ( + "context" + "net/http" + "net/url" + "strings" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client/model" + "github.com/go-resty/resty/v2" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" +) + +type PipedriveAuthClient struct { + client *resty.Client + clientID string + clientSecret string +} + +func NewPipedriveAuthClient(clientID, clientSecret string) PipedriveAuthClient { + otelClient := otelhttp.DefaultClient + otelClient.Transport = otelhttp.NewTransport(&http.Transport{ + Proxy: http.ProxyFromEnvironment, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 30 * time.Second, + ResponseHeaderTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + }) + return PipedriveAuthClient{ + client: resty.NewWithClient(otelClient). + SetHostURL("https://oauth.pipedrive.com"). + SetRetryCount(0). + SetRetryWaitTime(1000 * time.Millisecond). + SetRetryMaxWaitTime(1500 * time.Millisecond). + SetLogger(log.NewEmptyLogger()). + AddRetryCondition(func(r *resty.Response, err error) bool { + return r.StatusCode() == http.StatusTooManyRequests + }), + clientID: clientID, + clientSecret: clientSecret, + } +} + +func (c PipedriveAuthClient) GetAccessToken(ctx context.Context, code, redirectURI string) (model.Token, error) { + var resp model.Token + if _, err := url.ParseRequestURI(redirectURI); err != nil { + return resp, ErrInvalidUrlFormat + } + + res, err := c.client.R(). + SetHeader("Content-Type", "application/x-www-form-urlencoded"). + SetContext(ctx). + SetBody(strings.NewReader(url.Values{ + "grant_type": []string{"authorization_code"}, + "code": []string{code}, + "redirect_uri": []string{redirectURI}, + }.Encode())). + SetBasicAuth(c.clientID, c.clientSecret). + SetResult(&resp). + Post("/oauth/token") + + if err != nil { + return resp, err + } + + if res.StatusCode() != http.StatusOK { + return resp, &UnexpectedStatusCodeError{ + Action: "get access token", + Code: res.StatusCode(), + } + } + + return resp, resp.Validate() +} + +func (c PipedriveAuthClient) RefreshAccessToken(ctx context.Context, refreshToken string) (model.Token, error) { + var resp model.Token + + res, err := c.client.R(). + SetHeader("Content-Type", "application/x-www-form-urlencoded"). + SetContext(ctx). + SetBody(strings.NewReader(url.Values{ + "grant_type": []string{"refresh_token"}, + "refresh_token": []string{refreshToken}, + }.Encode())). + SetBasicAuth(c.clientID, c.clientSecret). + SetResult(&resp). + Post("/oauth/token") + + if err != nil { + return resp, err + } + + if res.StatusCode() != http.StatusOK { + return resp, &UnexpectedStatusCodeError{ + Action: "refresh access token", + Code: res.StatusCode(), + } + } + + return resp, resp.Validate() +} diff --git a/backend/services/shared/client/command.go b/backend/services/shared/client/command.go new file mode 100644 index 0000000..e98d1cd --- /dev/null +++ b/backend/services/shared/client/command.go @@ -0,0 +1,76 @@ +package client + +import ( + "context" + "errors" + "fmt" + "net/http" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" + "github.com/go-resty/resty/v2" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" +) + +var ErrCommandServiceError = errors.New("got a command service error 1 status") + +type CommandClient struct { + client *resty.Client + jwtManager crypto.JwtManager +} + +func NewCommandClient(jwtManager crypto.JwtManager) CommandClient { + otelClient := otelhttp.DefaultClient + otelClient.Transport = otelhttp.NewTransport(&http.Transport{ + Proxy: http.ProxyFromEnvironment, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 30 * time.Second, + ResponseHeaderTimeout: 6 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + }) + return CommandClient{ + client: resty.NewWithClient(otelClient). + SetRetryCount(0). + SetRetryWaitTime(120 * time.Millisecond). + SetRetryMaxWaitTime(900 * time.Millisecond). + SetLogger(log.NewEmptyLogger()). + AddRetryCondition(func(r *resty.Response, err error) bool { + return r.StatusCode() == http.StatusTooManyRequests + }), + jwtManager: jwtManager, + } +} + +func (p *CommandClient) License(ctx context.Context, url, secret string) error { + var resp response.BaseCommandResponse + + token, err := p.jwtManager.Sign(secret, request.BaseCommandRequest{ + C: "version", + }) + + if err != nil { + return err + } + + res, err := p.client.R(). + SetContext(ctx). + SetBody(request.TokenCommandRequest{ + Token: token, + }). + SetResult(&resp). + Post(fmt.Sprintf("%scoauthoring/CommandService.ashx", url)) + + if err != nil { + return err + } + + if res.StatusCode() >= 300 || resp.Error != 0 { + return ErrCommandServiceError + } + + return nil +} diff --git a/backend/services/shared/client/error.go b/backend/services/shared/client/error.go new file mode 100644 index 0000000..caf3ea6 --- /dev/null +++ b/backend/services/shared/client/error.go @@ -0,0 +1,17 @@ +package client + +import ( + "errors" + "fmt" +) + +var ErrInvalidUrlFormat error = errors.New("url is not valid") + +type UnexpectedStatusCodeError struct { + Action string + Code int +} + +func (e *UnexpectedStatusCodeError) Error() string { + return fmt.Sprintf("could not perform zoom %s action. Status code: %d", e.Action, e.Code) +} diff --git a/backend/services/shared/client/model/access.go b/backend/services/shared/client/model/access.go new file mode 100644 index 0000000..2fdaf4c --- /dev/null +++ b/backend/services/shared/client/model/access.go @@ -0,0 +1,7 @@ +package model + +type Access struct { + App string `json:"app" mapstructure:"app"` + Admin bool `json:"admin" mapstructure:"admin"` + PermissionID string `json:"permission_set_id" mapstructure:"permission_set_id"` +} diff --git a/backend/services/shared/client/model/error.go b/backend/services/shared/client/model/error.go new file mode 100644 index 0000000..d3e50bf --- /dev/null +++ b/backend/services/shared/client/model/error.go @@ -0,0 +1,5 @@ +package model + +import "errors" + +var ErrInvalidTokenFormat error = errors.New("could not perform zoom action due to unexpected token format") diff --git a/backend/services/shared/client/model/lang.go b/backend/services/shared/client/model/lang.go new file mode 100644 index 0000000..770b797 --- /dev/null +++ b/backend/services/shared/client/model/lang.go @@ -0,0 +1,18 @@ +package model + +type Language struct { + Code string `json:"country_code" mapstructure:"country_code"` + Lang string `json:"language_code" mapstructure:"language_code"` +} + +func (l *Language) Validate() error { + if l.Code == "" { + l.Code = "US" + } + + if l.Lang == "" { + l.Lang = "en" + } + + return nil +} diff --git a/backend/services/shared/client/model/token.go b/backend/services/shared/client/model/token.go new file mode 100644 index 0000000..4488b87 --- /dev/null +++ b/backend/services/shared/client/model/token.go @@ -0,0 +1,46 @@ +package model + +import "strings" + +type Token struct { + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + TokenType string `json:"token_type"` + Scope string `json:"scope"` + ApiDomain string `json:"api_domain"` + ExpiresIn int `json:"expires_in"` +} + +func (t *Token) Validate() error { + t.AccessToken = strings.TrimSpace(t.AccessToken) + t.RefreshToken = strings.TrimSpace(t.RefreshToken) + t.TokenType = strings.TrimSpace(t.TokenType) + t.ApiDomain = strings.TrimSpace(t.ApiDomain) + t.Scope = strings.TrimSpace(t.Scope) + + if t.AccessToken == "" { + return ErrInvalidTokenFormat + } + + if t.RefreshToken == "" { + return ErrInvalidTokenFormat + } + + if t.TokenType == "" { + return ErrInvalidTokenFormat + } + + if t.ApiDomain == "" { + return ErrInvalidTokenFormat + } + + if t.Scope == "" { + return ErrInvalidTokenFormat + } + + if t.ExpiresIn < 1 { + return ErrInvalidTokenFormat + } + + return nil +} diff --git a/backend/services/shared/client/model/user.go b/backend/services/shared/client/model/user.go new file mode 100644 index 0000000..94cbb88 --- /dev/null +++ b/backend/services/shared/client/model/user.go @@ -0,0 +1,27 @@ +package model + +import "strings" + +type User struct { + ID int `json:"id"` + CompanyID int `json:"company_id" mapstructure:"company_id"` + Name string `json:"name"` + Email string `json:"email"` + Language Language `json:"language" mapstructure:"language"` + Access []Access `json:"access" mapstructure:"access"` +} + +func (u *User) Validate() error { + u.Name = strings.TrimSpace(u.Name) + u.Email = strings.TrimSpace(u.Email) + + if u.Name == "" { + return ErrInvalidTokenFormat + } + + if err := u.Language.Validate(); err != nil { + return err + } + + return nil +} diff --git a/backend/services/shared/config.go b/backend/services/shared/config.go new file mode 100644 index 0000000..f8a7645 --- /dev/null +++ b/backend/services/shared/config.go @@ -0,0 +1,73 @@ +package shared + +import ( + "context" + "os" + "time" + + "github.com/sethvargo/go-envconfig" + "gopkg.in/yaml.v2" +) + +type OnlyofficeConfig struct { + Onlyoffice struct { + Builder OnlyofficeBuilderConfig `yaml:"builder"` + Callback OnlyofficeCallbackConfig `yaml:"callback"` + } `yaml:"onlyoffice"` +} + +func (oc *OnlyofficeConfig) Validate() error { + if err := oc.Onlyoffice.Builder.Validate(); err != nil { + return err + } + + return oc.Onlyoffice.Callback.Validate() +} + +func BuildNewOnlyofficeConfig(path string) func() (*OnlyofficeConfig, error) { + return func() (*OnlyofficeConfig, error) { + var config OnlyofficeConfig + config.Onlyoffice.Callback.MaxSize = 20000000 + config.Onlyoffice.Callback.UploadTimeout = 120 + if path != "" { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + decoder := yaml.NewDecoder(file) + + if err := decoder.Decode(&config); err != nil { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) + defer cancel() + if err := envconfig.Process(ctx, &config); err != nil { + return nil, err + } + + return &config, config.Validate() + } +} + +type OnlyofficeBuilderConfig struct { + GatewayURL string `yaml:"gateway_url" env:"ONLYOFFICE_GATEWAY_URL,overwrite"` + CallbackURL string `yaml:"callback_url" env:"ONLYOFFICE_CALLBACK_URL,overwrite"` + AllowedDownloads int `yaml:"allowed_downloads" env:"ONLYOFFICE_ALLOWED_DOWNLOADS,overwrite"` +} + +func (oc *OnlyofficeBuilderConfig) Validate() error { + return nil +} + +type OnlyofficeCallbackConfig struct { + MaxSize int64 `yaml:"max_size" env:"ONLYOFFICE_CALLBACK_MAX_SIZE,overwrite"` + UploadTimeout int `yaml:"upload_timeout" env:"ONLYOFFICE_CALLBACK_UPLOAD_TIMEOUT,overwrite"` +} + +func (c *OnlyofficeCallbackConfig) Validate() error { + return nil +} diff --git a/backend/services/shared/constants/file.go b/backend/services/shared/constants/file.go new file mode 100644 index 0000000..4f92c15 --- /dev/null +++ b/backend/services/shared/constants/file.go @@ -0,0 +1,88 @@ +package constants + +import ( + "errors" + "strings" +) + +var ErrOnlyofficeExtensionNotSupported = errors.New("file extension is not supported") + +const ( + _OnlyofficeWordType string = "word" + _OnlyofficeCellType string = "cell" + _OnlyofficeSlideType string = "slide" +) + +var OnlyofficeEditableExtensions map[string]string = map[string]string{ + "xlsx": _OnlyofficeCellType, + "pptx": _OnlyofficeSlideType, + "docx": _OnlyofficeWordType, +} + +var OnlyofficeFileExtensions map[string]string = map[string]string{ + "xls": _OnlyofficeCellType, + "xlsx": _OnlyofficeCellType, + "xlsm": _OnlyofficeCellType, + "xlt": _OnlyofficeCellType, + "xltx": _OnlyofficeCellType, + "xltm": _OnlyofficeCellType, + "ods": _OnlyofficeCellType, + "fods": _OnlyofficeCellType, + "ots": _OnlyofficeCellType, + "csv": _OnlyofficeCellType, + "pps": _OnlyofficeSlideType, + "ppsx": _OnlyofficeSlideType, + "ppsm": _OnlyofficeSlideType, + "ppt": _OnlyofficeSlideType, + "pptx": _OnlyofficeSlideType, + "pptm": _OnlyofficeSlideType, + "pot": _OnlyofficeSlideType, + "potx": _OnlyofficeSlideType, + "potm": _OnlyofficeSlideType, + "odp": _OnlyofficeSlideType, + "fodp": _OnlyofficeSlideType, + "otp": _OnlyofficeSlideType, + "doc": _OnlyofficeWordType, + "docx": _OnlyofficeWordType, + "docm": _OnlyofficeWordType, + "dot": _OnlyofficeWordType, + "dotx": _OnlyofficeWordType, + "dotm": _OnlyofficeWordType, + "odt": _OnlyofficeWordType, + "fodt": _OnlyofficeWordType, + "ott": _OnlyofficeWordType, + "rtf": _OnlyofficeWordType, + "txt": _OnlyofficeWordType, + "html": _OnlyofficeWordType, + "htm": _OnlyofficeWordType, + "mht": _OnlyofficeWordType, + "pdf": _OnlyofficeWordType, + "djvu": _OnlyofficeWordType, + "fb2": _OnlyofficeWordType, + "epub": _OnlyofficeWordType, + "xps": _OnlyofficeWordType, +} + +func IsExtensionSupported(fileExt string) bool { + _, exists := OnlyofficeFileExtensions[strings.ToLower(fileExt)] + if exists { + return true + } + return false +} + +func IsExtensionEditable(fileExt string) bool { + _, exists := OnlyofficeEditableExtensions[strings.ToLower(fileExt)] + if exists { + return true + } + return false +} + +func GetFileType(fileExt string) (string, error) { + fileType, exists := OnlyofficeFileExtensions[strings.ToLower(fileExt)] + if !exists { + return "", ErrOnlyofficeExtensionNotSupported + } + return fileType, nil +} diff --git a/backend/services/shared/constants/session.go b/backend/services/shared/constants/session.go new file mode 100644 index 0000000..334d8c1 --- /dev/null +++ b/backend/services/shared/constants/session.go @@ -0,0 +1,7 @@ +package constants + +const ( + SESSION_KEY = "ZMOFFICE" + SESSION_KEY_VERIFIER = "verifier" + SESSION_KEY_STATE = "state" +) diff --git a/backend/services/shared/crypto/aes.go b/backend/services/shared/crypto/aes.go new file mode 100644 index 0000000..6d5825d --- /dev/null +++ b/backend/services/shared/crypto/aes.go @@ -0,0 +1,77 @@ +package crypto + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/base64" + "errors" + "io" +) + +var _ErrInvalidNonceSize = errors.New("invalid nonce size") + +type Encryptor interface { + Encrypt(text string) (string, error) + Decrypt(ciphertext string) (string, error) +} + +type aesEncryptor struct { + key []byte +} + +func NewAesEncryptor(key []byte) Encryptor { + validKey := make([]byte, 32) + copy(validKey, key) + return aesEncryptor{ + key: validKey, + } +} + +func (e aesEncryptor) Encrypt(text string) (string, error) { + c, err := aes.NewCipher(e.key) + if err != nil { + return "", err + } + + gcm, err := cipher.NewGCM(c) + if err != nil { + return "", err + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return "", err + } + + result := gcm.Seal(nonce, nonce, []byte(text), nil) + + return base64.StdEncoding.EncodeToString(result), nil +} + +func (e aesEncryptor) Decrypt(text string) (string, error) { + c, err := aes.NewCipher(e.key) + if err != nil { + return "", err + } + + gcm, err := cipher.NewGCM(c) + if err != nil { + return "", err + } + + buf, err := base64.StdEncoding.DecodeString(text) + if err != nil { + return "", err + } + + nonceSize := gcm.NonceSize() + if len(buf) < nonceSize { + return "", _ErrInvalidNonceSize + } + + nonce, ciphertext := buf[:nonceSize], buf[nonceSize:] + plaintext, _ := gcm.Open(nil, nonce, ciphertext, nil) + + return string(plaintext), nil +} diff --git a/backend/services/shared/crypto/aes_test.go b/backend/services/shared/crypto/aes_test.go new file mode 100644 index 0000000..7f6d40d --- /dev/null +++ b/backend/services/shared/crypto/aes_test.go @@ -0,0 +1,49 @@ +package crypto + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +const ( + _AES_SECRET = "sec" + _AES_TEXT = "mock" + _AES_ENCRYPTED = "79xG0i1SV2Kxz7KB8FKwMQqozRm1ADf45zLDq2MHBd8=" +) + +func TestEncryptText(t *testing.T) { + type test struct { + text string + isErr bool + } + + t.Parallel() + encryptor := NewAesEncryptor([]byte(_AES_SECRET)) + + tests := []test{ + {text: _AES_TEXT, isErr: false}, + {text: "1235423523623", isErr: false}, + {text: "477bbd54-3475-4036-bb64-cafd07275632", isErr: false}, + {text: "b29b5cca7ea66fa4aaeda02238b652b5dad0f31ab52a6f81a785ca4b73c577e97dac14dbf0bc24f8a0371e891de6bd304bddda26bef10f921d7079df7e0a7ccca52b9ab4a47e170f3a2a2d3c3dffeae9", isErr: false}, + } + + for _, test := range tests { + cipher, err := encryptor.Encrypt(test.text) + if test.isErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.NotEmpty(t, cipher) + } + } +} + +func TestDecryptText(t *testing.T) { + t.Parallel() + encryptor := NewAesEncryptor([]byte(_AES_SECRET)) + + text, err := encryptor.Decrypt(_AES_ENCRYPTED) + assert.NoError(t, err) + assert.Equal(t, _AES_TEXT, text) +} diff --git a/backend/services/shared/crypto/jwt.go b/backend/services/shared/crypto/jwt.go new file mode 100644 index 0000000..7baae20 --- /dev/null +++ b/backend/services/shared/crypto/jwt.go @@ -0,0 +1,75 @@ +package crypto + +import ( + "errors" + + "github.com/golang-jwt/jwt" + "github.com/mitchellh/mapstructure" +) + +var ErrJwtManagerSigning = errors.New("could not generate a new jwt") +var ErrJwtManagerEmptyToken = errors.New("could not verify an empty jwt") +var ErrJwtManagerEmptySecret = errors.New("could not sign/verify witn an empty secret") +var ErrJwtManagerEmptyDecodingBody = errors.New("could not decode a jwt. Got empty interface") +var ErrJwtManagerInvalidSigningMethod = errors.New("unexpected jwt signing method") +var ErrJwtManagerCastOrInvalidToken = errors.New("could not cast claims or invalid jwt") + +type JwtManager interface { + Sign(secret string, payload interface { + Valid() error + }) (string, error) + Verify(secret, jwtToken string, body interface{}) error +} + +type onlyofficeJwtManager struct { + key []byte +} + +func NewOnlyofficeJwtManager() JwtManager { + return onlyofficeJwtManager{} +} + +func (j onlyofficeJwtManager) Sign(secret string, payload interface { + Valid() error +}) (string, error) { + token := jwt.NewWithClaims(jwt.SigningMethodHS256, payload) + ss, err := token.SignedString([]byte(secret)) + + if err != nil { + return "", ErrJwtManagerSigning + } + + return ss, nil +} + +func (j onlyofficeJwtManager) Verify(secret, jwtToken string, body interface{}) error { + if secret == "" { + return ErrJwtManagerEmptySecret + } + + if jwtToken == "" { + return ErrJwtManagerEmptyToken + } + + if body == nil { + return ErrJwtManagerEmptyDecodingBody + } + + token, err := jwt.Parse(jwtToken, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, ErrJwtManagerInvalidSigningMethod + } + + return []byte(secret), nil + }) + + if err != nil { + return err + } + + if claims, ok := token.Claims.(jwt.MapClaims); !ok || !token.Valid { + return ErrJwtManagerCastOrInvalidToken + } else { + return mapstructure.Decode(claims, body) + } +} diff --git a/backend/services/shared/error.go b/backend/services/shared/error.go new file mode 100644 index 0000000..6a09662 --- /dev/null +++ b/backend/services/shared/error.go @@ -0,0 +1,14 @@ +package shared + +import ( + "fmt" +) + +type InvalidConfigurationParameterError struct { + Parameter string + Reason string +} + +func (e *InvalidConfigurationParameterError) Error() string { + return fmt.Sprintf("invald configuration [%s] parameter. Reason: %s", e.Parameter, e.Reason) +} diff --git a/backend/services/shared/message/job.go b/backend/services/shared/message/job.go new file mode 100644 index 0000000..1a645ef --- /dev/null +++ b/backend/services/shared/message/job.go @@ -0,0 +1,16 @@ +package message + +import "encoding/json" + +type JobMessage struct { + UID string `json:"uid"` + Deal string `json:"deal"` + FileID string `json:"fileID"` + Filename string `json:"filename"` + Url string `json:"url"` +} + +func (s JobMessage) ToJSON() []byte { + buf, _ := json.Marshal(s) + return buf +} diff --git a/backend/services/shared/request/callback.go b/backend/services/shared/request/callback.go new file mode 100644 index 0000000..73dec79 --- /dev/null +++ b/backend/services/shared/request/callback.go @@ -0,0 +1,65 @@ +package request + +import ( + "encoding/json" + "fmt" + "strings" +) + +type MissingRequestFieldsError struct { + Request string + Field string + Reason string +} + +func (e *MissingRequestFieldsError) Error() string { + return fmt.Sprintf("missing %s's field %s. Reason: %s", e.Request, e.Field, e.Reason) +} + +type CallbackRequest struct { + Actions []struct { + Type int `json:"type"` + UserID string `json:"userid"` + } `json:"actions"` + Key string `json:"key"` + Status int `json:"status"` + Users []string `json:"users"` + URL string `json:"url"` + Token string `json:"token"` +} + +func (cr CallbackRequest) ToJSON() []byte { + buf, _ := json.Marshal(cr) + return buf +} + +func (c *CallbackRequest) Validate() error { + c.Key = strings.TrimSpace(c.Key) + c.Token = strings.TrimSpace(c.Token) + + if c.Key == "" { + return &MissingRequestFieldsError{ + Request: "Callback", + Field: "Key", + Reason: "Should not be empty", + } + } + + if c.Token == "" { + return &MissingRequestFieldsError{ + Request: "Callback", + Field: "Token", + Reason: "Should not be empty", + } + } + + if c.Status <= 0 || c.Status > 7 { + return &MissingRequestFieldsError{ + Request: "Callback", + Field: "Status", + Reason: "Invalid status. Exptected 0 < status <= 7", + } + } + + return nil +} diff --git a/backend/services/shared/request/command.go b/backend/services/shared/request/command.go new file mode 100644 index 0000000..3481a07 --- /dev/null +++ b/backend/services/shared/request/command.go @@ -0,0 +1,26 @@ +package request + +import ( + "encoding/json" + + "github.com/golang-jwt/jwt" +) + +type BaseCommandRequest struct { + jwt.StandardClaims + C string `json:"c"` +} + +func (c BaseCommandRequest) ToJSON() []byte { + buf, _ := json.Marshal(c) + return buf +} + +type TokenCommandRequest struct { + Token string `json:"token"` +} + +func (c TokenCommandRequest) ToJSON() []byte { + buf, _ := json.Marshal(c) + return buf +} diff --git a/backend/services/shared/request/config.go b/backend/services/shared/request/config.go new file mode 100644 index 0000000..c41d967 --- /dev/null +++ b/backend/services/shared/request/config.go @@ -0,0 +1,18 @@ +package request + +import "encoding/json" + +type BuildConfigRequest struct { + UID int `json:"uid"` + CID int `json:"cid"` + Deal string `json:"deal_id"` + UserAgent string `json:"user_agent"` + FileID string `json:"file_id"` + Filename string `json:"file_name"` + DocKey string `json:"doc_key"` +} + +func (c BuildConfigRequest) ToJSON() []byte { + buf, _ := json.Marshal(c) + return buf +} diff --git a/backend/services/shared/request/session.go b/backend/services/shared/request/session.go new file mode 100644 index 0000000..5012972 --- /dev/null +++ b/backend/services/shared/request/session.go @@ -0,0 +1,13 @@ +package request + +import "encoding/json" + +type OwnerRemoveSessionRequest struct { + Uid string `json:"uid" mapstructure:"uid"` + Mid string `json:"mid" mapstructure:"mid"` +} + +func (r OwnerRemoveSessionRequest) ToJSON() []byte { + buf, _ := json.Marshal(r) + return buf +} diff --git a/backend/services/shared/request/settings.go b/backend/services/shared/request/settings.go new file mode 100644 index 0000000..37afe37 --- /dev/null +++ b/backend/services/shared/request/settings.go @@ -0,0 +1,42 @@ +package request + +import ( + "encoding/json" + "errors" + "strings" +) + +var _ErrInvalidCompanyID = errors.New("invalid company id") +var _ErrInvalidDocAddress = errors.New("invalid doc server address") +var _ErrInvalidDocSecret = errors.New("invalid doc server secret") +var _ErrInvalidDocHeader = errors.New("invalid doc server header") + +type DocSettings struct { + CompanyID int `json:"company_id" mapstructure:"company_id"` + DocAddress string `json:"doc_address" mapstructure:"doc_address"` + DocSecret string `json:"doc_secret" mapstructure:"doc_secret"` +} + +func (c DocSettings) ToJSON() []byte { + buf, _ := json.Marshal(c) + return buf +} + +func (c DocSettings) Validate() error { + c.DocAddress = strings.TrimSpace(c.DocAddress) + c.DocSecret = strings.TrimSpace(c.DocSecret) + + if c.CompanyID <= 0 { + return _ErrInvalidCompanyID + } + + if c.DocAddress == "" { + return _ErrInvalidDocAddress + } + + if c.DocSecret == "" { + return _ErrInvalidDocSecret + } + + return nil +} diff --git a/backend/services/shared/request/token.go b/backend/services/shared/request/token.go new file mode 100644 index 0000000..292d141 --- /dev/null +++ b/backend/services/shared/request/token.go @@ -0,0 +1,20 @@ +package request + +import ( + "encoding/json" + + "github.com/golang-jwt/jwt" +) + +type PipedriveTokenContext struct { + jwt.StandardClaims + UID int `json:"userId" mapstructure:"userId"` + CID int `json:"companyId" mapstructure:"companyId"` + IAT int `json:"iat" mapstructure:"iat"` + EXP int `json:"exp" mapstructure:"exp"` +} + +func (c PipedriveTokenContext) ToJSON() []byte { + buf, _ := json.Marshal(c) + return buf +} diff --git a/backend/services/shared/request/uninstall.go b/backend/services/shared/request/uninstall.go new file mode 100644 index 0000000..9722ebb --- /dev/null +++ b/backend/services/shared/request/uninstall.go @@ -0,0 +1,15 @@ +package request + +import "encoding/json" + +type UninstallRequest struct { + ClientID string `json:"client_id"` + CompanyID int `json:"company_id"` + UserID int `json:"user_id"` + Timestamp string `json:"timestamp"` +} + +func (r UninstallRequest) ToJSON() []byte { + buf, _ := json.Marshal(r) + return buf +} diff --git a/backend/services/shared/response/callback.go b/backend/services/shared/response/callback.go new file mode 100644 index 0000000..34bf788 --- /dev/null +++ b/backend/services/shared/response/callback.go @@ -0,0 +1,12 @@ +package response + +import "encoding/json" + +type CallbackResponse struct { + Error int `json:"error"` +} + +func (c CallbackResponse) ToJSON() []byte { + buf, _ := json.Marshal(c) + return buf +} diff --git a/backend/services/shared/response/command.go b/backend/services/shared/response/command.go new file mode 100644 index 0000000..582285f --- /dev/null +++ b/backend/services/shared/response/command.go @@ -0,0 +1,5 @@ +package response + +type BaseCommandResponse struct { + Error int `json:"error"` +} diff --git a/backend/services/shared/response/config.go b/backend/services/shared/response/config.go new file mode 100644 index 0000000..d0d8dae --- /dev/null +++ b/backend/services/shared/response/config.go @@ -0,0 +1,67 @@ +package response + +import ( + "encoding/json" + + "github.com/golang-jwt/jwt" +) + +type BuildConfigResponse struct { + jwt.StandardClaims + Document Document `json:"document"` + DocumentType string `json:"documentType"` + EditorConfig EditorConfig `json:"editorConfig"` + Type string `json:"type"` + Token string `json:"token,omitempty"` + Session bool `json:"is_session,omitempty"` + ServerURL string `json:"server_url"` +} + +func (r BuildConfigResponse) ToJSON() []byte { + buf, _ := json.Marshal(r) + return buf +} + +type Permissions struct { + Comment bool `json:"comment"` + Copy bool `json:"copy"` + DeleteCommentAuthorOnly bool `json:"deleteCommentAuthorOnly"` + Download bool `json:"download"` + Edit bool `json:"edit"` + EditCommentAuthorOnly bool `json:"editCommentAuthorOnly"` + FillForms bool `json:"fillForms"` + ModifyContentControl bool `json:"modifyContentControl"` + ModifyFilter bool `json:"modifyFilter"` + Print bool `json:"print"` + Review bool `json:"review"` +} + +type Document struct { + FileType string `json:"fileType"` + Key string `json:"key"` + Title string `json:"title"` + URL string `json:"url"` + Permissions Permissions `json:"permissions"` +} + +type EditorConfig struct { + User User `json:"user"` + CallbackURL string `json:"callbackUrl"` + Customization Customization `json:"customization"` + Lang string `json:"lang,omitempty"` +} + +type User struct { + ID string `json:"id"` + Name string `json:"name"` +} + +type Customization struct { + Goback Goback `json:"goback"` + Plugins bool `json:"plugins"` + HideRightMenu bool `json:"hideRightMenu"` +} + +type Goback struct { + RequestClose bool `json:"requestClose"` +} diff --git a/backend/services/shared/response/error.go b/backend/services/shared/response/error.go new file mode 100644 index 0000000..610ae23 --- /dev/null +++ b/backend/services/shared/response/error.go @@ -0,0 +1,15 @@ +package response + +import "encoding/json" + +type MicroError struct { + ID string `json:"id"` + Code int `json:"code"` + Detail string `json:"detail"` + Status string `json:"status"` +} + +func (e MicroError) ToJSON() []byte { + buf, _ := json.Marshal(e) + return buf +} diff --git a/backend/services/shared/response/generic.go b/backend/services/shared/response/generic.go new file mode 100644 index 0000000..e38ff5a --- /dev/null +++ b/backend/services/shared/response/generic.go @@ -0,0 +1,13 @@ +package response + +import "encoding/json" + +type GenericReponse struct { + Error int `json:"error"` + Reason string `json:"reason,omitempty"` +} + +func (r GenericReponse) ToJSON() []byte { + buf, _ := json.Marshal(r) + return buf +} diff --git a/backend/services/shared/response/settings.go b/backend/services/shared/response/settings.go new file mode 100644 index 0000000..ea49f4f --- /dev/null +++ b/backend/services/shared/response/settings.go @@ -0,0 +1,13 @@ +package response + +import "encoding/json" + +type DocSettingsResponse struct { + DocAddress string `json:"doc_address"` + DocSecret string `json:"doc_secret"` +} + +func (r DocSettingsResponse) ToJSON() []byte { + buf, _ := json.Marshal(r) + return buf +} diff --git a/backend/services/shared/response/user.go b/backend/services/shared/response/user.go new file mode 100644 index 0000000..0f6ed48 --- /dev/null +++ b/backend/services/shared/response/user.go @@ -0,0 +1,29 @@ +package response + +import "encoding/json" + +type UserResponse struct { + ID string `json:"id" mapstructure:"id"` + AccessToken string `json:"access_token" mapstructure:"access_token"` + RefreshToken string `json:"refresh_token" mapstructure:"refresh_token"` + TokenType string `json:"token_type" mapstructure:"token_type"` + Scope string `json:"scope" mapstructure:"scope"` + ApiDomain string `json:"api_domain" mapstructure:"api_domain"` + ExpiresAt int64 `json:"expires_at" mapstructure:"expires_at"` +} + +func (ur UserResponse) ToJSON() []byte { + buf, _ := json.Marshal(ur) + return buf +} + +type UserTokenResponse struct { + ID string `json:"id"` + AccessToken string `json:"access_token"` + ExpiresAt int64 `json:"expires_at"` +} + +func (ut UserTokenResponse) ToJSON() []byte { + buf, _ := json.Marshal(ut) + return buf +} diff --git a/frontend/.eslintignore b/frontend/.eslintignore new file mode 100644 index 0000000..4194e88 --- /dev/null +++ b/frontend/.eslintignore @@ -0,0 +1,9 @@ +assets + +node_modules + +__tests__ +__snapshots__ +__mocks__ + +setupTests.ts diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js new file mode 100644 index 0000000..dc4ac9e --- /dev/null +++ b/frontend/.eslintrc.js @@ -0,0 +1,66 @@ +/* eslint-disable */ +const path = require("path"); + +module.exports = { + env: { + browser: true, + node: true, + es2020: true, + }, + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaVersion: 2020, + sourceType: "module", + ecmaFeatures: { + jsx: true, + }, + }, + plugins: ["@typescript-eslint", "react", "prettier"], + extends: [ + "airbnb", + "airbnb/hooks", + "plugin:@typescript-eslint/recommended", + "plugin:react/recommended", + "plugin:import/errors", + "plugin:import/warnings", + "plugin:import/typescript", + "prettier", + ], + rules: { + "react/jsx-filename-extension": [1, { extensions: [".ts", ".tsx"] }], + "import/extensions": "off", + "react/prop-types": "off", + "jsx-a11y/anchor-is-valid": "off", + "react/jsx-props-no-spreading": ["error", { custom: "ignore" }], + "prettier/prettier": "error", + "react/no-unescaped-entities": "off", + "import/no-cycle": [0, { ignoreExternal: true }], + "prefer-const": "off", + "no-use-before-define": "off", + "react/function-component-definition": "off", + "import/prefer-default-export": "off", + "react/require-default-props": "off", + "@typescript-eslint/no-use-before-define": [ + "error", + { functions: false, classes: false, variables: true }, + ], + }, + settings: { + "import/parsers": { + "@typescript-eslint/parser": [".ts", ".tsx"], + }, + "import/resolver": { + "babel-module": { + extensions: [".js", ".jsx", ".ts", ".tsx"], + }, + node: { + extensions: [".js", ".jsx", ".ts", ".tsx"], + paths: ["src"], + }, + typescript: { + alwaysTryTypes: true, + project: path.resolve(__dirname, "tsconfig.json"), + }, + }, + }, +}; diff --git a/frontend/.vscode/settings.json b/frontend/.vscode/settings.json new file mode 100644 index 0000000..1a98714 --- /dev/null +++ b/frontend/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "editor.defaultFormatter": "dbaeumer.vscode-eslint", + "editor.formatOnSave": true, + "eslint.alwaysShowStatus": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + } +} diff --git a/frontend/jest.config.js b/frontend/jest.config.js new file mode 100644 index 0000000..a5df951 --- /dev/null +++ b/frontend/jest.config.js @@ -0,0 +1,25 @@ +module.exports = { + verbose: true, + preset: "ts-jest", + testEnvironment: "jsdom", + moduleNameMapper: { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": + "/src/__mocks__/fileMock.ts", + "\\.(css|less)$": "/src/__mocks__/styleMock.ts", + }, + setupFilesAfterEnv: ["/src/setupTests.ts"], + collectCoverageFrom: [ + "**/*.{ts,tsx}", + "!**/*.d.ts", + "!**/node_modules/**", + "!**/vendor/**", + ], + coverageThreshold: { + global: { + branches: 50, + functions: 50, + lines: 50, + statements: 50, + }, + }, +}; diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..c1b973f --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,102 @@ +{ + "name": "onlyoffice-pipedrive", + "description": "ONLYOFFICE Pipedrive App Frontend", + "author": "Ascensio System SIA (https://www.onlyoffice.com)", + "maintainers": [ + "Ascensio System SIA (https://www.onlyoffice.com)" + ], + "license": "Apache-2.0", + "version": "0.0.1", + "private": true, + "repository": { + "type": "git", + "url": "https://github.com/ONLYOFFICE/onlyoffice-pipedrive.git" + }, + "keywords": [ + "docs", + "editor", + "office", + "pipedrive", + "onlyoffice" + ], + "bugs": { + "url": "https://github.com/ONLYOFFICE/onlyoffice-pipedrive/issues" + }, + "homepage": "https://github.com/ONLYOFFICE/onlyoffice-pipedrive#readme", + "scripts": { + "start:dev": "webpack serve --config webpack.development.js", + "eslint:fix": "eslint \"**/*.{ts,tsx}\" --fix", + "test": "jest --passWithNoTests", + "test:watch": "jest --watch --passWithNoTests", + "test:coverage": "jest --coverage --passWithNoTests", + "build": "webpack --config webpack.production.js" + }, + "engines": { + "node": ">=16.15.1", + "npm": ">=8.11.0" + }, + "devDependencies": { + "@babel/core": "^7.20.12", + "@svgr/webpack": "^6.5.1", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@types/jest": "^29.2.0", + "@types/md5": "^2.3.2", + "@types/node": "^18.11.0", + "@types/react": "^18.0.21", + "@types/react-dom": "^18.0.6", + "@typescript-eslint/eslint-plugin": "^5.40.1", + "@typescript-eslint/parser": "^5.40.1", + "autoprefixer": "^10.4.12", + "babel-plugin-module-resolver": "^5.0.0", + "copy-webpack-plugin": "^11.0.0", + "css-loader": "^6.7.1", + "dotenv": "^16.0.3", + "dotenv-webpack": "^8.0.1", + "eslint": "^8.25.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-prettier": "^8.5.0", + "eslint-import-resolver-alias": "^1.1.2", + "eslint-import-resolver-babel-module": "^5.3.1", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-jsx-a11y": "^6.6.1", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-react": "^7.31.10", + "eslint-plugin-react-hooks": "^4.6.0", + "html-webpack-plugin": "^5.5.0", + "jest": "^29.2.0", + "jest-environment-jsdom": "^29.2.0", + "postcss": "^8.4.18", + "postcss-loader": "^7.0.1", + "prettier": "^2.8.3", + "style-loader": "^3.3.1", + "tailwindcss": "^3.1.8", + "ts-jest": "^29.0.3", + "ts-loader": "^9.4.1", + "ts-node": "^10.9.1", + "typescript": "^4.8.4", + "webpack": "^5.74.0", + "webpack-cli": "^4.10.0", + "webpack-dev-server": "^4.11.1", + "webpack-merge": "^5.8.0" + }, + "dependencies": { + "@onlyoffice/document-editor-react": "^1.2.0", + "@pipedrive/app-extensions-sdk": "^0.6.1", + "axios": "^1.3.2", + "axios-retry": "^3.4.0", + "classnames": "^2.3.2", + "i18next": "^22.4.9", + "i18next-http-backend": "^2.1.1", + "md5": "^2.3.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-dropzone": "^14.2.3", + "react-i18next": "^12.1.5", + "react-query": "^3.39.2", + "react-router-dom": "^6.4.2", + "react-tabs": "^6.0.0", + "valtio": "^1.10.1" + } +} diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 0000000..1ebcf24 --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,3 @@ +module.exports = { + plugins: ["tailwindcss", "autoprefixer"], +}; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 0000000..50b1d2e --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,76 @@ +import React from "react"; +import { + BrowserRouter as Router, + Routes, + Route, + Navigate, + useLocation, +} from "react-router-dom"; + +import { MainPage } from "@pages/Main"; +import { CreatePage } from "@pages/Creation"; +import { SettingsPage } from "@pages/Settings"; +import { OnlyofficeEditorPage } from "@pages/Editor"; + +import { OnlyofficeSpinner } from "@components/spinner"; + +const CenteredOnlyofficeSpinner = () => ( +
+ +
+); + +const LazyRoutes: React.FC = () => { + const location = useLocation(); + return ( + + + }> + + + } + /> + }> + + + } + /> + }> + + + } + /> + }> + + + } + /> + + } /> + + ); +}; + +function App() { + return ( +
+ + + +
+ ); +} + +export default App; diff --git a/frontend/src/__mocks__/fileMock.ts b/frontend/src/__mocks__/fileMock.ts new file mode 100644 index 0000000..dbc304a --- /dev/null +++ b/frontend/src/__mocks__/fileMock.ts @@ -0,0 +1 @@ +export default "test-file-stub"; diff --git a/frontend/src/__mocks__/styleMock.ts b/frontend/src/__mocks__/styleMock.ts new file mode 100644 index 0000000..ff8b4c5 --- /dev/null +++ b/frontend/src/__mocks__/styleMock.ts @@ -0,0 +1 @@ +export default {}; diff --git a/frontend/src/__tests__/__snapshots__/.keep b/frontend/src/__tests__/__snapshots__/.keep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/assets/arrow-down.svg b/frontend/src/assets/arrow-down.svg new file mode 100644 index 0000000..d61904e --- /dev/null +++ b/frontend/src/assets/arrow-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/background-error.svg b/frontend/src/assets/background-error.svg new file mode 100644 index 0000000..83c2362 --- /dev/null +++ b/frontend/src/assets/background-error.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/src/assets/docx.svg b/frontend/src/assets/docx.svg new file mode 100644 index 0000000..15f98e5 --- /dev/null +++ b/frontend/src/assets/docx.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/src/assets/favicon.ico b/frontend/src/assets/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/assets/import.svg b/frontend/src/assets/import.svg new file mode 100644 index 0000000..8e822c4 --- /dev/null +++ b/frontend/src/assets/import.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/index.css b/frontend/src/assets/index.css new file mode 100644 index 0000000..b29b3af --- /dev/null +++ b/frontend/src/assets/index.css @@ -0,0 +1,76 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +*{ + box-sizing: border-box; +} + +.table-shadow { + box-shadow: 0 -5px 5px -5px rgb(231, 231, 231); +} + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", + monospace; +} + +.no-scrollbar::-webkit-scrollbar { + display: none; +} + +.no-scrollbar { + -ms-overflow-style: none; + scrollbar-width: none; +} + +.spinner { + display: flex; + position: relative; + justify-content: center; + align-items: center; + height: 56px; + width: 56px; + box-sizing: border-box; + background: conic-gradient(from 90deg at 50% 50%, rgba(255, 255, 255, 0.0001) 0deg, #454545 359.96deg, rgba(255, 255, 255, 0.0001) 360deg); + border-radius: 56px; + animation: 1s rotate infinite linear; +} + +.spinner::before { + content: ""; + height: 40px; + width: 40px; + background: #fff; + border-radius: 48px; +} + +.spinner::after { + content: ""; + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); + height: 8px; + width: 8px; + background: #454545; + border-radius: 8px; +} + +@keyframes rotate { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/frontend/src/assets/nofile copy.svg b/frontend/src/assets/nofile copy.svg new file mode 100644 index 0000000..8e21b29 --- /dev/null +++ b/frontend/src/assets/nofile copy.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/nofile.svg b/frontend/src/assets/nofile.svg new file mode 100644 index 0000000..8e21b29 --- /dev/null +++ b/frontend/src/assets/nofile.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/pencil.svg b/frontend/src/assets/pencil.svg new file mode 100644 index 0000000..7299568 --- /dev/null +++ b/frontend/src/assets/pencil.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/pptx.svg b/frontend/src/assets/pptx.svg new file mode 100644 index 0000000..66eb13d --- /dev/null +++ b/frontend/src/assets/pptx.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/shape.svg b/frontend/src/assets/shape.svg new file mode 100644 index 0000000..3859d6a --- /dev/null +++ b/frontend/src/assets/shape.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/trash.svg b/frontend/src/assets/trash.svg new file mode 100644 index 0000000..3c35c91 --- /dev/null +++ b/frontend/src/assets/trash.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/unsupported.svg b/frontend/src/assets/unsupported.svg new file mode 100644 index 0000000..2ee28b3 --- /dev/null +++ b/frontend/src/assets/unsupported.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/xlsx.svg b/frontend/src/assets/xlsx.svg new file mode 100644 index 0000000..c993e61 --- /dev/null +++ b/frontend/src/assets/xlsx.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/src/components/button/Button.tsx b/frontend/src/components/button/Button.tsx new file mode 100644 index 0000000..bde872d --- /dev/null +++ b/frontend/src/components/button/Button.tsx @@ -0,0 +1,38 @@ +import React from "react"; +import cx from "classnames"; + +type ButtonProps = { + text: string; + disabled?: boolean; + primary?: boolean; + fullWidth?: boolean; + onClick?: React.MouseEventHandler; +}; + +export const OnlyofficeButton: React.FC = ({ + text, + disabled = false, + primary = false, + fullWidth = false, + onClick, +}) => { + const classes = cx({ + "hover:shadow-lg duration-200": !disabled, + "bg-green-600 text-slate-200": primary, + "bg-white text-black border-2 border-slate-300 border-solid": !primary, + "min-w-[62px] h-[32px]": true, + "w-full": fullWidth, + "bg-opacity-50 cursor-not-allowed": disabled, + }); + + return ( + + ); +}; diff --git a/frontend/src/components/button/index.ts b/frontend/src/components/button/index.ts new file mode 100644 index 0000000..d99df4c --- /dev/null +++ b/frontend/src/components/button/index.ts @@ -0,0 +1 @@ +export { OnlyofficeButton } from "./Button"; diff --git a/frontend/src/components/divider/Divider.tsx b/frontend/src/components/divider/Divider.tsx new file mode 100644 index 0000000..187beef --- /dev/null +++ b/frontend/src/components/divider/Divider.tsx @@ -0,0 +1,13 @@ +import React from "react"; + +type DividerProps = { + text: string; +}; + +export const OnlyofficeDivider: React.FC = ({ text }) => ( +
+
+ {text} +
+
+); diff --git a/frontend/src/components/divider/index.ts b/frontend/src/components/divider/index.ts new file mode 100644 index 0000000..63e0b7b --- /dev/null +++ b/frontend/src/components/divider/index.ts @@ -0,0 +1 @@ +export { OnlyofficeDivider } from "./Divider"; diff --git a/frontend/src/components/drop/Drop.tsx b/frontend/src/components/drop/Drop.tsx new file mode 100644 index 0000000..a2d77b1 --- /dev/null +++ b/frontend/src/components/drop/Drop.tsx @@ -0,0 +1,152 @@ +/* eslint-disable react/jsx-props-no-spreading */ +import React, { useState } from "react"; +import { useDropzone, DropEvent, FileRejection } from "react-dropzone"; +import cx from "classnames"; + +type DragDropProps = { + onDrop: ( + acceptedFiles: T[], + fileRejections: FileRejection[], + event: DropEvent + ) => Promise; + errorText?: string; + uploadingText?: string; + selectText?: string; + dragdropText?: string; + subtext?: string; + errorTimeout?: number; +}; + +const UploadIcon: React.FC = () => ( + + + + + + +); + +export const OnlyofficeDragDrop: React.FC = ({ + onDrop, + errorText = "Could not upload your file. Please contact ONLYOFFICE support.", + uploadingText = "Uploading...", + selectText = "Select a file", + dragdropText = "or drag and drop here", + subtext = "File size is limited", + errorTimeout = 4000, +}) => { + const [uploading, setUploading] = useState(() => false); + const [error, setError] = useState(false); + const uploadRef = React.useRef(null); + + const uploadFile = async ( + file: File | undefined, + event: DropEvent, + rejection?: FileRejection + ) => { + setError(false); + setUploading(true); + if (file) { + try { + await onDrop([file], rejection ? [rejection] : [], event); + } catch { + setError(true); + setTimeout(() => setError(false), errorTimeout); + } finally { + setUploading(false); + } + } + }; + + const { getRootProps, getInputProps, isDragActive } = useDropzone({ + onDrop: (files, rejections, event) => { + uploadFile(files[0], event, rejections[0]); + }, + noClick: true, + noKeyboard: true, + }); + + const style = cx({ + "flex flex-col items-center justify-center p-5": true, + "border-2 border-slate-300 border-dashed rounded-lg": true, + "bg-transparent bg-opacity-20 text-black": true, + "transition-all transition-timing-function: ease-in-out": true, + "transition-duration: 300ms": true, + "bg-sky-100": isDragActive, + "bg-emerald-100": uploading, + "bg-red-100": error, + }); + + return ( +
+ + {error && ( + + {errorText} + + )} + {uploading && !error && ( + + {uploadingText} + + )} + {!uploading && !error && ( + <> + + uploadFile(e.target?.files?.[0], e)} + /> +
+ + + {dragdropText} + +
+ + {subtext} + + + )} +
+ ); +}; diff --git a/frontend/src/components/drop/index.ts b/frontend/src/components/drop/index.ts new file mode 100644 index 0000000..6db7797 --- /dev/null +++ b/frontend/src/components/drop/index.ts @@ -0,0 +1 @@ +export { OnlyofficeDragDrop } from "./Drop"; diff --git a/frontend/src/components/error/Error.tsx b/frontend/src/components/error/Error.tsx new file mode 100644 index 0000000..b418384 --- /dev/null +++ b/frontend/src/components/error/Error.tsx @@ -0,0 +1,11 @@ +import React from "react"; + +type ErrorProps = { + text: string; +}; + +export const OnlyofficeError: React.FC = ({ text }) => ( +
+ {text} +
+); diff --git a/frontend/src/components/error/index.ts b/frontend/src/components/error/index.ts new file mode 100644 index 0000000..6320c57 --- /dev/null +++ b/frontend/src/components/error/index.ts @@ -0,0 +1 @@ +export { OnlyofficeError } from "./Error"; diff --git a/frontend/src/components/file/File.tsx b/frontend/src/components/file/File.tsx new file mode 100644 index 0000000..833cf93 --- /dev/null +++ b/frontend/src/components/file/File.tsx @@ -0,0 +1,64 @@ +import React, { useState } from "react"; + +import DetailsIcon from "@assets/arrow-down.svg"; + +type FileProps = { + Icon: any; + name: string; + supported?: boolean; + actions?: React.ReactNode; + children?: React.ReactNode; + onClick?: React.MouseEventHandler; +}; + +export const OnlyofficeFile: React.FC = ({ + Icon, + name, + supported = false, + actions, + children, + onClick, +}) => { + const [showDetails, setShowDetails] = useState(false); + return ( + <> +
+
+
setShowDetails(!showDetails)} + onKeyDown={() => setShowDetails(!showDetails)} + className={`w-[16px] h-[16px] hover:cursor-pointer mx-1 ${ + showDetails ? "rotate-180" : "rotate-0" + }`} + > + +
+
+
+
+ +
+ +
+
{actions}
+
+
+ {children} +
+ + ); +}; diff --git a/frontend/src/components/file/index.ts b/frontend/src/components/file/index.ts new file mode 100644 index 0000000..47d72fd --- /dev/null +++ b/frontend/src/components/file/index.ts @@ -0,0 +1 @@ +export { OnlyofficeFile } from "./File"; diff --git a/frontend/src/components/header/Header.tsx b/frontend/src/components/header/Header.tsx new file mode 100644 index 0000000..97307b0 --- /dev/null +++ b/frontend/src/components/header/Header.tsx @@ -0,0 +1,22 @@ +import React from "react"; + +import { OnlyofficeTitle, OnlyofficeSubtitle } from "@components/title"; + +type MainHeaderProps = { + title: string; + subtitle: string; +}; + +export const OnlyofficeMainHeader: React.FC = ({ + title, + subtitle, +}) => ( +
+
+ +
+
+ +
+
+); diff --git a/frontend/src/components/header/index.ts b/frontend/src/components/header/index.ts new file mode 100644 index 0000000..a4e0832 --- /dev/null +++ b/frontend/src/components/header/index.ts @@ -0,0 +1 @@ +export { OnlyofficeMainHeader } from "./Header"; diff --git a/frontend/src/components/info/Info.tsx b/frontend/src/components/info/Info.tsx new file mode 100644 index 0000000..a41d878 --- /dev/null +++ b/frontend/src/components/info/Info.tsx @@ -0,0 +1,28 @@ +import React from "react"; + +import { OnlyofficeSubtitle, OnlyofficeTitle } from "@components/title"; + +type FileInfoProps = { + info: { + [key: string]: string; + }; +}; + +export const OnlyofficeFileInfo: React.FC = ({ info }) => ( + + + {Object.keys(info).map((subtitle) => ( + + + + + ))} + +
+ + +
+ +
+
+); diff --git a/frontend/src/components/info/index.ts b/frontend/src/components/info/index.ts new file mode 100644 index 0000000..225f6f0 --- /dev/null +++ b/frontend/src/components/info/index.ts @@ -0,0 +1 @@ +export { OnlyofficeFileInfo } from "./Info"; diff --git a/frontend/src/components/input/Input.tsx b/frontend/src/components/input/Input.tsx new file mode 100644 index 0000000..769fe22 --- /dev/null +++ b/frontend/src/components/input/Input.tsx @@ -0,0 +1,64 @@ +/* eslint-disable jsx-a11y/label-has-associated-control */ +import React from "react"; +import cx from "classnames"; + +type InputProps = { + text: string; + value?: string; + placeholder?: string; + type?: "text" | "password"; + errorText?: string; + valid?: boolean; + disabled?: boolean; + textSize?: "sm" | "xs"; + labelSize?: "sm" | "xs"; + autocomplete?: boolean; + onChange?: React.ChangeEventHandler; +}; + +export const OnlyofficeInput: React.FC = ({ + text, + value, + placeholder, + type = "text", + errorText = "Please fill out this field", + valid = true, + disabled = false, + textSize = "sm", + labelSize = "xs", + autocomplete = false, + onChange, +}) => { + const istyle = cx({ + "font-normal text-sm text-gray-700 appearance-none block select-auto": true, + "text-xs": textSize === "xs", + "w-full border rounded-sm h-10 px-4": true, + "border-gray-light bg-slate-100": valid, + "border-red-600": !valid, + "bg-slate-200": disabled, + }); + + const pstyle = cx({ + hidden: valid, + }); + + return ( +
+ + +

{errorText}

+
+ ); +}; diff --git a/frontend/src/components/input/index.ts b/frontend/src/components/input/index.ts new file mode 100644 index 0000000..5b0b6a8 --- /dev/null +++ b/frontend/src/components/input/index.ts @@ -0,0 +1 @@ +export { OnlyofficeInput } from "./Input"; diff --git a/frontend/src/components/nofile/NoFile.tsx b/frontend/src/components/nofile/NoFile.tsx new file mode 100644 index 0000000..f78cba3 --- /dev/null +++ b/frontend/src/components/nofile/NoFile.tsx @@ -0,0 +1,16 @@ +import React from "react"; + +import Nofiles from "@assets/nofile.svg"; + +type NoFileProps = { + title: string; +}; + +export const OnlyofficeNoFile: React.FC = ({ title }) => ( +
+ + + {title} + +
+); diff --git a/frontend/src/components/nofile/index.ts b/frontend/src/components/nofile/index.ts new file mode 100644 index 0000000..a754832 --- /dev/null +++ b/frontend/src/components/nofile/index.ts @@ -0,0 +1 @@ +export { OnlyofficeNoFile } from "./NoFile"; diff --git a/frontend/src/components/search/Search.tsx b/frontend/src/components/search/Search.tsx new file mode 100644 index 0000000..a75c210 --- /dev/null +++ b/frontend/src/components/search/Search.tsx @@ -0,0 +1,46 @@ +import React from "react"; + +type SearchProps = { + value?: string; + placeholder?: string; + disabled?: boolean; + autocomplete?: boolean; + onChange?: React.ChangeEventHandler; +}; + +export const OnlyofficeSearchBar: React.FC = ({ + value, + placeholder, + disabled, + autocomplete = false, + onChange, +}) => ( +
+
+ + +
+
+); diff --git a/frontend/src/components/search/index.ts b/frontend/src/components/search/index.ts new file mode 100644 index 0000000..0052822 --- /dev/null +++ b/frontend/src/components/search/index.ts @@ -0,0 +1 @@ +export { OnlyofficeSearchBar } from "./Search"; diff --git a/frontend/src/components/spinner/Spinner.tsx b/frontend/src/components/spinner/Spinner.tsx new file mode 100644 index 0000000..e8276d4 --- /dev/null +++ b/frontend/src/components/spinner/Spinner.tsx @@ -0,0 +1,3 @@ +import React from "react"; + +export const OnlyofficeSpinner: React.FC = () =>
; diff --git a/frontend/src/components/spinner/index.ts b/frontend/src/components/spinner/index.ts new file mode 100644 index 0000000..c308183 --- /dev/null +++ b/frontend/src/components/spinner/index.ts @@ -0,0 +1 @@ +export { OnlyofficeSpinner } from "./Spinner"; diff --git a/frontend/src/components/tile/Tile.tsx b/frontend/src/components/tile/Tile.tsx new file mode 100644 index 0000000..de22625 --- /dev/null +++ b/frontend/src/components/tile/Tile.tsx @@ -0,0 +1,56 @@ +import React from "react"; +import cx from "classnames"; + +type TileProps = { + Icon: any; + text: string; + size?: "xs" | "sm"; + selected?: boolean; + onClick?: React.MouseEventHandler; + onKeyDown?: React.KeyboardEventHandler; +}; + +export const OnlyofficeTile: React.FC = ({ + Icon, + text, + size = "xs", + selected = false, + onClick, + onKeyDown, +}) => { + const card = cx({ + "px-5 py-3.5 rounded-lg transform shadow mb-5 outline-none": true, + "transition duration-100 ease-linear": true, + "w-[108px] h-[94px]": true, + "max-h-36 flex flex-col justify-center": true, + "hover:-translate-y-[0.125rem] hover:shadow-xl cursor-pointer": !selected, + "bg-white": !selected, + "bg-gray-100 border": selected, + }); + + const spn = cx({ + "text-sm": size === "sm", + "text-xs text-[9px]": size === "xs", + "font-semibold text-slate-500": true, + "overflow-hidden whitespace-nowrap inline-block text-ellipsis": true, + }); + + return ( +
+
+
+ +
+
+
+ {text} +
+
+ ); +}; diff --git a/frontend/src/components/tile/index.ts b/frontend/src/components/tile/index.ts new file mode 100644 index 0000000..3f46063 --- /dev/null +++ b/frontend/src/components/tile/index.ts @@ -0,0 +1 @@ +export { OnlyofficeTile } from "./Tile"; diff --git a/frontend/src/components/title/Subtitle.tsx b/frontend/src/components/title/Subtitle.tsx new file mode 100644 index 0000000..17d7bac --- /dev/null +++ b/frontend/src/components/title/Subtitle.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import cx from "classnames"; + +type SubtitleProps = { + text: string; + large?: boolean; +}; + +export const OnlyofficeSubtitle: React.FC = ({ + text, + large = false, +}) => { + const style = cx({ + "text-slate-700 font-normal text-center": !!text, + "text-sm": !large, + "text-base": large, + }); + + return

{text}

; +}; diff --git a/frontend/src/components/title/Title.tsx b/frontend/src/components/title/Title.tsx new file mode 100644 index 0000000..d0276a6 --- /dev/null +++ b/frontend/src/components/title/Title.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import cx from "classnames"; + +type TitleProps = { + text: string; + large?: boolean; +}; + +export const OnlyofficeTitle: React.FC = ({ + text, + large = false, +}) => { + const style = cx({ + "font-semibold text-slate-800 text-center": !!text, + "text-base": large, + "text-sm": !large, + }); + + return

{text}

; +}; diff --git a/frontend/src/components/title/index.ts b/frontend/src/components/title/index.ts new file mode 100644 index 0000000..0165afc --- /dev/null +++ b/frontend/src/components/title/index.ts @@ -0,0 +1,2 @@ +export { OnlyofficeTitle } from "./Title"; +export { OnlyofficeSubtitle } from "./Subtitle"; diff --git a/frontend/src/context/PipedriveContext.tsx b/frontend/src/context/PipedriveContext.tsx new file mode 100644 index 0000000..a0e138c --- /dev/null +++ b/frontend/src/context/PipedriveContext.tsx @@ -0,0 +1,4 @@ +import AppExtensionsSDK from "@pipedrive/app-extensions-sdk"; +import { proxy } from "valtio"; + +export const PipedriveSDK = proxy({ sdk: new AppExtensionsSDK().initialize() }); diff --git a/frontend/src/context/TokenContext.tsx b/frontend/src/context/TokenContext.tsx new file mode 100644 index 0000000..8183bc7 --- /dev/null +++ b/frontend/src/context/TokenContext.tsx @@ -0,0 +1,42 @@ +import React, { useEffect } from "react"; +import { proxy, useSnapshot } from "valtio"; + +import { getMe } from "@services/me"; + +import { PipedriveSDK } from "./PipedriveContext"; + +export const AuthToken = proxy({ + access_token: "", + expires_at: Date.now(), + error: false, +}); + +type ProviderProps = { + children?: JSX.Element | JSX.Element[]; +}; + +const TokenContext = React.createContext(true); + +export const TokenProvider: React.FC = ({ children }) => { + const { sdk } = useSnapshot(PipedriveSDK); + useEffect(() => { + let timerID = setTimeout(async function update() { + if ( + !AuthToken.error && + (!AuthToken.access_token || AuthToken.expires_at <= Date.now() - 1) + ) { + try { + const token = await getMe(sdk); + AuthToken.access_token = token.response.access_token; + AuthToken.expires_at = token.response.expires_at; + } catch { + AuthToken.error = true; + } + } + timerID = setTimeout(update, 1000); + }, 1000); + + return () => clearTimeout(timerID); + }, [sdk]); + return {children}; +}; diff --git a/frontend/src/custom.d.ts b/frontend/src/custom.d.ts new file mode 100644 index 0000000..1a3dd3c --- /dev/null +++ b/frontend/src/custom.d.ts @@ -0,0 +1,4 @@ +declare module "*.svg" { + const content: any; + export default content; +} diff --git a/frontend/src/hooks/useBuildConfig.tsx b/frontend/src/hooks/useBuildConfig.tsx new file mode 100644 index 0000000..f26be2f --- /dev/null +++ b/frontend/src/hooks/useBuildConfig.tsx @@ -0,0 +1,24 @@ +import { useQuery } from "react-query"; +import { useSnapshot } from "valtio"; + +import { fetchConfig } from "@services/config"; + +import { PipedriveSDK } from "@context/PipedriveContext"; + +export function useBuildConfig( + id: string, + name: string, + key: string, + dealID: string +) { + const { sdk } = useSnapshot(PipedriveSDK); + const { isLoading, error, data } = useQuery({ + queryKey: ["config", id], + queryFn: ({ signal }) => fetchConfig(sdk, id, name, key, dealID, signal), + staleTime: 0, + cacheTime: 0, + refetchOnWindowFocus: false, + }); + + return { isLoading, error, data }; +} diff --git a/frontend/src/hooks/useDeleteFile.tsx b/frontend/src/hooks/useDeleteFile.tsx new file mode 100644 index 0000000..fed3d59 --- /dev/null +++ b/frontend/src/hooks/useDeleteFile.tsx @@ -0,0 +1,14 @@ +import { useMutation, useQueryClient } from "react-query"; + +import { deleteFile } from "@services/file"; + +export const useDeleteFile = (url: string) => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: () => deleteFile(url), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["filesData"] }); + }, + }); +}; diff --git a/frontend/src/hooks/useFileSearch.tsx b/frontend/src/hooks/useFileSearch.tsx new file mode 100644 index 0000000..0d5069c --- /dev/null +++ b/frontend/src/hooks/useFileSearch.tsx @@ -0,0 +1,39 @@ +import { useInfiniteQuery } from "react-query"; + +import { fetchFiles } from "@services/file"; + +export function useFileSearch(url: string, limit: number) { + const { + data, + isLoading, + error, + refetch, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + } = useInfiniteQuery({ + queryKey: ["filesData", url], + queryFn: ({ signal, pageParam }) => + fetchFiles(url, pageParam, limit, signal), + getNextPageParam: (lastPage) => + lastPage.pagination.more_items_in_collection + ? lastPage.pagination.next_start + : undefined, + staleTime: 2000, + cacheTime: 2500, + refetchInterval: 2000, + }); + + return { + files: data?.pages + .map((page) => page.response) + .filter(Boolean) + .flat(), + isLoading, + error, + refetch, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + }; +} diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts new file mode 100644 index 0000000..18bb863 --- /dev/null +++ b/frontend/src/i18n.ts @@ -0,0 +1,16 @@ +import i18n from "i18next"; +import I18NextHttpBackend from "i18next-http-backend"; +import { initReactI18next } from "react-i18next"; + +i18n + .use(I18NextHttpBackend) + .use(initReactI18next) + .init({ + fallbackLng: "en-US", + debug: false, + interpolation: { + escapeValue: false, + }, + }); + +export default i18n; diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx new file mode 100644 index 0000000..8772934 --- /dev/null +++ b/frontend/src/index.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import { QueryClient, QueryClientProvider } from "react-query"; + +import { TokenProvider } from "@context/TokenContext"; + +import App from "./App"; +// import "./i18n"; + +import "@assets/index.css"; + +const root = ReactDOM.createRoot( + document.getElementById("root") as HTMLElement +); + +const queryClient = new QueryClient(); +root.render( + + + + + + + +); diff --git a/frontend/src/layout/ErrorBackground.tsx b/frontend/src/layout/ErrorBackground.tsx new file mode 100644 index 0000000..1a5a299 --- /dev/null +++ b/frontend/src/layout/ErrorBackground.tsx @@ -0,0 +1,36 @@ +import React from "react"; + +import { OnlyofficeButton } from "@components/button"; +import { OnlyofficeSubtitle } from "@components/title"; +import { OnlyofficeError } from "@components/error/Error"; + +import BackgroundError from "@assets/background-error.svg"; + +type ErrorProps = { + title: string; + subtitle: string; + button?: string; + onClick?: React.MouseEventHandler | undefined; +}; + +export const OnlyofficeBackgroundError: React.FC = ({ + title, + subtitle, + button, + onClick, +}) => ( +
+
+ +
+
+ +
+ + {onClick && button && ( +
+ +
+ )} +
+); diff --git a/frontend/src/lib/.keep b/frontend/src/lib/.keep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx new file mode 100644 index 0000000..a05f8bb --- /dev/null +++ b/frontend/src/pages/Creation/Creation.tsx @@ -0,0 +1,121 @@ +import React, { useState } from "react"; +import md5 from "md5"; +import { useSnapshot } from "valtio"; +import { useNavigate } from "react-router-dom"; +import { Command } from "@pipedrive/app-extensions-sdk"; + +import { OnlyofficeButton } from "@components/button"; +import { OnlyofficeInput } from "@components/input"; +import { OnlyofficeTile } from "@components/tile"; +import { OnlyofficeTitle } from "@components/title"; + +import { uploadFile } from "@services/file"; + +import { PipedriveSDK } from "@context/PipedriveContext"; + +import { getFileIcon, getMimeType } from "@utils/file"; +import { getCurrentURL } from "@utils/url"; + +export const Creation: React.FC = () => { + const navigate = useNavigate(); + const { sdk } = useSnapshot(PipedriveSDK); + const [file, setFile] = useState("New Document"); + const [fileType, setFileType] = useState<"docx" | "pptx" | "xlsx">("docx"); + const handleChangeFile = (newType: "docx" | "pptx" | "xlsx") => { + setFileType(newType); + }; + + return ( +
+
+
+ +
+ setFile(e.target.value)} + /> +
+
+
+ handleChangeFile("docx")} + onKeyDown={() => handleChangeFile("docx")} + selected={fileType === "docx"} + /> +
+
+ handleChangeFile("xlsx")} + onKeyDown={() => handleChangeFile("xlsx")} + selected={fileType === "xlsx"} + /> +
+
+ handleChangeFile("pptx")} + onKeyDown={() => handleChangeFile("pptx")} + selected={fileType === "pptx"} + /> +
+
+
+
+
+
+
+ { + await sdk.execute(Command.CLOSE_MODAL); + }} + /> +
+
+ { + const { url, parameters } = getCurrentURL(); + const filename = `${file}.${fileType}`; + const binary = new File([], filename, { + type: getMimeType(filename), + }); + try { + const res = await uploadFile( + `${url}api/v1/files`, + parameters.get("selectedIds") || "", + binary + ); + navigate( + `/editor?data=${JSON.stringify({ + id: res.data.id, + deal_id: res.data.deal_id, + name: res.data.name, + key: md5(res.data.id + res.data.update_time), + })}` + ); + } catch { + await sdk.execute(Command.SHOW_SNACKBAR, { + message: "Could not create a new file", + }); + } + }} + /> +
+
+
+
+ ); +}; diff --git a/frontend/src/pages/Creation/Upload.tsx b/frontend/src/pages/Creation/Upload.tsx new file mode 100644 index 0000000..5d00246 --- /dev/null +++ b/frontend/src/pages/Creation/Upload.tsx @@ -0,0 +1,54 @@ +import { FileRejection, DropEvent } from "react-dropzone"; +import React from "react"; +import { useSnapshot } from "valtio"; +import { Command } from "@pipedrive/app-extensions-sdk"; + +import { OnlyofficeButton } from "@components/button"; +import { OnlyofficeDragDrop } from "@components/drop"; + +import { PipedriveSDK } from "@context/PipedriveContext"; + +import { uploadFile } from "@services/file"; + +import { getCurrentURL } from "@utils/url"; + +const onDrop = ( + acceptedFiles: T[], + _: FileRejection[], + __: DropEvent +): Promise => { + const { url, parameters } = getCurrentURL(); + return uploadFile( + `${url}api/v1/files`, + parameters.get("selectedIds") || "", + acceptedFiles[0] + ); +}; + +export const Upload: React.FC = () => { + const { sdk } = useSnapshot(PipedriveSDK); + return ( +
+
+
+ +
+
+
+
+
+ { + await sdk.execute(Command.CLOSE_MODAL); + }} + /> +
+
+
+
+ ); +}; diff --git a/frontend/src/pages/Creation/index.tsx b/frontend/src/pages/Creation/index.tsx new file mode 100644 index 0000000..839b7bb --- /dev/null +++ b/frontend/src/pages/Creation/index.tsx @@ -0,0 +1,63 @@ +import { Tabs, TabList, Tab, TabPanel } from "react-tabs"; +import React, { useState } from "react"; +import { useSnapshot } from "valtio"; +import { Command } from "@pipedrive/app-extensions-sdk"; + +import { PipedriveSDK } from "@context/PipedriveContext"; + +import { Creation } from "./Creation"; +import { Upload } from "./Upload"; + +export const CreatePage: React.FC = () => { + const { sdk } = useSnapshot(PipedriveSDK); + const [selected, setSelected] = useState(0); + sdk.execute(Command.RESIZE, { + height: 500, + width: 622, + }); + + return ( +
+ setSelected(index)} + > + + + Create + + + Upload + + + + + + + + + +
+ ); +}; diff --git a/frontend/src/pages/Editor/index.tsx b/frontend/src/pages/Editor/index.tsx new file mode 100644 index 0000000..2a6c265 --- /dev/null +++ b/frontend/src/pages/Editor/index.tsx @@ -0,0 +1,141 @@ +import React, { useEffect } from "react"; +import { useSearchParams } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { useSnapshot } from "valtio"; +import { Command } from "@pipedrive/app-extensions-sdk"; +import { DocumentEditor } from "@onlyoffice/document-editor-react"; + +import { OnlyofficeButton } from "@components/button"; +import { OnlyofficeError } from "@components/error"; +import { OnlyofficeSpinner } from "@components/spinner"; + +import { useBuildConfig } from "@hooks/useBuildConfig"; +import { PipedriveSDK } from "@context/PipedriveContext"; + +import Icon from "@assets/nofile.svg"; + +const onEditor = () => { + const loader = document.getElementById("eloader"); + if (loader) { + loader.classList.add("opacity-0"); + loader.classList.add("-z-100"); + loader.classList.add("hidden"); + } + + const editor = document.getElementById("editor"); + if (editor) { + editor.classList.remove("opacity-0"); + } +}; + +export const OnlyofficeEditorPage: React.FC = () => { + const [params] = useSearchParams(); + const pData = JSON.parse(params.get("data") || "{}"); + const { sdk } = useSnapshot(PipedriveSDK); + const { t } = useTranslation(); + const { isLoading, error, data } = useBuildConfig( + pData.id || "", + pData.name || "new.docx", + pData.key || new Date().toString(), + pData.deal_id || "1" + ); + + useEffect(() => { + if (sdk) { + (async () => { + await sdk.execute(Command.RESIZE, { + height: 500, + width: 700, + }); + })(); + } + }, [sdk]); + + const validConfig = !error && !isLoading && data; + return ( +
+ {!error && ( +
+
+ +
+
+ sdk.execute(Command.CLOSE_MODAL)} + /> +
+
+ )} + {!!error && ( +
+ + +
+ sdk.execute(Command.CLOSE_MODAL)} + /> +
+
+ )} + {validConfig && data.server_url && ( +
+ { + await sdk.execute(Command.CLOSE_MODAL); + }, + onAppReady: onEditor, + onError: () => { + onEditor(); + }, + onWarning: onEditor, + }, + }} + /> +
+ )} +
+ ); +}; + +export default OnlyofficeEditorPage; diff --git a/frontend/src/pages/Main/Actions.tsx b/frontend/src/pages/Main/Actions.tsx new file mode 100644 index 0000000..fc3b9db --- /dev/null +++ b/frontend/src/pages/Main/Actions.tsx @@ -0,0 +1,80 @@ +import React from "react"; +import md5 from "md5"; +import { useSnapshot } from "valtio"; +import { Command, Modal } from "@pipedrive/app-extensions-sdk"; + +import { useDeleteFile } from "@hooks/useDeleteFile"; + +import { isFileSupported } from "@utils/file"; +import { getCurrentURL } from "@utils/url"; + +import { PipedriveSDK } from "@context/PipedriveContext"; + +import { File } from "src/types/file"; + +import Pencil from "@assets/pencil.svg"; +import Trash from "@assets/trash.svg"; + +type FileActionsProps = { + file: File; +}; + +export const OnlyofficeFileActions: React.FC = ({ file }) => { + const { sdk } = useSnapshot(PipedriveSDK); + const { url, parameters } = getCurrentURL(); + const mutator = useDeleteFile(`${url}api/v1/files/${file.id}`); + const handleDelete = () => { + mutator + .mutateAsync() + .then(async () => { + await sdk.execute(Command.SHOW_SNACKBAR, { + message: `File ${file.name} has been removed`, + }); + }) + .catch(async () => { + await sdk.execute(Command.SHOW_SNACKBAR, { + message: `Could not remove file ${file.name}`, + }); + }); + }; + + const handleEditor = async () => { + await sdk.execute(Command.OPEN_MODAL, { + type: Modal.CUSTOM_MODAL, + action_id: process.env.PIPEDRIVE_EDITOR_MODAL_ID || "", + data: { + deal_id: parameters.get("selectedIds") || "", + id: file.id, + name: file.name, + key: md5(file.id + file.update_time), + }, + }); + }; + + return ( + <> +
handleEditor()} + onKeyDown={() => handleEditor()} + > + +
+
handleDelete()} + onKeyDown={() => handleDelete()} + > + +
+ + ); +}; diff --git a/frontend/src/pages/Main/Main.tsx b/frontend/src/pages/Main/Main.tsx new file mode 100644 index 0000000..1517a2e --- /dev/null +++ b/frontend/src/pages/Main/Main.tsx @@ -0,0 +1,128 @@ +import React, { useCallback, useRef } from "react"; +import { useSnapshot } from "valtio"; +import { Command, Modal } from "@pipedrive/app-extensions-sdk"; + +import { OnlyofficeButton } from "@components/button"; +import { OnlyofficeFile } from "@components/file"; +import { OnlyofficeFileInfo } from "@components/info"; +import { OnlyofficeNoFile } from "@components/nofile"; +import { OnlyofficeSpinner } from "@components/spinner"; + +import { useFileSearch } from "@hooks/useFileSearch"; + +import { PipedriveSDK } from "@context/PipedriveContext"; + +import { formatBytes, getFileIcon, isFileSupported } from "@utils/file"; +import { getCurrentURL } from "@utils/url"; + +import { OnlyofficeFileActions } from "./Actions"; + +export const Main: React.FC = () => { + const { sdk } = useSnapshot(PipedriveSDK); + const { url, parameters } = getCurrentURL(); + const { isLoading, fetchNextPage, isFetchingNextPage, files, hasNextPage } = + useFileSearch( + `${url}api/v1/deals/${parameters.get("selectedIds")}/files`, + 20 + ); + const observer = useRef(); + const lastItem = useCallback( + (node: Element | null) => { + if (isLoading) return; + if (observer.current) observer.current.disconnect(); + observer.current = new IntersectionObserver(async (entries) => { + if (entries[0].isIntersecting && hasNextPage) { + fetchNextPage(); + } + }); + if (node) observer.current.observe(node); + }, + [isLoading, fetchNextPage, hasNextPage] + ); + + return ( +
+
+ {isLoading && ( +
+ +
+ )} + {!isLoading && (!files || files.length === 0) && ( + + )} + {!isLoading && + files && + files.length > 0 && + files.map((file, index) => { + if (files.length === index + 1) { + return ( +
+ } + > + + +
+ ); + } + return ( +
+ } + > + + +
+ ); + })} + {isFetchingNextPage && ( +
+ +
+ )} +
+
+ { + await sdk.execute(Command.OPEN_MODAL, { + type: Modal.CUSTOM_MODAL, + action_id: process.env.PIPEDRIVE_CREATE_MODAL_ID || "", + }); + }} + /> +
+
+ ); +}; diff --git a/frontend/src/pages/Main/index.tsx b/frontend/src/pages/Main/index.tsx new file mode 100644 index 0000000..7384089 --- /dev/null +++ b/frontend/src/pages/Main/index.tsx @@ -0,0 +1,32 @@ +import React from "react"; +import { useSnapshot } from "valtio"; + +import { OnlyofficeSpinner } from "@components/spinner"; + +import { AuthToken } from "@context/TokenContext"; + +import { OnlyofficeBackgroundError } from "@layouts/ErrorBackground"; +import { Main } from "./Main"; + +export const MainPage: React.FC = () => { + const { access_token: accessToken, error } = useSnapshot(AuthToken); + const loading = !accessToken && !error; + const loadingError = !accessToken && error; + const loaded = accessToken && !error; + return ( +
+ {loading && ( +
+ +
+ )} + {loadingError && ( + + )} + {loaded &&
} +
+ ); +}; diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx new file mode 100644 index 0000000..4b2bb8d --- /dev/null +++ b/frontend/src/pages/Settings/index.tsx @@ -0,0 +1,140 @@ +import React, { useEffect, useState } from "react"; +import { Command } from "@pipedrive/app-extensions-sdk"; +import { useSnapshot } from "valtio"; + +import { OnlyofficeButton } from "@components/button"; +import { OnlyofficeInput } from "@components/input"; +import { OnlyofficeTitle } from "@components/title"; +import { OnlyofficeSpinner } from "@components/spinner"; +import { OnlyofficeBackgroundError } from "@layouts/ErrorBackground"; + +import { postSettings, getSettings } from "@services/settings"; +import { getPipedriveMe } from "@services/me"; + +import { PipedriveSDK } from "@context/PipedriveContext"; +import { AuthToken } from "@context/TokenContext"; + +export const SettingsPage: React.FC = () => { + const { sdk } = useSnapshot(PipedriveSDK); + const { access_token: accessToken, error } = useSnapshot(AuthToken); + const [admin, setAdmin] = useState(false); + const [loading, setLoading] = useState(true); + const [address, setAddress] = useState(undefined); + const [secret, setSecret] = useState(undefined); + const [saving, setSaving] = useState(false); + + useEffect(() => { + if (accessToken && !error) { + getPipedriveMe( + `${window.parent[0].location.ancestorOrigins[0]}/api/v1/users/me` + ) + .then(async (ures) => { + try { + if (ures.data.access.find((a) => a.app === "global" && a.admin)) { + const res = await getSettings(sdk); + setAddress(res.doc_address); + setSecret(res.doc_secret); + setAdmin(true); + } + } catch { + setAdmin(false); + } finally { + setLoading(false); + } + }) + .catch(() => { + setLoading(false); + }); + } + + if (error) setLoading(false); + }, [sdk, accessToken, error]); + + const handleSettings = async () => { + if (address && secret) { + try { + setSaving(true); + await postSettings(sdk, address, secret); + await sdk.execute(Command.SHOW_SNACKBAR, { + message: "ONLYOFFICE settings have been saved", + }); + } catch { + await sdk.execute(Command.SHOW_SNACKBAR, { + message: "Could not save ONLYOFFICE settings", + }); + } finally { + setSaving(false); + } + } + }; + + return ( +
+ {loading && !error && ( +
+ +
+ )} + {!loading && error && ( + window.location.reload()} + /> + )} + {!loading && !error && !admin && ( + window.location.reload()} + /> + )} + {!loading && !error && admin && ( + <> +
+
+ +
+

+ The plugin which enables the users to edit office documents from + Pipedrive using ONLYOFFICE Document Server, allows multiple users + to collaborate in real time and to save back those changes to + Pipedrive +

+
+
+
+ setAddress(e.target.value)} + /> +
+
+ setSecret(e.target.value)} + type="password" + /> +
+
+ +
+
+ + )} +
+ ); +}; diff --git a/frontend/src/public/index.html b/frontend/src/public/index.html new file mode 100644 index 0000000..3d98c3b --- /dev/null +++ b/frontend/src/public/index.html @@ -0,0 +1,16 @@ + + + + + + + + ONLYOFFICE + + + +
+ + diff --git a/frontend/src/services/config.ts b/frontend/src/services/config.ts new file mode 100644 index 0000000..bc0e416 --- /dev/null +++ b/frontend/src/services/config.ts @@ -0,0 +1,37 @@ +import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; +import axios from "axios"; +import axiosRetry from "axios-retry"; + +import { ConfigResponse } from "src/types/config"; + +export const fetchConfig = async ( + sdk: AppExtensionsSDK, + id: string, + name: string, + key: string, + dealID: string, + signal?: AbortSignal +) => { + const pctx = await sdk.execute(Command.GET_SIGNED_TOKEN); + const client = axios.create(); + axiosRetry(client, { + retries: 2, + retryCondition: (error) => error.status !== 200, + }); + const res = await axios({ + method: "GET", + url: `${process.env.BACKEND_GATEWAY}/api/config`, + params: { + id, + name, + key, + deal_id: dealID, + }, + headers: { + "Content-Type": "application/json", + "X-Pipedrive-App-Context": pctx.token, + }, + signal, + }); + return res.data; +}; diff --git a/frontend/src/services/file.ts b/frontend/src/services/file.ts new file mode 100644 index 0000000..66d6e30 --- /dev/null +++ b/frontend/src/services/file.ts @@ -0,0 +1,68 @@ +import { AuthToken } from "@context/TokenContext"; +import axios from "axios"; + +import { FileResponse } from "src/types/file"; + +export const fetchFiles = async ( + url: string, + start = 0, + limit = 50, + signal: AbortSignal | undefined = undefined, + sort = "add_time ASC" +) => { + const res = await axios({ + method: "GET", + url, + params: { + start, + limit, + sort, + }, + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${AuthToken.access_token}`, + }, + signal, + }); + + return { + response: res.data.data, + pagination: res.data.additional_data.pagination, + }; +}; + +export const deleteFile = async ( + url: string, + signal: AbortSignal | undefined = undefined +) => { + const res = await axios({ + method: "DELETE", + url, + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${AuthToken.access_token}`, + }, + signal, + }); + + return res.status === 200; +}; + +export const uploadFile = async (url: string, deal: string, file: File) => { + const form = new FormData(); + form.append("file", file); + form.append("deal_id", deal); + + const res = await axios({ + method: "POST", + url, + headers: { + Accept: "application/json", + "Content-Type": "multipart/form-data", + Authorization: `Bearer ${AuthToken.access_token}`, + }, + data: form, + }); + + return res.data; +}; diff --git a/frontend/src/services/me.ts b/frontend/src/services/me.ts new file mode 100644 index 0000000..744ab52 --- /dev/null +++ b/frontend/src/services/me.ts @@ -0,0 +1,36 @@ +import axios from "axios"; +import axiosRetry from "axios-retry"; +import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; +import { PipedriveUserResponse, UserResponse } from "src/types/user"; +import { AuthToken } from "@context/TokenContext"; + +export const getMe = async (sdk: AppExtensionsSDK) => { + const pctx = await sdk.execute(Command.GET_SIGNED_TOKEN); + const client = axios.create({ baseURL: process.env.BACKEND_GATEWAY }); + axiosRetry(client, { + retries: 2, + retryCondition: (error) => error.status !== 200, + }); + const res = await client({ + method: "GET", + url: `/api/me`, + headers: { + "Content-Type": "application/json", + "X-Pipedrive-App-Context": pctx.token, + }, + }); + + return { response: res.data }; +}; + +export const getPipedriveMe = async (url: string) => { + const res = await axios({ + method: "GET", + url, + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${AuthToken.access_token}`, + }, + }); + return res.data; +}; diff --git a/frontend/src/services/settings.ts b/frontend/src/services/settings.ts new file mode 100644 index 0000000..a348119 --- /dev/null +++ b/frontend/src/services/settings.ts @@ -0,0 +1,52 @@ +import axios from "axios"; +import axiosRetry from "axios-retry"; +import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; +import { UserResponse } from "src/types/user"; +import { SettingsResponse } from "src/types/settings"; + +export const postSettings = async ( + sdk: AppExtensionsSDK, + address: string, + secret: string +) => { + const pctx = await sdk.execute(Command.GET_SIGNED_TOKEN); + const client = axios.create({ baseURL: process.env.BACKEND_GATEWAY }); + axiosRetry(client, { + retries: 2, + retryCondition: (error) => error.status === 429, + }); + + await client({ + method: "POST", + url: `/api/settings`, + headers: { + "Content-Type": "application/json", + "X-Pipedrive-App-Context": pctx.token, + }, + data: { + doc_address: address, + doc_secret: secret, + }, + timeout: 4000, + }); +}; + +export const getSettings = async (sdk: AppExtensionsSDK) => { + const pctx = await sdk.execute(Command.GET_SIGNED_TOKEN); + const client = axios.create({ baseURL: process.env.BACKEND_GATEWAY }); + axiosRetry(client, { + retries: 2, + retryCondition: (error) => error.status !== 200, + }); + + const settings = await client({ + method: "GET", + url: `/api/settings`, + headers: { + "Content-Type": "application/json", + "X-Pipedrive-App-Context": pctx.token, + }, + }); + + return settings.data; +}; diff --git a/frontend/src/setupTests.ts b/frontend/src/setupTests.ts new file mode 100644 index 0000000..d0de870 --- /dev/null +++ b/frontend/src/setupTests.ts @@ -0,0 +1 @@ +import "@testing-library/jest-dom"; diff --git a/frontend/src/types/config.ts b/frontend/src/types/config.ts new file mode 100644 index 0000000..1c566f5 --- /dev/null +++ b/frontend/src/types/config.ts @@ -0,0 +1,52 @@ +type Permissions = { + comment: boolean; + copy: boolean; + deleteCommentAuthorOnly: boolean; + download: boolean; + edit: boolean; + editCommentAuthorOnly: boolean; + fillForms: boolean; + modifyContentControl: boolean; + modifyFilter: boolean; + print: boolean; + review: boolean; +}; + +type Document = { + fileType: string; + key: string; + title: string; + url: string; + permissions: Permissions; +}; + +type User = { + id: string; + name: string; +}; + +type Goback = { + requestClost: boolean; +}; + +type Customization = { + goback: Goback; +}; + +type EditorConfig = { + user: User; + callbackUrl: string; + customization: Customization; + lang: string; +}; + +export type ConfigResponse = { + document: Document; + documentType: string; + editorConfig: EditorConfig; + type: string; + token: string; + server_url: string; + is_session?: boolean; + is_owner?: boolean; +}; diff --git a/frontend/src/types/file.ts b/frontend/src/types/file.ts new file mode 100644 index 0000000..0a964b4 --- /dev/null +++ b/frontend/src/types/file.ts @@ -0,0 +1,26 @@ +export type File = { + id: string; + name: string; + file_size: number; + file_type: string; + add_time: string; + update_time: string; + url: string; + person_name: string; + remote_location: string; +}; + +type Pagination = { + pagination: { + start: number; + next_start: number; + limit: number; + more_items_in_collection: boolean; + }; +}; + +export type FileResponse = { + success: boolean; + data: File[]; + additional_data: Pagination; +}; diff --git a/frontend/src/types/settings.ts b/frontend/src/types/settings.ts new file mode 100644 index 0000000..4b97d48 --- /dev/null +++ b/frontend/src/types/settings.ts @@ -0,0 +1,4 @@ +export type SettingsResponse = { + doc_address: string; + doc_secret: string; +}; diff --git a/frontend/src/types/user.ts b/frontend/src/types/user.ts new file mode 100644 index 0000000..a6fa7a9 --- /dev/null +++ b/frontend/src/types/user.ts @@ -0,0 +1,19 @@ +export type UserResponse = { + id: string; + access_token: string; + expires_at: number; +}; + +type PipedriveAccess = { + app: string; + admin: boolean; +}; + +export type PipedriveUserResponse = { + success: boolean; + data: { + id: number; + access: PipedriveAccess[]; + active_flag: true; + }; +}; diff --git a/frontend/src/utils/file.ts b/frontend/src/utils/file.ts new file mode 100644 index 0000000..b75b8b5 --- /dev/null +++ b/frontend/src/utils/file.ts @@ -0,0 +1,139 @@ +import Docx from "@assets/docx.svg"; +import Pptx from "@assets/pptx.svg"; +import Xlsx from "@assets/xlsx.svg"; +import Unsupported from "@assets/unsupported.svg"; + +const DOCUMENT_EXTS = [ + "doc", + "docx", + "docm", + "dot", + "dotx", + "dotm", + "odt", + "fodt", + "ott", + "rtf", + "txt", + "html", + "htm", + "mht", + "xml", + "pdf", + "djvu", + "fb2", + "epub", + "xps", + "oxps", +]; + +const SPREADSHEET_EXTS = [ + "xls", + "xlsx", + "xlsm", + "xlt", + "xltx", + "xltm", + "ods", + "fods", + "ots", + "csv", +]; + +const PRESENTATION_EXTS = [ + "pps", + "ppsx", + "ppsm", + "ppt", + "pptx", + "pptm", + "pot", + "potx", + "potm", + "odp", + "fodp", + "otp", +]; + +const EDITABLE_EXTS = ["docx", "pptx", "xlsx"]; +const OPENABLE_EXTS = + DOCUMENT_EXTS.concat(SPREADSHEET_EXTS).concat(PRESENTATION_EXTS); + +const WORD = "word"; +const SLIDE = "slide"; +const CELL = "cell"; + +const getFileExt = (filename: string): string => + filename.split(".").pop() || ""; + +export const isFileEditable = (filename: string) => { + const ext = getFileExt(filename).toLowerCase(); + return EDITABLE_EXTS.includes(ext); +}; + +export const isFileSupported = (filename: string) => { + const e = getFileExt(filename).toLowerCase(); + return OPENABLE_EXTS.includes(e); +}; + +export const getFileType = (filename: string) => { + const e = getFileExt(filename).toLowerCase(); + + if (DOCUMENT_EXTS.includes(e)) return WORD; + if (SPREADSHEET_EXTS.includes(e)) return CELL; + if (PRESENTATION_EXTS.includes(e)) return SLIDE; + + return null; +}; + +export const getMimeType = (filename: string) => { + const e = getFileExt(filename).toLowerCase(); + + switch (e) { + case "docx": + return "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; + case "pptx": + return "application/vnd.openxmlformats-officedocument.presentationml.presentation"; + case "xlsx": + return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + default: + return "application/octet-stream"; + } +}; + +export const getFileIcon = (filename: string) => { + const e = getFileExt(filename).toLowerCase(); + + if (DOCUMENT_EXTS.includes(e)) return Docx; + if (SPREADSHEET_EXTS.includes(e)) return Xlsx; + if (PRESENTATION_EXTS.includes(e)) return Pptx; + + return Unsupported; +}; + +export const getCreateFileUrl = ( + fileType: "docx" | "pptx" | "xlsx" | undefined +) => { + switch (fileType) { + case "docx": + return encodeURIComponent(process.env.WORD_FILE || ""); + case "pptx": + return encodeURIComponent(process.env.SLIDE_FILE || ""); + case "xlsx": + return encodeURIComponent(process.env.SPREADSHEET_FILE || ""); + default: + return encodeURIComponent(process.env.WORD_FILE || ""); + } +}; + +export const formatBytes = (bytes: number, decimals = 2) => { + if (!+bytes) return "0 Bytes"; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`; +}; diff --git a/frontend/src/utils/url.ts b/frontend/src/utils/url.ts new file mode 100644 index 0000000..eb98e7c --- /dev/null +++ b/frontend/src/utils/url.ts @@ -0,0 +1,9 @@ +export const getCurrentURL = () => { + const url = + window.location !== window.parent.location + ? document.referrer + : document.location.href; + const params = new URL(document.location.href).searchParams; + + return { url, parameters: params }; +}; diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js new file mode 100644 index 0000000..36b6bd6 --- /dev/null +++ b/frontend/tailwind.config.js @@ -0,0 +1,67 @@ +module.exports = { + content: ["./src/**/*.{ts,tsx}"], + theme: { + extend: { + width: { + 1500: "1500px", + 1400: "1400px", + 1300: "1300px", + 1200: "1200px", + 1100: "1100px", + 1000: "1000px", + 900: "900px", + 800: "800px", + 700: "700px", + 600: "600px", + 536: "536px", + 454: "454px", + 400: "400px", + 327: "327px", + 300: "300px", + 200: "200px", + 197: "197px", + }, + minWidth: { + xxs: "14rem", + }, + maxWidth: { + xxs: "14rem", + }, + height: { + 1500: "1500px", + 1400: "1400px", + 1300: "1300px", + 1200: "1200px", + 1100: "1100px", + 1000: "1000px", + 900: "900px", + 800: "800px", + 700: "700px", + 600: "600px", + 536: "536px", + 454: "454px", + 400: "400px", + 327: "327px", + 300: "300px", + 200: "200px", + 197: "197px", + }, + colors: { + onlyoffice: "#0F4071", + "smoke-light": "rgba(0, 0, 0, 0.4)", + }, + screens: { + wrap: { raw: "(max-width: 713px)" }, + scrollable: { raw: "(max-height: 550px)" }, + large: { raw: "(min-height: 730px) and (min-width: 900px)" }, + small: { raw: "(max-width: 452px)" }, + xsmall: { raw: "(max-width: 300px)" }, + xxsmall: { raw: "(max-width: 150px)" }, + }, + }, + fontFamily: { + open: ['"Open Sans"'], + }, + }, + plugins: [], +}; diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..b5abb30 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "rootDir": "src", + "outDir": "build", + "lib": ["dom", "esnext"], + "target": "es2015", + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noImplicitAny": true, + "noImplicitThis": true, + "noUnusedLocals": false, + "jsx": "react-jsx" + }, + "exclude": ["node_modules", "build", "coverage", "webpack.*.js", "*.config.js", "*.test.ts*"], + "ts-node": { + "compilerOptions": { + "module": "CommonJS" + } + }, + "extends": "./tsconfig.paths.json" +} diff --git a/frontend/tsconfig.paths.json b/frontend/tsconfig.paths.json new file mode 100644 index 0000000..e267511 --- /dev/null +++ b/frontend/tsconfig.paths.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@components/*": ["./src/components/*"], + "@pages/*": ["./src/pages/*"], + "@layouts/*": ["./src/layout/*"], + "@hooks/*": ["./src/hooks/*"], + "@context/*": ["./src/context/*"], + "@utils/*": ["./src/utils/*"], + "@services/*": ["./src/services/*"], + "@assets/*": ["./src/assets/*"], + } + } +} diff --git a/frontend/webpack.common.js b/frontend/webpack.common.js new file mode 100644 index 0000000..c1680b5 --- /dev/null +++ b/frontend/webpack.common.js @@ -0,0 +1,59 @@ +/* eslint-disable */ +const path = require("path"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const CopyPlugin = require("copy-webpack-plugin"); + +module.exports = { + entry: "./src/index.tsx", + plugins: [ + new HtmlWebpackPlugin({ + template: "src/public/index.html", + }), + new CopyPlugin({ + patterns: [{ from: "src/assets" }], + }), + ], + module: { + rules: [ + { + test: /\.tsx?$/, + use: "ts-loader", + exclude: /node_modules/, + }, + { + test: /\.css$/i, + use: ["style-loader", "css-loader", "postcss-loader"], + }, + { + test: /\.(png|jpg|jpeg|gif|ico)$/i, + type: "asset/resource", + }, + { + test: /\.svg$/, + use: ['@svgr/webpack'], + }, + { + test: /\.(woff|woff2|eot|ttf|otf)$/i, + type: "asset/resource", + }, + ], + }, + resolve: { + extensions: [".tsx", ".ts", ".js"], + alias: { + "@components": path.resolve(__dirname, "src/components"), + "@pages": path.resolve(__dirname, "src/pages"), + "@layouts": path.resolve(__dirname, "src/layout"), + "@hooks": path.resolve(__dirname, "src/hooks"), + "@context": path.resolve(__dirname, "src/context"), + "@utils": path.resolve(__dirname, "src/utils"), + "@services": path.resolve(__dirname, "src/services"), + "@assets": path.resolve(__dirname, "src/assets"), + }, + }, + output: { + filename: "bundle.js", + path: path.resolve(__dirname, "build"), + clean: true, + }, +}; diff --git a/frontend/webpack.development.js b/frontend/webpack.development.js new file mode 100644 index 0000000..a9e1c38 --- /dev/null +++ b/frontend/webpack.development.js @@ -0,0 +1,27 @@ +/* eslint-disable */ +const { merge } = require("webpack-merge"); +const common = require("./webpack.common.js"); +const Dotenv = require("dotenv-webpack"); + +module.exports = merge(common, { + mode: "development", + devtool: "inline-source-map", + devServer: { + historyApiFallback: true, + client: { + logging: "info", + overlay: true, + }, + compress: true, + open: true, + static: "./build", + port: 3000, + allowedHosts: "all", + }, + stats: { + errorDetails: true, + }, + plugins: [ + new Dotenv(), + ], +}); diff --git a/frontend/webpack.production.js b/frontend/webpack.production.js new file mode 100644 index 0000000..e08e382 --- /dev/null +++ b/frontend/webpack.production.js @@ -0,0 +1,14 @@ +/* eslint-disable */ +const { merge } = require("webpack-merge"); +const webpack = require('webpack'); +const dotenv = require('dotenv'); +const common = require("./webpack.common.js"); + +module.exports = merge(common, { + mode: "production", + plugins: [ + new webpack.DefinePlugin({ + 'process.env.PIPEDRIVE_CREATE_MODAL_ID': JSON.stringify(process.env.PIPEDRIVE_CREATE_MODAL_ID), + }), + ], +}); diff --git a/frontend/yarn.lock b/frontend/yarn.lock new file mode 100644 index 0000000..d198416 --- /dev/null +++ b/frontend/yarn.lock @@ -0,0 +1,7757 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@adobe/css-tools@^4.0.1": + version "4.1.0" + resolved "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.1.0.tgz" + integrity sha512-mMVJ/j/GbZ/De4ZHWbQAQO1J6iVnjtZLc9WEdkUQb8S/Bu2cAF2bETXUgMAdvMG3/ngtKmcNBe+Zms9bg6jnQQ== + +"@ampproject/remapping@^2.1.0", "@ampproject/remapping@^2.2.0": + version "2.2.0" + resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== + dependencies: + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.0.tgz#c241dc454e5b5917e40d37e525e2f4530c399298" + integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g== + +"@babel/compat-data@^7.20.5": + version "7.20.14" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.14.tgz" + integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== + +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.20.12": + version "7.20.12" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz" + integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.7" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helpers" "^7.20.7" + "@babel/parser" "^7.20.7" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.12" + "@babel/types" "^7.20.7" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + +"@babel/core@^7.19.6": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.3.tgz#cf1c877284a469da5d1ce1d1e53665253fae712e" + integrity sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.21.3" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.3" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.3" + "@babel/types" "^7.21.3" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + +"@babel/generator@^7.20.7", "@babel/generator@^7.7.2": + version "7.20.14" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz" + integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg== + dependencies: + "@babel/types" "^7.20.7" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" + +"@babel/generator@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.3.tgz#232359d0874b392df04045d72ce2fd9bb5045fce" + integrity sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA== + dependencies: + "@babel/types" "^7.21.3" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" + integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.18.6" + "@babel/types" "^7.18.9" + +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7": + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz" + integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== + dependencies: + "@babel/compat-data" "^7.20.5" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.21.3" + lru-cache "^5.1.1" + semver "^6.3.0" + +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz#64f49ecb0020532f19b1d014b03bccaa1ab85fb9" + integrity sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-member-expression-to-functions" "^7.21.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-split-export-declaration" "^7.18.6" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.0.tgz#53ff78472e5ce10a52664272a239787107603ebb" + integrity sha512-N+LaFW/auRSWdx7SHD/HiARwXQju1vXTW4fKr4u5SgBUTm51OKEjKgj+cs00ggW3kEvNqwErnlwuq7Y3xBe4eg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.3.1" + +"@babel/helper-define-polyfill-provider@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" + integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== + dependencies: + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== + +"@babel/helper-explode-assignable-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" + integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== + dependencies: + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" + +"@babel/helper-function-name@^7.19.0": + version "7.19.0" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz" + integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== + dependencies: + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.0" + +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5" + integrity sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q== + dependencies: + "@babel/types" "^7.21.0" + +"@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" + integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" + +"@babel/helper-module-transforms@^7.20.11": + version "7.20.11" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz" + integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.10" + "@babel/types" "^7.20.7" + +"@babel/helper-optimise-call-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" + integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.20.2" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz" + integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== + +"@babel/helper-remap-async-to-generator@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" + integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-wrap-function" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" + integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.20.7" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.7" + "@babel/types" "^7.20.7" + +"@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" + integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== + dependencies: + "@babel/types" "^7.20.0" + +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + +"@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== + +"@babel/helper-validator-option@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" + integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== + +"@babel/helper-wrap-function@^7.18.9": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" + integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== + dependencies: + "@babel/helper-function-name" "^7.19.0" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + +"@babel/helpers@^7.20.7": + version "7.20.13" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.13.tgz" + integrity sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg== + dependencies: + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.13" + "@babel/types" "^7.20.7" + +"@babel/helpers@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" + integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== + dependencies: + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" + +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7": + version "7.20.15" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.20.15.tgz" + integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg== + +"@babel/parser@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3" + integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ== + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" + integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" + integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-proposal-optional-chaining" "^7.20.7" + +"@babel/plugin-proposal-async-generator-functions@^7.20.1": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" + integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-proposal-class-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-class-static-block@^7.18.6": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz#77bdd66fb7b605f3a61302d224bdfacf5547977d" + integrity sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-proposal-dynamic-import@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" + integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-proposal-export-namespace-from@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" + integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" + integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-proposal-logical-assignment-operators@^7.18.9": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" + integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" + integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-numeric-separator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@^7.20.2": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" + integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== + dependencies: + "@babel/compat-data" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.20.7" + +"@babel/plugin-proposal-optional-catch-binding@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-proposal-optional-chaining@^7.18.9", "@babel/plugin-proposal-optional-chaining@^7.20.7": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" + integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-private-methods@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" + integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-private-property-in-object@^7.18.6": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz#19496bd9883dd83c23c7d7fc45dcd9ad02dfa1dc" + integrity sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" + integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.13" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-import-assertions@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" + integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" + +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.7.2": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz" + integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.20.0", "@babel/plugin-syntax-typescript@^7.7.2": + version "7.20.0" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz" + integrity sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" + +"@babel/plugin-transform-arrow-functions@^7.18.6": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" + integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-async-to-generator@^7.18.6": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" + integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== + dependencies: + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" + +"@babel/plugin-transform-block-scoped-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" + integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-block-scoping@^7.20.2": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" + integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-classes@^7.20.2": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" + integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-split-export-declaration" "^7.18.6" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.18.9": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" + integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/template" "^7.20.7" + +"@babel/plugin-transform-destructuring@^7.20.2": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" + integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" + integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-duplicate-keys@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" + integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-exponentiation-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" + integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-for-of@^7.18.8": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz#964108c9988de1a60b4be2354a7d7e245f36e86e" + integrity sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" + integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== + dependencies: + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" + integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-member-expression-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" + integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-modules-amd@^7.19.6": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" + integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== + dependencies: + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-modules-commonjs@^7.19.6": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" + integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== + dependencies: + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-simple-access" "^7.20.2" + +"@babel/plugin-transform-modules-systemjs@^7.19.6": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" + integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== + dependencies: + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-identifier" "^7.19.1" + +"@babel/plugin-transform-modules-umd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" + integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== + dependencies: + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.19.1": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" + integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.20.5" + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-new-target@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" + integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-object-super@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" + integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" + +"@babel/plugin-transform-parameters@^7.20.1", "@babel/plugin-transform-parameters@^7.20.7": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" + integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-property-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" + integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-react-constant-elements@^7.18.12": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.21.3.tgz#b32a5556100d424b25e388dd689050d78396884d" + integrity sha512-4DVcFeWe/yDYBLp0kBmOGFJ6N2UYg7coGid1gdxb4co62dy/xISDMaYBXBVXEDhfgMk7qkbcYiGtwd5Q/hwDDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-react-display-name@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" + integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-react-jsx-development@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz#dbe5c972811e49c7405b630e4d0d2e1380c0ddc5" + integrity sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.18.6" + +"@babel/plugin-transform-react-jsx@^7.18.6": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz#656b42c2fdea0a6d8762075d58ef9d4e3c4ab8a2" + integrity sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-jsx" "^7.18.6" + "@babel/types" "^7.21.0" + +"@babel/plugin-transform-react-pure-annotations@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz#561af267f19f3e5d59291f9950fd7b9663d0d844" + integrity sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-regenerator@^7.18.6": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" + integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + regenerator-transform "^0.15.1" + +"@babel/plugin-transform-reserved-words@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" + integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-shorthand-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" + integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-spread@^7.19.0": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" + integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + +"@babel/plugin-transform-sticky-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" + integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-template-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" + integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-typeof-symbol@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" + integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-typescript@^7.21.0": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz#316c5be579856ea890a57ebc5116c5d064658f2b" + integrity sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-typescript" "^7.20.0" + +"@babel/plugin-transform-unicode-escapes@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" + integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-unicode-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" + integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/preset-env@^7.19.4": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.20.2.tgz#9b1642aa47bb9f43a86f9630011780dab7f86506" + integrity sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg== + dependencies: + "@babel/compat-data" "^7.20.1" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-async-generator-functions" "^7.20.1" + "@babel/plugin-proposal-class-properties" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.18.6" + "@babel/plugin-proposal-dynamic-import" "^7.18.6" + "@babel/plugin-proposal-export-namespace-from" "^7.18.9" + "@babel/plugin-proposal-json-strings" "^7.18.6" + "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-object-rest-spread" "^7.20.2" + "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-private-methods" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.18.6" + "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.20.0" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.18.6" + "@babel/plugin-transform-async-to-generator" "^7.18.6" + "@babel/plugin-transform-block-scoped-functions" "^7.18.6" + "@babel/plugin-transform-block-scoping" "^7.20.2" + "@babel/plugin-transform-classes" "^7.20.2" + "@babel/plugin-transform-computed-properties" "^7.18.9" + "@babel/plugin-transform-destructuring" "^7.20.2" + "@babel/plugin-transform-dotall-regex" "^7.18.6" + "@babel/plugin-transform-duplicate-keys" "^7.18.9" + "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-for-of" "^7.18.8" + "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-literals" "^7.18.9" + "@babel/plugin-transform-member-expression-literals" "^7.18.6" + "@babel/plugin-transform-modules-amd" "^7.19.6" + "@babel/plugin-transform-modules-commonjs" "^7.19.6" + "@babel/plugin-transform-modules-systemjs" "^7.19.6" + "@babel/plugin-transform-modules-umd" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1" + "@babel/plugin-transform-new-target" "^7.18.6" + "@babel/plugin-transform-object-super" "^7.18.6" + "@babel/plugin-transform-parameters" "^7.20.1" + "@babel/plugin-transform-property-literals" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.18.6" + "@babel/plugin-transform-reserved-words" "^7.18.6" + "@babel/plugin-transform-shorthand-properties" "^7.18.6" + "@babel/plugin-transform-spread" "^7.19.0" + "@babel/plugin-transform-sticky-regex" "^7.18.6" + "@babel/plugin-transform-template-literals" "^7.18.9" + "@babel/plugin-transform-typeof-symbol" "^7.18.9" + "@babel/plugin-transform-unicode-escapes" "^7.18.10" + "@babel/plugin-transform-unicode-regex" "^7.18.6" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.20.2" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" + core-js-compat "^3.25.1" + semver "^6.3.0" + +"@babel/preset-modules@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" + integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-react@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.18.6.tgz#979f76d6277048dc19094c217b507f3ad517dd2d" + integrity sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-react-display-name" "^7.18.6" + "@babel/plugin-transform-react-jsx" "^7.18.6" + "@babel/plugin-transform-react-jsx-development" "^7.18.6" + "@babel/plugin-transform-react-pure-annotations" "^7.18.6" + +"@babel/preset-typescript@^7.18.6": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz#bcbbca513e8213691fe5d4b23d9251e01f00ebff" + integrity sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-option" "^7.21.0" + "@babel/plugin-transform-typescript" "^7.21.0" + +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== + +"@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.20.6", "@babel/runtime@^7.20.7", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.9.2": + version "7.20.13" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz" + integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA== + dependencies: + regenerator-runtime "^0.13.11" + +"@babel/runtime@^7.8.4": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" + integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== + dependencies: + regenerator-runtime "^0.13.11" + +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz" + integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + +"@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13", "@babel/traverse@^7.7.2": + version "7.20.13" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz" + integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.20.13" + "@babel/types" "^7.20.7" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.3.tgz#4747c5e7903d224be71f90788b06798331896f67" + integrity sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.21.3" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.3" + "@babel/types" "^7.21.3" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.20.7" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz" + integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + +"@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.5", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3", "@babel/types@^7.4.4": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05" + integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@eslint/eslintrc@^1.4.1": + version "1.4.1" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz" + integrity sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.4.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@humanwhocodes/config-array@^0.11.8": + version "0.11.8" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz" + integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^29.4.1": + version "29.4.1" + resolved "https://registry.npmjs.org/@jest/console/-/console-29.4.1.tgz" + integrity sha512-m+XpwKSi3PPM9znm5NGS8bBReeAJJpSkL1OuFCqaMaJL2YX9YXLkkI+MBchMPwu+ZuM2rynL51sgfkQteQ1CKQ== + dependencies: + "@jest/types" "^29.4.1" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.4.1" + jest-util "^29.4.1" + slash "^3.0.0" + +"@jest/core@^29.4.1": + version "29.4.1" + resolved "https://registry.npmjs.org/@jest/core/-/core-29.4.1.tgz" + integrity sha512-RXFTohpBqpaTebNdg5l3I5yadnKo9zLBajMT0I38D0tDhreVBYv3fA8kywthI00sWxPztWLD3yjiUkewwu/wKA== + dependencies: + "@jest/console" "^29.4.1" + "@jest/reporters" "^29.4.1" + "@jest/test-result" "^29.4.1" + "@jest/transform" "^29.4.1" + "@jest/types" "^29.4.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.4.0" + jest-config "^29.4.1" + jest-haste-map "^29.4.1" + jest-message-util "^29.4.1" + jest-regex-util "^29.2.0" + jest-resolve "^29.4.1" + jest-resolve-dependencies "^29.4.1" + jest-runner "^29.4.1" + jest-runtime "^29.4.1" + jest-snapshot "^29.4.1" + jest-util "^29.4.1" + jest-validate "^29.4.1" + jest-watcher "^29.4.1" + micromatch "^4.0.4" + pretty-format "^29.4.1" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^29.4.1": + version "29.4.1" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-29.4.1.tgz" + integrity sha512-pJ14dHGSQke7Q3mkL/UZR9ZtTOxqskZaC91NzamEH4dlKRt42W+maRBXiw/LWkdJe+P0f/zDR37+SPMplMRlPg== + dependencies: + "@jest/fake-timers" "^29.4.1" + "@jest/types" "^29.4.1" + "@types/node" "*" + jest-mock "^29.4.1" + +"@jest/expect-utils@^29.4.1": + version "29.4.1" + resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.4.1.tgz" + integrity sha512-w6YJMn5DlzmxjO00i9wu2YSozUYRBhIoJ6nQwpMYcBMtiqMGJm1QBzOf6DDgRao8dbtpDoaqLg6iiQTvv0UHhQ== + dependencies: + jest-get-type "^29.2.0" + +"@jest/expect@^29.4.1": + version "29.4.1" + resolved "https://registry.npmjs.org/@jest/expect/-/expect-29.4.1.tgz" + integrity sha512-ZxKJP5DTUNF2XkpJeZIzvnzF1KkfrhEF6Rz0HGG69fHl6Bgx5/GoU3XyaeFYEjuuKSOOsbqD/k72wFvFxc3iTw== + dependencies: + expect "^29.4.1" + jest-snapshot "^29.4.1" + +"@jest/fake-timers@^29.4.1": + version "29.4.1" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.4.1.tgz" + integrity sha512-/1joI6rfHFmmm39JxNfmNAO3Nwm6Y0VoL5fJDy7H1AtWrD1CgRtqJbN9Ld6rhAkGO76qqp4cwhhxJ9o9kYjQMw== + dependencies: + "@jest/types" "^29.4.1" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.4.1" + jest-mock "^29.4.1" + jest-util "^29.4.1" + +"@jest/globals@^29.4.1": + version "29.4.1" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-29.4.1.tgz" + integrity sha512-znoK2EuFytbHH0ZSf2mQK2K1xtIgmaw4Da21R2C/NE/+NnItm5mPEFQmn8gmF3f0rfOlmZ3Y3bIf7bFj7DHxAA== + dependencies: + "@jest/environment" "^29.4.1" + "@jest/expect" "^29.4.1" + "@jest/types" "^29.4.1" + jest-mock "^29.4.1" + +"@jest/reporters@^29.4.1": + version "29.4.1" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-29.4.1.tgz" + integrity sha512-AISY5xpt2Xpxj9R6y0RF1+O6GRy9JsGa8+vK23Lmzdy1AYcpQn5ItX79wJSsTmfzPKSAcsY1LNt/8Y5Xe5LOSg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.4.1" + "@jest/test-result" "^29.4.1" + "@jest/transform" "^29.4.1" + "@jest/types" "^29.4.1" + "@jridgewell/trace-mapping" "^0.3.15" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.4.1" + jest-util "^29.4.1" + jest-worker "^29.4.1" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + +"@jest/schemas@^29.4.0": + version "29.4.0" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.0.tgz" + integrity sha512-0E01f/gOZeNTG76i5eWWSupvSHaIINrTie7vCyjiYFKgzNdyEGd12BUv4oNBFHOqlHDbtoJi3HrQ38KCC90NsQ== + dependencies: + "@sinclair/typebox" "^0.25.16" + +"@jest/source-map@^29.2.0": + version "29.2.0" + resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-29.2.0.tgz" + integrity sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ== + dependencies: + "@jridgewell/trace-mapping" "^0.3.15" + callsites "^3.0.0" + graceful-fs "^4.2.9" + +"@jest/test-result@^29.4.1": + version "29.4.1" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-29.4.1.tgz" + integrity sha512-WRt29Lwt+hEgfN8QDrXqXGgCTidq1rLyFqmZ4lmJOpVArC8daXrZWkWjiaijQvgd3aOUj2fM8INclKHsQW9YyQ== + dependencies: + "@jest/console" "^29.4.1" + "@jest/types" "^29.4.1" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^29.4.1": + version "29.4.1" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.4.1.tgz" + integrity sha512-v5qLBNSsM0eHzWLXsQ5fiB65xi49A3ILPSFQKPXzGL4Vyux0DPZAIN7NAFJa9b4BiTDP9MBF/Zqc/QA1vuiJ0w== + dependencies: + "@jest/test-result" "^29.4.1" + graceful-fs "^4.2.9" + jest-haste-map "^29.4.1" + slash "^3.0.0" + +"@jest/transform@^29.4.1": + version "29.4.1" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-29.4.1.tgz" + integrity sha512-5w6YJrVAtiAgr0phzKjYd83UPbCXsBRTeYI4BXokv9Er9CcrH9hfXL/crCvP2d2nGOcovPUnlYiLPFLZrkG5Hg== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.4.1" + "@jridgewell/trace-mapping" "^0.3.15" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.4.1" + jest-regex-util "^29.2.0" + jest-util "^29.4.1" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^5.0.0" + +"@jest/types@^29.4.1": + version "29.4.1" + resolved "https://registry.npmjs.org/@jest/types/-/types-29.4.1.tgz" + integrity sha512-zbrAXDUOnpJ+FMST2rV7QZOgec8rskg2zv8g2ajeqitp4tvZiyqTCYXANrKsM+ryj5o+LI+ZN2EgU9drrkiwSA== + dependencies: + "@jest/schemas" "^29.4.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": + version "3.1.0" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.2": + version "0.3.2" + resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz" + integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.17" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.4" + resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz" + integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@onlyoffice/document-editor-react@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@onlyoffice/document-editor-react/-/document-editor-react-1.2.0.tgz" + integrity sha512-0/D+scPmbJKQHdvCiE+5+iFivodZM0QSunbU6Ttcjy2WVJgy648yxQ3kYsEHpPZEJkXD+AFkh1mFyk6V0jhktw== + +"@pipedrive/app-extensions-sdk@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@pipedrive/app-extensions-sdk/-/app-extensions-sdk-0.6.1.tgz#ecda40803a81f412bf5f6c85f5c4c556c9733358" + integrity sha512-ST8KO5CEywYisbWpQ6tN+GTMpR2Z37IRcXRle26KLxLCFEngSgVprLFBns4ktcPlENvhQF4I+UZULUMcYzbC6A== + +"@pkgr/utils@^2.3.1": + version "2.3.1" + resolved "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz" + integrity sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw== + dependencies: + cross-spawn "^7.0.3" + is-glob "^4.0.3" + open "^8.4.0" + picocolors "^1.0.0" + tiny-glob "^0.2.9" + tslib "^2.4.0" + +"@remix-run/router@1.3.2": + version "1.3.2" + resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.3.2.tgz" + integrity sha512-t54ONhl/h75X94SWsHGQ4G/ZrCEguKSRQr7DrjTciJXW0YU1QhlwYeycvK5JgkzlxmvrK7wq1NB/PLtHxoiDcA== + +"@sinclair/typebox@^0.25.16": + version "0.25.21" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.21.tgz" + integrity sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g== + +"@sinonjs/commons@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz" + integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.0.2" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz" + integrity sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw== + dependencies: + "@sinonjs/commons" "^2.0.0" + +"@svgr/babel-plugin-add-jsx-attribute@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz#74a5d648bd0347bda99d82409d87b8ca80b9a1ba" + integrity sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ== + +"@svgr/babel-plugin-remove-jsx-attribute@*": + version "6.5.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.5.0.tgz#652bfd4ed0a0699843585cda96faeb09d6e1306e" + integrity sha512-8zYdkym7qNyfXpWvu4yq46k41pyNM9SOstoWhKlm+IfdCE1DdnRKeMUPsWIEO/DEkaWxJ8T9esNdG3QwQ93jBA== + +"@svgr/babel-plugin-remove-jsx-empty-expression@*": + version "6.5.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.5.0.tgz#4b78994ab7d39032c729903fc2dd5c0fa4565cb8" + integrity sha512-NFdxMq3xA42Kb1UbzCVxplUc0iqSyM9X8kopImvFnB+uSDdzIHOdbs1op8ofAvVRtbg4oZiyRl3fTYeKcOe9Iw== + +"@svgr/babel-plugin-replace-jsx-attribute-value@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz#fb9d22ea26d2bc5e0a44b763d4c46d5d3f596c60" + integrity sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg== + +"@svgr/babel-plugin-svg-dynamic-title@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz#01b2024a2b53ffaa5efceaa0bf3e1d5a4c520ce4" + integrity sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw== + +"@svgr/babel-plugin-svg-em-dimensions@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz#dd3fa9f5b24eb4f93bcf121c3d40ff5facecb217" + integrity sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA== + +"@svgr/babel-plugin-transform-react-native-svg@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz#1d8e945a03df65b601551097d8f5e34351d3d305" + integrity sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg== + +"@svgr/babel-plugin-transform-svg-component@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz#48620b9e590e25ff95a80f811544218d27f8a250" + integrity sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ== + +"@svgr/babel-preset@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-6.5.1.tgz#b90de7979c8843c5c580c7e2ec71f024b49eb828" + integrity sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^6.5.1" + "@svgr/babel-plugin-remove-jsx-attribute" "*" + "@svgr/babel-plugin-remove-jsx-empty-expression" "*" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^6.5.1" + "@svgr/babel-plugin-svg-dynamic-title" "^6.5.1" + "@svgr/babel-plugin-svg-em-dimensions" "^6.5.1" + "@svgr/babel-plugin-transform-react-native-svg" "^6.5.1" + "@svgr/babel-plugin-transform-svg-component" "^6.5.1" + +"@svgr/core@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-6.5.1.tgz#d3e8aa9dbe3fbd747f9ee4282c1c77a27410488a" + integrity sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw== + dependencies: + "@babel/core" "^7.19.6" + "@svgr/babel-preset" "^6.5.1" + "@svgr/plugin-jsx" "^6.5.1" + camelcase "^6.2.0" + cosmiconfig "^7.0.1" + +"@svgr/hast-util-to-babel-ast@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz#81800bd09b5bcdb968bf6ee7c863d2288fdb80d2" + integrity sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw== + dependencies: + "@babel/types" "^7.20.0" + entities "^4.4.0" + +"@svgr/plugin-jsx@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz#0e30d1878e771ca753c94e69581c7971542a7072" + integrity sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw== + dependencies: + "@babel/core" "^7.19.6" + "@svgr/babel-preset" "^6.5.1" + "@svgr/hast-util-to-babel-ast" "^6.5.1" + svg-parser "^2.0.4" + +"@svgr/plugin-svgo@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz#0f91910e988fc0b842f88e0960c2862e022abe84" + integrity sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ== + dependencies: + cosmiconfig "^7.0.1" + deepmerge "^4.2.2" + svgo "^2.8.0" + +"@svgr/webpack@^6.5.1": + version "6.5.1" + resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-6.5.1.tgz#ecf027814fc1cb2decc29dc92f39c3cf691e40e8" + integrity sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA== + dependencies: + "@babel/core" "^7.19.6" + "@babel/plugin-transform-react-constant-elements" "^7.18.12" + "@babel/preset-env" "^7.19.4" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.18.6" + "@svgr/core" "^6.5.1" + "@svgr/plugin-jsx" "^6.5.1" + "@svgr/plugin-svgo" "^6.5.1" + +"@testing-library/dom@^8.5.0": + version "8.20.0" + resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz" + integrity sha512-d9ULIT+a4EXLX3UU8FBjauG9NnsZHkHztXoIcTsOKoOw030fyjheN9svkTULjJxtYag9DZz5Jz5qkWZDPxTFwA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "^5.0.0" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.4.4" + pretty-format "^27.0.2" + +"@testing-library/jest-dom@^5.16.5": + version "5.16.5" + resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz" + integrity sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA== + dependencies: + "@adobe/css-tools" "^4.0.1" + "@babel/runtime" "^7.9.2" + "@types/testing-library__jest-dom" "^5.9.1" + aria-query "^5.0.0" + chalk "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.5.6" + lodash "^4.17.15" + redent "^3.0.0" + +"@testing-library/react@^13.4.0": + version "13.4.0" + resolved "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz" + integrity sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw== + dependencies: + "@babel/runtime" "^7.12.5" + "@testing-library/dom" "^8.5.0" + "@types/react-dom" "^18.0.0" + +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.3" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + +"@types/aria-query@^5.0.1": + version "5.0.1" + resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz" + integrity sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q== + +"@types/babel__core@^7.1.14": + version "7.20.0" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz" + integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.4" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz" + integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.1" + resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz" + integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.18.3" + resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz" + integrity sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w== + dependencies: + "@babel/types" "^7.3.0" + +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/bonjour@^3.5.9": + version "3.5.10" + resolved "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz" + integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw== + dependencies: + "@types/node" "*" + +"@types/connect-history-api-fallback@^1.3.5": + version "1.3.5" + resolved "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz" + integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw== + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.35" + resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.3": + version "3.7.4" + resolved "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz" + integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.21.0" + resolved "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.0.tgz" + integrity sha512-35EhHNOXgxnUgh4XCJsGhE7zdlDhYDN/aMG6UbkByCFFNgQ7b3U+uVoqBpicFydR8JEfgdjCF7SJ7MiJfzuiTA== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*": + version "1.0.0" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz" + integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== + +"@types/estree@^0.0.51": + version "0.0.51" + resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": + version "4.17.33" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz" + integrity sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*", "@types/express@^4.17.13": + version "4.17.17" + resolved "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz" + integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/graceful-fs@^4.1.3": + version "4.1.6" + resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz" + integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== + dependencies: + "@types/node" "*" + +"@types/html-minifier-terser@^6.0.0": + version "6.1.0" + resolved "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz" + integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== + +"@types/http-proxy@^1.17.8": + version "1.17.9" + resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz" + integrity sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.4" + resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@*", "@types/jest@^29.2.0": + version "29.4.0" + resolved "https://registry.npmjs.org/@types/jest/-/jest-29.4.0.tgz" + integrity sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/jsdom@^20.0.0": + version "20.0.1" + resolved "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz" + integrity sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ== + dependencies: + "@types/node" "*" + "@types/tough-cookie" "*" + parse5 "^7.0.0" + +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.11" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@types/md5@^2.3.2": + version "2.3.2" + resolved "https://registry.npmjs.org/@types/md5/-/md5-2.3.2.tgz" + integrity sha512-v+JFDu96+UYJ3/UWzB0mEglIS//MZXgRaJ4ubUPwOM0gvLc/kcQ3TWNYwENEK7/EcXGQVrW8h/XqednSjBd/Og== + +"@types/mime@*": + version "3.0.1" + resolved "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz" + integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== + +"@types/node@*", "@types/node@^18.11.0": + version "18.13.0" + resolved "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz" + integrity sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg== + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + +"@types/prettier@^2.1.5": + version "2.7.2" + resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz" + integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== + +"@types/prop-types@*": + version "15.7.5" + resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz" + integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== + +"@types/qs@*": + version "6.9.7" + resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + +"@types/react-dom@^18.0.0", "@types/react-dom@^18.0.6": + version "18.0.10" + resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.10.tgz" + integrity sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg== + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@^18.0.21": + version "18.0.27" + resolved "https://registry.npmjs.org/@types/react/-/react-18.0.27.tgz" + integrity sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== + +"@types/scheduler@*": + version "0.16.2" + resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz" + integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + +"@types/semver@^7.3.12": + version "7.3.13" + resolved "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz" + integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== + +"@types/serve-index@^1.9.1": + version "1.9.1" + resolved "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz" + integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg== + dependencies: + "@types/express" "*" + +"@types/serve-static@*", "@types/serve-static@^1.13.10": + version "1.15.0" + resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz" + integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg== + dependencies: + "@types/mime" "*" + "@types/node" "*" + +"@types/sockjs@^0.3.33": + version "0.3.33" + resolved "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz" + integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw== + dependencies: + "@types/node" "*" + +"@types/stack-utils@^2.0.0": + version "2.0.1" + resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz" + integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== + +"@types/testing-library__jest-dom@^5.9.1": + version "5.14.5" + resolved "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz" + integrity sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ== + dependencies: + "@types/jest" "*" + +"@types/tough-cookie@*": + version "4.0.2" + resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz" + integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== + +"@types/ws@^8.5.1": + version "8.5.4" + resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz" + integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== + dependencies: + "@types/node" "*" + +"@types/yargs-parser@*": + version "21.0.0" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz" + integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + +"@types/yargs@^17.0.8": + version "17.0.22" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz" + integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^5.40.1": + version "5.51.0" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.51.0.tgz" + integrity sha512-wcAwhEWm1RgNd7dxD/o+nnLW8oH+6RK1OGnmbmkj/GGoDPV1WWMVP0FXYQBivKHdwM1pwii3bt//RC62EriIUQ== + dependencies: + "@typescript-eslint/scope-manager" "5.51.0" + "@typescript-eslint/type-utils" "5.51.0" + "@typescript-eslint/utils" "5.51.0" + debug "^4.3.4" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + regexpp "^3.2.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.40.1": + version "5.51.0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.51.0.tgz" + integrity sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA== + dependencies: + "@typescript-eslint/scope-manager" "5.51.0" + "@typescript-eslint/types" "5.51.0" + "@typescript-eslint/typescript-estree" "5.51.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.51.0": + version "5.51.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.51.0.tgz" + integrity sha512-gNpxRdlx5qw3yaHA0SFuTjW4rxeYhpHxt491PEcKF8Z6zpq0kMhe0Tolxt0qjlojS+/wArSDlj/LtE69xUJphQ== + dependencies: + "@typescript-eslint/types" "5.51.0" + "@typescript-eslint/visitor-keys" "5.51.0" + +"@typescript-eslint/type-utils@5.51.0": + version "5.51.0" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.51.0.tgz" + integrity sha512-QHC5KKyfV8sNSyHqfNa0UbTbJ6caB8uhcx2hYcWVvJAZYJRBo5HyyZfzMdRx8nvS+GyMg56fugMzzWnojREuQQ== + dependencies: + "@typescript-eslint/typescript-estree" "5.51.0" + "@typescript-eslint/utils" "5.51.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.51.0": + version "5.51.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.51.0.tgz" + integrity sha512-SqOn0ANn/v6hFn0kjvLwiDi4AzR++CBZz0NV5AnusT2/3y32jdc0G4woXPWHCumWtUXZKPAS27/9vziSsC9jnw== + +"@typescript-eslint/typescript-estree@5.51.0": + version "5.51.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.51.0.tgz" + integrity sha512-TSkNupHvNRkoH9FMA3w7TazVFcBPveAAmb7Sz+kArY6sLT86PA5Vx80cKlYmd8m3Ha2SwofM1KwraF24lM9FvA== + dependencies: + "@typescript-eslint/types" "5.51.0" + "@typescript-eslint/visitor-keys" "5.51.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.51.0": + version "5.51.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.51.0.tgz" + integrity sha512-76qs+5KWcaatmwtwsDJvBk4H76RJQBFe+Gext0EfJdC3Vd2kpY2Pf//OHHzHp84Ciw0/rYoGTDnIAr3uWhhJYw== + dependencies: + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.51.0" + "@typescript-eslint/types" "5.51.0" + "@typescript-eslint/typescript-estree" "5.51.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.51.0": + version "5.51.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.51.0.tgz" + integrity sha512-Oh2+eTdjHjOFjKA27sxESlA87YPSOJafGCR0md5oeMdh1ZcCfAGCIOL216uTBAkAIptvLIfKQhl7lHxMJet4GQ== + dependencies: + "@typescript-eslint/types" "5.51.0" + eslint-visitor-keys "^3.3.0" + +"@webassemblyjs/ast@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz" + integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + +"@webassemblyjs/floating-point-hex-parser@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz" + integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== + +"@webassemblyjs/helper-api-error@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz" + integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== + +"@webassemblyjs/helper-buffer@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz" + integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== + +"@webassemblyjs/helper-numbers@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz" + integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz" + integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== + +"@webassemblyjs/helper-wasm-section@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz" + integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + +"@webassemblyjs/ieee754@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz" + integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz" + integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz" + integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== + +"@webassemblyjs/wasm-edit@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz" + integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-wasm-section" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-opt" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/wast-printer" "1.11.1" + +"@webassemblyjs/wasm-gen@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz" + integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wasm-opt@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz" + integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + +"@webassemblyjs/wasm-parser@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz" + integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wast-printer@1.11.1": + version "1.11.1" + resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz" + integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz" + integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== + +"@webpack-cli/info@^1.5.0": + version "1.5.0" + resolved "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz" + integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== + dependencies: + envinfo "^7.7.3" + +"@webpack-cli/serve@^1.7.0": + version "1.7.0" + resolved "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz" + integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +abab@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-globals@^7.0.0: + version "7.0.1" + resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz" + integrity sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q== + dependencies: + acorn "^8.1.0" + acorn-walk "^8.0.2" + +acorn-import-assertions@^1.7.6: + version "1.8.0" + resolved "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz" + integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-node@^1.8.2: + version "1.8.2" + resolved "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz" + integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== + dependencies: + acorn "^7.0.0" + acorn-walk "^7.0.0" + xtend "^4.0.2" + +acorn-walk@^7.0.0: + version "7.2.0" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn-walk@^8.0.2, acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^7.0.0: + version "7.4.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.1.0, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.1: + version "8.8.2" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + +agent-base@6: + version "6.0.2" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv-keywords@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.0, ajv@^8.8.0: + version "8.12.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-html-community@^0.0.8: + version "0.0.8" + resolved "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +anymatch@^3.0.3, anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +aria-query@^5.0.0, aria-query@^5.1.3: + version "5.1.3" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + dependencies: + deep-equal "^2.0.5" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-flatten@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + +array-includes@^3.1.5, array-includes@^3.1.6: + version "3.1.6" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + is-string "^1.0.7" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.flat@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz" + integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +array.prototype.tosorted@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz" + integrity sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.1.3" + +ast-types-flow@^0.0.7: + version "0.0.7" + resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz" + integrity sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +attr-accept@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b" + integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg== + +autoprefixer@^10.4.12: + version "10.4.13" + resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz" + integrity sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg== + dependencies: + browserslist "^4.21.4" + caniuse-lite "^1.0.30001426" + fraction.js "^4.2.0" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +axe-core@^4.6.2: + version "4.6.3" + resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.6.3.tgz" + integrity sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg== + +axios-retry@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.4.0.tgz#f464dbe9408e5aa78fa319afd38bb69b533d8854" + integrity sha512-VdgaP+gHH4iQYCCNUWF2pcqeciVOdGrBBAYUfTY+wPcO5Ltvp/37MLFNCmJKo7Gj3SHvCSdL8ouI1qLYJN3liA== + dependencies: + "@babel/runtime" "^7.15.4" + is-retry-allowed "^2.2.0" + +axios@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.2.tgz#7ac517f0fa3ec46e0e636223fd973713a09c72b3" + integrity sha512-1M3O703bYqYuPhbHeya5bnhpYVsDDRyQSabNja04mZtboLNSuZ4YrltestrLXfHgmzua4TpUqRiVKbiQuo2epw== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +axobject-query@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz" + integrity sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg== + dependencies: + deep-equal "^2.0.5" + +babel-jest@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.4.1.tgz" + integrity sha512-xBZa/pLSsF/1sNpkgsiT3CmY7zV1kAsZ9OxxtrFqYucnOuRftXAfcJqcDVyOPeN4lttWTwhLdu0T9f8uvoPEUg== + dependencies: + "@jest/transform" "^29.4.1" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.4.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^29.4.0: + version "29.4.0" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.4.0.tgz" + integrity sha512-a/sZRLQJEmsmejQ2rPEUe35nO1+C9dc9O1gplH1SXmJxveQSRUYdBk8yGZG/VOUuZs1u2aHZJusEGoRMbhhwCg== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + +babel-plugin-module-resolver@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.0.tgz" + integrity sha512-g0u+/ChLSJ5+PzYwLwP8Rp8Rcfowz58TJNCe+L/ui4rpzE/mg//JVX0EWBUYoxaextqnwuGHzfGp2hh0PPV25Q== + dependencies: + find-babel-config "^2.0.0" + glob "^8.0.3" + pkg-up "^3.1.0" + reselect "^4.1.7" + resolve "^1.22.1" + +babel-plugin-polyfill-corejs2@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" + integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== + dependencies: + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.3.3" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" + integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" + core-js-compat "^3.25.1" + +babel-plugin-polyfill-regenerator@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" + integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" + +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +babel-preset-jest@^29.4.0: + version "29.4.0" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.4.0.tgz" + integrity sha512-fUB9vZflUSM3dO/6M2TCAepTzvA4VkOvl67PjErcrQMGt9Eve7uazaeyCZ2th3UtI7ljpiBJES0F7A1vBRsLZA== + dependencies: + babel-plugin-jest-hoist "^29.4.0" + babel-preset-current-node-syntax "^1.0.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz" + integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== + +big-integer@^1.6.16: + version "1.6.51" + resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +bonjour-service@^1.0.11: + version "1.1.0" + resolved "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.0.tgz" + integrity sha512-LVRinRB3k1/K0XzZ2p58COnWvkQknIY6sf0zF2rpErvcJXpMBttEPQSxK+HEXSS9VmpZlDoDnQWv8ftJT20B0Q== + dependencies: + array-flatten "^2.1.2" + dns-equal "^1.0.0" + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +broadcast-channel@^3.4.1: + version "3.7.0" + resolved "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz" + integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg== + dependencies: + "@babel/runtime" "^7.7.2" + detect-node "^2.1.0" + js-sha3 "0.8.0" + microseconds "0.2.0" + nano-time "1.0.0" + oblivious-set "1.0.0" + rimraf "3.0.2" + unload "2.2.0" + +browserslist@^4.14.5, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5: + version "4.21.5" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz" + integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== + dependencies: + caniuse-lite "^1.0.30001449" + electron-to-chromium "^1.4.284" + node-releases "^2.0.8" + update-browserslist-db "^1.0.10" + +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz" + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001426, caniuse-lite@^1.0.30001449: + version "1.0.30001450" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz" + integrity sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew== + +chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +charenc@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== + +chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +ci-info@^3.2.0: + version "3.7.1" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz" + integrity sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w== + +cjs-module-lexer@^1.0.0: + version "1.2.2" + resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz" + integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== + +classnames@^2.3.2: + version "2.3.2" + resolved "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz" + integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== + +clean-css@^5.2.2: + version "5.3.2" + resolved "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz" + integrity sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww== + dependencies: + source-map "~0.6.0" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clsx@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@^1.1.4, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.10, colorette@^2.0.14: + version "2.0.19" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^7.0.0, commander@^7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +confusing-browser-globals@^1.0.10: + version "1.0.11" + resolved "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz" + integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== + +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4: + version "1.0.5" + resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + +copy-webpack-plugin@^11.0.0: + version "11.0.0" + resolved "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz" + integrity sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ== + dependencies: + fast-glob "^3.2.11" + glob-parent "^6.0.1" + globby "^13.1.1" + normalize-path "^3.0.0" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + +core-js-compat@^3.25.1: + version "3.29.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.1.tgz#15c0fb812ea27c973c18d425099afa50b934b41b" + integrity sha512-QmchCua884D8wWskMX8tW5ydINzd8oSJVx38lx/pVkFGqztxt73GYre3pm/hyYq8bPf+MW5In4I/uRShFDsbrA== + dependencies: + browserslist "^4.21.5" + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: + version "7.1.0" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-fetch@3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" + integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== + dependencies: + node-fetch "2.6.7" + +cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypt@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== + +css-loader@^6.7.1: + version "6.7.3" + resolved "https://registry.npmjs.org/css-loader/-/css-loader-6.7.3.tgz" + integrity sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ== + dependencies: + icss-utils "^5.1.0" + postcss "^8.4.19" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.3.8" + +css-select@^4.1.3: + version "4.3.0" + resolved "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + +css-tree@^1.1.2, css-tree@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + +css-what@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz" + integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +csso@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" + integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== + dependencies: + css-tree "^1.1.2" + +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + +csstype@^3.0.2: + version "3.1.1" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz" + integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== + +damerau-levenshtein@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz" + integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== + +data-urls@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz" + integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== + dependencies: + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +decimal.js@^10.4.2: + version "10.4.3" + resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== + +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" + integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== + +deep-equal@^2.0.5: + version "2.2.0" + resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz" + integrity sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw== + dependencies: + call-bind "^1.0.2" + es-get-iterator "^1.1.2" + get-intrinsic "^1.1.3" + is-arguments "^1.1.1" + is-array-buffer "^3.0.1" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + +deep-is@^0.1.3, deep-is@~0.1.3: + version "0.1.4" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +deepmerge@^4.2.2: + version "4.3.0" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz" + integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== + +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.0" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +define-properties@^1.1.3, define-properties@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +defined@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz" + integrity sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +detect-node@^2.0.4, detect-node@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +detective@^5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz" + integrity sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw== + dependencies: + acorn-node "^1.8.2" + defined "^1.0.0" + minimist "^1.2.6" + +didyoumean@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz" + integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== + +diff-sequences@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.3.1.tgz" + integrity sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz" + integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg== + +dns-packet@^5.2.2: + version "5.4.0" + resolved "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz" + integrity sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: + version "0.5.16" + resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== + +dom-converter@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + +dom-serializer@^1.0.1: + version "1.4.1" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== + dependencies: + webidl-conversions "^7.0.0" + +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + +domutils@^2.5.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +dotenv-defaults@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz" + integrity sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg== + dependencies: + dotenv "^8.2.0" + +dotenv-webpack@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-8.0.1.tgz" + integrity sha512-CdrgfhZOnx4uB18SgaoP9XHRN2v48BbjuXQsZY5ixs5A8579NxQkmMxRtI7aTwSiSQcM2ao12Fdu+L3ZS3bG4w== + dependencies: + dotenv-defaults "^2.0.2" + +dotenv@^16.0.3: + version "16.0.3" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz" + integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== + +dotenv@^8.2.0: + version "8.6.0" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz" + integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.284: + version "1.4.288" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.288.tgz" + integrity sha512-8s9aJf3YiokIrR+HOQzNOGmEHFXVUQzXM/JaViVvKdCkNUjS+lEa/uT7xw3nDVG/IgfxiIwUGkwJ6AR1pTpYsQ== + +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +enhanced-resolve@^5.0.0, enhanced-resolve@^5.10.0: + version "5.12.0" + resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz" + integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +entities@^4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz" + integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== + +envinfo@^7.7.3: + version "7.8.1" + resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz" + integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.19.0, es-abstract@^1.20.4: + version "1.21.1" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz" + integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.3" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.4" + is-array-buffer "^3.0.1" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.10" + is-weakref "^1.0.2" + object-inspect "^1.12.2" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.9" + +es-get-iterator@^1.1.2: + version "1.1.3" + resolved "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +es-module-lexer@^0.9.0: + version "0.9.3" + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz" + integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== + +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-config-airbnb-base@^15.0.0: + version "15.0.0" + resolved "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz" + integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig== + dependencies: + confusing-browser-globals "^1.0.10" + object.assign "^4.1.2" + object.entries "^1.1.5" + semver "^6.3.0" + +eslint-config-airbnb@^19.0.4: + version "19.0.4" + resolved "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz" + integrity sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew== + dependencies: + eslint-config-airbnb-base "^15.0.0" + object.assign "^4.1.2" + object.entries "^1.1.5" + +eslint-config-prettier@^8.5.0: + version "8.6.0" + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz" + integrity sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA== + +eslint-import-resolver-alias@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz" + integrity sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w== + +eslint-import-resolver-babel-module@^5.3.1: + version "5.3.2" + resolved "https://registry.npmjs.org/eslint-import-resolver-babel-module/-/eslint-import-resolver-babel-module-5.3.2.tgz" + integrity sha512-K7D8n0O6p/JJncPote8yiuB7chJfu26Yn/Q3gzT53cNzJNS0NUCkI0iuimj4/vWVRHVQvPnYWeq07V8RvKjz/A== + dependencies: + pkg-up "^3.1.0" + resolve "^1.20.0" + +eslint-import-resolver-node@^0.3.7: + version "0.3.7" + resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz" + integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== + dependencies: + debug "^3.2.7" + is-core-module "^2.11.0" + resolve "^1.22.1" + +eslint-import-resolver-typescript@^3.5.2: + version "3.5.3" + resolved "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.3.tgz" + integrity sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ== + dependencies: + debug "^4.3.4" + enhanced-resolve "^5.10.0" + get-tsconfig "^4.2.0" + globby "^13.1.2" + is-core-module "^2.10.0" + is-glob "^4.0.3" + synckit "^0.8.4" + +eslint-module-utils@^2.7.4: + version "2.7.4" + resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz" + integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== + dependencies: + debug "^3.2.7" + +eslint-plugin-import@^2.26.0: + version "2.27.5" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz" + integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.7.4" + has "^1.0.3" + is-core-module "^2.11.0" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.values "^1.1.6" + resolve "^1.22.1" + semver "^6.3.0" + tsconfig-paths "^3.14.1" + +eslint-plugin-jsx-a11y@^6.6.1: + version "6.7.1" + resolved "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz" + integrity sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA== + dependencies: + "@babel/runtime" "^7.20.7" + aria-query "^5.1.3" + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + ast-types-flow "^0.0.7" + axe-core "^4.6.2" + axobject-query "^3.1.1" + damerau-levenshtein "^1.0.8" + emoji-regex "^9.2.2" + has "^1.0.3" + jsx-ast-utils "^3.3.3" + language-tags "=1.0.5" + minimatch "^3.1.2" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + semver "^6.3.0" + +eslint-plugin-prettier@^4.2.1: + version "4.2.1" + resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-plugin-react-hooks@^4.6.0: + version "4.6.0" + resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz" + integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== + +eslint-plugin-react@^7.31.10: + version "7.32.2" + resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz" + integrity sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg== + dependencies: + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" + doctrine "^2.1.0" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" + prop-types "^15.8.1" + resolve "^2.0.0-next.4" + semver "^6.3.0" + string.prototype.matchall "^4.0.8" + +eslint-scope@5.1.1, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint@^8.25.0: + version "8.33.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz" + integrity sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA== + dependencies: + "@eslint/eslintrc" "^1.4.1" + "@humanwhocodes/config-array" "^0.11.8" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.4.0" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-sdsl "^4.1.4" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + +espree@^9.4.0: + version "9.4.1" + resolved "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz" + integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg== + dependencies: + acorn "^8.8.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.3.0" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== + +expect@^29.0.0, expect@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/expect/-/expect-29.4.1.tgz" + integrity sha512-OKrGESHOaMxK3b6zxIq9SOW8kEXztKff/Dvg88j4xIJxur1hspEbedVkR3GpHe5LO+WB2Qw7OWN0RMTdp6as5A== + dependencies: + "@jest/expect-utils" "^29.4.1" + jest-get-type "^29.2.0" + jest-matcher-utils "^29.4.1" + jest-message-util "^29.4.1" + jest-util "^29.4.1" + +express@^4.17.3: + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9: + version "3.2.12" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz" + integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== + dependencies: + bser "2.1.1" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-selector@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.6.0.tgz#fa0a8d9007b829504db4d07dd4de0310b65287dc" + integrity sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw== + dependencies: + tslib "^2.4.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-babel-config@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/find-babel-config/-/find-babel-config-2.0.0.tgz" + integrity sha512-dOKT7jvF3hGzlW60Gc3ONox/0rRZ/tz7WCil0bqA1In/3I8f1BctpXahRnEKDySZqci7u+dqq93sZST9fOJpFw== + dependencies: + json5 "^2.1.1" + path-exists "^4.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.2.7" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== + +follow-redirects@^1.0.0, follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fraction.js@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz" + integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-monkey@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz" + integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@^2.3.2, fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + +functions-have-names@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: + version "1.2.0" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz" + integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +get-tsconfig@^4.2.0: + version "4.4.0" + resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.4.0.tgz" + integrity sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ== + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.1, glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.1.3, glob@^7.1.4: + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^8.0.3: + version "8.1.0" + resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.19.0: + version "13.20.0" + resolved "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz" + integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globalyzer@0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz" + integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +globby@^13.1.1, globby@^13.1.2: + version "13.1.3" + resolved "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz" + integrity sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.2.11" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^4.0.0" + +globrex@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz" + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.10" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz" + integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== + dependencies: + whatwg-encoding "^2.0.0" + +html-entities@^2.3.2: + version "2.3.3" + resolved "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz" + integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +html-minifier-terser@^6.0.2: + version "6.1.0" + resolved "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz" + integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== + dependencies: + camel-case "^4.1.2" + clean-css "^5.2.2" + commander "^8.3.0" + he "^1.2.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.10.0" + +html-parse-stringify@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" + integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== + dependencies: + void-elements "3.1.0" + +html-webpack-plugin@^5.5.0: + version "5.5.0" + resolved "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz" + integrity sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw== + dependencies: + "@types/html-minifier-terser" "^6.0.0" + html-minifier-terser "^6.0.2" + lodash "^4.17.21" + pretty-error "^4.0.0" + tapable "^2.0.0" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" + integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== + +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + +http-proxy-middleware@^2.0.3: + version "2.0.6" + resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +https-proxy-agent@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +i18next-http-backend@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/i18next-http-backend/-/i18next-http-backend-2.1.1.tgz#72a21d61c2e96eea9ad45ba1b9dd0090e119709a" + integrity sha512-jByfUCDVgQ8+/Wens7queQhYYvMcGTW/lR4IJJNEDDXnmqjLrwi8ubXKpmp76/JIWEZHffNdWqnxFJcTVGeaOw== + dependencies: + cross-fetch "3.1.5" + +i18next@^22.4.9: + version "22.4.9" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-22.4.9.tgz#98c8384c6bd41ff937da98b1e809ba03d3b41053" + integrity sha512-8gWMmUz460KJDQp/ob3MNUX84cVuDRY9PLFPnV8d+Qezz/6dkjxwOaH70xjrCNDO+JrUL25iXfAIN9wUkInNZw== + dependencies: + "@babel/runtime" "^7.20.6" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + +ignore@^5.2.0: + version "5.2.4" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +internal-slot@^1.0.3, internal-slot@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz" + integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + side-channel "^1.0.4" + +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +ipaddr.js@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz" + integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== + +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz" + integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-typed-array "^1.1.10" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-buffer@~1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.10.0, is-core-module@^2.11.0, is-core-module@^2.9.0: + version "2.11.0" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1, is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-map@^2.0.1, is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-retry-allowed@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz#88f34cbd236e043e71b6932d09b0c65fb7b4d71d" + integrity sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg== + +is-set@^2.0.1, is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.10, is-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: + version "5.2.1" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.1.3: + version "3.1.5" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz" + integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-changed-files@^29.4.0: + version "29.4.0" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.4.0.tgz" + integrity sha512-rnI1oPxgFghoz32Y8eZsGJMjW54UlqT17ycQeCEktcxxwqqKdlj9afl8LNeO0Pbu+h2JQHThQP0BzS67eTRx4w== + dependencies: + execa "^5.0.0" + p-limit "^3.1.0" + +jest-circus@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-29.4.1.tgz" + integrity sha512-v02NuL5crMNY4CGPHBEflLzl4v91NFb85a+dH9a1pUNx6Xjggrd8l9pPy4LZ1VYNRXlb+f65+7O/MSIbLir6pA== + dependencies: + "@jest/environment" "^29.4.1" + "@jest/expect" "^29.4.1" + "@jest/test-result" "^29.4.1" + "@jest/types" "^29.4.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + is-generator-fn "^2.0.0" + jest-each "^29.4.1" + jest-matcher-utils "^29.4.1" + jest-message-util "^29.4.1" + jest-runtime "^29.4.1" + jest-snapshot "^29.4.1" + jest-util "^29.4.1" + p-limit "^3.1.0" + pretty-format "^29.4.1" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-cli@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-29.4.1.tgz" + integrity sha512-jz7GDIhtxQ37M+9dlbv5K+/FVcIo1O/b1sX3cJgzlQUf/3VG25nvuWzlDC4F1FLLzUThJeWLu8I7JF9eWpuURQ== + dependencies: + "@jest/core" "^29.4.1" + "@jest/test-result" "^29.4.1" + "@jest/types" "^29.4.1" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^29.4.1" + jest-util "^29.4.1" + jest-validate "^29.4.1" + prompts "^2.0.1" + yargs "^17.3.1" + +jest-config@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-29.4.1.tgz" + integrity sha512-g7p3q4NuXiM4hrS4XFATTkd+2z0Ml2RhFmFPM8c3WyKwVDNszbl4E7cV7WIx1YZeqqCtqbtTtZhGZWJlJqngzg== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.4.1" + "@jest/types" "^29.4.1" + babel-jest "^29.4.1" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.4.1" + jest-environment-node "^29.4.1" + jest-get-type "^29.2.0" + jest-regex-util "^29.2.0" + jest-resolve "^29.4.1" + jest-runner "^29.4.1" + jest-util "^29.4.1" + jest-validate "^29.4.1" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.4.1" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-diff@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.4.1.tgz" + integrity sha512-uazdl2g331iY56CEyfbNA0Ut7Mn2ulAG5vUaEHXycf1L6IPyuImIxSz4F0VYBKi7LYIuxOwTZzK3wh5jHzASMw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.3.1" + jest-get-type "^29.2.0" + pretty-format "^29.4.1" + +jest-docblock@^29.2.0: + version "29.2.0" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.2.0.tgz" + integrity sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A== + dependencies: + detect-newline "^3.0.0" + +jest-each@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-29.4.1.tgz" + integrity sha512-QlYFiX3llJMWUV0BtWht/esGEz9w+0i7BHwODKCze7YzZzizgExB9MOfiivF/vVT0GSQ8wXLhvHXh3x2fVD4QQ== + dependencies: + "@jest/types" "^29.4.1" + chalk "^4.0.0" + jest-get-type "^29.2.0" + jest-util "^29.4.1" + pretty-format "^29.4.1" + +jest-environment-jsdom@^29.2.0: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.4.1.tgz" + integrity sha512-+KfYmRTl5CBHQst9hIz77TiiriHYvuWoLjMT855gx2AMxhHxpk1vtKvag1DQfyWCPVTWV/AG7SIqVh5WI1O/uw== + dependencies: + "@jest/environment" "^29.4.1" + "@jest/fake-timers" "^29.4.1" + "@jest/types" "^29.4.1" + "@types/jsdom" "^20.0.0" + "@types/node" "*" + jest-mock "^29.4.1" + jest-util "^29.4.1" + jsdom "^20.0.0" + +jest-environment-node@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.4.1.tgz" + integrity sha512-x/H2kdVgxSkxWAIlIh9MfMuBa0hZySmfsC5lCsWmWr6tZySP44ediRKDUiNggX/eHLH7Cd5ZN10Rw+XF5tXsqg== + dependencies: + "@jest/environment" "^29.4.1" + "@jest/fake-timers" "^29.4.1" + "@jest/types" "^29.4.1" + "@types/node" "*" + jest-mock "^29.4.1" + jest-util "^29.4.1" + +jest-get-type@^29.2.0: + version "29.2.0" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz" + integrity sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA== + +jest-haste-map@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.4.1.tgz" + integrity sha512-imTjcgfVVTvg02khXL11NNLTx9ZaofbAWhilrMg/G8dIkp+HYCswhxf0xxJwBkfhWb3e8dwbjuWburvxmcr58w== + dependencies: + "@jest/types" "^29.4.1" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.2.0" + jest-util "^29.4.1" + jest-worker "^29.4.1" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + +jest-leak-detector@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.4.1.tgz" + integrity sha512-akpZv7TPyGMnH2RimOCgy+hPmWZf55EyFUvymQ4LMsQP8xSPlZumCPtXGoDhFNhUE2039RApZkTQDKU79p/FiQ== + dependencies: + jest-get-type "^29.2.0" + pretty-format "^29.4.1" + +jest-matcher-utils@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.4.1.tgz" + integrity sha512-k5h0u8V4nAEy6lSACepxL/rw78FLDkBnXhZVgFneVpnJONhb2DhZj/Gv4eNe+1XqQ5IhgUcqj745UwH0HJmMnA== + dependencies: + chalk "^4.0.0" + jest-diff "^29.4.1" + jest-get-type "^29.2.0" + pretty-format "^29.4.1" + +jest-message-util@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.4.1.tgz" + integrity sha512-H4/I0cXUaLeCw6FM+i4AwCnOwHRgitdaUFOdm49022YD5nfyr8C/DrbXOBEyJaj+w/y0gGJ57klssOaUiLLQGQ== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.4.1" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.4.1" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-29.4.1.tgz" + integrity sha512-MwA4hQ7zBOcgVCVnsM8TzaFLVUD/pFWTfbkY953Y81L5ret3GFRZtmPmRFAjKQSdCKoJvvqOu6Bvfpqlwwb0dQ== + dependencies: + "@jest/types" "^29.4.1" + "@types/node" "*" + jest-util "^29.4.1" + +jest-pnp-resolver@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== + +jest-regex-util@^29.2.0: + version "29.2.0" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.2.0.tgz" + integrity sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA== + +jest-resolve-dependencies@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.4.1.tgz" + integrity sha512-Y3QG3M1ncAMxfjbYgtqNXC5B595zmB6e//p/qpA/58JkQXu/IpLDoLeOa8YoYfsSglBKQQzNUqtfGJJT/qLmJg== + dependencies: + jest-regex-util "^29.2.0" + jest-snapshot "^29.4.1" + +jest-resolve@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.4.1.tgz" + integrity sha512-j/ZFNV2lm9IJ2wmlq1uYK0Y/1PiyDq9g4HEGsNTNr3viRbJdV+8Lf1SXIiLZXFvyiisu0qUyIXGBnw+OKWkJwQ== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.4.1" + jest-pnp-resolver "^1.2.2" + jest-util "^29.4.1" + jest-validate "^29.4.1" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + +jest-runner@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-29.4.1.tgz" + integrity sha512-8d6XXXi7GtHmsHrnaqBKWxjKb166Eyj/ksSaUYdcBK09VbjPwIgWov1VwSmtupCIz8q1Xv4Qkzt/BTo3ZqiCeg== + dependencies: + "@jest/console" "^29.4.1" + "@jest/environment" "^29.4.1" + "@jest/test-result" "^29.4.1" + "@jest/transform" "^29.4.1" + "@jest/types" "^29.4.1" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.2.0" + jest-environment-node "^29.4.1" + jest-haste-map "^29.4.1" + jest-leak-detector "^29.4.1" + jest-message-util "^29.4.1" + jest-resolve "^29.4.1" + jest-runtime "^29.4.1" + jest-util "^29.4.1" + jest-watcher "^29.4.1" + jest-worker "^29.4.1" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.4.1.tgz" + integrity sha512-UXTMU9uKu2GjYwTtoAw5rn4STxWw/nadOfW7v1sx6LaJYa3V/iymdCLQM6xy3+7C6mY8GfX22vKpgxY171UIoA== + dependencies: + "@jest/environment" "^29.4.1" + "@jest/fake-timers" "^29.4.1" + "@jest/globals" "^29.4.1" + "@jest/source-map" "^29.2.0" + "@jest/test-result" "^29.4.1" + "@jest/transform" "^29.4.1" + "@jest/types" "^29.4.1" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.4.1" + jest-message-util "^29.4.1" + jest-mock "^29.4.1" + jest-regex-util "^29.2.0" + jest-resolve "^29.4.1" + jest-snapshot "^29.4.1" + jest-util "^29.4.1" + semver "^7.3.5" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-snapshot@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.4.1.tgz" + integrity sha512-l4iV8EjGgQWVz3ee/LR9sULDk2pCkqb71bjvlqn+qp90lFwpnulHj4ZBT8nm1hA1C5wowXLc7MGnw321u0tsYA== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.4.1" + "@jest/transform" "^29.4.1" + "@jest/types" "^29.4.1" + "@types/babel__traverse" "^7.0.6" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.4.1" + graceful-fs "^4.2.9" + jest-diff "^29.4.1" + jest-get-type "^29.2.0" + jest-haste-map "^29.4.1" + jest-matcher-utils "^29.4.1" + jest-message-util "^29.4.1" + jest-util "^29.4.1" + natural-compare "^1.4.0" + pretty-format "^29.4.1" + semver "^7.3.5" + +jest-util@^29.0.0, jest-util@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.4.1.tgz" + integrity sha512-bQy9FPGxVutgpN4VRc0hk6w7Hx/m6L53QxpDreTZgJd9gfx/AV2MjyPde9tGyZRINAUrSv57p2inGBu2dRLmkQ== + dependencies: + "@jest/types" "^29.4.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-29.4.1.tgz" + integrity sha512-qNZXcZQdIQx4SfUB/atWnI4/I2HUvhz8ajOSYUu40CSmf9U5emil8EDHgE7M+3j9/pavtk3knlZBDsgFvv/SWw== + dependencies: + "@jest/types" "^29.4.1" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.2.0" + leven "^3.1.0" + pretty-format "^29.4.1" + +jest-watcher@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.4.1.tgz" + integrity sha512-vFOzflGFs27nU6h8dpnVRER3O2rFtL+VMEwnG0H3KLHcllLsU8y9DchSh0AL/Rg5nN1/wSiQ+P4ByMGpuybaVw== + dependencies: + "@jest/test-result" "^29.4.1" + "@jest/types" "^29.4.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.4.1" + string-length "^4.0.1" + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest-worker@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.4.1.tgz" + integrity sha512-O9doU/S1EBe+yp/mstQ0VpPwpv0Clgn68TkNwGxL6/usX/KUW9Arnn4ag8C3jc6qHcXznhsT5Na1liYzAsuAbQ== + dependencies: + "@types/node" "*" + jest-util "^29.4.1" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^29.2.0: + version "29.4.1" + resolved "https://registry.npmjs.org/jest/-/jest-29.4.1.tgz" + integrity sha512-cknimw7gAXPDOmj0QqztlxVtBVCw2lYY9CeIE5N6kD+kET1H4H79HSNISJmijb1HF+qk+G+ploJgiDi5k/fRlg== + dependencies: + "@jest/core" "^29.4.1" + "@jest/types" "^29.4.1" + import-local "^3.0.2" + jest-cli "^29.4.1" + +js-sdsl@^4.1.4: + version "4.3.0" + resolved "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz" + integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== + +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsdom@^20.0.0: + version "20.0.3" + resolved "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz" + integrity sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ== + dependencies: + abab "^2.0.6" + acorn "^8.8.1" + acorn-globals "^7.0.0" + cssom "^0.5.0" + cssstyle "^2.3.0" + data-urls "^3.0.2" + decimal.js "^10.4.2" + domexception "^4.0.0" + escodegen "^2.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.2" + parse5 "^7.1.1" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.2" + w3c-xmlserializer "^4.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + ws "^8.11.0" + xml-name-validator "^4.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== + +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + +json5@^2.1.1, json5@^2.2.2, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.3: + version "3.3.3" + resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz" + integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw== + dependencies: + array-includes "^3.1.5" + object.assign "^4.1.3" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +klona@^2.0.5: + version "2.0.6" + resolved "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz" + integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== + +language-subtag-registry@~0.3.2: + version "0.3.22" + resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz" + integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w== + +language-tags@=1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz" + integrity sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ== + dependencies: + language-subtag-registry "~0.3.2" + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lilconfig@^2.0.5, lilconfig@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz" + integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + +lodash.memoize@4.x: + version "4.1.2" + resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +lz-string@^1.4.4: + version "1.4.4" + resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz" + integrity sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ== + +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-error@1.x, make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + +match-sorter@^6.0.2: + version "6.3.1" + resolved "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz" + integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw== + dependencies: + "@babel/runtime" "^7.12.5" + remove-accents "0.4.2" + +md5@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" + integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== + dependencies: + charenc "0.0.2" + crypt "0.0.2" + is-buffer "~1.1.6" + +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" + integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memfs@^3.4.3: + version "3.4.13" + resolved "https://registry.npmjs.org/memfs/-/memfs-3.4.13.tgz" + integrity sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg== + dependencies: + fs-monkey "^1.0.3" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.5" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +microseconds@0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz" + integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA== + +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.7" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + +nano-time@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz" + integrity sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA== + dependencies: + big-integer "^1.6.16" + +nanoid@^3.3.4: + version "3.3.4" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-fetch@2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + +node-forge@^1: + version "1.3.1" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + +node-releases@^2.0.8: + version "2.0.10" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz" + integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +nwsapi@^2.2.2: + version "2.2.2" + resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz" + integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + +object-inspect@^1.12.2, object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +object-is@^1.1.5: + version "1.1.5" + resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.2, object.assign@^4.1.3, object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.entries@^1.1.5, object.entries@^1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz" + integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.fromentries@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz" + integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.hasown@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz" + integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw== + dependencies: + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.values@^1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +oblivious-set@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz" + integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw== + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +open@^8.0.9, open@^8.4.0: + version "8.4.0" + resolved "https://registry.npmjs.org/open/-/open-8.4.0.tgz" + integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2, p-limit@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-retry@^4.5.0: + version "4.6.2" + resolved "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^5.0.0, parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse5@^7.0.0, parse5@^7.1.1: + version "7.1.2" + resolved "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pirates@^4.0.4: + version "4.0.5" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz" + integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkg-up@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz" + integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== + dependencies: + find-up "^3.0.0" + +postcss-import@^14.1.0: + version "14.1.0" + resolved "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz" + integrity sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-js@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz" + integrity sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ== + dependencies: + camelcase-css "^2.0.1" + +postcss-load-config@^3.1.4: + version "3.1.4" + resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz" + integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== + dependencies: + lilconfig "^2.0.5" + yaml "^1.10.2" + +postcss-loader@^7.0.1: + version "7.0.2" + resolved "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.2.tgz" + integrity sha512-fUJzV/QH7NXUAqV8dWJ9Lg4aTkDCezpTS5HgJ2DvqznexTbSTxgi/dTECvTZ15BwKTtk8G/bqI/QTu2HPd3ZCg== + dependencies: + cosmiconfig "^7.0.0" + klona "^2.0.5" + semver "^7.3.8" + +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== + +postcss-modules-local-by-default@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz" + integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-nested@6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz" + integrity sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w== + dependencies: + postcss-selector-parser "^6.0.10" + +postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: + version "6.0.11" + resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz" + integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.4.18, postcss@^8.4.19: + version "8.4.21" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz" + integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.8.3: + version "2.8.3" + resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz" + integrity sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw== + +pretty-error@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz" + integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw== + dependencies: + lodash "^4.17.20" + renderkid "^3.0.0" + +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + +pretty-format@^29.0.0, pretty-format@^29.4.1: + version "29.4.1" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.1.tgz" + integrity sha512-dt/Z761JUVsrIKaY215o1xQJBGlSmTx/h4cSqXqjHLnU1+Kt+mavVE7UgqJJO5ukx5HjSswHfmXz4LjS2oIJfg== + dependencies: + "@jest/schemas" "^29.4.0" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +prompts@^2.0.1: + version "2.4.2" + resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +prop-types@^15.5.0, prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +proxy-compare@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/proxy-compare/-/proxy-compare-2.4.1.tgz#663b45f1212f1a423c8973374fa47542084df02f" + integrity sha512-xkL1upOHOOW6zSSNUDZ09hqGDO9lq//Sg92KF8YK+YsD/Et1dP8fZql/h6vgbFa/ScIAlttE4WjTD7QPEg1Ssg== + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +psl@^1.1.33: + version "1.9.0" + resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + +punycode@^2.1.0, punycode@^2.1.1: + version "2.3.0" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react-dropzone@^14.2.3: + version "14.2.3" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-14.2.3.tgz#0acab68308fda2d54d1273a1e626264e13d4e84b" + integrity sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug== + dependencies: + attr-accept "^2.2.2" + file-selector "^0.6.0" + prop-types "^15.8.1" + +react-i18next@^12.1.5: + version "12.1.5" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-12.1.5.tgz#b65f5733dd2f96188a9359c009b7dbe27443f009" + integrity sha512-7PQAv6DA0TcStG96fle+8RfTwxVbHVlZZJPoEszwUNvDuWpGldJmNWa3ZPesEsZQZGF6GkzwvEh6p57qpFD2gQ== + dependencies: + "@babel/runtime" "^7.20.6" + html-parse-stringify "^3.0.1" + +react-is@^16.13.1: + version "16.13.1" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + +react-query@^3.39.2: + version "3.39.3" + resolved "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz" + integrity sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g== + dependencies: + "@babel/runtime" "^7.5.5" + broadcast-channel "^3.4.1" + match-sorter "^6.0.2" + +react-router-dom@^6.4.2: + version "6.8.1" + resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.8.1.tgz" + integrity sha512-67EXNfkQgf34P7+PSb6VlBuaacGhkKn3kpE51+P6zYSG2kiRoumXEL6e27zTa9+PGF2MNXbgIUHTVlleLbIcHQ== + dependencies: + "@remix-run/router" "1.3.2" + react-router "6.8.1" + +react-router@6.8.1: + version "6.8.1" + resolved "https://registry.npmjs.org/react-router/-/react-router-6.8.1.tgz" + integrity sha512-Jgi8BzAJQ8MkPt8ipXnR73rnD7EmZ0HFFb7jdQU24TynGW1Ooqin2KVDN9voSC+7xhqbbCd2cjGUepb6RObnyg== + dependencies: + "@remix-run/router" "1.3.2" + +react-tabs@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/react-tabs/-/react-tabs-6.0.0.tgz#328fd61ca534e0517d24f4927e37e99b29461880" + integrity sha512-8jKLKrlwxmn5/+xsa76yi27ZdB8E/WhlhQZw739O5UlOeUGtVoVeWnpqIewv/KbjTw7gQf/uA51zWUNt4IVygQ== + dependencies: + clsx "^1.1.0" + prop-types "^15.5.0" + +react@^18.2.0: + version "18.2.0" + resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + +readable-stream@^2.0.1: + version "2.3.7" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6: + version "3.6.0" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.7.0: + version "0.7.1" + resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz" + integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== + dependencies: + resolve "^1.9.0" + +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + +regenerate-unicode-properties@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" + integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.13.11: + version "0.13.11" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + +regenerator-transform@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" + integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== + dependencies: + "@babel/runtime" "^7.8.4" + +regexp.prototype.flags@^1.4.3: + version "1.4.3" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz" + integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + functions-have-names "^1.2.2" + +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== + dependencies: + "@babel/regjsgen" "^0.8.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" + integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== + dependencies: + jsesc "~0.5.0" + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + +remove-accents@0.4.2: + version "0.4.2" + resolved "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz" + integrity sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA== + +renderkid@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz" + integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg== + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^6.0.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +reselect@^4.1.7: + version "4.1.7" + resolved "https://registry.npmjs.org/reselect/-/reselect-4.1.7.tgz" + integrity sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve.exports@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.0.tgz" + integrity sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg== + +resolve@^1.1.7, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.9.0: + version "1.22.1" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^2.0.0-next.4: + version "2.0.0-next.4" + resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz" + integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@3.0.2, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" + +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + +schema-utils@^3.1.0, schema-utils@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz" + integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz" + integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.8.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.0.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz" + integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + +selfsigned@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz" + integrity sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ== + dependencies: + node-forge "^1" + +semver@7.x, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: + version "7.3.8" + resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + dependencies: + lru-cache "^6.0.0" + +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +send@0.18.0: + version "0.18.0" + resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz" + integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + dependencies: + randombytes "^2.1.0" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz" + integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string.prototype.matchall@^4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz" + integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + regexp.prototype.flags "^1.4.3" + side-channel "^1.0.4" + +string.prototype.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +style-loader@^3.3.1: + version "3.3.1" + resolved "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz" + integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svg-parser@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" + integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== + +svgo@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" + integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^4.1.3" + css-tree "^1.1.3" + csso "^4.2.0" + picocolors "^1.0.0" + stable "^0.1.8" + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +synckit@^0.8.4: + version "0.8.5" + resolved "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz" + integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== + dependencies: + "@pkgr/utils" "^2.3.1" + tslib "^2.5.0" + +tailwindcss@^3.1.8: + version "3.2.4" + resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz" + integrity sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ== + dependencies: + arg "^5.0.2" + chokidar "^3.5.3" + color-name "^1.1.4" + detective "^5.2.1" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.2.12" + glob-parent "^6.0.2" + is-glob "^4.0.3" + lilconfig "^2.0.6" + micromatch "^4.0.5" + normalize-path "^3.0.0" + object-hash "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.18" + postcss-import "^14.1.0" + postcss-js "^4.0.0" + postcss-load-config "^3.1.4" + postcss-nested "6.0.0" + postcss-selector-parser "^6.0.10" + postcss-value-parser "^4.2.0" + quick-lru "^5.1.1" + resolve "^1.22.1" + +tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.1.3: + version "5.3.6" + resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz" + integrity sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ== + dependencies: + "@jridgewell/trace-mapping" "^0.3.14" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.0" + terser "^5.14.1" + +terser@^5.10.0, terser@^5.14.1: + version "5.16.3" + resolved "https://registry.npmjs.org/terser/-/terser-5.16.3.tgz" + integrity sha512-v8wWLaS/xt3nE9dgKEWhNUFP6q4kngO5B8eYFUuebsu7Dw/UNAnpUod6UHo04jSSkv8TzKHjZDSd7EXdDQAl8Q== + dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" + commander "^2.20.0" + source-map-support "~0.5.20" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +tiny-glob@^0.2.9: + version "0.2.9" + resolved "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz" + integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg== + dependencies: + globalyzer "0.1.0" + globrex "^0.1.2" + +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tough-cookie@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz" + integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + +tr46@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz" + integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== + dependencies: + punycode "^2.1.1" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +ts-jest@^29.0.3: + version "29.0.5" + resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.5.tgz" + integrity sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "4.x" + make-error "1.x" + semver "7.x" + yargs-parser "^21.0.1" + +ts-loader@^9.4.1: + version "9.4.2" + resolved "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz" + integrity sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.0.0" + micromatch "^4.0.0" + semver "^7.3.4" + +ts-node@^10.9.1: + version "10.9.1" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +tsconfig-paths@^3.14.1: + version "3.14.1" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz" + integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.0.3, tslib@^2.4.0, tslib@^2.5.0: + version "2.5.0" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + +typescript@^4.8.4: + version "4.9.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== + +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + +unload@2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz" + integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA== + dependencies: + "@babel/runtime" "^7.6.2" + detect-node "^2.0.4" + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.0.10: + version "1.0.10" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +use-sync-external-store@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utila@~0.4: + version "0.4.0" + resolved "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz" + integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +v8-to-istanbul@^9.0.1: + version "9.0.1" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz" + integrity sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + +valtio@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/valtio/-/valtio-1.10.1.tgz#0339bb17422aa4d64f522ffec747cd07c0ac4aed" + integrity sha512-VhK/adrsgQsInQ8SvcUV0NYu2XyPItAAWhAX1uhmN0tS0wIroqjUNhb0OG8iEqu6khUdfMsR7s+Q91hZT6pjcg== + dependencies: + proxy-compare "2.4.1" + use-sync-external-store "1.2.0" + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +void-elements@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== + +w3c-xmlserializer@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz" + integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== + dependencies: + xml-name-validator "^4.0.0" + +walker@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + +webpack-cli@^4.10.0: + version "4.10.0" + resolved "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz" + integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^1.2.0" + "@webpack-cli/info" "^1.5.0" + "@webpack-cli/serve" "^1.7.0" + colorette "^2.0.14" + commander "^7.0.0" + cross-spawn "^7.0.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^2.2.0" + rechoir "^0.7.0" + webpack-merge "^5.7.3" + +webpack-dev-middleware@^5.3.1: + version "5.3.3" + resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz" + integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== + dependencies: + colorette "^2.0.10" + memfs "^3.4.3" + mime-types "^2.1.31" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@^4.11.1: + version "4.11.1" + resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz" + integrity sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw== + dependencies: + "@types/bonjour" "^3.5.9" + "@types/connect-history-api-fallback" "^1.3.5" + "@types/express" "^4.17.13" + "@types/serve-index" "^1.9.1" + "@types/serve-static" "^1.13.10" + "@types/sockjs" "^0.3.33" + "@types/ws" "^8.5.1" + ansi-html-community "^0.0.8" + bonjour-service "^1.0.11" + chokidar "^3.5.3" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + default-gateway "^6.0.3" + express "^4.17.3" + graceful-fs "^4.2.6" + html-entities "^2.3.2" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.0.1" + open "^8.0.9" + p-retry "^4.5.0" + rimraf "^3.0.2" + schema-utils "^4.0.0" + selfsigned "^2.1.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^5.3.1" + ws "^8.4.2" + +webpack-merge@^5.7.3, webpack-merge@^5.8.0: + version "5.8.0" + resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz" + integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + dependencies: + clone-deep "^4.0.1" + wildcard "^2.0.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@^5.74.0: + version "5.75.0" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz" + integrity sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^0.0.51" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + acorn "^8.7.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.10.0" + es-module-lexer "^0.9.0" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.3" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^11.0.0: + version "11.0.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz" + integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + +which-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz" + integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== + +word-wrap@^1.2.3, word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.0.tgz" + integrity sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + +ws@^8.11.0, ws@^8.4.2: + version "8.12.0" + resolved "https://registry.npmjs.org/ws/-/ws-8.12.0.tgz" + integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig== + +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +xtend@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.10.0, yaml@^1.10.2: + version "1.10.2" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yargs-parser@^21.0.1, yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.3.1: + version "17.6.2" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz" + integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 942ee851f1708f6950b0fe1d5a661930a0bdbfc4 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 22 Mar 2023 17:33:51 +0500 Subject: [PATCH 002/145] fix(builder): validate doc server settings --- backend/services/builder/web/handler/build.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/backend/services/builder/web/handler/build.go b/backend/services/builder/web/handler/build.go index 57d1f0b..439c64b 100644 --- a/backend/services/builder/web/handler/build.go +++ b/backend/services/builder/web/handler/build.go @@ -2,6 +2,7 @@ package handler import ( "context" + "errors" "fmt" "path/filepath" "strings" @@ -20,6 +21,8 @@ import ( "golang.org/x/sync/singleflight" ) +var _ErrNoSettingsFound = errors.New("could not find document server settings") + type ConfigHandler struct { namespace string logger plog.Logger @@ -86,6 +89,12 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui return } + if docs.DocAddress == "" || docs.DocSecret == "" { + c.logger.Debugf("no settings found") + errorsChan <- _ErrNoSettingsFound + return + } + c.logger.Debugf("populating document server %d settings channel", req.CID) settingsChan <- docs c.logger.Debugf("successfully populated document server settings channel") From 98da2f1288bd0ab02734b8023828dac5a4b63f30 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 22 Mar 2023 17:34:19 +0500 Subject: [PATCH 003/145] fix(page): validate doc address --- frontend/src/pages/Settings/index.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx index 4b2bb8d..a923268 100644 --- a/frontend/src/pages/Settings/index.tsx +++ b/frontend/src/pages/Settings/index.tsx @@ -54,7 +54,12 @@ export const SettingsPage: React.FC = () => { if (address && secret) { try { setSaving(true); - await postSettings(sdk, address, secret); + if (!address.endsWith("/")) { + await postSettings(sdk, `${address}/`, secret); + setAddress(`${address}/`); + } else { + await postSettings(sdk, address, secret); + } await sdk.execute(Command.SHOW_SNACKBAR, { message: "ONLYOFFICE settings have been saved", }); From ce3e9ed821a4b7b110257d3f1fe01e70ee967fd0 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 23 Mar 2023 13:23:22 +0500 Subject: [PATCH 004/145] fix(editor): open editor in a new tab --- frontend/src/App.tsx | 30 ++++++++++------ frontend/src/context/PipedriveContext.tsx | 4 --- frontend/src/context/TokenContext.tsx | 44 ++++++++++++----------- frontend/src/hooks/useBuildConfig.tsx | 7 ++-- frontend/src/index.tsx | 10 ++---- frontend/src/pages/Creation/Creation.tsx | 37 ++++++++++--------- frontend/src/pages/Creation/Upload.tsx | 18 ++++++---- frontend/src/pages/Creation/index.tsx | 18 +++++----- frontend/src/pages/Editor/index.tsx | 35 ++++++------------ frontend/src/pages/Main/Actions.tsx | 41 +++++++++++---------- frontend/src/pages/Main/Main.tsx | 21 +++++++---- frontend/src/pages/Settings/index.tsx | 18 +++++++--- frontend/src/services/config.ts | 6 ++-- 13 files changed, 150 insertions(+), 139 deletions(-) delete mode 100644 frontend/src/context/PipedriveContext.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 50b1d2e..e68c4c0 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -13,6 +13,7 @@ import { SettingsPage } from "@pages/Settings"; import { OnlyofficeEditorPage } from "@pages/Editor"; import { OnlyofficeSpinner } from "@components/spinner"; +import { TokenProvider } from "@context/TokenContext"; const CenteredOnlyofficeSpinner = () => (
@@ -29,7 +30,9 @@ const LazyRoutes: React.FC = () => { index element={ }> - + + + } /> @@ -37,15 +40,9 @@ const LazyRoutes: React.FC = () => { path="create" element={ }> - - - } - /> - }> - + + + } /> @@ -53,11 +50,22 @@ const LazyRoutes: React.FC = () => { path="settings" element={ }> - + + + } /> + }> + + + } + /> + ASD
} /> } /> ); diff --git a/frontend/src/context/PipedriveContext.tsx b/frontend/src/context/PipedriveContext.tsx deleted file mode 100644 index a0e138c..0000000 --- a/frontend/src/context/PipedriveContext.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import AppExtensionsSDK from "@pipedrive/app-extensions-sdk"; -import { proxy } from "valtio"; - -export const PipedriveSDK = proxy({ sdk: new AppExtensionsSDK().initialize() }); diff --git a/frontend/src/context/TokenContext.tsx b/frontend/src/context/TokenContext.tsx index 8183bc7..9a01c0f 100644 --- a/frontend/src/context/TokenContext.tsx +++ b/frontend/src/context/TokenContext.tsx @@ -1,10 +1,9 @@ +import AppExtensionsSDK from "@pipedrive/app-extensions-sdk"; import React, { useEffect } from "react"; -import { proxy, useSnapshot } from "valtio"; +import { proxy } from "valtio"; import { getMe } from "@services/me"; -import { PipedriveSDK } from "./PipedriveContext"; - export const AuthToken = proxy({ access_token: "", expires_at: Date.now(), @@ -18,25 +17,30 @@ type ProviderProps = { const TokenContext = React.createContext(true); export const TokenProvider: React.FC = ({ children }) => { - const { sdk } = useSnapshot(PipedriveSDK); useEffect(() => { - let timerID = setTimeout(async function update() { - if ( - !AuthToken.error && - (!AuthToken.access_token || AuthToken.expires_at <= Date.now() - 1) - ) { - try { - const token = await getMe(sdk); - AuthToken.access_token = token.response.access_token; - AuthToken.expires_at = token.response.expires_at; - } catch { - AuthToken.error = true; - } - } - timerID = setTimeout(update, 1000); - }, 1000); + let timerID: NodeJS.Timeout; + new AppExtensionsSDK() + .initialize() + .then((sdk) => { + timerID = setTimeout(async function update() { + if ( + !AuthToken.error && + (!AuthToken.access_token || AuthToken.expires_at <= Date.now() - 1) + ) { + try { + const token = await getMe(sdk); + AuthToken.access_token = token.response.access_token; + AuthToken.expires_at = token.response.expires_at; + } catch { + AuthToken.error = true; + } + } + timerID = setTimeout(update, 1000); + }, 1000); + }) + .catch(() => null); return () => clearTimeout(timerID); - }, [sdk]); + }, []); return {children}; }; diff --git a/frontend/src/hooks/useBuildConfig.tsx b/frontend/src/hooks/useBuildConfig.tsx index f26be2f..ca7f9cf 100644 --- a/frontend/src/hooks/useBuildConfig.tsx +++ b/frontend/src/hooks/useBuildConfig.tsx @@ -1,20 +1,17 @@ import { useQuery } from "react-query"; -import { useSnapshot } from "valtio"; import { fetchConfig } from "@services/config"; -import { PipedriveSDK } from "@context/PipedriveContext"; - export function useBuildConfig( + token: string, id: string, name: string, key: string, dealID: string ) { - const { sdk } = useSnapshot(PipedriveSDK); const { isLoading, error, data } = useQuery({ queryKey: ["config", id], - queryFn: ({ signal }) => fetchConfig(sdk, id, name, key, dealID, signal), + queryFn: ({ signal }) => fetchConfig(token, id, name, key, dealID, signal), staleTime: 0, cacheTime: 0, refetchOnWindowFocus: false, diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 8772934..3b2a077 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -2,8 +2,6 @@ import React from "react"; import ReactDOM from "react-dom/client"; import { QueryClient, QueryClientProvider } from "react-query"; -import { TokenProvider } from "@context/TokenContext"; - import App from "./App"; // import "./i18n"; @@ -16,10 +14,8 @@ const root = ReactDOM.createRoot( const queryClient = new QueryClient(); root.render( - - - - - + + + ); diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx index a05f8bb..7c65f43 100644 --- a/frontend/src/pages/Creation/Creation.tsx +++ b/frontend/src/pages/Creation/Creation.tsx @@ -1,8 +1,6 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import md5 from "md5"; -import { useSnapshot } from "valtio"; -import { useNavigate } from "react-router-dom"; -import { Command } from "@pipedrive/app-extensions-sdk"; +import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; import { OnlyofficeButton } from "@components/button"; import { OnlyofficeInput } from "@components/input"; @@ -11,20 +9,24 @@ import { OnlyofficeTitle } from "@components/title"; import { uploadFile } from "@services/file"; -import { PipedriveSDK } from "@context/PipedriveContext"; - import { getFileIcon, getMimeType } from "@utils/file"; import { getCurrentURL } from "@utils/url"; export const Creation: React.FC = () => { - const navigate = useNavigate(); - const { sdk } = useSnapshot(PipedriveSDK); + const [sdk, setSDK] = useState(); const [file, setFile] = useState("New Document"); const [fileType, setFileType] = useState<"docx" | "pptx" | "xlsx">("docx"); const handleChangeFile = (newType: "docx" | "pptx" | "xlsx") => { setFileType(newType); }; + useEffect(() => { + new AppExtensionsSDK() + .initialize() + .then((s) => setSDK(s)) + .catch(() => setSDK(null)); + }, []); + return (
@@ -78,7 +80,7 @@ export const Creation: React.FC = () => { { - await sdk.execute(Command.CLOSE_MODAL); + await sdk?.execute(Command.CLOSE_MODAL); }} />
@@ -87,6 +89,8 @@ export const Creation: React.FC = () => { text="Create document" primary onClick={async () => { + const token = await sdk?.execute(Command.GET_SIGNED_TOKEN); + if (!token) return; const { url, parameters } = getCurrentURL(); const filename = `${file}.${fileType}`; const binary = new File([], filename, { @@ -98,16 +102,15 @@ export const Creation: React.FC = () => { parameters.get("selectedIds") || "", binary ); - navigate( - `/editor?data=${JSON.stringify({ - id: res.data.id, - deal_id: res.data.deal_id, - name: res.data.name, - key: md5(res.data.id + res.data.update_time), - })}` + window.open( + `/editor?token=${token.token}&id=${res.data.id}&deal_id=${ + res.data.deal_id + }&name=${res.data.name}&key=${md5( + res.data.id + res.data.update_time + )}` ); } catch { - await sdk.execute(Command.SHOW_SNACKBAR, { + await sdk?.execute(Command.SHOW_SNACKBAR, { message: "Could not create a new file", }); } diff --git a/frontend/src/pages/Creation/Upload.tsx b/frontend/src/pages/Creation/Upload.tsx index 5d00246..7b3715c 100644 --- a/frontend/src/pages/Creation/Upload.tsx +++ b/frontend/src/pages/Creation/Upload.tsx @@ -1,13 +1,10 @@ import { FileRejection, DropEvent } from "react-dropzone"; -import React from "react"; -import { useSnapshot } from "valtio"; -import { Command } from "@pipedrive/app-extensions-sdk"; +import React, { useEffect, useState } from "react"; +import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; import { OnlyofficeButton } from "@components/button"; import { OnlyofficeDragDrop } from "@components/drop"; -import { PipedriveSDK } from "@context/PipedriveContext"; - import { uploadFile } from "@services/file"; import { getCurrentURL } from "@utils/url"; @@ -26,7 +23,14 @@ const onDrop = ( }; export const Upload: React.FC = () => { - const { sdk } = useSnapshot(PipedriveSDK); + const [sdk, setSDK] = useState(); + useEffect(() => { + new AppExtensionsSDK() + .initialize() + .then((s) => setSDK(s)) + .catch(() => setSDK(null)); + }, []); + return (
@@ -43,7 +47,7 @@ export const Upload: React.FC = () => { { - await sdk.execute(Command.CLOSE_MODAL); + await sdk?.execute(Command.CLOSE_MODAL); }} />
diff --git a/frontend/src/pages/Creation/index.tsx b/frontend/src/pages/Creation/index.tsx index 839b7bb..8228e8b 100644 --- a/frontend/src/pages/Creation/index.tsx +++ b/frontend/src/pages/Creation/index.tsx @@ -1,19 +1,19 @@ +import React, { useEffect, useState } from "react"; +import AppExtensionsSDK from "@pipedrive/app-extensions-sdk"; import { Tabs, TabList, Tab, TabPanel } from "react-tabs"; -import React, { useState } from "react"; -import { useSnapshot } from "valtio"; -import { Command } from "@pipedrive/app-extensions-sdk"; - -import { PipedriveSDK } from "@context/PipedriveContext"; import { Creation } from "./Creation"; import { Upload } from "./Upload"; export const CreatePage: React.FC = () => { - const { sdk } = useSnapshot(PipedriveSDK); const [selected, setSelected] = useState(0); - sdk.execute(Command.RESIZE, { - height: 500, - width: 622, + useEffect(() => { + new AppExtensionsSDK().initialize({ + size: { + height: 500, + width: 622, + }, + }); }); return ( diff --git a/frontend/src/pages/Editor/index.tsx b/frontend/src/pages/Editor/index.tsx index 2a6c265..f010406 100644 --- a/frontend/src/pages/Editor/index.tsx +++ b/frontend/src/pages/Editor/index.tsx @@ -1,8 +1,6 @@ -import React, { useEffect } from "react"; +import React from "react"; import { useSearchParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import { useSnapshot } from "valtio"; -import { Command } from "@pipedrive/app-extensions-sdk"; import { DocumentEditor } from "@onlyoffice/document-editor-react"; import { OnlyofficeButton } from "@components/button"; @@ -10,7 +8,6 @@ import { OnlyofficeError } from "@components/error"; import { OnlyofficeSpinner } from "@components/spinner"; import { useBuildConfig } from "@hooks/useBuildConfig"; -import { PipedriveSDK } from "@context/PipedriveContext"; import Icon from "@assets/nofile.svg"; @@ -30,27 +27,15 @@ const onEditor = () => { export const OnlyofficeEditorPage: React.FC = () => { const [params] = useSearchParams(); - const pData = JSON.parse(params.get("data") || "{}"); - const { sdk } = useSnapshot(PipedriveSDK); const { t } = useTranslation(); const { isLoading, error, data } = useBuildConfig( - pData.id || "", - pData.name || "new.docx", - pData.key || new Date().toString(), - pData.deal_id || "1" + params.get("token") || "", + params.get("id") || "", + params.get("name") || "new.docx", + params.get("key") || new Date().toTimeString(), + params.get("deal_id") || "1" ); - useEffect(() => { - if (sdk) { - (async () => { - await sdk.execute(Command.RESIZE, { - height: 500, - width: 700, - }); - })(); - } - }, [sdk]); - const validConfig = !error && !isLoading && data; return (
@@ -67,7 +52,7 @@ export const OnlyofficeEditorPage: React.FC = () => { primary text="Cancel" fullWidth - onClick={() => sdk.execute(Command.CLOSE_MODAL)} + onClick={() => window.close()} />
@@ -84,8 +69,8 @@ export const OnlyofficeEditorPage: React.FC = () => {
sdk.execute(Command.CLOSE_MODAL)} + text={t("button.close") || "Close"} + onClick={() => window.close()} />
@@ -122,7 +107,7 @@ export const OnlyofficeEditorPage: React.FC = () => { type: data.type, events: { onRequestClose: async () => { - await sdk.execute(Command.CLOSE_MODAL); + window.close(); }, onAppReady: onEditor, onError: () => { diff --git a/frontend/src/pages/Main/Actions.tsx b/frontend/src/pages/Main/Actions.tsx index fc3b9db..f0630a8 100644 --- a/frontend/src/pages/Main/Actions.tsx +++ b/frontend/src/pages/Main/Actions.tsx @@ -1,15 +1,12 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import md5 from "md5"; -import { useSnapshot } from "valtio"; -import { Command, Modal } from "@pipedrive/app-extensions-sdk"; +import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; import { useDeleteFile } from "@hooks/useDeleteFile"; import { isFileSupported } from "@utils/file"; import { getCurrentURL } from "@utils/url"; -import { PipedriveSDK } from "@context/PipedriveContext"; - import { File } from "src/types/file"; import Pencil from "@assets/pencil.svg"; @@ -20,35 +17,43 @@ type FileActionsProps = { }; export const OnlyofficeFileActions: React.FC = ({ file }) => { - const { sdk } = useSnapshot(PipedriveSDK); const { url, parameters } = getCurrentURL(); + const [sdk, setSDK] = useState(); const mutator = useDeleteFile(`${url}api/v1/files/${file.id}`); + + useEffect(() => { + new AppExtensionsSDK() + .initialize() + .then((s) => setSDK(s)) + .catch(() => setSDK(null)); + }, []); + const handleDelete = () => { mutator .mutateAsync() .then(async () => { - await sdk.execute(Command.SHOW_SNACKBAR, { + await sdk?.execute(Command.SHOW_SNACKBAR, { message: `File ${file.name} has been removed`, }); }) .catch(async () => { - await sdk.execute(Command.SHOW_SNACKBAR, { + await sdk?.execute(Command.SHOW_SNACKBAR, { message: `Could not remove file ${file.name}`, }); }); }; const handleEditor = async () => { - await sdk.execute(Command.OPEN_MODAL, { - type: Modal.CUSTOM_MODAL, - action_id: process.env.PIPEDRIVE_EDITOR_MODAL_ID || "", - data: { - deal_id: parameters.get("selectedIds") || "", - id: file.id, - name: file.name, - key: md5(file.id + file.update_time), - }, - }); + const token = await sdk?.execute(Command.GET_SIGNED_TOKEN); + if (token) { + window.open( + `/editor?token=${token.token}&deal_id=${ + parameters.get("selectedIds") || "1" + }&id=${file.id}&name=${file.name}&key=${md5( + file.id + file.update_time + )}` + ); + } }; return ( diff --git a/frontend/src/pages/Main/Main.tsx b/frontend/src/pages/Main/Main.tsx index 1517a2e..a69ef93 100644 --- a/frontend/src/pages/Main/Main.tsx +++ b/frontend/src/pages/Main/Main.tsx @@ -1,6 +1,8 @@ -import React, { useCallback, useRef } from "react"; -import { useSnapshot } from "valtio"; -import { Command, Modal } from "@pipedrive/app-extensions-sdk"; +import React, { useCallback, useEffect, useRef, useState } from "react"; +import AppExtensionsSDK, { + Command, + Modal, +} from "@pipedrive/app-extensions-sdk"; import { OnlyofficeButton } from "@components/button"; import { OnlyofficeFile } from "@components/file"; @@ -10,16 +12,14 @@ import { OnlyofficeSpinner } from "@components/spinner"; import { useFileSearch } from "@hooks/useFileSearch"; -import { PipedriveSDK } from "@context/PipedriveContext"; - import { formatBytes, getFileIcon, isFileSupported } from "@utils/file"; import { getCurrentURL } from "@utils/url"; import { OnlyofficeFileActions } from "./Actions"; export const Main: React.FC = () => { - const { sdk } = useSnapshot(PipedriveSDK); const { url, parameters } = getCurrentURL(); + const [sdk, setSDK] = useState(); const { isLoading, fetchNextPage, isFetchingNextPage, files, hasNextPage } = useFileSearch( `${url}api/v1/deals/${parameters.get("selectedIds")}/files`, @@ -40,6 +40,13 @@ export const Main: React.FC = () => { [isLoading, fetchNextPage, hasNextPage] ); + useEffect(() => { + new AppExtensionsSDK() + .initialize() + .then((s) => setSDK(s)) + .catch(() => setSDK(null)); + }, []); + return (
@@ -116,7 +123,7 @@ export const Main: React.FC = () => { fullWidth primary onClick={async () => { - await sdk.execute(Command.OPEN_MODAL, { + await sdk?.execute(Command.OPEN_MODAL, { type: Modal.CUSTOM_MODAL, action_id: process.env.PIPEDRIVE_CREATE_MODAL_ID || "", }); diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx index a923268..0f313f8 100644 --- a/frontend/src/pages/Settings/index.tsx +++ b/frontend/src/pages/Settings/index.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { Command } from "@pipedrive/app-extensions-sdk"; +import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; import { useSnapshot } from "valtio"; import { OnlyofficeButton } from "@components/button"; @@ -11,11 +11,10 @@ import { OnlyofficeBackgroundError } from "@layouts/ErrorBackground"; import { postSettings, getSettings } from "@services/settings"; import { getPipedriveMe } from "@services/me"; -import { PipedriveSDK } from "@context/PipedriveContext"; import { AuthToken } from "@context/TokenContext"; export const SettingsPage: React.FC = () => { - const { sdk } = useSnapshot(PipedriveSDK); + const [sdk, setSDK] = useState(); const { access_token: accessToken, error } = useSnapshot(AuthToken); const [admin, setAdmin] = useState(false); const [loading, setLoading] = useState(true); @@ -24,7 +23,16 @@ export const SettingsPage: React.FC = () => { const [saving, setSaving] = useState(false); useEffect(() => { - if (accessToken && !error) { + new AppExtensionsSDK() + .initialize() + .then((s) => { + setSDK(s); + }) + .catch(() => setSDK(null)); + }, []); + + useEffect(() => { + if (accessToken && !error && !!sdk) { getPipedriveMe( `${window.parent[0].location.ancestorOrigins[0]}/api/v1/users/me` ) @@ -51,7 +59,7 @@ export const SettingsPage: React.FC = () => { }, [sdk, accessToken, error]); const handleSettings = async () => { - if (address && secret) { + if (address && secret && sdk) { try { setSaving(true); if (!address.endsWith("/")) { diff --git a/frontend/src/services/config.ts b/frontend/src/services/config.ts index bc0e416..7b354fa 100644 --- a/frontend/src/services/config.ts +++ b/frontend/src/services/config.ts @@ -1,18 +1,16 @@ -import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; import axios from "axios"; import axiosRetry from "axios-retry"; import { ConfigResponse } from "src/types/config"; export const fetchConfig = async ( - sdk: AppExtensionsSDK, + token: string, id: string, name: string, key: string, dealID: string, signal?: AbortSignal ) => { - const pctx = await sdk.execute(Command.GET_SIGNED_TOKEN); const client = axios.create(); axiosRetry(client, { retries: 2, @@ -29,7 +27,7 @@ export const fetchConfig = async ( }, headers: { "Content-Type": "application/json", - "X-Pipedrive-App-Context": pctx.token, + "X-Pipedrive-App-Context": token, }, signal, }); From c4a35629607e9440309a7f311638c099dbf8e601 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 23 Mar 2023 13:39:02 +0500 Subject: [PATCH 005/145] fix(modal): bottom action bar background color --- frontend/src/pages/Creation/Creation.tsx | 12 +++++++----- frontend/src/pages/Creation/Upload.tsx | 5 +---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx index 7c65f43..22561b3 100644 --- a/frontend/src/pages/Creation/Creation.tsx +++ b/frontend/src/pages/Creation/Creation.tsx @@ -22,7 +22,12 @@ export const Creation: React.FC = () => { useEffect(() => { new AppExtensionsSDK() - .initialize() + .initialize({ + size: { + height: 500, + width: 600, + }, + }) .then((s) => setSDK(s)) .catch(() => setSDK(null)); }, []); @@ -71,10 +76,7 @@ export const Creation: React.FC = () => {
-
+
{
-
+
Date: Thu, 23 Mar 2023 16:16:47 +0500 Subject: [PATCH 006/145] fix(files): proper created by value --- frontend/src/hooks/useUserSearch.tsx | 14 +++++++++++ frontend/src/pages/Main/Main.tsx | 35 +++++++++++++++++++++------- frontend/src/services/user.ts | 22 +++++++++++++++++ frontend/src/types/file.ts | 1 + frontend/src/types/user.ts | 11 +++++++++ 5 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 frontend/src/hooks/useUserSearch.tsx create mode 100644 frontend/src/services/user.ts diff --git a/frontend/src/hooks/useUserSearch.tsx b/frontend/src/hooks/useUserSearch.tsx new file mode 100644 index 0000000..d1f1989 --- /dev/null +++ b/frontend/src/hooks/useUserSearch.tsx @@ -0,0 +1,14 @@ +import { useQuery } from "react-query"; +import { fetchUsers } from "@services/user"; + +export function useUserSearch(url: string) { + const { isLoading, error, data } = useQuery({ + queryKey: ["users"], + queryFn: ({ signal }) => fetchUsers(url, signal), + staleTime: 30000, + cacheTime: 30000, + refetchOnWindowFocus: false, + }); + + return { isLoading, error, data }; +} diff --git a/frontend/src/pages/Main/Main.tsx b/frontend/src/pages/Main/Main.tsx index a69ef93..7fcac30 100644 --- a/frontend/src/pages/Main/Main.tsx +++ b/frontend/src/pages/Main/Main.tsx @@ -15,20 +15,30 @@ import { useFileSearch } from "@hooks/useFileSearch"; import { formatBytes, getFileIcon, isFileSupported } from "@utils/file"; import { getCurrentURL } from "@utils/url"; +import { useUserSearch } from "@hooks/useUserSearch"; import { OnlyofficeFileActions } from "./Actions"; export const Main: React.FC = () => { const { url, parameters } = getCurrentURL(); const [sdk, setSDK] = useState(); - const { isLoading, fetchNextPage, isFetchingNextPage, files, hasNextPage } = - useFileSearch( - `${url}api/v1/deals/${parameters.get("selectedIds")}/files`, - 20 - ); + const { + isLoading: filesLoading, + fetchNextPage, + isFetchingNextPage, + files, + hasNextPage, + } = useFileSearch( + `${url}api/v1/deals/${parameters.get("selectedIds")}/files`, + 20 + ); + const { isLoading: usersLoading, data: userData } = useUserSearch( + `${url}api/v1/users` + ); + const observer = useRef(); const lastItem = useCallback( (node: Element | null) => { - if (isLoading) return; + if (filesLoading || usersLoading) return; if (observer.current) observer.current.disconnect(); observer.current = new IntersectionObserver(async (entries) => { if (entries[0].isIntersecting && hasNextPage) { @@ -37,7 +47,7 @@ export const Main: React.FC = () => { }); if (node) observer.current.observe(node); }, - [isLoading, fetchNextPage, hasNextPage] + [filesLoading, usersLoading, fetchNextPage, hasNextPage] ); useEffect(() => { @@ -47,6 +57,7 @@ export const Main: React.FC = () => { .catch(() => setSDK(null)); }, []); + const isLoading = filesLoading && usersLoading; return (
@@ -73,7 +84,10 @@ export const Main: React.FC = () => { > u.id === file.user_id + )?.name || "Anonymous", Workspace: file.remote_location, Type: file.file_type, "Date modified": file.update_time, @@ -95,7 +109,10 @@ export const Main: React.FC = () => { > u.id === file.user_id + )?.name || "Anonymous", Workspace: file.remote_location, Type: file.file_type, "Date modified": file.update_time, diff --git a/frontend/src/services/user.ts b/frontend/src/services/user.ts new file mode 100644 index 0000000..99fd92d --- /dev/null +++ b/frontend/src/services/user.ts @@ -0,0 +1,22 @@ +import { AuthToken } from "@context/TokenContext"; +import axios from "axios"; +import { PipedriveSearchUsersResponse } from "src/types/user"; + +export const fetchUsers = async ( + url: string, + signal: AbortSignal | undefined = undefined +) => { + const res = await axios({ + method: "GET", + url, + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${AuthToken.access_token}`, + }, + signal, + }); + + return { + response: res.data, + }; +}; diff --git a/frontend/src/types/file.ts b/frontend/src/types/file.ts index 0a964b4..0b4af22 100644 --- a/frontend/src/types/file.ts +++ b/frontend/src/types/file.ts @@ -1,5 +1,6 @@ export type File = { id: string; + user_id: number; name: string; file_size: number; file_type: string; diff --git a/frontend/src/types/user.ts b/frontend/src/types/user.ts index a6fa7a9..a69a37b 100644 --- a/frontend/src/types/user.ts +++ b/frontend/src/types/user.ts @@ -9,11 +9,22 @@ type PipedriveAccess = { admin: boolean; }; +type PipedriveUser = { + id: number; + name: string; +} + export type PipedriveUserResponse = { success: boolean; data: { id: number; + name: string; access: PipedriveAccess[]; active_flag: true; }; }; + +export type PipedriveSearchUsersResponse = { + success: boolean; + data: PipedriveUser[]; +}; From 7e6813a3ad9b9f24bad0207cf4a32e2b547b3e7b Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 23 Mar 2023 16:58:37 +0500 Subject: [PATCH 007/145] fix(pages): creation page layout --- frontend/src/assets/nofile copy.svg | 37 ----------------------- frontend/src/assets/redirect.svg | 3 ++ frontend/src/components/button/Button.tsx | 7 +++-- frontend/src/components/tile/Tile.tsx | 2 +- frontend/src/pages/Creation/Creation.tsx | 3 ++ 5 files changed, 12 insertions(+), 40 deletions(-) delete mode 100644 frontend/src/assets/nofile copy.svg create mode 100644 frontend/src/assets/redirect.svg diff --git a/frontend/src/assets/nofile copy.svg b/frontend/src/assets/nofile copy.svg deleted file mode 100644 index 8e21b29..0000000 --- a/frontend/src/assets/nofile copy.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/redirect.svg b/frontend/src/assets/redirect.svg new file mode 100644 index 0000000..d800559 --- /dev/null +++ b/frontend/src/assets/redirect.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/components/button/Button.tsx b/frontend/src/components/button/Button.tsx index bde872d..19842ee 100644 --- a/frontend/src/components/button/Button.tsx +++ b/frontend/src/components/button/Button.tsx @@ -6,6 +6,7 @@ type ButtonProps = { disabled?: boolean; primary?: boolean; fullWidth?: boolean; + Icon?: React.ReactElement; onClick?: React.MouseEventHandler; }; @@ -14,11 +15,12 @@ export const OnlyofficeButton: React.FC = ({ disabled = false, primary = false, fullWidth = false, + Icon, onClick, }) => { const classes = cx({ "hover:shadow-lg duration-200": !disabled, - "bg-green-600 text-slate-200": primary, + "bg-green-700 text-white": primary, "bg-white text-black border-2 border-slate-300 border-solid": !primary, "min-w-[62px] h-[32px]": true, "w-full": fullWidth, @@ -29,10 +31,11 @@ export const OnlyofficeButton: React.FC = ({ ); }; diff --git a/frontend/src/components/tile/Tile.tsx b/frontend/src/components/tile/Tile.tsx index de22625..ef2ea6c 100644 --- a/frontend/src/components/tile/Tile.tsx +++ b/frontend/src/components/tile/Tile.tsx @@ -21,7 +21,7 @@ export const OnlyofficeTile: React.FC = ({ const card = cx({ "px-5 py-3.5 rounded-lg transform shadow mb-5 outline-none": true, "transition duration-100 ease-linear": true, - "w-[108px] h-[94px]": true, + "w-[97px] h-[82px]": true, "max-h-36 flex flex-col justify-center": true, "hover:-translate-y-[0.125rem] hover:shadow-xl cursor-pointer": !selected, "bg-white": !selected, diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx index 22561b3..a4bf6b7 100644 --- a/frontend/src/pages/Creation/Creation.tsx +++ b/frontend/src/pages/Creation/Creation.tsx @@ -12,6 +12,8 @@ import { uploadFile } from "@services/file"; import { getFileIcon, getMimeType } from "@utils/file"; import { getCurrentURL } from "@utils/url"; +import Redirect from "@assets/redirect.svg"; + export const Creation: React.FC = () => { const [sdk, setSDK] = useState(); const [file, setFile] = useState("New Document"); @@ -90,6 +92,7 @@ export const Creation: React.FC = () => { } onClick={async () => { const token = await sdk?.execute(Command.GET_SIGNED_TOKEN); if (!token) return; From abac98928bcf86bbd217007b33df9ff3cb9351b1 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 23 Mar 2023 19:13:01 +0500 Subject: [PATCH 008/145] fix(components): tile width --- frontend/src/components/tile/Tile.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/tile/Tile.tsx b/frontend/src/components/tile/Tile.tsx index ef2ea6c..de22625 100644 --- a/frontend/src/components/tile/Tile.tsx +++ b/frontend/src/components/tile/Tile.tsx @@ -21,7 +21,7 @@ export const OnlyofficeTile: React.FC = ({ const card = cx({ "px-5 py-3.5 rounded-lg transform shadow mb-5 outline-none": true, "transition duration-100 ease-linear": true, - "w-[97px] h-[82px]": true, + "w-[108px] h-[94px]": true, "max-h-36 flex flex-col justify-center": true, "hover:-translate-y-[0.125rem] hover:shadow-xl cursor-pointer": !selected, "bg-white": !selected, From 1b3113311b9f5e1f7bd2fca6411389f89ef20f22 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 23 Mar 2023 20:00:44 +0500 Subject: [PATCH 009/145] fix(builder): check ctx done --- backend/services/builder/web/handler/build.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/services/builder/web/handler/build.go b/backend/services/builder/web/handler/build.go index 439c64b..3a5f2c7 100644 --- a/backend/services/builder/web/handler/build.go +++ b/backend/services/builder/web/handler/build.go @@ -22,6 +22,7 @@ import ( ) var _ErrNoSettingsFound = errors.New("could not find document server settings") +var _ErrOperationTimeout = errors.New("operation timeout") type ConfigHandler struct { namespace string @@ -107,6 +108,8 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui select { case err := <-errorsChan: return config, err + case <-ctx.Done(): + return config, _ErrOperationTimeout default: c.logger.Debugf("select default") } From d753122c80689f69be7176521f343fe552c7d2ce Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 23 Mar 2023 21:01:01 +0500 Subject: [PATCH 010/145] refactor(gateway): optimize endpoint calls --- .../services/gateway/web/controller/api.go | 87 ++++++++++++------- .../services/gateway/web/controller/auth.go | 7 ++ 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/backend/services/gateway/web/controller/api.go b/backend/services/gateway/web/controller/api.go index 44a839e..ae4e04d 100644 --- a/backend/services/gateway/web/controller/api.go +++ b/backend/services/gateway/web/controller/api.go @@ -8,7 +8,9 @@ import ( "io" "io/ioutil" "net/http" + "strconv" "strings" + "sync" "time" "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" @@ -21,6 +23,8 @@ import ( "golang.org/x/sync/semaphore" ) +var _ErrNotAdmin = errors.New("no admin access") + type apiController struct { namespace string client client.Client @@ -92,6 +96,12 @@ func (c *apiController) BuildPostSettings() http.HandlerFunc { return } + len, err := strconv.ParseInt(r.Header.Get("Content-Length"), 10, 0) + if err != nil || (len/100000) > 10 { + rw.WriteHeader(http.StatusBadRequest) + return + } + var settings request.DocSettings buf, _ := ioutil.ReadAll(r.Body) if err := json.Unmarshal(buf, &settings); err != nil { @@ -110,53 +120,62 @@ func (c *apiController) BuildPostSettings() http.HandlerFunc { ctx, cancel := context.WithTimeout(r.Context(), 4*time.Second) defer cancel() - if err := c.commandClient.License(ctx, settings.DocAddress, settings.DocSecret); err != nil { - c.logger.Errorf("could not validate ONLYOFFICE document server credentials: %s", err.Error()) - rw.WriteHeader(http.StatusForbidden) - return - } + var wg sync.WaitGroup + errChan := make(chan error, 2) + cidChan := make(chan int, 1) - var ures response.UserResponse - if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", fmt.Sprint(pctx.UID+pctx.CID)), &ures); err != nil { - c.logger.Errorf("could not get user access info: %s", err.Error()) - if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { - rw.WriteHeader(http.StatusRequestTimeout) + go func() { + wg.Add(1) + defer wg.Done() + if err := c.commandClient.License(ctx, settings.DocAddress, settings.DocSecret); err != nil { + c.logger.Errorf("could not validate ONLYOFFICE document server credentials: %s", err.Error()) + errChan <- err return } + }() - microErr := response.MicroError{} - if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { - rw.WriteHeader(http.StatusUnauthorized) + go func() { + var ures response.UserResponse + if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", fmt.Sprint(pctx.UID+pctx.CID)), &ures); err != nil { + c.logger.Errorf("could not get user access info: %s", err.Error()) + errChan <- err return } - rw.WriteHeader(microErr.Code) - return - } - - urs, err := c.apiClient.GetMe(ctx, model.Token{ - AccessToken: ures.AccessToken, - RefreshToken: ures.RefreshToken, - TokenType: ures.TokenType, - Scope: ures.Scope, - ApiDomain: ures.ApiDomain, - }) + urs, err := c.apiClient.GetMe(ctx, model.Token{ + AccessToken: ures.AccessToken, + RefreshToken: ures.RefreshToken, + TokenType: ures.TokenType, + Scope: ures.Scope, + ApiDomain: ures.ApiDomain, + }) + + for _, access := range urs.Access { + if access.App == "global" && !access.Admin { + errChan <- _ErrNotAdmin + return + } + } - for _, access := range urs.Access { - if access.App == "global" && !access.Admin { - rw.WriteHeader(http.StatusForbidden) + if err != nil { + c.logger.Errorf("could not get pipedrive user or no user has admin permissions") + errChan <- err return } - } - if err != nil { - c.logger.Errorf("could not get pipedrive user or no user has admin permissions") + cidChan <- urs.CompanyID + }() + + wg.Wait() + select { + case <-errChan: rw.WriteHeader(http.StatusForbidden) return + default: } msg := c.client.NewMessage("insert-settings", request.DocSettings{ - CompanyID: urs.CompanyID, + CompanyID: <-cidChan, DocAddress: settings.DocAddress, DocSecret: settings.DocSecret, }) @@ -254,7 +273,8 @@ func (c apiController) BuildGetConfig() http.HandlerFunc { rw.Header().Set("Content-Type", "application/json") query := r.URL.Query() - id, filename, key, dealID := strings.TrimSpace(query.Get("id")), strings.TrimSpace(query.Get("name")), strings.TrimSpace(query.Get("key")), strings.TrimSpace(query.Get("deal_id")) + id, filename, key, dealID := strings.TrimSpace(query.Get("id")), strings.TrimSpace(query.Get("name")), + strings.TrimSpace(query.Get("key")), strings.TrimSpace(query.Get("deal_id")) pctx, ok := r.Context().Value(request.PipedriveTokenContext{}).(request.PipedriveTokenContext) if !ok { @@ -321,7 +341,8 @@ func (c apiController) BuildGetFile() http.HandlerFunc { defer sem.Release(1) - fid, cid, token := strings.TrimSpace(r.URL.Query().Get("fid")), strings.TrimSpace(r.URL.Query().Get("cid")), strings.TrimSpace(r.URL.Query().Get("token")) + fid, cid, token := strings.TrimSpace(r.URL.Query().Get("fid")), + strings.TrimSpace(r.URL.Query().Get("cid")), strings.TrimSpace(r.URL.Query().Get("token")) var pctx request.PipedriveTokenContext if token == "" { diff --git a/backend/services/gateway/web/controller/auth.go b/backend/services/gateway/web/controller/auth.go index e6ae272..166961a 100644 --- a/backend/services/gateway/web/controller/auth.go +++ b/backend/services/gateway/web/controller/auth.go @@ -7,6 +7,7 @@ import ( "fmt" "io/ioutil" "net/http" + "strconv" "strings" "time" @@ -97,6 +98,12 @@ func (c authController) BuildDeleteAuth() http.HandlerFunc { c.logger.Debug("a new uninstall request") var ureq request.UninstallRequest + len, err := strconv.ParseInt(r.Header.Get("Content-Length"), 10, 0) + if err != nil || (len/100000) > 10 { + rw.WriteHeader(http.StatusBadRequest) + return + } + buf, err := ioutil.ReadAll(r.Body) if err != nil { rw.WriteHeader(http.StatusBadRequest) From bac210e23d25783a4aae00e0a23980b49ef36dbc Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 24 Mar 2023 12:41:02 +0500 Subject: [PATCH 011/145] fix(context): run token context when token is about to expire --- frontend/src/context/TokenContext.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/context/TokenContext.tsx b/frontend/src/context/TokenContext.tsx index 9a01c0f..8047051 100644 --- a/frontend/src/context/TokenContext.tsx +++ b/frontend/src/context/TokenContext.tsx @@ -35,7 +35,7 @@ export const TokenProvider: React.FC = ({ children }) => { AuthToken.error = true; } } - timerID = setTimeout(update, 1000); + timerID = setTimeout(update, AuthToken.expires_at - Date.now() - 100); }, 1000); }) .catch(() => null); From 529c427a330bde3ee8914f51e89e7bcbb3913b89 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 24 Mar 2023 12:51:19 +0500 Subject: [PATCH 012/145] fix(services): fetch config delay --- frontend/src/hooks/useBuildConfig.tsx | 2 +- frontend/src/services/config.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/hooks/useBuildConfig.tsx b/frontend/src/hooks/useBuildConfig.tsx index ca7f9cf..3b89629 100644 --- a/frontend/src/hooks/useBuildConfig.tsx +++ b/frontend/src/hooks/useBuildConfig.tsx @@ -10,7 +10,7 @@ export function useBuildConfig( dealID: string ) { const { isLoading, error, data } = useQuery({ - queryKey: ["config", id], + queryKey: ["config", id, key], queryFn: ({ signal }) => fetchConfig(token, id, name, key, dealID, signal), staleTime: 0, cacheTime: 0, diff --git a/frontend/src/services/config.ts b/frontend/src/services/config.ts index 7b354fa..c4398a0 100644 --- a/frontend/src/services/config.ts +++ b/frontend/src/services/config.ts @@ -15,6 +15,7 @@ export const fetchConfig = async ( axiosRetry(client, { retries: 2, retryCondition: (error) => error.status !== 200, + retryDelay: (count) => count * 50, }); const res = await axios({ method: "GET", From 79b70f0208bdb9b451b1ac65d23c1c922b556cd2 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 24 Mar 2023 13:31:57 +0500 Subject: [PATCH 013/145] fix(pages): files page remove get user info --- frontend/src/hooks/useUserSearch.tsx | 14 ------------ frontend/src/pages/Main/Main.tsx | 34 ++++++++-------------------- frontend/src/types/user.ts | 3 ++- 3 files changed, 11 insertions(+), 40 deletions(-) delete mode 100644 frontend/src/hooks/useUserSearch.tsx diff --git a/frontend/src/hooks/useUserSearch.tsx b/frontend/src/hooks/useUserSearch.tsx deleted file mode 100644 index d1f1989..0000000 --- a/frontend/src/hooks/useUserSearch.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { useQuery } from "react-query"; -import { fetchUsers } from "@services/user"; - -export function useUserSearch(url: string) { - const { isLoading, error, data } = useQuery({ - queryKey: ["users"], - queryFn: ({ signal }) => fetchUsers(url, signal), - staleTime: 30000, - cacheTime: 30000, - refetchOnWindowFocus: false, - }); - - return { isLoading, error, data }; -} diff --git a/frontend/src/pages/Main/Main.tsx b/frontend/src/pages/Main/Main.tsx index 7fcac30..b08bada 100644 --- a/frontend/src/pages/Main/Main.tsx +++ b/frontend/src/pages/Main/Main.tsx @@ -15,30 +15,21 @@ import { useFileSearch } from "@hooks/useFileSearch"; import { formatBytes, getFileIcon, isFileSupported } from "@utils/file"; import { getCurrentURL } from "@utils/url"; -import { useUserSearch } from "@hooks/useUserSearch"; import { OnlyofficeFileActions } from "./Actions"; export const Main: React.FC = () => { const { url, parameters } = getCurrentURL(); const [sdk, setSDK] = useState(); - const { - isLoading: filesLoading, - fetchNextPage, - isFetchingNextPage, - files, - hasNextPage, - } = useFileSearch( - `${url}api/v1/deals/${parameters.get("selectedIds")}/files`, - 20 - ); - const { isLoading: usersLoading, data: userData } = useUserSearch( - `${url}api/v1/users` - ); + const { isLoading, fetchNextPage, isFetchingNextPage, files, hasNextPage } = + useFileSearch( + `${url}api/v1/deals/${parameters.get("selectedIds")}/files`, + 20 + ); const observer = useRef(); const lastItem = useCallback( (node: Element | null) => { - if (filesLoading || usersLoading) return; + if (isLoading) return; if (observer.current) observer.current.disconnect(); observer.current = new IntersectionObserver(async (entries) => { if (entries[0].isIntersecting && hasNextPage) { @@ -47,7 +38,7 @@ export const Main: React.FC = () => { }); if (node) observer.current.observe(node); }, - [filesLoading, usersLoading, fetchNextPage, hasNextPage] + [isLoading, fetchNextPage, hasNextPage] ); useEffect(() => { @@ -57,7 +48,6 @@ export const Main: React.FC = () => { .catch(() => setSDK(null)); }, []); - const isLoading = filesLoading && usersLoading; return (
@@ -84,10 +74,7 @@ export const Main: React.FC = () => { > u.id === file.user_id - )?.name || "Anonymous", + // "Created by": file.person_name, Workspace: file.remote_location, Type: file.file_type, "Date modified": file.update_time, @@ -109,10 +96,7 @@ export const Main: React.FC = () => { > u.id === file.user_id - )?.name || "Anonymous", + // "Created by": file.person_name, Workspace: file.remote_location, Type: file.file_type, "Date modified": file.update_time, diff --git a/frontend/src/types/user.ts b/frontend/src/types/user.ts index a69a37b..70c6874 100644 --- a/frontend/src/types/user.ts +++ b/frontend/src/types/user.ts @@ -11,8 +11,9 @@ type PipedriveAccess = { type PipedriveUser = { id: number; + user_id: number; name: string; -} +}; export type PipedriveUserResponse = { success: boolean; From 2d68935d1af8387b8cd335564f6c1f064987b69b Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 24 Mar 2023 15:19:53 +0500 Subject: [PATCH 014/145] chore: cleanup --- frontend/src/App.tsx | 1 - frontend/src/pages/Editor/index.tsx | 6 ------ frontend/src/services/me.ts | 1 + frontend/src/types/config.ts | 2 -- 4 files changed, 1 insertion(+), 9 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e68c4c0..c65a2ee 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -65,7 +65,6 @@ const LazyRoutes: React.FC = () => { } /> - ASD
} /> } /> ); diff --git a/frontend/src/pages/Editor/index.tsx b/frontend/src/pages/Editor/index.tsx index f010406..8470741 100644 --- a/frontend/src/pages/Editor/index.tsx +++ b/frontend/src/pages/Editor/index.tsx @@ -96,12 +96,6 @@ export const OnlyofficeEditorPage: React.FC = () => { callbackUrl: data.editorConfig.callbackUrl, user: data.editorConfig.user, lang: data.editorConfig.lang, - customization: { - goback: { - requestClose: true, - text: "Close", - }, - }, }, token: data.token, type: data.type, diff --git a/frontend/src/services/me.ts b/frontend/src/services/me.ts index 744ab52..e9da6da 100644 --- a/frontend/src/services/me.ts +++ b/frontend/src/services/me.ts @@ -10,6 +10,7 @@ export const getMe = async (sdk: AppExtensionsSDK) => { axiosRetry(client, { retries: 2, retryCondition: (error) => error.status !== 200, + retryDelay: (count) => count * 50, }); const res = await client({ method: "GET", diff --git a/frontend/src/types/config.ts b/frontend/src/types/config.ts index 1c566f5..cfb459d 100644 --- a/frontend/src/types/config.ts +++ b/frontend/src/types/config.ts @@ -47,6 +47,4 @@ export type ConfigResponse = { type: string; token: string; server_url: string; - is_session?: boolean; - is_owner?: boolean; }; From 8257c82ace34cc69fadcf473b9f9adcb2bf481c2 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 24 Mar 2023 17:18:06 +0500 Subject: [PATCH 015/145] feat(i18n): translations --- frontend/package.json | 1 + .../src/assets/locales/en-US/translation.json | 42 +++++++++++++++ .../src/assets/locales/en/translation.json | 42 +++++++++++++++ .../src/assets/locales/ru-RU/translation.json | 42 +++++++++++++++ .../src/assets/locales/ru/translation.json | 42 +++++++++++++++ frontend/src/i18n.ts | 2 + frontend/src/index.tsx | 2 +- frontend/src/pages/Creation/Creation.tsx | 25 +++++---- frontend/src/pages/Creation/Upload.tsx | 27 +++++++++- frontend/src/pages/Creation/index.tsx | 6 ++- frontend/src/pages/Editor/index.tsx | 12 ++--- frontend/src/pages/Main/Actions.tsx | 14 ++++- frontend/src/pages/Main/Main.tsx | 38 ++++++++----- frontend/src/pages/Main/index.tsx | 12 +++-- frontend/src/pages/Settings/index.tsx | 53 +++++++++++++------ frontend/yarn.lock | 9 +++- 16 files changed, 315 insertions(+), 54 deletions(-) create mode 100644 frontend/src/assets/locales/en-US/translation.json create mode 100644 frontend/src/assets/locales/en/translation.json create mode 100644 frontend/src/assets/locales/ru-RU/translation.json create mode 100644 frontend/src/assets/locales/ru/translation.json diff --git a/frontend/package.json b/frontend/package.json index c1b973f..a0121d3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -88,6 +88,7 @@ "axios-retry": "^3.4.0", "classnames": "^2.3.2", "i18next": "^22.4.9", + "i18next-browser-languagedetector": "^7.0.1", "i18next-http-backend": "^2.1.1", "md5": "^2.3.0", "react": "^18.2.0", diff --git a/frontend/src/assets/locales/en-US/translation.json b/frontend/src/assets/locales/en-US/translation.json new file mode 100644 index 0000000..f6cd1b8 --- /dev/null +++ b/frontend/src/assets/locales/en-US/translation.json @@ -0,0 +1,42 @@ +{ + "files.error.nofiles": "Could not find Pipedrive documents", + "files.info.workspace": "Workspace", + "files.info.type": "Type", + "files.info.modified": "Date modified", + "files.info.creation": "Creation date", + "files.info.size": "Size", + "snackbar.fileremoved.ok": "File {{file}} has been removed", + "snackbar.fileremoved.error": "Could not remove file {{file}}", + "creation.title": "Create with ONLYOFFICE", + "creation.inputs.title": "Title", + "creation.tiles.doc": "Document", + "creation.tiles.spreadsheet": "Spreadsheet", + "creation.tiles.presentation": "Presentation", + "creation.error": "Could not create a new file", + "upload.error": "Could not upload your file. Please contact ONLYOFFICE support.", + "upload.uploading": "Uploading...", + "upload.select": "Select a file", + "upload.dragdrop": "or drag and drop here", + "upload.subtext": "File size is limited", + "settings.title": "Configure ONLYOFFICE app settings", + "settings.text": "The plugin which enables the users to edit office documents from Pipedrive using ONLYOFFICE Document Server, allows multiple users to collaborate in real time and to save back those changes to Pipedrive", + "settings.inputs.address": "Document Server Address", + "settings.inputs.secret": "Document Server Secret", + "settings.saving.ok": "ONLYOFFICE settings have been saved", + "settings.saving.error": "Could not save ONLYOFFICE settings", + "editor.error": "Could not open the file. Something went wrong", + "background.error.title": "Error", + "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the pipedrive window", + "background.access.title": "Access denied", + "background.access.subtitle": "Something went wrong or access denied", + "background.reinstall.subtitle": "Something went wrong. Please reload or reinstall the app.", + "document.new": "New Document", + "button.upload": "Create or upload document", + "button.reload": "Reload", + "button.save": "Save", + "button.cancel": "Cancel", + "button.close": "Close", + "button.create": "Create document", + "button.creation.create": "Create", + "button.creation.upload": "Upload" +} diff --git a/frontend/src/assets/locales/en/translation.json b/frontend/src/assets/locales/en/translation.json new file mode 100644 index 0000000..f6cd1b8 --- /dev/null +++ b/frontend/src/assets/locales/en/translation.json @@ -0,0 +1,42 @@ +{ + "files.error.nofiles": "Could not find Pipedrive documents", + "files.info.workspace": "Workspace", + "files.info.type": "Type", + "files.info.modified": "Date modified", + "files.info.creation": "Creation date", + "files.info.size": "Size", + "snackbar.fileremoved.ok": "File {{file}} has been removed", + "snackbar.fileremoved.error": "Could not remove file {{file}}", + "creation.title": "Create with ONLYOFFICE", + "creation.inputs.title": "Title", + "creation.tiles.doc": "Document", + "creation.tiles.spreadsheet": "Spreadsheet", + "creation.tiles.presentation": "Presentation", + "creation.error": "Could not create a new file", + "upload.error": "Could not upload your file. Please contact ONLYOFFICE support.", + "upload.uploading": "Uploading...", + "upload.select": "Select a file", + "upload.dragdrop": "or drag and drop here", + "upload.subtext": "File size is limited", + "settings.title": "Configure ONLYOFFICE app settings", + "settings.text": "The plugin which enables the users to edit office documents from Pipedrive using ONLYOFFICE Document Server, allows multiple users to collaborate in real time and to save back those changes to Pipedrive", + "settings.inputs.address": "Document Server Address", + "settings.inputs.secret": "Document Server Secret", + "settings.saving.ok": "ONLYOFFICE settings have been saved", + "settings.saving.error": "Could not save ONLYOFFICE settings", + "editor.error": "Could not open the file. Something went wrong", + "background.error.title": "Error", + "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the pipedrive window", + "background.access.title": "Access denied", + "background.access.subtitle": "Something went wrong or access denied", + "background.reinstall.subtitle": "Something went wrong. Please reload or reinstall the app.", + "document.new": "New Document", + "button.upload": "Create or upload document", + "button.reload": "Reload", + "button.save": "Save", + "button.cancel": "Cancel", + "button.close": "Close", + "button.create": "Create document", + "button.creation.create": "Create", + "button.creation.upload": "Upload" +} diff --git a/frontend/src/assets/locales/ru-RU/translation.json b/frontend/src/assets/locales/ru-RU/translation.json new file mode 100644 index 0000000..f6cd1b8 --- /dev/null +++ b/frontend/src/assets/locales/ru-RU/translation.json @@ -0,0 +1,42 @@ +{ + "files.error.nofiles": "Could not find Pipedrive documents", + "files.info.workspace": "Workspace", + "files.info.type": "Type", + "files.info.modified": "Date modified", + "files.info.creation": "Creation date", + "files.info.size": "Size", + "snackbar.fileremoved.ok": "File {{file}} has been removed", + "snackbar.fileremoved.error": "Could not remove file {{file}}", + "creation.title": "Create with ONLYOFFICE", + "creation.inputs.title": "Title", + "creation.tiles.doc": "Document", + "creation.tiles.spreadsheet": "Spreadsheet", + "creation.tiles.presentation": "Presentation", + "creation.error": "Could not create a new file", + "upload.error": "Could not upload your file. Please contact ONLYOFFICE support.", + "upload.uploading": "Uploading...", + "upload.select": "Select a file", + "upload.dragdrop": "or drag and drop here", + "upload.subtext": "File size is limited", + "settings.title": "Configure ONLYOFFICE app settings", + "settings.text": "The plugin which enables the users to edit office documents from Pipedrive using ONLYOFFICE Document Server, allows multiple users to collaborate in real time and to save back those changes to Pipedrive", + "settings.inputs.address": "Document Server Address", + "settings.inputs.secret": "Document Server Secret", + "settings.saving.ok": "ONLYOFFICE settings have been saved", + "settings.saving.error": "Could not save ONLYOFFICE settings", + "editor.error": "Could not open the file. Something went wrong", + "background.error.title": "Error", + "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the pipedrive window", + "background.access.title": "Access denied", + "background.access.subtitle": "Something went wrong or access denied", + "background.reinstall.subtitle": "Something went wrong. Please reload or reinstall the app.", + "document.new": "New Document", + "button.upload": "Create or upload document", + "button.reload": "Reload", + "button.save": "Save", + "button.cancel": "Cancel", + "button.close": "Close", + "button.create": "Create document", + "button.creation.create": "Create", + "button.creation.upload": "Upload" +} diff --git a/frontend/src/assets/locales/ru/translation.json b/frontend/src/assets/locales/ru/translation.json new file mode 100644 index 0000000..f6cd1b8 --- /dev/null +++ b/frontend/src/assets/locales/ru/translation.json @@ -0,0 +1,42 @@ +{ + "files.error.nofiles": "Could not find Pipedrive documents", + "files.info.workspace": "Workspace", + "files.info.type": "Type", + "files.info.modified": "Date modified", + "files.info.creation": "Creation date", + "files.info.size": "Size", + "snackbar.fileremoved.ok": "File {{file}} has been removed", + "snackbar.fileremoved.error": "Could not remove file {{file}}", + "creation.title": "Create with ONLYOFFICE", + "creation.inputs.title": "Title", + "creation.tiles.doc": "Document", + "creation.tiles.spreadsheet": "Spreadsheet", + "creation.tiles.presentation": "Presentation", + "creation.error": "Could not create a new file", + "upload.error": "Could not upload your file. Please contact ONLYOFFICE support.", + "upload.uploading": "Uploading...", + "upload.select": "Select a file", + "upload.dragdrop": "or drag and drop here", + "upload.subtext": "File size is limited", + "settings.title": "Configure ONLYOFFICE app settings", + "settings.text": "The plugin which enables the users to edit office documents from Pipedrive using ONLYOFFICE Document Server, allows multiple users to collaborate in real time and to save back those changes to Pipedrive", + "settings.inputs.address": "Document Server Address", + "settings.inputs.secret": "Document Server Secret", + "settings.saving.ok": "ONLYOFFICE settings have been saved", + "settings.saving.error": "Could not save ONLYOFFICE settings", + "editor.error": "Could not open the file. Something went wrong", + "background.error.title": "Error", + "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the pipedrive window", + "background.access.title": "Access denied", + "background.access.subtitle": "Something went wrong or access denied", + "background.reinstall.subtitle": "Something went wrong. Please reload or reinstall the app.", + "document.new": "New Document", + "button.upload": "Create or upload document", + "button.reload": "Reload", + "button.save": "Save", + "button.cancel": "Cancel", + "button.close": "Close", + "button.create": "Create document", + "button.creation.create": "Create", + "button.creation.upload": "Upload" +} diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index 18bb863..df96116 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -1,8 +1,10 @@ import i18n from "i18next"; import I18NextHttpBackend from "i18next-http-backend"; +import LanguageDetector from "i18next-browser-languagedetector"; import { initReactI18next } from "react-i18next"; i18n + .use(LanguageDetector) .use(I18NextHttpBackend) .use(initReactI18next) .init({ diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 3b2a077..6ec12fb 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -3,7 +3,7 @@ import ReactDOM from "react-dom/client"; import { QueryClient, QueryClientProvider } from "react-query"; import App from "./App"; -// import "./i18n"; +import "./i18n"; import "@assets/index.css"; diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx index a4bf6b7..033d62f 100644 --- a/frontend/src/pages/Creation/Creation.tsx +++ b/frontend/src/pages/Creation/Creation.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from "react"; import md5 from "md5"; import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; +import { useTranslation } from "react-i18next"; import { OnlyofficeButton } from "@components/button"; import { OnlyofficeInput } from "@components/input"; @@ -15,8 +16,11 @@ import { getCurrentURL } from "@utils/url"; import Redirect from "@assets/redirect.svg"; export const Creation: React.FC = () => { + const { t } = useTranslation(); const [sdk, setSDK] = useState(); - const [file, setFile] = useState("New Document"); + const [file, setFile] = useState( + t("document.new", "New Document") || "New Document" + ); const [fileType, setFileType] = useState<"docx" | "pptx" | "xlsx">("docx"); const handleChangeFile = (newType: "docx" | "pptx" | "xlsx") => { setFileType(newType); @@ -38,10 +42,13 @@ export const Creation: React.FC = () => {
- +
setFile(e.target.value)} @@ -51,7 +58,7 @@ export const Creation: React.FC = () => {
handleChangeFile("docx")} onKeyDown={() => handleChangeFile("docx")} selected={fileType === "docx"} @@ -60,7 +67,7 @@ export const Creation: React.FC = () => {
handleChangeFile("xlsx")} onKeyDown={() => handleChangeFile("xlsx")} selected={fileType === "xlsx"} @@ -69,7 +76,7 @@ export const Creation: React.FC = () => {
handleChangeFile("pptx")} onKeyDown={() => handleChangeFile("pptx")} selected={fileType === "pptx"} @@ -82,7 +89,7 @@ export const Creation: React.FC = () => {
{ await sdk?.execute(Command.CLOSE_MODAL); }} @@ -90,7 +97,7 @@ export const Creation: React.FC = () => {
} onClick={async () => { @@ -116,7 +123,7 @@ export const Creation: React.FC = () => { ); } catch { await sdk?.execute(Command.SHOW_SNACKBAR, { - message: "Could not create a new file", + message: t("creation.error", "Could not create a new file"), }); } }} diff --git a/frontend/src/pages/Creation/Upload.tsx b/frontend/src/pages/Creation/Upload.tsx index ea57112..cbb8b4d 100644 --- a/frontend/src/pages/Creation/Upload.tsx +++ b/frontend/src/pages/Creation/Upload.tsx @@ -1,6 +1,7 @@ import { FileRejection, DropEvent } from "react-dropzone"; import React, { useEffect, useState } from "react"; import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; +import { useTranslation } from "react-i18next"; import { OnlyofficeButton } from "@components/button"; import { OnlyofficeDragDrop } from "@components/drop"; @@ -23,6 +24,7 @@ const onDrop = ( }; export const Upload: React.FC = () => { + const { t } = useTranslation(); const [sdk, setSDK] = useState(); useEffect(() => { new AppExtensionsSDK() @@ -35,14 +37,35 @@ export const Upload: React.FC = () => {
- +
{ await sdk?.execute(Command.CLOSE_MODAL); }} diff --git a/frontend/src/pages/Creation/index.tsx b/frontend/src/pages/Creation/index.tsx index 8228e8b..8d95245 100644 --- a/frontend/src/pages/Creation/index.tsx +++ b/frontend/src/pages/Creation/index.tsx @@ -1,11 +1,13 @@ import React, { useEffect, useState } from "react"; import AppExtensionsSDK from "@pipedrive/app-extensions-sdk"; import { Tabs, TabList, Tab, TabPanel } from "react-tabs"; +import { useTranslation } from "react-i18next"; import { Creation } from "./Creation"; import { Upload } from "./Upload"; export const CreatePage: React.FC = () => { + const { t } = useTranslation(); const [selected, setSelected] = useState(0); useEffect(() => { new AppExtensionsSDK().initialize({ @@ -32,7 +34,7 @@ export const CreatePage: React.FC = () => { selected === 0 ? "text-sky-500" : "text-gray-400" }`} > - Create + {t("button.creation.create", "Create")} { selected === 1 ? "text-sky-500" : "text-gray-400" }`} > - Upload + {t("button.creation.upload", "Upload")} { }; export const OnlyofficeEditorPage: React.FC = () => { - const [params] = useSearchParams(); const { t } = useTranslation(); + const [params] = useSearchParams(); const { isLoading, error, data } = useBuildConfig( params.get("token") || "", params.get("id") || "", @@ -50,7 +50,7 @@ export const OnlyofficeEditorPage: React.FC = () => {
window.close()} /> @@ -61,15 +61,15 @@ export const OnlyofficeEditorPage: React.FC = () => {
window.close()} />
diff --git a/frontend/src/pages/Main/Actions.tsx b/frontend/src/pages/Main/Actions.tsx index f0630a8..789f0a7 100644 --- a/frontend/src/pages/Main/Actions.tsx +++ b/frontend/src/pages/Main/Actions.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from "react"; import md5 from "md5"; import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; +import { useTranslation } from "react-i18next"; import { useDeleteFile } from "@hooks/useDeleteFile"; @@ -17,6 +18,7 @@ type FileActionsProps = { }; export const OnlyofficeFileActions: React.FC = ({ file }) => { + const { t } = useTranslation(); const { url, parameters } = getCurrentURL(); const [sdk, setSDK] = useState(); const mutator = useDeleteFile(`${url}api/v1/files/${file.id}`); @@ -33,12 +35,20 @@ export const OnlyofficeFileActions: React.FC = ({ file }) => { .mutateAsync() .then(async () => { await sdk?.execute(Command.SHOW_SNACKBAR, { - message: `File ${file.name} has been removed`, + message: t( + "snackbar.fileremoved.ok", + `File ${file.name} has been removed`, + { file: file.name } + ), }); }) .catch(async () => { await sdk?.execute(Command.SHOW_SNACKBAR, { - message: `Could not remove file ${file.name}`, + message: t( + "snackbar.fileremoved.error", + `Could not remove file ${file.name}`, + { file: file.name } + ), }); }); }; diff --git a/frontend/src/pages/Main/Main.tsx b/frontend/src/pages/Main/Main.tsx index b08bada..f5d8e4d 100644 --- a/frontend/src/pages/Main/Main.tsx +++ b/frontend/src/pages/Main/Main.tsx @@ -3,6 +3,7 @@ import AppExtensionsSDK, { Command, Modal, } from "@pipedrive/app-extensions-sdk"; +import { useTranslation } from "react-i18next"; import { OnlyofficeButton } from "@components/button"; import { OnlyofficeFile } from "@components/file"; @@ -18,6 +19,7 @@ import { getCurrentURL } from "@utils/url"; import { OnlyofficeFileActions } from "./Actions"; export const Main: React.FC = () => { + const { t } = useTranslation(); const { url, parameters } = getCurrentURL(); const [sdk, setSDK] = useState(); const { isLoading, fetchNextPage, isFetchingNextPage, files, hasNextPage } = @@ -57,7 +59,9 @@ export const Main: React.FC = () => {
)} {!isLoading && (!files || files.length === 0) && ( - + )} {!isLoading && files && @@ -75,11 +79,16 @@ export const Main: React.FC = () => { @@ -97,11 +106,16 @@ export const Main: React.FC = () => { @@ -120,7 +134,7 @@ export const Main: React.FC = () => {
{ diff --git a/frontend/src/pages/Main/index.tsx b/frontend/src/pages/Main/index.tsx index 7384089..f844c2e 100644 --- a/frontend/src/pages/Main/index.tsx +++ b/frontend/src/pages/Main/index.tsx @@ -1,14 +1,17 @@ import React from "react"; import { useSnapshot } from "valtio"; +import { useTranslation } from "react-i18next"; import { OnlyofficeSpinner } from "@components/spinner"; +import { OnlyofficeBackgroundError } from "@layouts/ErrorBackground"; + import { AuthToken } from "@context/TokenContext"; -import { OnlyofficeBackgroundError } from "@layouts/ErrorBackground"; import { Main } from "./Main"; export const MainPage: React.FC = () => { + const { t } = useTranslation(); const { access_token: accessToken, error } = useSnapshot(AuthToken); const loading = !accessToken && !error; const loadingError = !accessToken && error; @@ -22,8 +25,11 @@ export const MainPage: React.FC = () => { )} {loadingError && ( )} {loaded &&
} diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx index 0f313f8..7abd399 100644 --- a/frontend/src/pages/Settings/index.tsx +++ b/frontend/src/pages/Settings/index.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from "react"; import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; import { useSnapshot } from "valtio"; +import { useTranslation } from "react-i18next"; import { OnlyofficeButton } from "@components/button"; import { OnlyofficeInput } from "@components/input"; @@ -14,6 +15,7 @@ import { getPipedriveMe } from "@services/me"; import { AuthToken } from "@context/TokenContext"; export const SettingsPage: React.FC = () => { + const { t } = useTranslation(); const [sdk, setSDK] = useState(); const { access_token: accessToken, error } = useSnapshot(AuthToken); const [admin, setAdmin] = useState(false); @@ -69,11 +71,17 @@ export const SettingsPage: React.FC = () => { await postSettings(sdk, address, secret); } await sdk.execute(Command.SHOW_SNACKBAR, { - message: "ONLYOFFICE settings have been saved", + message: t( + "settings.saving.ok", + "ONLYOFFICE settings have been saved" + ), }); } catch { await sdk.execute(Command.SHOW_SNACKBAR, { - message: "Could not save ONLYOFFICE settings", + message: t( + "settings.saving.error", + "Could not save ONLYOFFICE settings" + ), }); } finally { setSaving(false); @@ -90,17 +98,23 @@ export const SettingsPage: React.FC = () => { )} {!loading && error && ( window.location.reload()} /> )} {!loading && !error && !admin && ( window.location.reload()} /> )} @@ -108,19 +122,26 @@ export const SettingsPage: React.FC = () => { <>
- +

- The plugin which enables the users to edit office documents from - Pipedrive using ONLYOFFICE Document Server, allows multiple users - to collaborate in real time and to save back those changes to - Pipedrive + {t( + "settings.text", + ` + The plugin which enables the users to edit office documents from + Pipedrive using ONLYOFFICE Document Server, allows multiple users + to collaborate in real time and to save back those changes to + Pipedrive + ` + )}

{
{
Date: Fri, 24 Mar 2023 18:34:52 +0500 Subject: [PATCH 016/145] fix(pages): remove scrollbars --- frontend/src/pages/Creation/Creation.tsx | 2 +- frontend/src/pages/Creation/Upload.tsx | 2 +- frontend/src/pages/Editor/index.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx index 033d62f..0ef9c5b 100644 --- a/frontend/src/pages/Creation/Creation.tsx +++ b/frontend/src/pages/Creation/Creation.tsx @@ -40,7 +40,7 @@ export const Creation: React.FC = () => { return (
-
+
{ return (
-
+
{ const validConfig = !error && !isLoading && data; return ( -
+
{!error && (
Date: Fri, 24 Mar 2023 19:56:15 +0500 Subject: [PATCH 017/145] fix(settings): add doc header --- .../services/gateway/web/controller/api.go | 5 ++++- .../settings/web/core/adapter/mongo.go | 3 +++ .../settings/web/core/domain/settings.go | 10 ++++++++++ .../settings/web/core/service/settings.go | 4 ++++ .../services/settings/web/handler/select.go | 1 + .../services/settings/web/message/insert.go | 1 + backend/services/shared/request/settings.go | 5 +++++ backend/services/shared/response/settings.go | 1 + .../src/assets/locales/en-US/translation.json | 1 + .../src/assets/locales/en/translation.json | 1 + .../src/assets/locales/ru-RU/translation.json | 1 + .../src/assets/locales/ru/translation.json | 1 + frontend/src/pages/Settings/index.tsx | 19 +++++++++++++++---- frontend/src/services/settings.ts | 7 ++++--- frontend/src/types/settings.ts | 1 + 15 files changed, 53 insertions(+), 8 deletions(-) diff --git a/backend/services/gateway/web/controller/api.go b/backend/services/gateway/web/controller/api.go index ae4e04d..2944109 100644 --- a/backend/services/gateway/web/controller/api.go +++ b/backend/services/gateway/web/controller/api.go @@ -121,11 +121,11 @@ func (c *apiController) BuildPostSettings() http.HandlerFunc { defer cancel() var wg sync.WaitGroup + wg.Add(2) errChan := make(chan error, 2) cidChan := make(chan int, 1) go func() { - wg.Add(1) defer wg.Done() if err := c.commandClient.License(ctx, settings.DocAddress, settings.DocSecret); err != nil { c.logger.Errorf("could not validate ONLYOFFICE document server credentials: %s", err.Error()) @@ -135,6 +135,7 @@ func (c *apiController) BuildPostSettings() http.HandlerFunc { }() go func() { + defer wg.Done() var ures response.UserResponse if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", fmt.Sprint(pctx.UID+pctx.CID)), &ures); err != nil { c.logger.Errorf("could not get user access info: %s", err.Error()) @@ -167,6 +168,7 @@ func (c *apiController) BuildPostSettings() http.HandlerFunc { }() wg.Wait() + select { case <-errChan: rw.WriteHeader(http.StatusForbidden) @@ -178,6 +180,7 @@ func (c *apiController) BuildPostSettings() http.HandlerFunc { CompanyID: <-cidChan, DocAddress: settings.DocAddress, DocSecret: settings.DocSecret, + DocHeader: settings.DocHeader, }) if err := c.client.Publish(ctx, msg); err != nil { diff --git a/backend/services/settings/web/core/adapter/mongo.go b/backend/services/settings/web/core/adapter/mongo.go index b9c1779..a350bc3 100644 --- a/backend/services/settings/web/core/adapter/mongo.go +++ b/backend/services/settings/web/core/adapter/mongo.go @@ -24,6 +24,7 @@ type docSettingsCollection struct { CompanyID string `json:"company_id" bson:"company_id"` DocAddress string `json:"doc_address" bson:"doc_address"` DocSecret string `json:"doc_secret" bson:"doc_secret"` + DocHeader string `json:"doc_header" bson:"doc_header"` } type mongoUserAdapter struct { @@ -50,6 +51,7 @@ func (m *mongoUserAdapter) save(ctx context.Context, settings domain.DocSettings CompanyID: settings.CompanyID, DocAddress: settings.DocAddress, DocSecret: settings.DocSecret, + DocHeader: settings.DocHeader, }); cerr != nil { return cerr } @@ -91,6 +93,7 @@ func (m *mongoUserAdapter) SelectSettings(ctx context.Context, cid string) (doma CompanyID: cid, DocAddress: settings.DocAddress, DocSecret: settings.DocSecret, + DocHeader: settings.DocHeader, }, collection.FirstWithCtx(ctx, bson.M{"company_id": cid}, settings) } diff --git a/backend/services/settings/web/core/domain/settings.go b/backend/services/settings/web/core/domain/settings.go index 3938e2e..510bbe8 100644 --- a/backend/services/settings/web/core/domain/settings.go +++ b/backend/services/settings/web/core/domain/settings.go @@ -11,6 +11,7 @@ type DocSettings struct { CompanyID string `json:"company_id" mapstructure:"company_id"` DocAddress string `json:"doc_address" mapstructure:"doc_address"` DocSecret string `json:"doc_secret" mapstructure:"doc_secret"` + DocHeader string `json:"doc_header" mapstructure:"doc_header"` } func (u DocSettings) ToJSON() []byte { @@ -22,6 +23,7 @@ func (u *DocSettings) Validate() error { u.CompanyID = strings.TrimSpace(u.CompanyID) u.DocAddress = strings.TrimSpace(u.DocAddress) u.DocSecret = strings.TrimSpace(u.DocSecret) + u.DocHeader = strings.TrimSpace(u.DocHeader) if u.CompanyID == "" { return &InvalidModelFieldError{ @@ -59,5 +61,13 @@ func (u *DocSettings) Validate() error { } } + if u.DocHeader == "" { + return &InvalidModelFieldError{ + Model: "Docserver", + Field: "Document Header", + Reason: "Should not be empty", + } + } + return nil } diff --git a/backend/services/settings/web/core/service/settings.go b/backend/services/settings/web/core/service/settings.go index bf5f1f7..e788f4c 100644 --- a/backend/services/settings/web/core/service/settings.go +++ b/backend/services/settings/web/core/service/settings.go @@ -47,6 +47,7 @@ func (s settingsService) CreateSettings(ctx context.Context, settings domain.Doc CompanyID: settings.CompanyID, DocAddress: settings.DocAddress, DocSecret: esecret, + DocHeader: settings.DocHeader, }); err != nil { return err } @@ -80,6 +81,7 @@ func (s settingsService) GetSettings(ctx context.Context, cid string) (domain.Do CompanyID: cid, DocAddress: settings.DocAddress, DocSecret: dsecret, + DocHeader: settings.DocHeader, }, nil } @@ -99,10 +101,12 @@ func (s settingsService) UpdateSettings(ctx context.Context, settings domain.Doc CompanyID: settings.CompanyID, DocAddress: settings.DocAddress, DocSecret: esecret, + DocHeader: settings.DocHeader, }); err != nil { return settings, err } + s.logger.Debugf("successfully persisted %s settings", settings.CompanyID) return settings, nil } diff --git a/backend/services/settings/web/handler/select.go b/backend/services/settings/web/handler/select.go index f04e859..651b4a3 100644 --- a/backend/services/settings/web/handler/select.go +++ b/backend/services/settings/web/handler/select.go @@ -46,6 +46,7 @@ func (u SettingsSelectHandler) GetSettings(ctx context.Context, cid *string, res *res = response.DocSettingsResponse{ DocAddress: set.DocAddress, DocSecret: set.DocSecret, + DocHeader: set.DocHeader, } return nil } diff --git a/backend/services/settings/web/message/insert.go b/backend/services/settings/web/message/insert.go index c0502c6..b54a3e1 100644 --- a/backend/services/settings/web/message/insert.go +++ b/backend/services/settings/web/message/insert.go @@ -30,6 +30,7 @@ func (i InsertMessageHandler) GetHandler() func(context.Context, interface{}) er CompanyID: fmt.Sprint(settings.CompanyID), DocAddress: settings.DocAddress, DocSecret: settings.DocSecret, + DocHeader: settings.DocHeader, }) return err } diff --git a/backend/services/shared/request/settings.go b/backend/services/shared/request/settings.go index 37afe37..b6f4ff2 100644 --- a/backend/services/shared/request/settings.go +++ b/backend/services/shared/request/settings.go @@ -15,6 +15,7 @@ type DocSettings struct { CompanyID int `json:"company_id" mapstructure:"company_id"` DocAddress string `json:"doc_address" mapstructure:"doc_address"` DocSecret string `json:"doc_secret" mapstructure:"doc_secret"` + DocHeader string `json:"doc_header" mapstructure:"doc_header"` } func (c DocSettings) ToJSON() []byte { @@ -38,5 +39,9 @@ func (c DocSettings) Validate() error { return _ErrInvalidDocSecret } + if c.DocHeader == "" { + return _ErrInvalidDocHeader + } + return nil } diff --git a/backend/services/shared/response/settings.go b/backend/services/shared/response/settings.go index ea49f4f..82995af 100644 --- a/backend/services/shared/response/settings.go +++ b/backend/services/shared/response/settings.go @@ -5,6 +5,7 @@ import "encoding/json" type DocSettingsResponse struct { DocAddress string `json:"doc_address"` DocSecret string `json:"doc_secret"` + DocHeader string `json:"doc_header"` } func (r DocSettingsResponse) ToJSON() []byte { diff --git a/frontend/src/assets/locales/en-US/translation.json b/frontend/src/assets/locales/en-US/translation.json index f6cd1b8..64d6b4f 100644 --- a/frontend/src/assets/locales/en-US/translation.json +++ b/frontend/src/assets/locales/en-US/translation.json @@ -22,6 +22,7 @@ "settings.text": "The plugin which enables the users to edit office documents from Pipedrive using ONLYOFFICE Document Server, allows multiple users to collaborate in real time and to save back those changes to Pipedrive", "settings.inputs.address": "Document Server Address", "settings.inputs.secret": "Document Server Secret", + "settings.inputs.header": "Document Server Header", "settings.saving.ok": "ONLYOFFICE settings have been saved", "settings.saving.error": "Could not save ONLYOFFICE settings", "editor.error": "Could not open the file. Something went wrong", diff --git a/frontend/src/assets/locales/en/translation.json b/frontend/src/assets/locales/en/translation.json index f6cd1b8..64d6b4f 100644 --- a/frontend/src/assets/locales/en/translation.json +++ b/frontend/src/assets/locales/en/translation.json @@ -22,6 +22,7 @@ "settings.text": "The plugin which enables the users to edit office documents from Pipedrive using ONLYOFFICE Document Server, allows multiple users to collaborate in real time and to save back those changes to Pipedrive", "settings.inputs.address": "Document Server Address", "settings.inputs.secret": "Document Server Secret", + "settings.inputs.header": "Document Server Header", "settings.saving.ok": "ONLYOFFICE settings have been saved", "settings.saving.error": "Could not save ONLYOFFICE settings", "editor.error": "Could not open the file. Something went wrong", diff --git a/frontend/src/assets/locales/ru-RU/translation.json b/frontend/src/assets/locales/ru-RU/translation.json index f6cd1b8..64d6b4f 100644 --- a/frontend/src/assets/locales/ru-RU/translation.json +++ b/frontend/src/assets/locales/ru-RU/translation.json @@ -22,6 +22,7 @@ "settings.text": "The plugin which enables the users to edit office documents from Pipedrive using ONLYOFFICE Document Server, allows multiple users to collaborate in real time and to save back those changes to Pipedrive", "settings.inputs.address": "Document Server Address", "settings.inputs.secret": "Document Server Secret", + "settings.inputs.header": "Document Server Header", "settings.saving.ok": "ONLYOFFICE settings have been saved", "settings.saving.error": "Could not save ONLYOFFICE settings", "editor.error": "Could not open the file. Something went wrong", diff --git a/frontend/src/assets/locales/ru/translation.json b/frontend/src/assets/locales/ru/translation.json index f6cd1b8..64d6b4f 100644 --- a/frontend/src/assets/locales/ru/translation.json +++ b/frontend/src/assets/locales/ru/translation.json @@ -22,6 +22,7 @@ "settings.text": "The plugin which enables the users to edit office documents from Pipedrive using ONLYOFFICE Document Server, allows multiple users to collaborate in real time and to save back those changes to Pipedrive", "settings.inputs.address": "Document Server Address", "settings.inputs.secret": "Document Server Secret", + "settings.inputs.header": "Document Server Header", "settings.saving.ok": "ONLYOFFICE settings have been saved", "settings.saving.error": "Could not save ONLYOFFICE settings", "editor.error": "Could not open the file. Something went wrong", diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx index 7abd399..b47b06c 100644 --- a/frontend/src/pages/Settings/index.tsx +++ b/frontend/src/pages/Settings/index.tsx @@ -22,6 +22,7 @@ export const SettingsPage: React.FC = () => { const [loading, setLoading] = useState(true); const [address, setAddress] = useState(undefined); const [secret, setSecret] = useState(undefined); + const [header, setHeader] = useState(undefined); const [saving, setSaving] = useState(false); useEffect(() => { @@ -44,6 +45,7 @@ export const SettingsPage: React.FC = () => { const res = await getSettings(sdk); setAddress(res.doc_address); setSecret(res.doc_secret); + setHeader(res.doc_header); setAdmin(true); } } catch { @@ -61,14 +63,14 @@ export const SettingsPage: React.FC = () => { }, [sdk, accessToken, error]); const handleSettings = async () => { - if (address && secret && sdk) { + if (address && secret && header && sdk) { try { setSaving(true); if (!address.endsWith("/")) { - await postSettings(sdk, `${address}/`, secret); + await postSettings(sdk, `${address}/`, secret, header); setAddress(`${address}/`); } else { - await postSettings(sdk, address, secret); + await postSettings(sdk, address, secret, header); } await sdk.execute(Command.SHOW_SNACKBAR, { message: t( @@ -148,7 +150,7 @@ export const SettingsPage: React.FC = () => { onChange={(e) => setAddress(e.target.value)} />
-
+
{ type="password" />
+
+ setHeader(e.target.value)} + /> +
{ const pctx = await sdk.execute(Command.GET_SIGNED_TOKEN); const client = axios.create({ baseURL: process.env.BACKEND_GATEWAY }); @@ -16,7 +16,7 @@ export const postSettings = async ( retryCondition: (error) => error.status === 429, }); - await client({ + await client({ method: "POST", url: `/api/settings`, headers: { @@ -26,6 +26,7 @@ export const postSettings = async ( data: { doc_address: address, doc_secret: secret, + doc_header: header, }, timeout: 4000, }); diff --git a/frontend/src/types/settings.ts b/frontend/src/types/settings.ts index 4b97d48..eb23002 100644 --- a/frontend/src/types/settings.ts +++ b/frontend/src/types/settings.ts @@ -1,4 +1,5 @@ export type SettingsResponse = { doc_address: string; doc_secret: string; + doc_header: string; }; From 7029f313bf36cea7d52a05e9703da9bd256c3cf0 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 24 Mar 2023 20:17:45 +0500 Subject: [PATCH 018/145] fix(gateway): validate download header --- .../services/gateway/web/controller/api.go | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/backend/services/gateway/web/controller/api.go b/backend/services/gateway/web/controller/api.go index 2944109..54882d3 100644 --- a/backend/services/gateway/web/controller/api.go +++ b/backend/services/gateway/web/controller/api.go @@ -361,10 +361,36 @@ func (c apiController) BuildGetFile() http.HandlerFunc { return } - if err := c.jwtManager.Verify(docs.DocSecret, token, &pctx); err != nil { - c.logger.Errorf("could not verify X-Pipedrive-App-Context: %s", err.Error()) + var wg sync.WaitGroup + wg.Add(2) + errChan := make(chan error, 2) + + go func() { + defer wg.Done() + if err := c.jwtManager.Verify(docs.DocSecret, token, &pctx); err != nil { + c.logger.Errorf("could not verify X-Pipedrive-App-Context: %s", err.Error()) + errChan <- err + return + } + }() + + go func() { + defer wg.Done() + var tkn interface{} + if err := c.jwtManager.Verify(docs.DocSecret, strings.ReplaceAll(r.Header.Get(docs.DocHeader), "Bearer ", "a"), &tkn); err != nil { + c.logger.Errorf("could not verify docs header: %s", err.Error()) + errChan <- err + return + } + }() + + wg.Wait() + + select { + case <-errChan: rw.WriteHeader(http.StatusForbidden) return + default: } ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) From abdae16e541d5dc9effbbc6351a663b911ecfa29 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 24 Mar 2023 20:19:46 +0500 Subject: [PATCH 019/145] fix(gateway): replacement typo --- backend/services/gateway/web/controller/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/services/gateway/web/controller/api.go b/backend/services/gateway/web/controller/api.go index 54882d3..8b75767 100644 --- a/backend/services/gateway/web/controller/api.go +++ b/backend/services/gateway/web/controller/api.go @@ -377,7 +377,7 @@ func (c apiController) BuildGetFile() http.HandlerFunc { go func() { defer wg.Done() var tkn interface{} - if err := c.jwtManager.Verify(docs.DocSecret, strings.ReplaceAll(r.Header.Get(docs.DocHeader), "Bearer ", "a"), &tkn); err != nil { + if err := c.jwtManager.Verify(docs.DocSecret, strings.ReplaceAll(r.Header.Get(docs.DocHeader), "Bearer ", ""), &tkn); err != nil { c.logger.Errorf("could not verify docs header: %s", err.Error()) errChan <- err return From a73b12c4fea931c7f37007c6367c5bee0650c7ca Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 24 Mar 2023 20:26:48 +0500 Subject: [PATCH 020/145] fix(builder): disable plugins --- backend/services/builder/web/handler/build.go | 4 ++-- frontend/src/pages/Editor/index.tsx | 4 ++++ frontend/src/types/config.ts | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/backend/services/builder/web/handler/build.go b/backend/services/builder/web/handler/build.go index 3a5f2c7..d990689 100644 --- a/backend/services/builder/web/handler/build.go +++ b/backend/services/builder/web/handler/build.go @@ -149,10 +149,10 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui ), Customization: response.Customization{ Goback: response.Goback{ - RequestClose: true, + RequestClose: false, }, Plugins: false, - HideRightMenu: true, + HideRightMenu: false, }, Lang: usr.Language.Lang, }, diff --git a/frontend/src/pages/Editor/index.tsx b/frontend/src/pages/Editor/index.tsx index 9ef9d64..b1f28a9 100644 --- a/frontend/src/pages/Editor/index.tsx +++ b/frontend/src/pages/Editor/index.tsx @@ -96,6 +96,10 @@ export const OnlyofficeEditorPage: React.FC = () => { callbackUrl: data.editorConfig.callbackUrl, user: data.editorConfig.user, lang: data.editorConfig.lang, + customization: { + hideRightMenu: data.editorConfig.customization.hideRightMenu, + plugins: data.editorConfig.customization.plugins, + }, }, token: data.token, type: data.type, diff --git a/frontend/src/types/config.ts b/frontend/src/types/config.ts index cfb459d..ee88429 100644 --- a/frontend/src/types/config.ts +++ b/frontend/src/types/config.ts @@ -31,6 +31,8 @@ type Goback = { type Customization = { goback: Goback; + hideRightMenu: boolean; + plugins: boolean; }; type EditorConfig = { From fe96b53c89a010ebe5074d366f0e26ee54a83942 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 27 Mar 2023 13:55:47 +0500 Subject: [PATCH 021/145] fix(editor): encode filename --- backend/services/builder/web/handler/build.go | 3 ++- backend/services/callback/web/worker/callback.go | 3 +-- frontend/src/pages/Creation/Creation.tsx | 9 +++++---- frontend/src/pages/Main/Actions.tsx | 9 +++++---- frontend/src/utils/file.ts | 5 +++++ 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/backend/services/builder/web/handler/build.go b/backend/services/builder/web/handler/build.go index d990689..5e5216f 100644 --- a/backend/services/builder/web/handler/build.go +++ b/backend/services/builder/web/handler/build.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "net/url" "path/filepath" "strings" "sync" @@ -145,7 +146,7 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui }, CallbackURL: fmt.Sprintf( "%s/callback?cid=%d&did=%s&fid=%s&filename=%s", - c.gatewayURL, usr.CompanyID, req.Deal, req.FileID, req.Filename, + c.gatewayURL, usr.CompanyID, req.Deal, req.FileID, url.QueryEscape(req.Filename), ), Customization: response.Customization{ Goback: response.Goback{ diff --git a/backend/services/callback/web/worker/callback.go b/backend/services/callback/web/worker/callback.go index 18478b8..8f358c3 100644 --- a/backend/services/callback/web/worker/callback.go +++ b/backend/services/callback/web/worker/callback.go @@ -60,12 +60,12 @@ func (c callbackWorker) UploadFile(ctx context.Context, payload []byte) error { c.logger.Debugf("got a new file %s upload job (%s)", msg.Filename, msg.UID) var wg sync.WaitGroup + wg.Add(2) userChan := make(chan response.UserResponse, 1) sizeChan := make(chan int64, 1) errChan := make(chan error, 2) go func() { - wg.Add(1) defer wg.Done() c.logger.Debugf("trying to get an access token") @@ -84,7 +84,6 @@ func (c callbackWorker) UploadFile(ctx context.Context, payload []byte) error { }() go func() { - wg.Add(1) defer wg.Done() headResp, err := otelhttp.Head(tctx, msg.Url) diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx index 0ef9c5b..92ed174 100644 --- a/frontend/src/pages/Creation/Creation.tsx +++ b/frontend/src/pages/Creation/Creation.tsx @@ -10,7 +10,7 @@ import { OnlyofficeTitle } from "@components/title"; import { uploadFile } from "@services/file"; -import { getFileIcon, getMimeType } from "@utils/file"; +import { getFileParts, getFileIcon, getMimeType } from "@utils/file"; import { getCurrentURL } from "@utils/url"; import Redirect from "@assets/redirect.svg"; @@ -114,12 +114,13 @@ export const Creation: React.FC = () => { parameters.get("selectedIds") || "", binary ); + const [name, ext] = getFileParts(res.data.name); window.open( `/editor?token=${token.token}&id=${res.data.id}&deal_id=${ res.data.deal_id - }&name=${res.data.name}&key=${md5( - res.data.id + res.data.update_time - )}` + }&name=${`${encodeURIComponent( + name.substring(0, 190) + )}.${ext}`}&key=${md5(res.data.id + res.data.update_time)}` ); } catch { await sdk?.execute(Command.SHOW_SNACKBAR, { diff --git a/frontend/src/pages/Main/Actions.tsx b/frontend/src/pages/Main/Actions.tsx index 789f0a7..d62667f 100644 --- a/frontend/src/pages/Main/Actions.tsx +++ b/frontend/src/pages/Main/Actions.tsx @@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next"; import { useDeleteFile } from "@hooks/useDeleteFile"; -import { isFileSupported } from "@utils/file"; +import { getFileParts, isFileSupported } from "@utils/file"; import { getCurrentURL } from "@utils/url"; import { File } from "src/types/file"; @@ -56,12 +56,13 @@ export const OnlyofficeFileActions: React.FC = ({ file }) => { const handleEditor = async () => { const token = await sdk?.execute(Command.GET_SIGNED_TOKEN); if (token) { + const [name, ext] = getFileParts(file.name); window.open( `/editor?token=${token.token}&deal_id=${ parameters.get("selectedIds") || "1" - }&id=${file.id}&name=${file.name}&key=${md5( - file.id + file.update_time - )}` + }&id=${file.id}&name=${`${encodeURIComponent( + name.substring(0, 190) + )}.${ext}`}&key=${md5(file.id + file.update_time)}` ); } }; diff --git a/frontend/src/utils/file.ts b/frontend/src/utils/file.ts index b75b8b5..3499370 100644 --- a/frontend/src/utils/file.ts +++ b/frontend/src/utils/file.ts @@ -63,6 +63,11 @@ const WORD = "word"; const SLIDE = "slide"; const CELL = "cell"; +export const getFileParts = (filename: string): [string, string] => { + const [name, ext] = filename.split("."); + return [name, ext]; +}; + const getFileExt = (filename: string): string => filename.split(".").pop() || ""; From f7c1b1793d1151ad8c04ed4ab5ca026f1cb4d087 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 27 Mar 2023 15:56:25 +0500 Subject: [PATCH 022/145] fix(files): file name validation --- backend/services/gateway/web/controller/api.go | 6 ++++++ frontend/src/assets/locales/en-US/translation.json | 1 + frontend/src/assets/locales/en/translation.json | 1 + frontend/src/assets/locales/ru-RU/translation.json | 1 + frontend/src/assets/locales/ru/translation.json | 1 + frontend/src/pages/Creation/Creation.tsx | 8 ++++++++ 6 files changed, 18 insertions(+) diff --git a/backend/services/gateway/web/controller/api.go b/backend/services/gateway/web/controller/api.go index 8b75767..ab7194b 100644 --- a/backend/services/gateway/web/controller/api.go +++ b/backend/services/gateway/web/controller/api.go @@ -292,6 +292,12 @@ func (c apiController) BuildGetConfig() http.HandlerFunc { return } + if len(filename) > 200 { + rw.WriteHeader(http.StatusBadRequest) + c.logger.Error("file length is greater than 200") + return + } + if key == "" { rw.WriteHeader(http.StatusBadRequest) c.logger.Error("could not extract doc key from URL Query") diff --git a/frontend/src/assets/locales/en-US/translation.json b/frontend/src/assets/locales/en-US/translation.json index 64d6b4f..1a2e125 100644 --- a/frontend/src/assets/locales/en-US/translation.json +++ b/frontend/src/assets/locales/en-US/translation.json @@ -9,6 +9,7 @@ "snackbar.fileremoved.error": "Could not remove file {{file}}", "creation.title": "Create with ONLYOFFICE", "creation.inputs.title": "Title", + "creation.inputs.error": "File name length should be less than 190 characters", "creation.tiles.doc": "Document", "creation.tiles.spreadsheet": "Spreadsheet", "creation.tiles.presentation": "Presentation", diff --git a/frontend/src/assets/locales/en/translation.json b/frontend/src/assets/locales/en/translation.json index 64d6b4f..1a2e125 100644 --- a/frontend/src/assets/locales/en/translation.json +++ b/frontend/src/assets/locales/en/translation.json @@ -9,6 +9,7 @@ "snackbar.fileremoved.error": "Could not remove file {{file}}", "creation.title": "Create with ONLYOFFICE", "creation.inputs.title": "Title", + "creation.inputs.error": "File name length should be less than 190 characters", "creation.tiles.doc": "Document", "creation.tiles.spreadsheet": "Spreadsheet", "creation.tiles.presentation": "Presentation", diff --git a/frontend/src/assets/locales/ru-RU/translation.json b/frontend/src/assets/locales/ru-RU/translation.json index 64d6b4f..1a2e125 100644 --- a/frontend/src/assets/locales/ru-RU/translation.json +++ b/frontend/src/assets/locales/ru-RU/translation.json @@ -9,6 +9,7 @@ "snackbar.fileremoved.error": "Could not remove file {{file}}", "creation.title": "Create with ONLYOFFICE", "creation.inputs.title": "Title", + "creation.inputs.error": "File name length should be less than 190 characters", "creation.tiles.doc": "Document", "creation.tiles.spreadsheet": "Spreadsheet", "creation.tiles.presentation": "Presentation", diff --git a/frontend/src/assets/locales/ru/translation.json b/frontend/src/assets/locales/ru/translation.json index 64d6b4f..1a2e125 100644 --- a/frontend/src/assets/locales/ru/translation.json +++ b/frontend/src/assets/locales/ru/translation.json @@ -9,6 +9,7 @@ "snackbar.fileremoved.error": "Could not remove file {{file}}", "creation.title": "Create with ONLYOFFICE", "creation.inputs.title": "Title", + "creation.inputs.error": "File name length should be less than 190 characters", "creation.tiles.doc": "Document", "creation.tiles.spreadsheet": "Spreadsheet", "creation.tiles.presentation": "Presentation", diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx index 92ed174..8505de5 100644 --- a/frontend/src/pages/Creation/Creation.tsx +++ b/frontend/src/pages/Creation/Creation.tsx @@ -50,6 +50,13 @@ export const Creation: React.FC = () => { setFile(e.target.value)} /> @@ -97,6 +104,7 @@ export const Creation: React.FC = () => {
190} text={t("button.create", "Create document")} primary Icon={} From 2cf04f4560e9a6ddc6979d12993f4ccacdf9433d Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 27 Mar 2023 16:44:55 +0500 Subject: [PATCH 023/145] fix(gateway): log me errors --- backend/services/gateway/web/controller/api.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/services/gateway/web/controller/api.go b/backend/services/gateway/web/controller/api.go index ab7194b..aae88d6 100644 --- a/backend/services/gateway/web/controller/api.go +++ b/backend/services/gateway/web/controller/api.go @@ -72,10 +72,12 @@ func (c *apiController) BuildGetMe() http.HandlerFunc { microErr := response.MicroError{} if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { rw.WriteHeader(http.StatusUnauthorized) + c.logger.Errorf("could not get me info: %s", err.Error()) return } rw.WriteHeader(microErr.Code) + c.logger.Errorf("could not get me info: %s", microErr.Detail) return } From e77464a0949a99748cacfaddf84a5c1db7d75587 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 27 Mar 2023 16:45:46 +0500 Subject: [PATCH 024/145] fix(settings): settings error image --- frontend/src/assets/settings-error.svg | 49 +++++++++++++++++++++++++ frontend/src/layout/ErrorBackground.tsx | 12 ++++-- frontend/src/pages/Main/index.tsx | 3 ++ frontend/src/pages/Settings/index.tsx | 4 ++ 4 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 frontend/src/assets/settings-error.svg diff --git a/frontend/src/assets/settings-error.svg b/frontend/src/assets/settings-error.svg new file mode 100644 index 0000000..12d6cc6 --- /dev/null +++ b/frontend/src/assets/settings-error.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/layout/ErrorBackground.tsx b/frontend/src/layout/ErrorBackground.tsx index 1a5a299..27b4cdf 100644 --- a/frontend/src/layout/ErrorBackground.tsx +++ b/frontend/src/layout/ErrorBackground.tsx @@ -7,6 +7,7 @@ import { OnlyofficeError } from "@components/error/Error"; import BackgroundError from "@assets/background-error.svg"; type ErrorProps = { + Icon: any; title: string; subtitle: string; button?: string; @@ -14,19 +15,22 @@ type ErrorProps = { }; export const OnlyofficeBackgroundError: React.FC = ({ + Icon, title, subtitle, button, onClick, }) => (
-
- +
+ {Icon}
-
+
- +
+ +
{onClick && button && (
diff --git a/frontend/src/pages/Main/index.tsx b/frontend/src/pages/Main/index.tsx index f844c2e..b06c0a0 100644 --- a/frontend/src/pages/Main/index.tsx +++ b/frontend/src/pages/Main/index.tsx @@ -8,6 +8,8 @@ import { OnlyofficeBackgroundError } from "@layouts/ErrorBackground"; import { AuthToken } from "@context/TokenContext"; +import BackgroundError from "@assets/background-error.svg"; + import { Main } from "./Main"; export const MainPage: React.FC = () => { @@ -25,6 +27,7 @@ export const MainPage: React.FC = () => { )} {loadingError && ( } title={t("background.error.title", "Error")} subtitle={t( "background.reinstall.subtitle", diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx index b47b06c..f811ea6 100644 --- a/frontend/src/pages/Settings/index.tsx +++ b/frontend/src/pages/Settings/index.tsx @@ -14,6 +14,8 @@ import { getPipedriveMe } from "@services/me"; import { AuthToken } from "@context/TokenContext"; +import SettingsError from "@assets/settings-error.svg"; + export const SettingsPage: React.FC = () => { const { t } = useTranslation(); const [sdk, setSDK] = useState(); @@ -100,6 +102,7 @@ export const SettingsPage: React.FC = () => { )} {!loading && error && ( } title={t("background.error.title", "Error")} subtitle={t( "background.error.subtitle", @@ -111,6 +114,7 @@ export const SettingsPage: React.FC = () => { )} {!loading && !error && !admin && ( } title={t("background.access.title", "Access Denied")} subtitle={t( "background.access.subtitle", From 461516fc057ddd213bbc4ef176db75c7800821e9 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 27 Mar 2023 18:22:00 +0500 Subject: [PATCH 025/145] feat(layouts): docs cloud banner --- frontend/src/assets/banner.svg | 57 ++++++++++++++++++ .../src/assets/locales/en-US/translation.json | 5 +- .../src/assets/locales/en/translation.json | 5 +- .../src/assets/locales/ru-RU/translation.json | 5 +- .../src/assets/locales/ru/translation.json | 5 +- frontend/src/components/title/Subtitle.tsx | 5 +- frontend/src/layout/Banner.tsx | 59 +++++++++++++++++++ frontend/src/pages/Settings/index.tsx | 14 +++-- 8 files changed, 145 insertions(+), 10 deletions(-) create mode 100644 frontend/src/assets/banner.svg create mode 100644 frontend/src/layout/Banner.tsx diff --git a/frontend/src/assets/banner.svg b/frontend/src/assets/banner.svg new file mode 100644 index 0000000..a72071b --- /dev/null +++ b/frontend/src/assets/banner.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/locales/en-US/translation.json b/frontend/src/assets/locales/en-US/translation.json index 1a2e125..f7e6252 100644 --- a/frontend/src/assets/locales/en-US/translation.json +++ b/frontend/src/assets/locales/en-US/translation.json @@ -1,4 +1,6 @@ { + "banner.title": "ONLYOFFICE Docs Cloud", + "banner.subtitle": "Easily launch the editors in the cloud without downloading and installation", "files.error.nofiles": "Could not find Pipedrive documents", "files.info.workspace": "Workspace", "files.info.type": "Type", @@ -40,5 +42,6 @@ "button.close": "Close", "button.create": "Create document", "button.creation.create": "Create", - "button.creation.upload": "Upload" + "button.creation.upload": "Upload", + "button.getnow": "Get Now" } diff --git a/frontend/src/assets/locales/en/translation.json b/frontend/src/assets/locales/en/translation.json index 1a2e125..f7e6252 100644 --- a/frontend/src/assets/locales/en/translation.json +++ b/frontend/src/assets/locales/en/translation.json @@ -1,4 +1,6 @@ { + "banner.title": "ONLYOFFICE Docs Cloud", + "banner.subtitle": "Easily launch the editors in the cloud without downloading and installation", "files.error.nofiles": "Could not find Pipedrive documents", "files.info.workspace": "Workspace", "files.info.type": "Type", @@ -40,5 +42,6 @@ "button.close": "Close", "button.create": "Create document", "button.creation.create": "Create", - "button.creation.upload": "Upload" + "button.creation.upload": "Upload", + "button.getnow": "Get Now" } diff --git a/frontend/src/assets/locales/ru-RU/translation.json b/frontend/src/assets/locales/ru-RU/translation.json index 1a2e125..f7e6252 100644 --- a/frontend/src/assets/locales/ru-RU/translation.json +++ b/frontend/src/assets/locales/ru-RU/translation.json @@ -1,4 +1,6 @@ { + "banner.title": "ONLYOFFICE Docs Cloud", + "banner.subtitle": "Easily launch the editors in the cloud without downloading and installation", "files.error.nofiles": "Could not find Pipedrive documents", "files.info.workspace": "Workspace", "files.info.type": "Type", @@ -40,5 +42,6 @@ "button.close": "Close", "button.create": "Create document", "button.creation.create": "Create", - "button.creation.upload": "Upload" + "button.creation.upload": "Upload", + "button.getnow": "Get Now" } diff --git a/frontend/src/assets/locales/ru/translation.json b/frontend/src/assets/locales/ru/translation.json index 1a2e125..f7e6252 100644 --- a/frontend/src/assets/locales/ru/translation.json +++ b/frontend/src/assets/locales/ru/translation.json @@ -1,4 +1,6 @@ { + "banner.title": "ONLYOFFICE Docs Cloud", + "banner.subtitle": "Easily launch the editors in the cloud without downloading and installation", "files.error.nofiles": "Could not find Pipedrive documents", "files.info.workspace": "Workspace", "files.info.type": "Type", @@ -40,5 +42,6 @@ "button.close": "Close", "button.create": "Create document", "button.creation.create": "Create", - "button.creation.upload": "Upload" + "button.creation.upload": "Upload", + "button.getnow": "Get Now" } diff --git a/frontend/src/components/title/Subtitle.tsx b/frontend/src/components/title/Subtitle.tsx index 17d7bac..00e067f 100644 --- a/frontend/src/components/title/Subtitle.tsx +++ b/frontend/src/components/title/Subtitle.tsx @@ -4,14 +4,17 @@ import cx from "classnames"; type SubtitleProps = { text: string; large?: boolean; + center?: boolean; }; export const OnlyofficeSubtitle: React.FC = ({ text, large = false, + center = true, }) => { const style = cx({ - "text-slate-700 font-normal text-center": !!text, + "text-slate-700 font-normal": !!text, + "text-center": center, "text-sm": !large, "text-base": large, }); diff --git a/frontend/src/layout/Banner.tsx b/frontend/src/layout/Banner.tsx new file mode 100644 index 0000000..4869dda --- /dev/null +++ b/frontend/src/layout/Banner.tsx @@ -0,0 +1,59 @@ +import React from "react"; + +import { OnlyofficeTitle, OnlyofficeSubtitle } from "@components/title"; + +import BannerIcon from "@assets/banner.svg"; +import { useTranslation } from "react-i18next"; + +export const Banner: React.FC = () => { + const { t } = useTranslation(); + return ( +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ ); +}; diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx index f811ea6..fb76b0b 100644 --- a/frontend/src/pages/Settings/index.tsx +++ b/frontend/src/pages/Settings/index.tsx @@ -8,6 +8,7 @@ import { OnlyofficeInput } from "@components/input"; import { OnlyofficeTitle } from "@components/title"; import { OnlyofficeSpinner } from "@components/spinner"; import { OnlyofficeBackgroundError } from "@layouts/ErrorBackground"; +import { Banner } from "@layouts/Banner"; import { postSettings, getSettings } from "@services/settings"; import { getPipedriveMe } from "@services/me"; @@ -94,7 +95,7 @@ export const SettingsPage: React.FC = () => { }; return ( -
+
{loading && !error && (
@@ -126,7 +127,7 @@ export const SettingsPage: React.FC = () => { )} {!loading && !error && admin && ( <> -
+
{

-
+
{ onChange={(e) => setAddress(e.target.value)} />
-
+
{ type="password" />
-
+
{ onClick={handleSettings} />
+
+ +
)} From 0185f41ca4a096b2b109944b2cd7f66d0bb5c3c1 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 27 Mar 2023 19:58:11 +0500 Subject: [PATCH 026/145] fix(files): token error page --- .../src/assets/locales/en-US/translation.json | 1 + .../src/assets/locales/en/translation.json | 1 + .../src/assets/locales/ru-RU/translation.json | 1 + .../src/assets/locales/ru/translation.json | 1 + frontend/src/assets/token-error.svg | 134 ++++++++++++++++++ frontend/src/pages/Main/index.tsx | 9 +- 6 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 frontend/src/assets/token-error.svg diff --git a/frontend/src/assets/locales/en-US/translation.json b/frontend/src/assets/locales/en-US/translation.json index f7e6252..6bb6a02 100644 --- a/frontend/src/assets/locales/en-US/translation.json +++ b/frontend/src/assets/locales/en-US/translation.json @@ -33,6 +33,7 @@ "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the pipedrive window", "background.access.title": "Access denied", "background.access.subtitle": "Something went wrong or access denied", + "background.reinstall.title": "The document security token has expired", "background.reinstall.subtitle": "Something went wrong. Please reload or reinstall the app.", "document.new": "New Document", "button.upload": "Create or upload document", diff --git a/frontend/src/assets/locales/en/translation.json b/frontend/src/assets/locales/en/translation.json index f7e6252..6bb6a02 100644 --- a/frontend/src/assets/locales/en/translation.json +++ b/frontend/src/assets/locales/en/translation.json @@ -33,6 +33,7 @@ "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the pipedrive window", "background.access.title": "Access denied", "background.access.subtitle": "Something went wrong or access denied", + "background.reinstall.title": "The document security token has expired", "background.reinstall.subtitle": "Something went wrong. Please reload or reinstall the app.", "document.new": "New Document", "button.upload": "Create or upload document", diff --git a/frontend/src/assets/locales/ru-RU/translation.json b/frontend/src/assets/locales/ru-RU/translation.json index f7e6252..6bb6a02 100644 --- a/frontend/src/assets/locales/ru-RU/translation.json +++ b/frontend/src/assets/locales/ru-RU/translation.json @@ -33,6 +33,7 @@ "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the pipedrive window", "background.access.title": "Access denied", "background.access.subtitle": "Something went wrong or access denied", + "background.reinstall.title": "The document security token has expired", "background.reinstall.subtitle": "Something went wrong. Please reload or reinstall the app.", "document.new": "New Document", "button.upload": "Create or upload document", diff --git a/frontend/src/assets/locales/ru/translation.json b/frontend/src/assets/locales/ru/translation.json index f7e6252..6bb6a02 100644 --- a/frontend/src/assets/locales/ru/translation.json +++ b/frontend/src/assets/locales/ru/translation.json @@ -33,6 +33,7 @@ "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the pipedrive window", "background.access.title": "Access denied", "background.access.subtitle": "Something went wrong or access denied", + "background.reinstall.title": "The document security token has expired", "background.reinstall.subtitle": "Something went wrong. Please reload or reinstall the app.", "document.new": "New Document", "button.upload": "Create or upload document", diff --git a/frontend/src/assets/token-error.svg b/frontend/src/assets/token-error.svg new file mode 100644 index 0000000..8c59c39 --- /dev/null +++ b/frontend/src/assets/token-error.svg @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/pages/Main/index.tsx b/frontend/src/pages/Main/index.tsx index b06c0a0..b689278 100644 --- a/frontend/src/pages/Main/index.tsx +++ b/frontend/src/pages/Main/index.tsx @@ -8,7 +8,7 @@ import { OnlyofficeBackgroundError } from "@layouts/ErrorBackground"; import { AuthToken } from "@context/TokenContext"; -import BackgroundError from "@assets/background-error.svg"; +import TokenError from "@assets/token-error.svg"; import { Main } from "./Main"; @@ -27,8 +27,11 @@ export const MainPage: React.FC = () => { )} {loadingError && ( } - title={t("background.error.title", "Error")} + Icon={} + title={t( + "background.reinstall.title", + "The document security token has expired" + )} subtitle={t( "background.reinstall.subtitle", "Something went wrong. Please reload or reinstall the app." From 7680e75f0f4dd51fa658a324dd083ea2b5a01ce3 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 28 Mar 2023 12:27:27 +0500 Subject: [PATCH 027/145] fix(files): button styles --- frontend/src/components/button/Button.tsx | 2 +- frontend/src/pages/Main/Main.tsx | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/button/Button.tsx b/frontend/src/components/button/Button.tsx index 19842ee..56fe905 100644 --- a/frontend/src/components/button/Button.tsx +++ b/frontend/src/components/button/Button.tsx @@ -31,7 +31,7 @@ export const OnlyofficeButton: React.FC = ({
)}
-
+
{ await sdk?.execute(Command.OPEN_MODAL, { From ceab23f7a579df75141b0eb810569541e62b5838 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 28 Mar 2023 15:18:55 +0500 Subject: [PATCH 028/145] fix(settings): banner position --- frontend/src/pages/Settings/index.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx index fb76b0b..8dad92c 100644 --- a/frontend/src/pages/Settings/index.tsx +++ b/frontend/src/pages/Settings/index.tsx @@ -127,7 +127,7 @@ export const SettingsPage: React.FC = () => { )} {!loading && !error && admin && ( <> -
+
{

-
+
{ onChange={(e) => setAddress(e.target.value)} />
-
+
{ type="password" />
-
+
{ onChange={(e) => setHeader(e.target.value)} />
-
+
{ onClick={handleSettings} />
-
+
From 8d11620f18ceea77fd8d40412486e478a2ca360b Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 28 Mar 2023 15:19:15 +0500 Subject: [PATCH 029/145] fix(creation): tile styles --- frontend/src/components/tile/Tile.tsx | 6 +++--- frontend/src/pages/Creation/Creation.tsx | 14 +++++++------- frontend/src/pages/Creation/index.tsx | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/tile/Tile.tsx b/frontend/src/components/tile/Tile.tsx index de22625..b097db7 100644 --- a/frontend/src/components/tile/Tile.tsx +++ b/frontend/src/components/tile/Tile.tsx @@ -21,11 +21,11 @@ export const OnlyofficeTile: React.FC = ({ const card = cx({ "px-5 py-3.5 rounded-lg transform shadow mb-5 outline-none": true, "transition duration-100 ease-linear": true, - "w-[108px] h-[94px]": true, + "h-[82px]": true, "max-h-36 flex flex-col justify-center": true, - "hover:-translate-y-[0.125rem] hover:shadow-xl cursor-pointer": !selected, + "hover:bg-gray-100 cursor-pointer": !selected, "bg-white": !selected, - "bg-gray-100 border": selected, + "bg-gray-200": selected, }); const spn = cx({ diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx index 8505de5..a8839ad 100644 --- a/frontend/src/pages/Creation/Creation.tsx +++ b/frontend/src/pages/Creation/Creation.tsx @@ -39,14 +39,14 @@ export const Creation: React.FC = () => { }, []); return ( -
-
+
+
-
+
{ onChange={(e) => setFile(e.target.value)} />
-
-
+
+
{ selected={fileType === "docx"} />
-
+
{ selected={fileType === "xlsx"} />
-
+
{ }); return ( -
+
setSelected(index)} From ef85b6e3ea3a656778d6466bf99dc234f7f6769b Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 28 Mar 2023 15:25:03 +0500 Subject: [PATCH 030/145] fix(input): background color --- frontend/src/components/input/Input.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/input/Input.tsx b/frontend/src/components/input/Input.tsx index 769fe22..6b3b5d0 100644 --- a/frontend/src/components/input/Input.tsx +++ b/frontend/src/components/input/Input.tsx @@ -33,7 +33,7 @@ export const OnlyofficeInput: React.FC = ({ "font-normal text-sm text-gray-700 appearance-none block select-auto": true, "text-xs": textSize === "xs", "w-full border rounded-sm h-10 px-4": true, - "border-gray-light bg-slate-100": valid, + "border-gray-light": valid, "border-red-600": !valid, "bg-slate-200": disabled, }); From 529058b2d62d7a75515ab1740f061d4aab1ce4f5 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 28 Mar 2023 15:50:47 +0500 Subject: [PATCH 031/145] fix(creation): modal resize issue --- frontend/src/pages/Creation/Creation.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx index a8839ad..3016ed9 100644 --- a/frontend/src/pages/Creation/Creation.tsx +++ b/frontend/src/pages/Creation/Creation.tsx @@ -28,12 +28,7 @@ export const Creation: React.FC = () => { useEffect(() => { new AppExtensionsSDK() - .initialize({ - size: { - height: 500, - width: 600, - }, - }) + .initialize() .then((s) => setSDK(s)) .catch(() => setSDK(null)); }, []); From d5606867c0a333c9fa542973a8a3e17a93def142 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 28 Mar 2023 16:22:25 +0500 Subject: [PATCH 032/145] fix(creation): tabs styles --- frontend/src/pages/Creation/index.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/frontend/src/pages/Creation/index.tsx b/frontend/src/pages/Creation/index.tsx index ea6fd63..d2c1f3c 100644 --- a/frontend/src/pages/Creation/index.tsx +++ b/frontend/src/pages/Creation/index.tsx @@ -30,17 +30,23 @@ export const CreatePage: React.FC = () => { > {t("button.creation.create", "Create")} {t("button.creation.upload", "Upload")} From fc26a66e5abfd42507abedfa5a4f6e0d1bcf8294 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 28 Mar 2023 17:03:22 +0500 Subject: [PATCH 033/145] fix(auth): waitgroup --- backend/services/auth/web/core/service/user.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/backend/services/auth/web/core/service/user.go b/backend/services/auth/web/core/service/user.go index fe4467f..b7ad53c 100644 --- a/backend/services/auth/web/core/service/user.go +++ b/backend/services/auth/web/core/service/user.go @@ -39,12 +39,12 @@ func (s userService) CreateUser(ctx context.Context, user domain.UserAccess) err } var wg sync.WaitGroup + wg.Add(2) errChan := make(chan error, 2) atokenChan := make(chan string, 1) rtokenChan := make(chan string, 1) go func() { - wg.Add(1) defer wg.Done() aToken, err := s.encryptor.Encrypt(user.AccessToken) if err != nil { @@ -55,7 +55,6 @@ func (s userService) CreateUser(ctx context.Context, user domain.UserAccess) err }() go func() { - wg.Add(1) defer wg.Done() rToken, err := s.encryptor.Encrypt(user.RefreshToken) if err != nil { @@ -103,6 +102,7 @@ func (s userService) GetUser(ctx context.Context, uid string) (domain.UserAccess } var wg sync.WaitGroup + wg.Add(2) errChan := make(chan error, 2) atokenChan := make(chan string, 1) rtokenChan := make(chan string, 1) @@ -115,7 +115,6 @@ func (s userService) GetUser(ctx context.Context, uid string) (domain.UserAccess s.logger.Debugf("found a user: %v", user) go func() { - wg.Add(1) defer wg.Done() aToken, err := s.encryptor.Decrypt(user.AccessToken) if err != nil { @@ -126,7 +125,6 @@ func (s userService) GetUser(ctx context.Context, uid string) (domain.UserAccess }() go func() { - wg.Add(1) defer wg.Done() rToken, err := s.encryptor.Decrypt(user.RefreshToken) if err != nil { @@ -163,12 +161,12 @@ func (s userService) UpdateUser(ctx context.Context, user domain.UserAccess) (do } var wg sync.WaitGroup + wg.Add(2) errChan := make(chan error, 2) atokenChan := make(chan string, 1) rtokenChan := make(chan string, 1) go func() { - wg.Add(1) defer wg.Done() aToken, err := s.encryptor.Encrypt(user.AccessToken) if err != nil { @@ -179,7 +177,6 @@ func (s userService) UpdateUser(ctx context.Context, user domain.UserAccess) (do }() go func() { - wg.Add(1) defer wg.Done() rToken, err := s.encryptor.Encrypt(user.RefreshToken) if err != nil { From 44dfa9e8b7281067f6eefbc1b498bce2c871d352 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 28 Mar 2023 17:30:13 +0500 Subject: [PATCH 034/145] chore: cleanup --- backend/services/builder/web/handler/build.go | 5 ++--- backend/services/callback/scripts/{Makefile => .keep} | 0 backend/services/gateway/scripts/{Makefile => .keep} | 0 backend/services/settings/web/core/adapter/mongo.go | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) rename backend/services/callback/scripts/{Makefile => .keep} (100%) rename backend/services/gateway/scripts/{Makefile => .keep} (100%) diff --git a/backend/services/builder/web/handler/build.go b/backend/services/builder/web/handler/build.go index 5e5216f..51a88f9 100644 --- a/backend/services/builder/web/handler/build.go +++ b/backend/services/builder/web/handler/build.go @@ -55,12 +55,12 @@ func NewConfigHandler( func (c ConfigHandler) processConfig(user response.UserResponse, req request.BuildConfigRequest, ctx context.Context) (response.BuildConfigResponse, error) { var config response.BuildConfigResponse var wg sync.WaitGroup + wg.Add(2) usrChan := make(chan model.User, 1) settingsChan := make(chan response.DocSettingsResponse, 1) errorsChan := make(chan error, 2) go func() { - wg.Add(1) defer wg.Done() u, err := c.apiClient.GetMe(ctx, model.Token{ AccessToken: user.AccessToken, @@ -82,7 +82,6 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui }() go func() { - wg.Add(1) defer wg.Done() var docs response.DocSettingsResponse if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:settings", c.namespace), "SettingsSelectHandler.GetSettings", fmt.Sprint(req.CID)), &docs); err != nil { @@ -91,7 +90,7 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui return } - if docs.DocAddress == "" || docs.DocSecret == "" { + if docs.DocAddress == "" || docs.DocSecret == "" || docs.DocHeader == "" { c.logger.Debugf("no settings found") errorsChan <- _ErrNoSettingsFound return diff --git a/backend/services/callback/scripts/Makefile b/backend/services/callback/scripts/.keep similarity index 100% rename from backend/services/callback/scripts/Makefile rename to backend/services/callback/scripts/.keep diff --git a/backend/services/gateway/scripts/Makefile b/backend/services/gateway/scripts/.keep similarity index 100% rename from backend/services/gateway/scripts/Makefile rename to backend/services/gateway/scripts/.keep diff --git a/backend/services/settings/web/core/adapter/mongo.go b/backend/services/settings/web/core/adapter/mongo.go index a350bc3..aa06bb0 100644 --- a/backend/services/settings/web/core/adapter/mongo.go +++ b/backend/services/settings/web/core/adapter/mongo.go @@ -17,7 +17,6 @@ import ( ) var _ErrInvalidCompanyID error = errors.New("invalid cid format") -var _ErrUserAlreadyExists error = errors.New("user already exists") type docSettingsCollection struct { mgm.DefaultModel `bson:",inline"` From 1a25c47f3f3e3b9009d9fbd87994dc946b2ae459 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 28 Mar 2023 17:45:28 +0500 Subject: [PATCH 035/145] fix(context): token context initial timeout --- frontend/src/context/TokenContext.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/context/TokenContext.tsx b/frontend/src/context/TokenContext.tsx index 8047051..c5b4f6e 100644 --- a/frontend/src/context/TokenContext.tsx +++ b/frontend/src/context/TokenContext.tsx @@ -36,7 +36,7 @@ export const TokenProvider: React.FC = ({ children }) => { } } timerID = setTimeout(update, AuthToken.expires_at - Date.now() - 100); - }, 1000); + }, 0); }) .catch(() => null); From 54e9d08bc9d0ab7aeba19758a3d6375a7ae63d10 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 28 Mar 2023 17:48:05 +0500 Subject: [PATCH 036/145] fix: removed icon from error background --- frontend/src/layout/ErrorBackground.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/layout/ErrorBackground.tsx b/frontend/src/layout/ErrorBackground.tsx index 27b4cdf..f5e861c 100644 --- a/frontend/src/layout/ErrorBackground.tsx +++ b/frontend/src/layout/ErrorBackground.tsx @@ -4,8 +4,6 @@ import { OnlyofficeButton } from "@components/button"; import { OnlyofficeSubtitle } from "@components/title"; import { OnlyofficeError } from "@components/error/Error"; -import BackgroundError from "@assets/background-error.svg"; - type ErrorProps = { Icon: any; title: string; From 50307fc7dafd4a317f644cd9cb991f7bd8596d45 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 28 Mar 2023 17:50:48 +0500 Subject: [PATCH 037/145] fix(services): add timeouts --- frontend/src/services/file.ts | 1 + frontend/src/services/me.ts | 2 ++ frontend/src/services/settings.ts | 1 + frontend/src/services/user.ts | 1 + 4 files changed, 5 insertions(+) diff --git a/frontend/src/services/file.ts b/frontend/src/services/file.ts index 66d6e30..7cd9344 100644 --- a/frontend/src/services/file.ts +++ b/frontend/src/services/file.ts @@ -43,6 +43,7 @@ export const deleteFile = async ( Authorization: `Bearer ${AuthToken.access_token}`, }, signal, + timeout: 4500, }); return res.status === 200; diff --git a/frontend/src/services/me.ts b/frontend/src/services/me.ts index e9da6da..a76060f 100644 --- a/frontend/src/services/me.ts +++ b/frontend/src/services/me.ts @@ -19,6 +19,7 @@ export const getMe = async (sdk: AppExtensionsSDK) => { "Content-Type": "application/json", "X-Pipedrive-App-Context": pctx.token, }, + timeout: 5000, }); return { response: res.data }; @@ -32,6 +33,7 @@ export const getPipedriveMe = async (url: string) => { "Content-Type": "application/json", Authorization: `Bearer ${AuthToken.access_token}`, }, + timeout: 5000, }); return res.data; }; diff --git a/frontend/src/services/settings.ts b/frontend/src/services/settings.ts index 813c121..d3513e0 100644 --- a/frontend/src/services/settings.ts +++ b/frontend/src/services/settings.ts @@ -47,6 +47,7 @@ export const getSettings = async (sdk: AppExtensionsSDK) => { "Content-Type": "application/json", "X-Pipedrive-App-Context": pctx.token, }, + timeout: 5000, }); return settings.data; diff --git a/frontend/src/services/user.ts b/frontend/src/services/user.ts index 99fd92d..cae74f2 100644 --- a/frontend/src/services/user.ts +++ b/frontend/src/services/user.ts @@ -14,6 +14,7 @@ export const fetchUsers = async ( Authorization: `Bearer ${AuthToken.access_token}`, }, signal, + timeout: 4000, }); return { From ea724b55cf8b4bd6782c320329d015e45b2f8feb Mon Sep 17 00:00:00 2001 From: Sergey Linnik Date: Tue, 28 Mar 2023 18:31:32 +0500 Subject: [PATCH 038/145] changelog --- AUTHORS.md | 4 ++++ CHANGELOG.md | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 AUTHORS.md create mode 100644 CHANGELOG.md diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000..686d12b --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,4 @@ +# Authors + +* Ascensio System SIA: + diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d00fa3c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Change Log + +## +## Added +- configuration page +- collaboration editing for DOCX, XLSX, PPTX +- view option DOC, DOCX, DOCM, DOT, DOTX, DOTM, ODT, FODT, OTT, RTF, TXT, HTML, HTM, MHT, XML, PDF, DJVU, FB2, EPUB, XPS, OXPS, XLS, XLSX, XLSM, XLT, XLTX, XLTM, ODS, FODS, OTS, CSV, PPS, PPSX, PPSM, PPT, PPTX, PPTM, POT, POTX, POTM, ODP, FODP, OTP +- JWT support \ No newline at end of file From a075d59fb393e022377b672ea3f0fff0b51ce096 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 28 Mar 2023 18:39:23 +0500 Subject: [PATCH 039/145] doc: readme --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index e69de29..7d29a48 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,32 @@ +# ONLYOFFICE App for Pipedrive + +This app allows users to edit and collaborate on office documents right within Pipedrive deals. + +## App installation + +You can add the ONLYOFFICE app from the Pipedrive App Marketplace. +To do it, you need to sign into your account and allow the requested app permissions. + +*ONLYOFFICE app is using the connection to the special ONLYOFFICE Document Server instance to ensure document processing.* + +## App usage + +The app allows working with office documents directly within the Pipedrive frontend. + +**Creating and editing files** + +You can prepare and keep necessary materials such as text documents, spreadsheets, and presentations directly within your Pipedrive session. +Just click the *Create with ONLYOFFICE* button on the main app screen. + +To edit the created files, reach to the *ONLYOFFICE Documents* list on the main screen of the ONLYOFFICE app. + +## Project info + +Official website: [www.onlyoffice.com](https://www.onlyoffice.com/) + +Code repository: [github.com/ONLYOFFICE/onlyoffice-pipedrive](https://github.com/ONLYOFFICE/onlyoffice-pipedrive) + +## User feedback and support + +In case of technical problems, the best way to get help is to submit your issues [here](https://github.com/ONLYOFFICE/onlyoffice-pipedrive/issues). +Alternatively, you can contact ONLYOFFICE team on [forum.onlyoffice.com](https://forum.onlyoffice.com/). From 769674265f1f243929bf8eecb345801503040db1 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 28 Mar 2023 20:07:19 +0500 Subject: [PATCH 040/145] doc: licenses --- backend/3rd-Party.license | 89 ++++++++++ backend/licenses/alice.license | 20 +++ backend/licenses/chi-cors.license | 21 +++ backend/licenses/chi.license | 20 +++ backend/licenses/cli.license | 21 +++ backend/licenses/elastic.license | 20 +++ backend/licenses/fx.license | 19 ++ backend/licenses/gin.license | 21 +++ backend/licenses/go-envconfig.license | 202 ++++++++++++++++++++++ backend/licenses/go-limiter.license | 202 ++++++++++++++++++++++ backend/licenses/go-micro-plugins.license | 191 ++++++++++++++++++++ backend/licenses/go-micro.license | 191 ++++++++++++++++++++ backend/licenses/gorilla-sessions.license | 27 +++ backend/licenses/health-go.license | 201 +++++++++++++++++++++ backend/licenses/mapstructure.license | 21 +++ backend/licenses/mongo-driver.license | 201 +++++++++++++++++++++ backend/licenses/opentelemetry-go.license | 201 +++++++++++++++++++++ backend/licenses/prom-client.license | 201 +++++++++++++++++++++ backend/licenses/ratelimit.license | 21 +++ backend/licenses/sync.license | 27 +++ backend/licenses/testify.license | 21 +++ backend/licenses/uuid.license | 27 +++ backend/licenses/yaml.license | 50 ++++++ 23 files changed, 2015 insertions(+) create mode 100644 backend/3rd-Party.license create mode 100644 backend/licenses/alice.license create mode 100644 backend/licenses/chi-cors.license create mode 100644 backend/licenses/chi.license create mode 100644 backend/licenses/cli.license create mode 100644 backend/licenses/elastic.license create mode 100644 backend/licenses/fx.license create mode 100644 backend/licenses/gin.license create mode 100644 backend/licenses/go-envconfig.license create mode 100644 backend/licenses/go-limiter.license create mode 100644 backend/licenses/go-micro-plugins.license create mode 100644 backend/licenses/go-micro.license create mode 100644 backend/licenses/gorilla-sessions.license create mode 100644 backend/licenses/health-go.license create mode 100644 backend/licenses/mapstructure.license create mode 100644 backend/licenses/mongo-driver.license create mode 100644 backend/licenses/opentelemetry-go.license create mode 100644 backend/licenses/prom-client.license create mode 100644 backend/licenses/ratelimit.license create mode 100644 backend/licenses/sync.license create mode 100644 backend/licenses/testify.license create mode 100644 backend/licenses/uuid.license create mode 100644 backend/licenses/yaml.license diff --git a/backend/3rd-Party.license b/backend/3rd-Party.license new file mode 100644 index 0000000..de4ba19 --- /dev/null +++ b/backend/3rd-Party.license @@ -0,0 +1,89 @@ +ONLYOFFICE Applications example uses code from the following 3rd party projects: + +gin - Gin is a web framework written in Go. It features a martini-like API with performance that is up to 40 times faster thanks to httprouter. (https://github.com/gin-gonic/gin/blob/master/LICENSE) +License: MIT +License File: gin.license + +chi - lightweight, idiomatic and composable router for building Go HTTP services. (https://github.com/go-chi/chi/blob/master/LICENSE) +License: MIT +License File: chi.license + +chi-cors: - CORS net/http middleware for Go. (https://github.com/go-chi/cors/blob/master/LICENSE) +License: MIT +License File: chi-cors.license + +go-micro-plugins - Go Micro plugins and integrations. (https://github.com/go-micro/plugins/blob/main/LICENSE) +License: Apache-2.0 +License File: go-micro-plugins.license + +uuid - Go package for UUIDs based on RFC 4122 and DCE 1.1: Authentication and Security Services. (https://github.com/google/uuid/blob/master/LICENSE) +License: BSD 3-Clause +License File: uuid.license + +gorilla-sessions - Package gorilla/sessions provides cookie and filesystem sessions and infrastructure for custom session backends. (https://github.com/gorilla/sessions) +License: BSD 3-Clause +License File: gorilla-sessions.license + +health-go - Library to provide basic healthcheck functionality to Go applications. (https://github.com/hellofresh/health-go/blob/master/LICENSE.txt) +License: Apache-2.0 +License File: health-go.license + +alice - Painless middleware chaining for Go. (https://github.com/justinas/alice/blob/master/LICENSE) +License: MIT +License File: alice.license + +mapstructure - Go library for decoding generic map values into native Go structures and vice versa. (https://github.com/mitchellh/mapstructure/blob/main/LICENSE) +License: MIT +License File: mapstructure.license + +elastic - Elastic is an Elasticsearch client for the Go programming language. (https://github.com/olivere/elastic/blob/release-branch.v7/LICENSE) +License: MIT +License File: elastic.license + +go_client - Prometheus instrumentation library for Go applications. (https://github.com/prometheus/client_golang/blob/main/LICENSE) +License: Apache-2.0 +License File: prom-client.license + +go-envconfig - A Go library for parsing struct tags from environment variables. (https://github.com/sethvargo/go-envconfig/blob/main/LICENSE) +License: Apache-2.0 +License File: go-envconfig.license + +go-limiter - A supersonic rate limiting package for Go with HTTP middleware. (https://github.com/sethvargo/go-limiter/blob/main/LICENSE) +License: Apache-2.0 +License File: go-limiter.license + +testify - A toolkit with common assertions and mocks that plays nicely with the standard library. (https://github.com/stretchr/testify/blob/master/LICENSE) +License: MIT +License File: testify.license + +cli - A simple, fast, and fun package for building command line apps in Go. (https://github.com/urfave/cli/blob/main/LICENSE) +License: MIT +License File: cli.license + +go-micro - A Go microservices framework. (https://github.com/go-micro/go-micro/blob/master/LICENSE) +License: Apache-2.0 +License File: go-micro.license + +mongo-driver - The MongoDB supported driver for Go. (https://pkg.go.dev/go.mongodb.org/mongo-driver?tab=licenses) +License: Apache-2.0 +License File: mongo-driver.license + +opentelemetry-go - OpenTelemetry-Go is the Go implementation of OpenTelemetry. It provides a set of APIs to directly measure performance and behavior of your software and send this data to observability platforms. (https://pkg.go.dev/go.opentelemetry.io/otel?tab=licenses) +License: Apache-2.0 +License File: opentelemetry-go.license + +fx - Fx is a dependency injection system for Go. (https://pkg.go.dev/go.uber.org/fx?tab=licenses) +License: MIT +License File: fx.license + +ratelimit - This package provides a Golang implementation of the leaky-bucket rate limit algorithm. This implementation refills the bucket based on the time elapsed between requests instead of requiring an interval clock to fill the bucket discretely. (https://pkg.go.dev/go.uber.org/ratelimit?tab=licenses) +License: MIT +License File: ratelimit.license + +sync - This repository provides Go concurrency primitives in addition to the ones provided by the language and "sync" and "sync/atomic" packages. (https://pkg.go.dev/golang.org/x/sync?tab=licenses) +License: BSD 3-Clause +License File: sync.license + +yaml - YAML support for the Go language. (https://github.com/go-yaml/yaml/blob/v3/LICENSE) +License: MIT +License File: yaml.license diff --git a/backend/licenses/alice.license b/backend/licenses/alice.license new file mode 100644 index 0000000..0d0d352 --- /dev/null +++ b/backend/licenses/alice.license @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014 Justinas Stankevicius + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/licenses/chi-cors.license b/backend/licenses/chi-cors.license new file mode 100644 index 0000000..aee6182 --- /dev/null +++ b/backend/licenses/chi-cors.license @@ -0,0 +1,21 @@ +Copyright (c) 2014 Olivier Poitrey +Copyright (c) 2016-Present https://github.com/go-chi authors + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/licenses/chi.license b/backend/licenses/chi.license new file mode 100644 index 0000000..d99f02f --- /dev/null +++ b/backend/licenses/chi.license @@ -0,0 +1,20 @@ +Copyright (c) 2015-present Peter Kieltyka (https://github.com/pkieltyka), Google Inc. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/licenses/cli.license b/backend/licenses/cli.license new file mode 100644 index 0000000..2c84c78 --- /dev/null +++ b/backend/licenses/cli.license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 urfave/cli maintainers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/backend/licenses/elastic.license b/backend/licenses/elastic.license new file mode 100644 index 0000000..8b22cdb --- /dev/null +++ b/backend/licenses/elastic.license @@ -0,0 +1,20 @@ +The MIT License (MIT) +Copyright © 2012-2015 Oliver Eilhard + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/backend/licenses/fx.license b/backend/licenses/fx.license new file mode 100644 index 0000000..20e81ea --- /dev/null +++ b/backend/licenses/fx.license @@ -0,0 +1,19 @@ +Copyright (c) 2016-2018 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/backend/licenses/gin.license b/backend/licenses/gin.license new file mode 100644 index 0000000..1ff7f37 --- /dev/null +++ b/backend/licenses/gin.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Manuel Martínez-Almeida + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/backend/licenses/go-envconfig.license b/backend/licenses/go-envconfig.license new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/backend/licenses/go-envconfig.license @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/backend/licenses/go-limiter.license b/backend/licenses/go-limiter.license new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/backend/licenses/go-limiter.license @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/backend/licenses/go-micro-plugins.license b/backend/licenses/go-micro-plugins.license new file mode 100644 index 0000000..7d74e02 --- /dev/null +++ b/backend/licenses/go-micro-plugins.license @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2015 Asim Aslam. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/backend/licenses/go-micro.license b/backend/licenses/go-micro.license new file mode 100644 index 0000000..7d74e02 --- /dev/null +++ b/backend/licenses/go-micro.license @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2015 Asim Aslam. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/backend/licenses/gorilla-sessions.license b/backend/licenses/gorilla-sessions.license new file mode 100644 index 0000000..6903df6 --- /dev/null +++ b/backend/licenses/gorilla-sessions.license @@ -0,0 +1,27 @@ +Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/backend/licenses/health-go.license b/backend/licenses/health-go.license new file mode 100644 index 0000000..329475a --- /dev/null +++ b/backend/licenses/health-go.license @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 HelloFresh SE + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/backend/licenses/mapstructure.license b/backend/licenses/mapstructure.license new file mode 100644 index 0000000..f9c841a --- /dev/null +++ b/backend/licenses/mapstructure.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Mitchell Hashimoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/backend/licenses/mongo-driver.license b/backend/licenses/mongo-driver.license new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/backend/licenses/mongo-driver.license @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/backend/licenses/opentelemetry-go.license b/backend/licenses/opentelemetry-go.license new file mode 100644 index 0000000..4c9ad98 --- /dev/null +++ b/backend/licenses/opentelemetry-go.license @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/backend/licenses/prom-client.license b/backend/licenses/prom-client.license new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/backend/licenses/prom-client.license @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/backend/licenses/ratelimit.license b/backend/licenses/ratelimit.license new file mode 100644 index 0000000..546ac3c --- /dev/null +++ b/backend/licenses/ratelimit.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/backend/licenses/sync.license b/backend/licenses/sync.license new file mode 100644 index 0000000..6a66aea --- /dev/null +++ b/backend/licenses/sync.license @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/backend/licenses/testify.license b/backend/licenses/testify.license new file mode 100644 index 0000000..4b0421c --- /dev/null +++ b/backend/licenses/testify.license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/backend/licenses/uuid.license b/backend/licenses/uuid.license new file mode 100644 index 0000000..5dc6826 --- /dev/null +++ b/backend/licenses/uuid.license @@ -0,0 +1,27 @@ +Copyright (c) 2009,2014 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/backend/licenses/yaml.license b/backend/licenses/yaml.license new file mode 100644 index 0000000..2683e4b --- /dev/null +++ b/backend/licenses/yaml.license @@ -0,0 +1,50 @@ + +This project is covered by two different licenses: MIT and Apache. + +#### MIT License #### + +The following files were ported to Go from C files of libyaml, and thus +are still covered by their original MIT license, with the additional +copyright staring in 2011 when the project was ported over: + + apic.go emitterc.go parserc.go readerc.go scannerc.go + writerc.go yamlh.go yamlprivateh.go + +Copyright (c) 2006-2010 Kirill Simonov +Copyright (c) 2006-2011 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +### Apache License ### + +All the remaining project files are covered by the Apache license: + +Copyright (c) 2011-2019 Canonical Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. From 0c4bf7203b0a7fb404bc792b412390f3a228dbde Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 28 Mar 2023 20:07:55 +0500 Subject: [PATCH 041/145] doc: fix typo --- backend/3rd-Party.license | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/3rd-Party.license b/backend/3rd-Party.license index de4ba19..3c70862 100644 --- a/backend/3rd-Party.license +++ b/backend/3rd-Party.license @@ -1,4 +1,4 @@ -ONLYOFFICE Applications example uses code from the following 3rd party projects: +ONLYOFFICE Pipedrive uses code from the following 3rd party projects: gin - Gin is a web framework written in Go. It features a martini-like API with performance that is up to 40 times faster thanks to httprouter. (https://github.com/gin-gonic/gin/blob/master/LICENSE) License: MIT From 7e4701b64f012c03334a0927cfd8b438f3c26434 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 28 Mar 2023 20:40:05 +0500 Subject: [PATCH 042/145] fix(webpack): pass envs --- frontend/webpack.production.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/webpack.production.js b/frontend/webpack.production.js index e08e382..966045f 100644 --- a/frontend/webpack.production.js +++ b/frontend/webpack.production.js @@ -8,7 +8,9 @@ module.exports = merge(common, { mode: "production", plugins: [ new webpack.DefinePlugin({ + 'process.env.BACKEND_GATEWAY': JSON.stringify(process.env.BACKEND_GATEWAY), 'process.env.PIPEDRIVE_CREATE_MODAL_ID': JSON.stringify(process.env.PIPEDRIVE_CREATE_MODAL_ID), + 'process.env.PIPEDRIVE_EDITOR_MODAL_ID': JSON.stringify(process.env.PIPEDRIVE_EDITOR_MODAL_ID), }), ], }); From c436a2325214cf3481f6d01b93129059df6cae17 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 29 Mar 2023 00:06:45 +0500 Subject: [PATCH 043/145] doc: frontend licenses --- frontend/3rd-Party.license | 205 ++++++++++++++++++ frontend/licenses/airbnb.license | 21 ++ frontend/licenses/autoprefixer.license | 20 ++ frontend/licenses/axios-retry.license | 13 ++ frontend/licenses/axios.license | 7 + frontend/licenses/babel-resolver.license | 21 ++ frontend/licenses/babel.license | 22 ++ frontend/licenses/classnames.license | 21 ++ frontend/licenses/copy-plugin.license | 20 ++ frontend/licenses/css-loader.license | 20 ++ frontend/licenses/dotenv-webpack.license | 21 ++ frontend/licenses/dotenv.license | 23 ++ frontend/licenses/editor.license | 201 +++++++++++++++++ frontend/licenses/eslint-alias.license | 21 ++ .../licenses/eslint-plugin-import.license | 21 ++ frontend/licenses/eslint-plugin-react.license | 21 ++ frontend/licenses/eslint-prettier.license | 21 ++ frontend/licenses/eslint-resolver.license | 21 ++ .../eslint-typescript-resolver.license | 5 + frontend/licenses/eslint.license | 19 ++ frontend/licenses/html-webpack-plugin.license | 20 ++ frontend/licenses/i18next-backend.license | 19 ++ frontend/licenses/i18next-detector.license | 21 ++ frontend/licenses/i18next.license | 21 ++ frontend/licenses/jest-dom.license | 20 ++ frontend/licenses/jest.license | 21 ++ frontend/licenses/jsx-a11y.license | 8 + frontend/licenses/md5.license | 27 +++ frontend/licenses/pipedrive.license | 21 ++ frontend/licenses/postcss-loader.license | 20 ++ frontend/licenses/postcss.license | 20 ++ frontend/licenses/prettier.license | 7 + frontend/licenses/react-dropzone.license | 21 ++ frontend/licenses/react-i18next.license | 21 ++ frontend/licenses/react-query.license | 21 ++ frontend/licenses/react-router.license | 22 ++ frontend/licenses/react-tabs.license | 19 ++ frontend/licenses/react-testing.license | 20 ++ frontend/licenses/react.license | 21 ++ frontend/licenses/style-loader.license | 20 ++ frontend/licenses/svgr.license | 7 + frontend/licenses/tailwindcss.license | 21 ++ frontend/licenses/ts-jest.license | 21 ++ frontend/licenses/ts-loader.license | 21 ++ frontend/licenses/ts-node.license | 21 ++ frontend/licenses/typescript-eslint.license | 26 +++ frontend/licenses/typescript.license | 55 +++++ frontend/licenses/valtio.license | 21 ++ frontend/licenses/webpack-cli.license | 20 ++ frontend/licenses/webpack-dev-server.license | 20 ++ frontend/licenses/webpack-merge.license | 20 ++ frontend/licenses/webpack.license | 20 ++ 52 files changed, 1407 insertions(+) create mode 100644 frontend/3rd-Party.license create mode 100644 frontend/licenses/airbnb.license create mode 100644 frontend/licenses/autoprefixer.license create mode 100644 frontend/licenses/axios-retry.license create mode 100644 frontend/licenses/axios.license create mode 100644 frontend/licenses/babel-resolver.license create mode 100644 frontend/licenses/babel.license create mode 100644 frontend/licenses/classnames.license create mode 100644 frontend/licenses/copy-plugin.license create mode 100644 frontend/licenses/css-loader.license create mode 100644 frontend/licenses/dotenv-webpack.license create mode 100644 frontend/licenses/dotenv.license create mode 100644 frontend/licenses/editor.license create mode 100644 frontend/licenses/eslint-alias.license create mode 100644 frontend/licenses/eslint-plugin-import.license create mode 100644 frontend/licenses/eslint-plugin-react.license create mode 100644 frontend/licenses/eslint-prettier.license create mode 100644 frontend/licenses/eslint-resolver.license create mode 100644 frontend/licenses/eslint-typescript-resolver.license create mode 100644 frontend/licenses/eslint.license create mode 100644 frontend/licenses/html-webpack-plugin.license create mode 100644 frontend/licenses/i18next-backend.license create mode 100644 frontend/licenses/i18next-detector.license create mode 100644 frontend/licenses/i18next.license create mode 100644 frontend/licenses/jest-dom.license create mode 100644 frontend/licenses/jest.license create mode 100644 frontend/licenses/jsx-a11y.license create mode 100644 frontend/licenses/md5.license create mode 100644 frontend/licenses/pipedrive.license create mode 100644 frontend/licenses/postcss-loader.license create mode 100644 frontend/licenses/postcss.license create mode 100644 frontend/licenses/prettier.license create mode 100644 frontend/licenses/react-dropzone.license create mode 100644 frontend/licenses/react-i18next.license create mode 100644 frontend/licenses/react-query.license create mode 100644 frontend/licenses/react-router.license create mode 100644 frontend/licenses/react-tabs.license create mode 100644 frontend/licenses/react-testing.license create mode 100644 frontend/licenses/react.license create mode 100644 frontend/licenses/style-loader.license create mode 100644 frontend/licenses/svgr.license create mode 100644 frontend/licenses/tailwindcss.license create mode 100644 frontend/licenses/ts-jest.license create mode 100644 frontend/licenses/ts-loader.license create mode 100644 frontend/licenses/ts-node.license create mode 100644 frontend/licenses/typescript-eslint.license create mode 100644 frontend/licenses/typescript.license create mode 100644 frontend/licenses/valtio.license create mode 100644 frontend/licenses/webpack-cli.license create mode 100644 frontend/licenses/webpack-dev-server.license create mode 100644 frontend/licenses/webpack-merge.license create mode 100644 frontend/licenses/webpack.license diff --git a/frontend/3rd-Party.license b/frontend/3rd-Party.license new file mode 100644 index 0000000..1fb379b --- /dev/null +++ b/frontend/3rd-Party.license @@ -0,0 +1,205 @@ +ONLYOFFICE Pipedrive uses code from the following 3rd party projects: + +document-editor-react - React component for ONLYOFFICE Document Server. (https://github.com/ONLYOFFICE/document-editor-react/blob/master/LICENSE) +License: Apache-2.0 +License File: editor.license + +pipedrive/app-extensions-sdk - SDK for Pipedrive app extensions. (https://github.com/pipedrive/app-extensions-sdk/blob/master/LICENSE.md) +License: MIT +License File: pipedrive.license + +axios - Promise based HTTP client for the browser and node.js. (https://github.com/axios/axios/blob/v1.x/LICENSE) +License: MIT +License File: axios.license + +axios-retry - Axios plugin that intercepts failed requests and retries them whenever possible. (https://github.com/softonic/axios-retry/blob/master/LICENSE) +License: Apache-2.0 +License File: axios-retry.license + +classnames - A simple javascript utility for conditionally joining classNames together. (https://github.com/JedWatson/classnames/blob/main/LICENSE) +License: MIT +License File: classnames.license + +i18next - i18next: learn once - translate everywhere. (https://github.com/i18next/i18next/blob/master/LICENSE) +License: MIT +License File: i18next.license + +i18next-browser - languagedetector - language detector used in browser environment for i18next. (https://github.com/i18next/i18next-browser-languageDetector/blob/master/LICENSE) +License: MIT +License File: i18next-detector.license + +i18next-http-backend - i18next-http-backend is a backend layer for i18next using in Node.js, in the browser and for Deno. (https://github.com/i18next/i18next-http-backend/blob/master/licence) +License: MIT +License File: i18next-backend.license + +md5 - a JavaScript function for hashing messages with MD5. (https://github.com/pvorb/node-md5/blob/master/LICENSE) +License: BSD 3-Clause +License File: md5.license + +react - The library for web and native user interfaces. (https://github.com/facebook/react/blob/main/LICENSE) +License: MIT +License File: react.license + +react-dropzone - Simple HTML5 drag-drop zone with React.js. (https://github.com/react-dropzone/react-dropzone/blob/master/LICENSE) +License: MIT +License File: react-dropzone.license + +react-i18next - Internationalization for react done right. Using the i18next i18n ecosystem. (https://github.com/i18next/react-i18next/blob/master/LICENSE) +License: MIT +License File: react-i18next.license + +react-query - Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query. (https://github.com/TanStack/query/blob/main/LICENSE) +License: MIT +License File: react-query.license + +react-router-dom - Declarative routing for React. (https://github.com/remix-run/react-router/blob/main/LICENSE.md) +License: MIT +License File: react-router.license + +react-tabs - An accessible and easy tab component for ReactJS. (https://github.com/reactjs/react-tabs/blob/main/LICENSE) +License: MIT +License File: react-tabs.license + +valtio - Valtio makes proxy-state simple for React and Vanilla. (https://github.com/pmndrs/valtio/blob/main/LICENSE) +License: MIT +License File: valtio.license + +babel/core - Babel is a compiler for writing next generation JavaScript. (https://github.com/babel/babel/blob/main/LICENSE) +License: MIT +License File: babel.license + +svgr - Transform SVGs into React components. (https://github.com/gregberge/svgr/blob/main/LICENSE) +License: MIT +License File: svgr.license + +jest-dom - Custom jest matchers to test the state of the DOM. (https://github.com/testing-library/jest-dom/blob/main/LICENSE) +License: MIT +License File: jest-dom.license + +react-testing-library - Simple and complete React DOM testing utilities that encourage good testing practices. (https://github.com/testing-library/react-testing-library/blob/main/LICENSE) +License: MIT +License File: react-testing.license + +typescript-eslint - Monorepo for all the tooling which enables ESLint to support TypeScript. (https://github.com/typescript-eslint/typescript-eslint/blob/main/LICENSE) +License: MIT +License File: typescript-eslint.license + +autoprefixer - Parse CSS and add vendor prefixes to rules by Can I Use. (https://github.com/postcss/autoprefixer/blob/main/LICENSE) +License: MIT +License File: autoprefixer.license + +babel-plugin-module-resolver - Custom module resolver plugin for Babel. (https://github.com/tleunen/babel-plugin-module-resolver/blob/master/LICENSE.md) +License: MIT +License File: babel-resolver.license + +copy-webpack-plugin - Copy files and directories with webpack. (https://github.com/webpack-contrib/copy-webpack-plugin/blob/master/LICENSE) +License: MIT +License File: copy-plugin.license + +css-loader - CSS Loader. (https://github.com/webpack-contrib/css-loader/blob/master/LICENSE) +License: MIT +License File: css-loader.license + +dotenv - Loads environment variables from .env for nodejs projects. (https://github.com/motdotla/dotenv/blob/master/LICENSE) +License: MIT +License File: dotenv.license + +dotenv-webpack - A secure webpack plugin that supports dotenv and other environment variables and only exposes what you choose and use. (https://github.com/mrsteele/dotenv-webpack/blob/master/LICENSE) +License: MIT +License File: dotenv-webpack.license + +eslint - Find and fix problems in your JavaScript code. (https://github.com/eslint/eslint/blob/main/LICENSE) +License: MIT +License File: eslint.license + +airbnb-javascript - JavaScript Style Guide. (https://github.com/airbnb/javascript/blob/master/LICENSE.md) +License: MIT +License File: airbnb.license + +eslint-config-prettier - Turns off all rules that are unnecessary or might conflict with Prettier. (https://github.com/prettier/eslint-config-prettier/blob/main/LICENSE) +License: MIT +License File: eslint-prettier.license + +eslint-import-resolver-alias - a simple Node behavior import resolution plugin for eslint-plugin-import, supporting module alias. (https://github.com/johvin/eslint-import-resolver-alias/blob/master/LICENSE) +License: MIT +License File: eslint-alias.license + +eslint-import-resolver-babel-module - Custom eslint resolve for babel-plugin-module-resolver. (https://github.com/tleunen/eslint-import-resolver-babel-module/blob/master/LICENSE.md) +License: MIT +License File: eslint-resolver.license + +eslint-import-resolver-typescript - This plugin adds `TypeScript` support to `eslint-plugin-import`. (https://github.com/import-js/eslint-import-resolver-typescript/blob/master/LICENSE) +License: ISC +License File: eslint-typescript-resolver.license + +eslint-plugin-import - ESLint plugin with rules that help validate proper imports. (https://github.com/import-js/eslint-plugin-import/blob/main/LICENSE) +License: MIT +License File: eslint-plugin-import.license + +eslint-plugin-jsx-a11y - Static AST checker for a11y rules on JSX elements. (https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/LICENSE.md) +License: MIT +License File: jsx-a11y.license + +eslint-plugin-react - React-specific linting rules for ESLint. (https://github.com/jsx-eslint/eslint-plugin-react/blob/master/LICENSE) +License: MIT +License File: eslint-plugin-react.license + +html-webpack-plugin - Simplifies creation of HTML files to serve your webpack bundles. (https://github.com/jantimon/html-webpack-plugin/blob/main/LICENSE) +License: MIT +License File: html-webpack-plugin.license + +jest - Delightful JavaScript Testing. (https://github.com/facebook/jest/blob/main/LICENSE) +License: MIT +License File: jest.license + +postcss - Transforming styles with JS plugins. (https://github.com/postcss/postcss/blob/main/LICENSE) +License: MIT +License File: postcss.license + +postcss-loader - PostCSS loader for webpack. (https://github.com/webpack-contrib/postcss-loader/blob/master/LICENSE) +License: MIT +License File: postcss-loader.license + +prettier - Prettier is an opinionated code formatter. (https://github.com/prettier/prettier/blob/main/LICENSE) +License: MIT +License File: prettier.license + +style-loader - Style Loader. (https://github.com/webpack-contrib/style-loader/blob/master/LICENSE) +License: MIT +License File: style-loader.license + +tailwindcss - A utility-first CSS framework for rapid UI development. (https://github.com/tailwindlabs/tailwindcss/blob/master/LICENSE) +License: MIT +License File: tailwindcss.license + +ts-jest - A Jest transformer with source map support that lets you use Jest to test projects written in TypeScript. (https://github.com/kulshekhar/ts-jest/blob/main/LICENSE.md) +License: MIT +License File: ts-jest.license + +ts-loader - TypeScript loader for webpack. (https://github.com/TypeStrong/ts-loader/blob/main/LICENSE) +License: MIT +License File: ts-loader.license + +ts-node - TypeScript execution and REPL for node.js. (https://github.com/TypeStrong/ts-node/blob/main/LICENSE) +License: MIT +License File: ts-node.license + +typescript - TypeScript is a superset of JavaScript that compiles to clean JavaScript output. (https://github.com/microsoft/TypeScript/blob/main/LICENSE.txt) +License: Apache-2.0 +License File: typescript.license + +webpack - A bundler for javascript and friends. Packs many modules into a few bundled assets. Code Splitting allows for loading parts of the application on demand. Through "loaders", modules can be CommonJs, AMD, ES6 modules, CSS, Images, JSON, Coffeescript, LESS, ... and your custom stuff. (https://github.com/webpack/webpack/blob/main/LICENSE) +License: MIT +License File: webpack.license + +webpack-cli - Webpack's Command Line Interface. (https://github.com/webpack/webpack-cli/blob/master/LICENSE) +License: MIT +License File: webpack-cli.license + +webpack-dev-server - Serves a webpack app. Updates the browser on changes. (https://github.com/webpack/webpack-dev-server/blob/master/LICENSE) +License: MIT +License File: webpack-dev-server.license + +webpack-merge - Merge designed for webpack. (https://github.com/survivejs/webpack-merge/blob/develop/LICENSE) +License: MIT +License File: webpack-merge.license diff --git a/frontend/licenses/airbnb.license b/frontend/licenses/airbnb.license new file mode 100644 index 0000000..69d80c0 --- /dev/null +++ b/frontend/licenses/airbnb.license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2012 Airbnb + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/autoprefixer.license b/frontend/licenses/autoprefixer.license new file mode 100644 index 0000000..da057b4 --- /dev/null +++ b/frontend/licenses/autoprefixer.license @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright 2013 Andrey Sitnik + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frontend/licenses/axios-retry.license b/frontend/licenses/axios-retry.license new file mode 100644 index 0000000..8f3d02b --- /dev/null +++ b/frontend/licenses/axios-retry.license @@ -0,0 +1,13 @@ +Copyright 2019 Softonic International S.A. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/frontend/licenses/axios.license b/frontend/licenses/axios.license new file mode 100644 index 0000000..05006a5 --- /dev/null +++ b/frontend/licenses/axios.license @@ -0,0 +1,7 @@ +# Copyright (c) 2014-present Matt Zabriskie & Collaborators + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frontend/licenses/babel-resolver.license b/frontend/licenses/babel-resolver.license new file mode 100644 index 0000000..a70d8ee --- /dev/null +++ b/frontend/licenses/babel-resolver.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Tommy Leunen (tommyleunen.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/frontend/licenses/babel.license b/frontend/licenses/babel.license new file mode 100644 index 0000000..f31575e --- /dev/null +++ b/frontend/licenses/babel.license @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2014-present Sebastian McKenzie and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frontend/licenses/classnames.license b/frontend/licenses/classnames.license new file mode 100644 index 0000000..4117bfa --- /dev/null +++ b/frontend/licenses/classnames.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Jed Watson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/copy-plugin.license b/frontend/licenses/copy-plugin.license new file mode 100644 index 0000000..8c11fc7 --- /dev/null +++ b/frontend/licenses/copy-plugin.license @@ -0,0 +1,20 @@ +Copyright JS Foundation and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frontend/licenses/css-loader.license b/frontend/licenses/css-loader.license new file mode 100644 index 0000000..8c11fc7 --- /dev/null +++ b/frontend/licenses/css-loader.license @@ -0,0 +1,20 @@ +Copyright JS Foundation and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frontend/licenses/dotenv-webpack.license b/frontend/licenses/dotenv-webpack.license new file mode 100644 index 0000000..2d892d5 --- /dev/null +++ b/frontend/licenses/dotenv-webpack.license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Matt Steele + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/dotenv.license b/frontend/licenses/dotenv.license new file mode 100644 index 0000000..c430ad8 --- /dev/null +++ b/frontend/licenses/dotenv.license @@ -0,0 +1,23 @@ +Copyright (c) 2015, Scott Motte +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/frontend/licenses/editor.license b/frontend/licenses/editor.license new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/frontend/licenses/editor.license @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/frontend/licenses/eslint-alias.license b/frontend/licenses/eslint-alias.license new file mode 100644 index 0000000..61a4bde --- /dev/null +++ b/frontend/licenses/eslint-alias.license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 johvin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/eslint-plugin-import.license b/frontend/licenses/eslint-plugin-import.license new file mode 100644 index 0000000..592c66b --- /dev/null +++ b/frontend/licenses/eslint-plugin-import.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Ben Mosher + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/eslint-plugin-react.license b/frontend/licenses/eslint-plugin-react.license new file mode 100644 index 0000000..2d248c8 --- /dev/null +++ b/frontend/licenses/eslint-plugin-react.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Yannick Croissant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/eslint-prettier.license b/frontend/licenses/eslint-prettier.license new file mode 100644 index 0000000..4b17469 --- /dev/null +++ b/frontend/licenses/eslint-prettier.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017, 2018, 2019, 2020, 2021, 2022, 2023 Simon Lydell and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/frontend/licenses/eslint-resolver.license b/frontend/licenses/eslint-resolver.license new file mode 100644 index 0000000..f86a048 --- /dev/null +++ b/frontend/licenses/eslint-resolver.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Tommy Leunen (tommyleunen.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/frontend/licenses/eslint-typescript-resolver.license b/frontend/licenses/eslint-typescript-resolver.license new file mode 100644 index 0000000..652813c --- /dev/null +++ b/frontend/licenses/eslint-typescript-resolver.license @@ -0,0 +1,5 @@ +Copyright 2021 Alex Gorbatchev + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/frontend/licenses/eslint.license b/frontend/licenses/eslint.license new file mode 100644 index 0000000..b607bb3 --- /dev/null +++ b/frontend/licenses/eslint.license @@ -0,0 +1,19 @@ +Copyright OpenJS Foundation and other contributors, + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/frontend/licenses/html-webpack-plugin.license b/frontend/licenses/html-webpack-plugin.license new file mode 100644 index 0000000..8c11fc7 --- /dev/null +++ b/frontend/licenses/html-webpack-plugin.license @@ -0,0 +1,20 @@ +Copyright JS Foundation and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frontend/licenses/i18next-backend.license b/frontend/licenses/i18next-backend.license new file mode 100644 index 0000000..8997340 --- /dev/null +++ b/frontend/licenses/i18next-backend.license @@ -0,0 +1,19 @@ +Copyright (c) 2022 i18next + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/frontend/licenses/i18next-detector.license b/frontend/licenses/i18next-detector.license new file mode 100644 index 0000000..5d9413f --- /dev/null +++ b/frontend/licenses/i18next-detector.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 i18next + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/i18next.license b/frontend/licenses/i18next.license new file mode 100644 index 0000000..5d9413f --- /dev/null +++ b/frontend/licenses/i18next.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 i18next + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/jest-dom.license b/frontend/licenses/jest-dom.license new file mode 100644 index 0000000..4c43675 --- /dev/null +++ b/frontend/licenses/jest-dom.license @@ -0,0 +1,20 @@ +The MIT License (MIT) +Copyright (c) 2017 Kent C. Dodds + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/jest.license b/frontend/licenses/jest.license new file mode 100644 index 0000000..b93be90 --- /dev/null +++ b/frontend/licenses/jest.license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Meta Platforms, Inc. and affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/jsx-a11y.license b/frontend/licenses/jsx-a11y.license new file mode 100644 index 0000000..a66c74b --- /dev/null +++ b/frontend/licenses/jsx-a11y.license @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2016 Ethan Cohen + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frontend/licenses/md5.license b/frontend/licenses/md5.license new file mode 100644 index 0000000..f476d11 --- /dev/null +++ b/frontend/licenses/md5.license @@ -0,0 +1,27 @@ +Copyright © 2011-2012, Paul Vorbach. +Copyright © 2009, Jeff Mott. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +* Neither the name Crypto-JS nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/frontend/licenses/pipedrive.license b/frontend/licenses/pipedrive.license new file mode 100644 index 0000000..42f794c --- /dev/null +++ b/frontend/licenses/pipedrive.license @@ -0,0 +1,21 @@ +(MIT License) + +Copyright (C) 2012-2022 by Pipedrive, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/frontend/licenses/postcss-loader.license b/frontend/licenses/postcss-loader.license new file mode 100644 index 0000000..8c11fc7 --- /dev/null +++ b/frontend/licenses/postcss-loader.license @@ -0,0 +1,20 @@ +Copyright JS Foundation and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frontend/licenses/postcss.license b/frontend/licenses/postcss.license new file mode 100644 index 0000000..da057b4 --- /dev/null +++ b/frontend/licenses/postcss.license @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright 2013 Andrey Sitnik + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frontend/licenses/prettier.license b/frontend/licenses/prettier.license new file mode 100644 index 0000000..5767e34 --- /dev/null +++ b/frontend/licenses/prettier.license @@ -0,0 +1,7 @@ +Copyright © James Long and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frontend/licenses/react-dropzone.license b/frontend/licenses/react-dropzone.license new file mode 100644 index 0000000..da6b989 --- /dev/null +++ b/frontend/licenses/react-dropzone.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Param Aggarwal + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/react-i18next.license b/frontend/licenses/react-i18next.license new file mode 100644 index 0000000..5d9413f --- /dev/null +++ b/frontend/licenses/react-i18next.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 i18next + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/react-query.license b/frontend/licenses/react-query.license new file mode 100644 index 0000000..1869e21 --- /dev/null +++ b/frontend/licenses/react-query.license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-present Tanner Linsley + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/react-router.license b/frontend/licenses/react-router.license new file mode 100644 index 0000000..47c96cb --- /dev/null +++ b/frontend/licenses/react-router.license @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) React Training 2015-2019 +Copyright (c) Remix Software 2020-2022 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/react-tabs.license b/frontend/licenses/react-tabs.license new file mode 100644 index 0000000..50769ac --- /dev/null +++ b/frontend/licenses/react-tabs.license @@ -0,0 +1,19 @@ +Copyright (c) Matt Zabriskie and Daniel Tschinder + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/frontend/licenses/react-testing.license b/frontend/licenses/react-testing.license new file mode 100644 index 0000000..ca399d5 --- /dev/null +++ b/frontend/licenses/react-testing.license @@ -0,0 +1,20 @@ +The MIT License (MIT) +Copyright (c) 2017-Present Kent C. Dodds + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/react.license b/frontend/licenses/react.license new file mode 100644 index 0000000..b93be90 --- /dev/null +++ b/frontend/licenses/react.license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Meta Platforms, Inc. and affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/style-loader.license b/frontend/licenses/style-loader.license new file mode 100644 index 0000000..8c11fc7 --- /dev/null +++ b/frontend/licenses/style-loader.license @@ -0,0 +1,20 @@ +Copyright JS Foundation and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frontend/licenses/svgr.license b/frontend/licenses/svgr.license new file mode 100644 index 0000000..eab3026 --- /dev/null +++ b/frontend/licenses/svgr.license @@ -0,0 +1,7 @@ +Copyright 2017 Smooth Code + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frontend/licenses/tailwindcss.license b/frontend/licenses/tailwindcss.license new file mode 100644 index 0000000..d6a8229 --- /dev/null +++ b/frontend/licenses/tailwindcss.license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Tailwind Labs, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/ts-jest.license b/frontend/licenses/ts-jest.license new file mode 100644 index 0000000..82e5bee --- /dev/null +++ b/frontend/licenses/ts-jest.license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016-2018 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/ts-loader.license b/frontend/licenses/ts-loader.license new file mode 100644 index 0000000..ea68b57 --- /dev/null +++ b/frontend/licenses/ts-loader.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-present TypeStrong + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/ts-node.license b/frontend/licenses/ts-node.license new file mode 100644 index 0000000..983fbe8 --- /dev/null +++ b/frontend/licenses/ts-node.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Blake Embrey (hello@blakeembrey.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/frontend/licenses/typescript-eslint.license b/frontend/licenses/typescript-eslint.license new file mode 100644 index 0000000..61fed56 --- /dev/null +++ b/frontend/licenses/typescript-eslint.license @@ -0,0 +1,26 @@ +typescript-eslint + +Originally extracted from: + +TypeScript ESLint Parser +Copyright JS Foundation and other contributors, https://js.foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/frontend/licenses/typescript.license b/frontend/licenses/typescript.license new file mode 100644 index 0000000..2df8c87 --- /dev/null +++ b/frontend/licenses/typescript.license @@ -0,0 +1,55 @@ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/frontend/licenses/valtio.license b/frontend/licenses/valtio.license new file mode 100644 index 0000000..f082b51 --- /dev/null +++ b/frontend/licenses/valtio.license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2022 Poimandres + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/frontend/licenses/webpack-cli.license b/frontend/licenses/webpack-cli.license new file mode 100644 index 0000000..8c11fc7 --- /dev/null +++ b/frontend/licenses/webpack-cli.license @@ -0,0 +1,20 @@ +Copyright JS Foundation and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frontend/licenses/webpack-dev-server.license b/frontend/licenses/webpack-dev-server.license new file mode 100644 index 0000000..8c11fc7 --- /dev/null +++ b/frontend/licenses/webpack-dev-server.license @@ -0,0 +1,20 @@ +Copyright JS Foundation and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frontend/licenses/webpack-merge.license b/frontend/licenses/webpack-merge.license new file mode 100644 index 0000000..5eaceff --- /dev/null +++ b/frontend/licenses/webpack-merge.license @@ -0,0 +1,20 @@ +Copyright (c) 2015 Juho Vepsalainen + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/frontend/licenses/webpack.license b/frontend/licenses/webpack.license new file mode 100644 index 0000000..8c11fc7 --- /dev/null +++ b/frontend/licenses/webpack.license @@ -0,0 +1,20 @@ +Copyright JS Foundation and other contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From 04c6f7db10195a18b2b7989cdf2d8d4851a92388 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 29 Mar 2023 12:35:15 +0500 Subject: [PATCH 044/145] doc: copyrights --- backend/pkg/bootstrap.go | 18 +++++++++++++++ backend/pkg/cache/cache.go | 18 +++++++++++++++ backend/pkg/cache/memory.go | 18 +++++++++++++++ backend/pkg/cache/redis.go | 18 +++++++++++++++ backend/pkg/config/cache.go | 18 +++++++++++++++ backend/pkg/config/cors.go | 18 +++++++++++++++ backend/pkg/config/credentials.go | 18 +++++++++++++++ backend/pkg/config/error.go | 18 +++++++++++++++ backend/pkg/config/logger.go | 18 +++++++++++++++ backend/pkg/config/messaging.go | 18 +++++++++++++++ backend/pkg/config/persistence.go | 18 +++++++++++++++ backend/pkg/config/registry.go | 18 +++++++++++++++ backend/pkg/config/resilience.go | 18 +++++++++++++++ backend/pkg/config/server.go | 18 +++++++++++++++ backend/pkg/config/trace.go | 18 +++++++++++++++ backend/pkg/config/worker.go | 18 +++++++++++++++ backend/pkg/log/error.go | 18 +++++++++++++++ backend/pkg/log/logger.go | 18 +++++++++++++++ backend/pkg/log/logrus.go | 18 +++++++++++++++ backend/pkg/messaging/broker.go | 18 +++++++++++++++ backend/pkg/middleware/cache.go | 18 +++++++++++++++ backend/pkg/middleware/cors.go | 18 +++++++++++++++ backend/pkg/middleware/log.go | 18 +++++++++++++++ backend/pkg/middleware/rate.go | 18 +++++++++++++++ backend/pkg/middleware/secure.go | 18 +++++++++++++++ backend/pkg/middleware/timeout.go | 18 +++++++++++++++ backend/pkg/middleware/trace.go | 18 +++++++++++++++ backend/pkg/middleware/version.go | 18 +++++++++++++++ backend/pkg/middleware/wrapper/trace.go | 18 +++++++++++++++ backend/pkg/registry/registry.go | 18 +++++++++++++++ backend/pkg/resilience/circuit.go | 18 +++++++++++++++ backend/pkg/service/http/service.go | 18 +++++++++++++++ backend/pkg/service/repl/service.go | 18 +++++++++++++++ backend/pkg/service/rpc/service.go | 18 +++++++++++++++ backend/pkg/shared/env.go | 18 +++++++++++++++ backend/pkg/trace/error.go | 18 +++++++++++++++ backend/pkg/trace/tracer.go | 18 +++++++++++++++ backend/pkg/trace/zipkin.go | 18 +++++++++++++++ backend/pkg/worker/asynq.go | 18 +++++++++++++++ backend/pkg/worker/enqueuer.go | 18 +++++++++++++++ backend/pkg/worker/option.go | 18 +++++++++++++++ backend/pkg/worker/worker.go | 18 +++++++++++++++ backend/services/auth/cmd/root.go | 18 +++++++++++++++ backend/services/auth/cmd/server.go | 18 +++++++++++++++ backend/services/auth/main.go | 18 +++++++++++++++ .../services/auth/web/core/adapter/memory.go | 18 +++++++++++++++ .../auth/web/core/adapter/memory_test.go | 18 +++++++++++++++ .../services/auth/web/core/adapter/mongo.go | 18 +++++++++++++++ .../auth/web/core/adapter/mongo_test.go | 18 +++++++++++++++ .../services/auth/web/core/domain/error.go | 18 +++++++++++++++ backend/services/auth/web/core/domain/user.go | 18 +++++++++++++++ backend/services/auth/web/core/port/input.go | 18 +++++++++++++++ backend/services/auth/web/core/port/output.go | 18 +++++++++++++++ .../services/auth/web/core/service/error.go | 18 +++++++++++++++ .../services/auth/web/core/service/user.go | 18 +++++++++++++++ .../auth/web/core/service/user_test.go | 18 +++++++++++++++ backend/services/auth/web/handler/delete.go | 18 +++++++++++++++ backend/services/auth/web/handler/select.go | 18 +++++++++++++++ .../services/auth/web/handler/select_test.go | 18 +++++++++++++++ backend/services/auth/web/message/error.go | 18 +++++++++++++++ backend/services/auth/web/message/insert.go | 18 +++++++++++++++ backend/services/auth/web/server.go | 18 +++++++++++++++ backend/services/builder/cmd/root.go | 18 +++++++++++++++ backend/services/builder/cmd/server.go | 18 +++++++++++++++ backend/services/builder/main.go | 18 +++++++++++++++ backend/services/builder/web/handler/build.go | 18 +++++++++++++++ backend/services/builder/web/handler/error.go | 18 +++++++++++++++ backend/services/builder/web/server.go | 18 +++++++++++++++ backend/services/callback/cmd/root.go | 18 +++++++++++++++ backend/services/callback/cmd/server.go | 18 +++++++++++++++ backend/services/callback/main.go | 18 +++++++++++++++ .../callback/web/controller/callback.go | 18 +++++++++++++++ backend/services/callback/web/server.go | 18 +++++++++++++++ .../services/callback/web/worker/callback.go | 18 +++++++++++++++ backend/services/gateway/cmd/root.go | 18 +++++++++++++++ backend/services/gateway/cmd/server.go | 18 +++++++++++++++ backend/services/gateway/main.go | 18 +++++++++++++++ .../services/gateway/web/controller/api.go | 18 +++++++++++++++ .../services/gateway/web/controller/auth.go | 18 +++++++++++++++ .../services/gateway/web/middleware/auth.go | 18 +++++++++++++++ .../gateway/web/middleware/context.go | 18 +++++++++++++++ backend/services/gateway/web/server.go | 18 +++++++++++++++ backend/services/settings/cmd/root.go | 18 +++++++++++++++ backend/services/settings/cmd/server.go | 18 +++++++++++++++ backend/services/settings/main.go | 18 +++++++++++++++ .../settings/web/core/adapter/memory.go | 18 +++++++++++++++ .../settings/web/core/adapter/memory_test.go | 18 +++++++++++++++ .../settings/web/core/adapter/mongo.go | 18 +++++++++++++++ .../settings/web/core/adapter/mongo_test.go | 18 +++++++++++++++ .../settings/web/core/domain/error.go | 18 +++++++++++++++ .../settings/web/core/domain/settings.go | 18 +++++++++++++++ .../services/settings/web/core/port/input.go | 18 +++++++++++++++ .../services/settings/web/core/port/output.go | 18 +++++++++++++++ .../settings/web/core/service/error.go | 18 +++++++++++++++ .../settings/web/core/service/settings.go | 18 +++++++++++++++ .../services/settings/web/handler/select.go | 18 +++++++++++++++ .../settings/web/handler/select_test.go | 18 +++++++++++++++ .../services/settings/web/message/delete.go | 18 +++++++++++++++ .../services/settings/web/message/error.go | 18 +++++++++++++++ .../services/settings/web/message/insert.go | 18 +++++++++++++++ backend/services/settings/web/server.go | 18 +++++++++++++++ backend/services/shared/client/api.go | 18 +++++++++++++++ backend/services/shared/client/auth.go | 18 +++++++++++++++ backend/services/shared/client/command.go | 18 +++++++++++++++ backend/services/shared/client/error.go | 18 +++++++++++++++ .../services/shared/client/model/access.go | 18 +++++++++++++++ backend/services/shared/client/model/error.go | 18 +++++++++++++++ backend/services/shared/client/model/lang.go | 18 +++++++++++++++ backend/services/shared/client/model/token.go | 18 +++++++++++++++ backend/services/shared/client/model/user.go | 18 +++++++++++++++ backend/services/shared/config.go | 18 +++++++++++++++ backend/services/shared/constants/file.go | 18 +++++++++++++++ backend/services/shared/constants/session.go | 18 +++++++++++++++ backend/services/shared/crypto/aes.go | 18 +++++++++++++++ backend/services/shared/crypto/aes_test.go | 18 +++++++++++++++ backend/services/shared/crypto/jwt.go | 18 +++++++++++++++ backend/services/shared/error.go | 18 +++++++++++++++ backend/services/shared/message/job.go | 18 +++++++++++++++ backend/services/shared/request/callback.go | 18 +++++++++++++++ backend/services/shared/request/command.go | 18 +++++++++++++++ backend/services/shared/request/config.go | 18 +++++++++++++++ backend/services/shared/request/session.go | 18 +++++++++++++++ backend/services/shared/request/settings.go | 18 +++++++++++++++ backend/services/shared/request/token.go | 18 +++++++++++++++ backend/services/shared/request/uninstall.go | 18 +++++++++++++++ backend/services/shared/response/callback.go | 18 +++++++++++++++ backend/services/shared/response/command.go | 18 +++++++++++++++ backend/services/shared/response/config.go | 18 +++++++++++++++ backend/services/shared/response/error.go | 18 +++++++++++++++ backend/services/shared/response/generic.go | 18 +++++++++++++++ backend/services/shared/response/settings.go | 18 +++++++++++++++ backend/services/shared/response/user.go | 18 +++++++++++++++ frontend/.eslintrc.js | 18 +++++++++++++++ frontend/jest.config.js | 18 +++++++++++++++ frontend/postcss.config.js | 18 +++++++++++++++ frontend/src/App.tsx | 18 +++++++++++++++ frontend/src/__mocks__/fileMock.ts | 18 +++++++++++++++ frontend/src/__mocks__/styleMock.ts | 18 +++++++++++++++ frontend/src/components/button/Button.tsx | 18 +++++++++++++++ frontend/src/components/button/index.ts | 18 +++++++++++++++ frontend/src/components/divider/Divider.tsx | 18 +++++++++++++++ frontend/src/components/divider/index.ts | 18 +++++++++++++++ frontend/src/components/drop/Drop.tsx | 18 +++++++++++++++ frontend/src/components/drop/index.ts | 18 +++++++++++++++ frontend/src/components/error/Error.tsx | 18 +++++++++++++++ frontend/src/components/error/index.ts | 18 +++++++++++++++ frontend/src/components/file/File.tsx | 18 +++++++++++++++ frontend/src/components/file/index.ts | 18 +++++++++++++++ frontend/src/components/header/Header.tsx | 18 +++++++++++++++ frontend/src/components/header/index.ts | 18 +++++++++++++++ frontend/src/components/info/Info.tsx | 18 +++++++++++++++ frontend/src/components/info/index.ts | 18 +++++++++++++++ frontend/src/components/input/Input.tsx | 18 +++++++++++++++ frontend/src/components/input/index.ts | 18 +++++++++++++++ frontend/src/components/nofile/NoFile.tsx | 18 +++++++++++++++ frontend/src/components/nofile/index.ts | 18 +++++++++++++++ frontend/src/components/search/Search.tsx | 18 +++++++++++++++ frontend/src/components/search/index.ts | 18 +++++++++++++++ frontend/src/components/spinner/Spinner.tsx | 18 +++++++++++++++ frontend/src/components/spinner/index.ts | 18 +++++++++++++++ frontend/src/components/tile/Tile.tsx | 18 +++++++++++++++ frontend/src/components/tile/index.ts | 18 +++++++++++++++ frontend/src/components/title/Subtitle.tsx | 18 +++++++++++++++ frontend/src/components/title/Title.tsx | 18 +++++++++++++++ frontend/src/components/title/index.ts | 18 +++++++++++++++ frontend/src/context/TokenContext.tsx | 18 +++++++++++++++ frontend/src/custom.d.ts | 18 +++++++++++++++ frontend/src/hooks/useBuildConfig.tsx | 18 +++++++++++++++ frontend/src/hooks/useDeleteFile.tsx | 18 +++++++++++++++ frontend/src/hooks/useFileSearch.tsx | 18 +++++++++++++++ frontend/src/i18n.ts | 18 +++++++++++++++ frontend/src/index.tsx | 18 +++++++++++++++ frontend/src/layout/Banner.tsx | 18 +++++++++++++++ frontend/src/layout/ErrorBackground.tsx | 18 +++++++++++++++ frontend/src/pages/Creation/Creation.tsx | 18 +++++++++++++++ frontend/src/pages/Creation/Upload.tsx | 18 +++++++++++++++ frontend/src/pages/Creation/index.tsx | 18 +++++++++++++++ frontend/src/pages/Editor/index.tsx | 18 +++++++++++++++ frontend/src/pages/Main/Actions.tsx | 18 +++++++++++++++ frontend/src/pages/Main/Main.tsx | 18 +++++++++++++++ frontend/src/pages/Main/index.tsx | 18 +++++++++++++++ frontend/src/pages/Settings/index.tsx | 18 +++++++++++++++ frontend/src/services/config.ts | 18 +++++++++++++++ frontend/src/services/file.ts | 21 +++++++++++++++++- frontend/src/services/me.ts | 22 ++++++++++++++++++- frontend/src/services/settings.ts | 19 ++++++++++++++++ frontend/src/services/user.ts | 22 ++++++++++++++++++- frontend/src/setupTests.ts | 18 +++++++++++++++ frontend/src/types/config.ts | 18 +++++++++++++++ frontend/src/types/file.ts | 18 +++++++++++++++ frontend/src/types/settings.ts | 18 +++++++++++++++ frontend/src/types/user.ts | 18 +++++++++++++++ frontend/src/utils/file.ts | 18 +++++++++++++++ frontend/src/utils/url.ts | 18 +++++++++++++++ frontend/tailwind.config.js | 18 +++++++++++++++ frontend/webpack.common.js | 18 +++++++++++++++ frontend/webpack.development.js | 18 +++++++++++++++ frontend/webpack.production.js | 18 +++++++++++++++ 198 files changed, 3573 insertions(+), 3 deletions(-) diff --git a/backend/pkg/bootstrap.go b/backend/pkg/bootstrap.go index 963d306..31264be 100644 --- a/backend/pkg/bootstrap.go +++ b/backend/pkg/bootstrap.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package pkg import ( diff --git a/backend/pkg/cache/cache.go b/backend/pkg/cache/cache.go index 4f87f1f..6c2cd38 100644 --- a/backend/pkg/cache/cache.go +++ b/backend/pkg/cache/cache.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package cache import ( diff --git a/backend/pkg/cache/memory.go b/backend/pkg/cache/memory.go index 0132163..653f25f 100644 --- a/backend/pkg/cache/memory.go +++ b/backend/pkg/cache/memory.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package cache import ( diff --git a/backend/pkg/cache/redis.go b/backend/pkg/cache/redis.go index 191da94..66700b6 100644 --- a/backend/pkg/cache/redis.go +++ b/backend/pkg/cache/redis.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package cache import ( diff --git a/backend/pkg/config/cache.go b/backend/pkg/config/cache.go index f1c96e3..854907b 100644 --- a/backend/pkg/config/cache.go +++ b/backend/pkg/config/cache.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package config import ( diff --git a/backend/pkg/config/cors.go b/backend/pkg/config/cors.go index 6771418..918ef47 100644 --- a/backend/pkg/config/cors.go +++ b/backend/pkg/config/cors.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package config import ( diff --git a/backend/pkg/config/credentials.go b/backend/pkg/config/credentials.go index 6fde7fb..6002f37 100644 --- a/backend/pkg/config/credentials.go +++ b/backend/pkg/config/credentials.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package config import ( diff --git a/backend/pkg/config/error.go b/backend/pkg/config/error.go index 350ba39..87151fb 100644 --- a/backend/pkg/config/error.go +++ b/backend/pkg/config/error.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package config import "fmt" diff --git a/backend/pkg/config/logger.go b/backend/pkg/config/logger.go index 5334a10..d6b504e 100644 --- a/backend/pkg/config/logger.go +++ b/backend/pkg/config/logger.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package config import ( diff --git a/backend/pkg/config/messaging.go b/backend/pkg/config/messaging.go index 6a7c7cc..ae16c56 100644 --- a/backend/pkg/config/messaging.go +++ b/backend/pkg/config/messaging.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package config import ( diff --git a/backend/pkg/config/persistence.go b/backend/pkg/config/persistence.go index 58b6aae..6a2e09c 100644 --- a/backend/pkg/config/persistence.go +++ b/backend/pkg/config/persistence.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package config import ( diff --git a/backend/pkg/config/registry.go b/backend/pkg/config/registry.go index fadd4c3..127a38d 100644 --- a/backend/pkg/config/registry.go +++ b/backend/pkg/config/registry.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package config import ( diff --git a/backend/pkg/config/resilience.go b/backend/pkg/config/resilience.go index d6cabdc..00a789c 100644 --- a/backend/pkg/config/resilience.go +++ b/backend/pkg/config/resilience.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package config import ( diff --git a/backend/pkg/config/server.go b/backend/pkg/config/server.go index 0b042b4..a6abec0 100644 --- a/backend/pkg/config/server.go +++ b/backend/pkg/config/server.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package config import ( diff --git a/backend/pkg/config/trace.go b/backend/pkg/config/trace.go index 2ea02dc..ce426b6 100644 --- a/backend/pkg/config/trace.go +++ b/backend/pkg/config/trace.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package config import ( diff --git a/backend/pkg/config/worker.go b/backend/pkg/config/worker.go index 083d877..c495678 100644 --- a/backend/pkg/config/worker.go +++ b/backend/pkg/config/worker.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package config import ( diff --git a/backend/pkg/log/error.go b/backend/pkg/log/error.go index 6f3daf7..979c88b 100644 --- a/backend/pkg/log/error.go +++ b/backend/pkg/log/error.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package log import "fmt" diff --git a/backend/pkg/log/logger.go b/backend/pkg/log/logger.go index 2d10611..94c098e 100644 --- a/backend/pkg/log/logger.go +++ b/backend/pkg/log/logger.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package log import ( diff --git a/backend/pkg/log/logrus.go b/backend/pkg/log/logrus.go index b405107..93971f1 100644 --- a/backend/pkg/log/logrus.go +++ b/backend/pkg/log/logrus.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package log import ( diff --git a/backend/pkg/messaging/broker.go b/backend/pkg/messaging/broker.go index 724db3d..f5d023f 100644 --- a/backend/pkg/messaging/broker.go +++ b/backend/pkg/messaging/broker.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package messaging import ( diff --git a/backend/pkg/middleware/cache.go b/backend/pkg/middleware/cache.go index dd3790b..5af62e6 100644 --- a/backend/pkg/middleware/cache.go +++ b/backend/pkg/middleware/cache.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package middleware import ( diff --git a/backend/pkg/middleware/cors.go b/backend/pkg/middleware/cors.go index 0580e67..ea02410 100644 --- a/backend/pkg/middleware/cors.go +++ b/backend/pkg/middleware/cors.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package middleware import ( diff --git a/backend/pkg/middleware/log.go b/backend/pkg/middleware/log.go index aca4fde..db2956b 100644 --- a/backend/pkg/middleware/log.go +++ b/backend/pkg/middleware/log.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package middleware import ( diff --git a/backend/pkg/middleware/rate.go b/backend/pkg/middleware/rate.go index 4d80a09..9628e5b 100644 --- a/backend/pkg/middleware/rate.go +++ b/backend/pkg/middleware/rate.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package middleware import ( diff --git a/backend/pkg/middleware/secure.go b/backend/pkg/middleware/secure.go index 9d705e2..55d5eef 100644 --- a/backend/pkg/middleware/secure.go +++ b/backend/pkg/middleware/secure.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package middleware import ( diff --git a/backend/pkg/middleware/timeout.go b/backend/pkg/middleware/timeout.go index 7d54c83..8886589 100644 --- a/backend/pkg/middleware/timeout.go +++ b/backend/pkg/middleware/timeout.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package middleware import ( diff --git a/backend/pkg/middleware/trace.go b/backend/pkg/middleware/trace.go index 3b17406..d0f6d78 100644 --- a/backend/pkg/middleware/trace.go +++ b/backend/pkg/middleware/trace.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package middleware import ( diff --git a/backend/pkg/middleware/version.go b/backend/pkg/middleware/version.go index 8fb11c5..d7f81b1 100644 --- a/backend/pkg/middleware/version.go +++ b/backend/pkg/middleware/version.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package middleware import ( diff --git a/backend/pkg/middleware/wrapper/trace.go b/backend/pkg/middleware/wrapper/trace.go index 4ea4ffd..cbb76a9 100644 --- a/backend/pkg/middleware/wrapper/trace.go +++ b/backend/pkg/middleware/wrapper/trace.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package wrapper import ( diff --git a/backend/pkg/registry/registry.go b/backend/pkg/registry/registry.go index 77809bf..9dcbb79 100644 --- a/backend/pkg/registry/registry.go +++ b/backend/pkg/registry/registry.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package registry import ( diff --git a/backend/pkg/resilience/circuit.go b/backend/pkg/resilience/circuit.go index 880b203..11f5859 100644 --- a/backend/pkg/resilience/circuit.go +++ b/backend/pkg/resilience/circuit.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package resilience import ( diff --git a/backend/pkg/service/http/service.go b/backend/pkg/service/http/service.go index 6185f03..a3e3c21 100644 --- a/backend/pkg/service/http/service.go +++ b/backend/pkg/service/http/service.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package http import ( diff --git a/backend/pkg/service/repl/service.go b/backend/pkg/service/repl/service.go index 7abfb54..f00fbb2 100644 --- a/backend/pkg/service/repl/service.go +++ b/backend/pkg/service/repl/service.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package repl import ( diff --git a/backend/pkg/service/rpc/service.go b/backend/pkg/service/rpc/service.go index e72d006..80517a3 100644 --- a/backend/pkg/service/rpc/service.go +++ b/backend/pkg/service/rpc/service.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package rpc import ( diff --git a/backend/pkg/shared/env.go b/backend/pkg/shared/env.go index c0c202d..9b428be 100644 --- a/backend/pkg/shared/env.go +++ b/backend/pkg/shared/env.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package shared const ( diff --git a/backend/pkg/trace/error.go b/backend/pkg/trace/error.go index 53dce64..fdfc2da 100644 --- a/backend/pkg/trace/error.go +++ b/backend/pkg/trace/error.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package trace import ( diff --git a/backend/pkg/trace/tracer.go b/backend/pkg/trace/tracer.go index 542a2b7..bafc042 100644 --- a/backend/pkg/trace/tracer.go +++ b/backend/pkg/trace/tracer.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package trace import ( diff --git a/backend/pkg/trace/zipkin.go b/backend/pkg/trace/zipkin.go index ef64da1..4c50c68 100644 --- a/backend/pkg/trace/zipkin.go +++ b/backend/pkg/trace/zipkin.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package trace import ( diff --git a/backend/pkg/worker/asynq.go b/backend/pkg/worker/asynq.go index d46ee1b..d43cb43 100644 --- a/backend/pkg/worker/asynq.go +++ b/backend/pkg/worker/asynq.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package worker import ( diff --git a/backend/pkg/worker/enqueuer.go b/backend/pkg/worker/enqueuer.go index 296fbf9..38e0986 100644 --- a/backend/pkg/worker/enqueuer.go +++ b/backend/pkg/worker/enqueuer.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package worker import ( diff --git a/backend/pkg/worker/option.go b/backend/pkg/worker/option.go index e970f7d..1418e48 100644 --- a/backend/pkg/worker/option.go +++ b/backend/pkg/worker/option.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package worker import ( diff --git a/backend/pkg/worker/worker.go b/backend/pkg/worker/worker.go index b0eb244..367162a 100644 --- a/backend/pkg/worker/worker.go +++ b/backend/pkg/worker/worker.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package worker import ( diff --git a/backend/services/auth/cmd/root.go b/backend/services/auth/cmd/root.go index 890313e..9f87ef4 100644 --- a/backend/services/auth/cmd/root.go +++ b/backend/services/auth/cmd/root.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package cmd import ( diff --git a/backend/services/auth/cmd/server.go b/backend/services/auth/cmd/server.go index fe4bda8..2fff9b3 100644 --- a/backend/services/auth/cmd/server.go +++ b/backend/services/auth/cmd/server.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package cmd import ( diff --git a/backend/services/auth/main.go b/backend/services/auth/main.go index 06d8f11..377a395 100644 --- a/backend/services/auth/main.go +++ b/backend/services/auth/main.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package main import ( diff --git a/backend/services/auth/web/core/adapter/memory.go b/backend/services/auth/web/core/adapter/memory.go index 9ded07c..48be0f5 100644 --- a/backend/services/auth/web/core/adapter/memory.go +++ b/backend/services/auth/web/core/adapter/memory.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package adapter import ( diff --git a/backend/services/auth/web/core/adapter/memory_test.go b/backend/services/auth/web/core/adapter/memory_test.go index 0c8c8cb..6347dca 100644 --- a/backend/services/auth/web/core/adapter/memory_test.go +++ b/backend/services/auth/web/core/adapter/memory_test.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package adapter import ( diff --git a/backend/services/auth/web/core/adapter/mongo.go b/backend/services/auth/web/core/adapter/mongo.go index 7029975..b111dc9 100644 --- a/backend/services/auth/web/core/adapter/mongo.go +++ b/backend/services/auth/web/core/adapter/mongo.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package adapter import ( diff --git a/backend/services/auth/web/core/adapter/mongo_test.go b/backend/services/auth/web/core/adapter/mongo_test.go index c6baa2f..4d2215d 100644 --- a/backend/services/auth/web/core/adapter/mongo_test.go +++ b/backend/services/auth/web/core/adapter/mongo_test.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package adapter import ( diff --git a/backend/services/auth/web/core/domain/error.go b/backend/services/auth/web/core/domain/error.go index b9a6aae..4eecf6a 100644 --- a/backend/services/auth/web/core/domain/error.go +++ b/backend/services/auth/web/core/domain/error.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package domain import "fmt" diff --git a/backend/services/auth/web/core/domain/user.go b/backend/services/auth/web/core/domain/user.go index f1f3c4f..a36947a 100644 --- a/backend/services/auth/web/core/domain/user.go +++ b/backend/services/auth/web/core/domain/user.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package domain import ( diff --git a/backend/services/auth/web/core/port/input.go b/backend/services/auth/web/core/port/input.go index 2970a3c..294eb6d 100644 --- a/backend/services/auth/web/core/port/input.go +++ b/backend/services/auth/web/core/port/input.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package port import ( diff --git a/backend/services/auth/web/core/port/output.go b/backend/services/auth/web/core/port/output.go index 6be9054..0b252ee 100644 --- a/backend/services/auth/web/core/port/output.go +++ b/backend/services/auth/web/core/port/output.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package port import ( diff --git a/backend/services/auth/web/core/service/error.go b/backend/services/auth/web/core/service/error.go index 496ab29..241bb98 100644 --- a/backend/services/auth/web/core/service/error.go +++ b/backend/services/auth/web/core/service/error.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package service import "fmt" diff --git a/backend/services/auth/web/core/service/user.go b/backend/services/auth/web/core/service/user.go index b7ad53c..44c8eac 100644 --- a/backend/services/auth/web/core/service/user.go +++ b/backend/services/auth/web/core/service/user.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package service import ( diff --git a/backend/services/auth/web/core/service/user_test.go b/backend/services/auth/web/core/service/user_test.go index 8ce9ee0..1fdccab 100644 --- a/backend/services/auth/web/core/service/user_test.go +++ b/backend/services/auth/web/core/service/user_test.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package service import ( diff --git a/backend/services/auth/web/handler/delete.go b/backend/services/auth/web/handler/delete.go index a37b475..b8d4b08 100644 --- a/backend/services/auth/web/handler/delete.go +++ b/backend/services/auth/web/handler/delete.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package handler import ( diff --git a/backend/services/auth/web/handler/select.go b/backend/services/auth/web/handler/select.go index b90851c..26c775d 100644 --- a/backend/services/auth/web/handler/select.go +++ b/backend/services/auth/web/handler/select.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package handler import ( diff --git a/backend/services/auth/web/handler/select_test.go b/backend/services/auth/web/handler/select_test.go index 5754c8a..dae2336 100644 --- a/backend/services/auth/web/handler/select_test.go +++ b/backend/services/auth/web/handler/select_test.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package handler import ( diff --git a/backend/services/auth/web/message/error.go b/backend/services/auth/web/message/error.go index 81e095e..a10e624 100644 --- a/backend/services/auth/web/message/error.go +++ b/backend/services/auth/web/message/error.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package message import "errors" diff --git a/backend/services/auth/web/message/insert.go b/backend/services/auth/web/message/insert.go index 91884a2..2d2a556 100644 --- a/backend/services/auth/web/message/insert.go +++ b/backend/services/auth/web/message/insert.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package message import ( diff --git a/backend/services/auth/web/server.go b/backend/services/auth/web/server.go index a37fef5..35b4218 100644 --- a/backend/services/auth/web/server.go +++ b/backend/services/auth/web/server.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package web import ( diff --git a/backend/services/builder/cmd/root.go b/backend/services/builder/cmd/root.go index 5b12c31..a7e4859 100644 --- a/backend/services/builder/cmd/root.go +++ b/backend/services/builder/cmd/root.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package cmd import ( diff --git a/backend/services/builder/cmd/server.go b/backend/services/builder/cmd/server.go index 8018f2f..e1811d9 100644 --- a/backend/services/builder/cmd/server.go +++ b/backend/services/builder/cmd/server.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package cmd import ( diff --git a/backend/services/builder/main.go b/backend/services/builder/main.go index 9053b39..47b85e2 100644 --- a/backend/services/builder/main.go +++ b/backend/services/builder/main.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package main import ( diff --git a/backend/services/builder/web/handler/build.go b/backend/services/builder/web/handler/build.go index 51a88f9..43f046f 100644 --- a/backend/services/builder/web/handler/build.go +++ b/backend/services/builder/web/handler/build.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package handler import ( diff --git a/backend/services/builder/web/handler/error.go b/backend/services/builder/web/handler/error.go index b313b08..3e9d75d 100644 --- a/backend/services/builder/web/handler/error.go +++ b/backend/services/builder/web/handler/error.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package handler import "errors" diff --git a/backend/services/builder/web/server.go b/backend/services/builder/web/server.go index 39a0dfe..59b0137 100644 --- a/backend/services/builder/web/server.go +++ b/backend/services/builder/web/server.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package web import ( diff --git a/backend/services/callback/cmd/root.go b/backend/services/callback/cmd/root.go index 91a9de5..6fd6c1c 100644 --- a/backend/services/callback/cmd/root.go +++ b/backend/services/callback/cmd/root.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package cmd import ( diff --git a/backend/services/callback/cmd/server.go b/backend/services/callback/cmd/server.go index 6b197ac..eec28df 100644 --- a/backend/services/callback/cmd/server.go +++ b/backend/services/callback/cmd/server.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package cmd import ( diff --git a/backend/services/callback/main.go b/backend/services/callback/main.go index 2577b6d..0d74160 100644 --- a/backend/services/callback/main.go +++ b/backend/services/callback/main.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package main import ( diff --git a/backend/services/callback/web/controller/callback.go b/backend/services/callback/web/controller/callback.go index a9b1214..ce4f525 100644 --- a/backend/services/callback/web/controller/callback.go +++ b/backend/services/callback/web/controller/callback.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package controller import ( diff --git a/backend/services/callback/web/server.go b/backend/services/callback/web/server.go index d8af4e2..a0a4132 100644 --- a/backend/services/callback/web/server.go +++ b/backend/services/callback/web/server.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package web import ( diff --git a/backend/services/callback/web/worker/callback.go b/backend/services/callback/web/worker/callback.go index 8f358c3..9989cf3 100644 --- a/backend/services/callback/web/worker/callback.go +++ b/backend/services/callback/web/worker/callback.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package worker import ( diff --git a/backend/services/gateway/cmd/root.go b/backend/services/gateway/cmd/root.go index 26039ad..e7f2f1a 100644 --- a/backend/services/gateway/cmd/root.go +++ b/backend/services/gateway/cmd/root.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package cmd import ( diff --git a/backend/services/gateway/cmd/server.go b/backend/services/gateway/cmd/server.go index 6acced4..cdbae16 100644 --- a/backend/services/gateway/cmd/server.go +++ b/backend/services/gateway/cmd/server.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package cmd import ( diff --git a/backend/services/gateway/main.go b/backend/services/gateway/main.go index d59f4b7..0cae8c1 100644 --- a/backend/services/gateway/main.go +++ b/backend/services/gateway/main.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package main import ( diff --git a/backend/services/gateway/web/controller/api.go b/backend/services/gateway/web/controller/api.go index aae88d6..6ca6a5e 100644 --- a/backend/services/gateway/web/controller/api.go +++ b/backend/services/gateway/web/controller/api.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package controller import ( diff --git a/backend/services/gateway/web/controller/auth.go b/backend/services/gateway/web/controller/auth.go index 166961a..390acee 100644 --- a/backend/services/gateway/web/controller/auth.go +++ b/backend/services/gateway/web/controller/auth.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package controller import ( diff --git a/backend/services/gateway/web/middleware/auth.go b/backend/services/gateway/web/middleware/auth.go index 80b7dc1..fa7b8b3 100644 --- a/backend/services/gateway/web/middleware/auth.go +++ b/backend/services/gateway/web/middleware/auth.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package middleware import ( diff --git a/backend/services/gateway/web/middleware/context.go b/backend/services/gateway/web/middleware/context.go index 8ab6213..dd21c1e 100644 --- a/backend/services/gateway/web/middleware/context.go +++ b/backend/services/gateway/web/middleware/context.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package middleware import ( diff --git a/backend/services/gateway/web/server.go b/backend/services/gateway/web/server.go index 0521401..f2db3d0 100644 --- a/backend/services/gateway/web/server.go +++ b/backend/services/gateway/web/server.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package web import ( diff --git a/backend/services/settings/cmd/root.go b/backend/services/settings/cmd/root.go index 67fc366..603222a 100644 --- a/backend/services/settings/cmd/root.go +++ b/backend/services/settings/cmd/root.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package cmd import ( diff --git a/backend/services/settings/cmd/server.go b/backend/services/settings/cmd/server.go index e7d190e..2577fba 100644 --- a/backend/services/settings/cmd/server.go +++ b/backend/services/settings/cmd/server.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package cmd import ( diff --git a/backend/services/settings/main.go b/backend/services/settings/main.go index 99c0ec7..59c37d0 100644 --- a/backend/services/settings/main.go +++ b/backend/services/settings/main.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package main import ( diff --git a/backend/services/settings/web/core/adapter/memory.go b/backend/services/settings/web/core/adapter/memory.go index db8afc6..9aa3097 100644 --- a/backend/services/settings/web/core/adapter/memory.go +++ b/backend/services/settings/web/core/adapter/memory.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package adapter import ( diff --git a/backend/services/settings/web/core/adapter/memory_test.go b/backend/services/settings/web/core/adapter/memory_test.go index f2a99d1..079d970 100644 --- a/backend/services/settings/web/core/adapter/memory_test.go +++ b/backend/services/settings/web/core/adapter/memory_test.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package adapter import ( diff --git a/backend/services/settings/web/core/adapter/mongo.go b/backend/services/settings/web/core/adapter/mongo.go index aa06bb0..0ac31dd 100644 --- a/backend/services/settings/web/core/adapter/mongo.go +++ b/backend/services/settings/web/core/adapter/mongo.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package adapter import ( diff --git a/backend/services/settings/web/core/adapter/mongo_test.go b/backend/services/settings/web/core/adapter/mongo_test.go index 2dbb6cb..fc4c80a 100644 --- a/backend/services/settings/web/core/adapter/mongo_test.go +++ b/backend/services/settings/web/core/adapter/mongo_test.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package adapter import ( diff --git a/backend/services/settings/web/core/domain/error.go b/backend/services/settings/web/core/domain/error.go index b9a6aae..4eecf6a 100644 --- a/backend/services/settings/web/core/domain/error.go +++ b/backend/services/settings/web/core/domain/error.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package domain import "fmt" diff --git a/backend/services/settings/web/core/domain/settings.go b/backend/services/settings/web/core/domain/settings.go index 510bbe8..38413c7 100644 --- a/backend/services/settings/web/core/domain/settings.go +++ b/backend/services/settings/web/core/domain/settings.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package domain import ( diff --git a/backend/services/settings/web/core/port/input.go b/backend/services/settings/web/core/port/input.go index 11d5403..cf663ab 100644 --- a/backend/services/settings/web/core/port/input.go +++ b/backend/services/settings/web/core/port/input.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package port import ( diff --git a/backend/services/settings/web/core/port/output.go b/backend/services/settings/web/core/port/output.go index a0cd81a..7e239cc 100644 --- a/backend/services/settings/web/core/port/output.go +++ b/backend/services/settings/web/core/port/output.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package port import ( diff --git a/backend/services/settings/web/core/service/error.go b/backend/services/settings/web/core/service/error.go index 496ab29..241bb98 100644 --- a/backend/services/settings/web/core/service/error.go +++ b/backend/services/settings/web/core/service/error.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package service import "fmt" diff --git a/backend/services/settings/web/core/service/settings.go b/backend/services/settings/web/core/service/settings.go index e788f4c..5054845 100644 --- a/backend/services/settings/web/core/service/settings.go +++ b/backend/services/settings/web/core/service/settings.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package service import ( diff --git a/backend/services/settings/web/handler/select.go b/backend/services/settings/web/handler/select.go index 651b4a3..b951f87 100644 --- a/backend/services/settings/web/handler/select.go +++ b/backend/services/settings/web/handler/select.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package handler import ( diff --git a/backend/services/settings/web/handler/select_test.go b/backend/services/settings/web/handler/select_test.go index ddefcfe..9785545 100644 --- a/backend/services/settings/web/handler/select_test.go +++ b/backend/services/settings/web/handler/select_test.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package handler import ( diff --git a/backend/services/settings/web/message/delete.go b/backend/services/settings/web/message/delete.go index 81723d9..62a5612 100644 --- a/backend/services/settings/web/message/delete.go +++ b/backend/services/settings/web/message/delete.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package message import ( diff --git a/backend/services/settings/web/message/error.go b/backend/services/settings/web/message/error.go index 004042d..3b70cef 100644 --- a/backend/services/settings/web/message/error.go +++ b/backend/services/settings/web/message/error.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package message import "errors" diff --git a/backend/services/settings/web/message/insert.go b/backend/services/settings/web/message/insert.go index b54a3e1..a351c4f 100644 --- a/backend/services/settings/web/message/insert.go +++ b/backend/services/settings/web/message/insert.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package message import ( diff --git a/backend/services/settings/web/server.go b/backend/services/settings/web/server.go index dc2e2c7..8897b25 100644 --- a/backend/services/settings/web/server.go +++ b/backend/services/settings/web/server.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package web import ( diff --git a/backend/services/shared/client/api.go b/backend/services/shared/client/api.go index d7ecea5..8bec3d5 100644 --- a/backend/services/shared/client/api.go +++ b/backend/services/shared/client/api.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package client import ( diff --git a/backend/services/shared/client/auth.go b/backend/services/shared/client/auth.go index a8976c1..c72348a 100644 --- a/backend/services/shared/client/auth.go +++ b/backend/services/shared/client/auth.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package client import ( diff --git a/backend/services/shared/client/command.go b/backend/services/shared/client/command.go index e98d1cd..0bb5541 100644 --- a/backend/services/shared/client/command.go +++ b/backend/services/shared/client/command.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package client import ( diff --git a/backend/services/shared/client/error.go b/backend/services/shared/client/error.go index caf3ea6..23cb22c 100644 --- a/backend/services/shared/client/error.go +++ b/backend/services/shared/client/error.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package client import ( diff --git a/backend/services/shared/client/model/access.go b/backend/services/shared/client/model/access.go index 2fdaf4c..1b359e9 100644 --- a/backend/services/shared/client/model/access.go +++ b/backend/services/shared/client/model/access.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package model type Access struct { diff --git a/backend/services/shared/client/model/error.go b/backend/services/shared/client/model/error.go index d3e50bf..95e5c0d 100644 --- a/backend/services/shared/client/model/error.go +++ b/backend/services/shared/client/model/error.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package model import "errors" diff --git a/backend/services/shared/client/model/lang.go b/backend/services/shared/client/model/lang.go index 770b797..4d705ec 100644 --- a/backend/services/shared/client/model/lang.go +++ b/backend/services/shared/client/model/lang.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package model type Language struct { diff --git a/backend/services/shared/client/model/token.go b/backend/services/shared/client/model/token.go index 4488b87..03d37ec 100644 --- a/backend/services/shared/client/model/token.go +++ b/backend/services/shared/client/model/token.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package model import "strings" diff --git a/backend/services/shared/client/model/user.go b/backend/services/shared/client/model/user.go index 94cbb88..c5b51c4 100644 --- a/backend/services/shared/client/model/user.go +++ b/backend/services/shared/client/model/user.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package model import "strings" diff --git a/backend/services/shared/config.go b/backend/services/shared/config.go index f8a7645..a506e5a 100644 --- a/backend/services/shared/config.go +++ b/backend/services/shared/config.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package shared import ( diff --git a/backend/services/shared/constants/file.go b/backend/services/shared/constants/file.go index 4f92c15..17601c9 100644 --- a/backend/services/shared/constants/file.go +++ b/backend/services/shared/constants/file.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package constants import ( diff --git a/backend/services/shared/constants/session.go b/backend/services/shared/constants/session.go index 334d8c1..e455545 100644 --- a/backend/services/shared/constants/session.go +++ b/backend/services/shared/constants/session.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package constants const ( diff --git a/backend/services/shared/crypto/aes.go b/backend/services/shared/crypto/aes.go index 6d5825d..0c0705f 100644 --- a/backend/services/shared/crypto/aes.go +++ b/backend/services/shared/crypto/aes.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package crypto import ( diff --git a/backend/services/shared/crypto/aes_test.go b/backend/services/shared/crypto/aes_test.go index 7f6d40d..9c463f9 100644 --- a/backend/services/shared/crypto/aes_test.go +++ b/backend/services/shared/crypto/aes_test.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package crypto import ( diff --git a/backend/services/shared/crypto/jwt.go b/backend/services/shared/crypto/jwt.go index 7baae20..80f591b 100644 --- a/backend/services/shared/crypto/jwt.go +++ b/backend/services/shared/crypto/jwt.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package crypto import ( diff --git a/backend/services/shared/error.go b/backend/services/shared/error.go index 6a09662..02eae1a 100644 --- a/backend/services/shared/error.go +++ b/backend/services/shared/error.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package shared import ( diff --git a/backend/services/shared/message/job.go b/backend/services/shared/message/job.go index 1a645ef..67d40c4 100644 --- a/backend/services/shared/message/job.go +++ b/backend/services/shared/message/job.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package message import "encoding/json" diff --git a/backend/services/shared/request/callback.go b/backend/services/shared/request/callback.go index 73dec79..7c79c2e 100644 --- a/backend/services/shared/request/callback.go +++ b/backend/services/shared/request/callback.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package request import ( diff --git a/backend/services/shared/request/command.go b/backend/services/shared/request/command.go index 3481a07..8bbb6ed 100644 --- a/backend/services/shared/request/command.go +++ b/backend/services/shared/request/command.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package request import ( diff --git a/backend/services/shared/request/config.go b/backend/services/shared/request/config.go index c41d967..886581b 100644 --- a/backend/services/shared/request/config.go +++ b/backend/services/shared/request/config.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package request import "encoding/json" diff --git a/backend/services/shared/request/session.go b/backend/services/shared/request/session.go index 5012972..0a73de3 100644 --- a/backend/services/shared/request/session.go +++ b/backend/services/shared/request/session.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package request import "encoding/json" diff --git a/backend/services/shared/request/settings.go b/backend/services/shared/request/settings.go index b6f4ff2..c16a121 100644 --- a/backend/services/shared/request/settings.go +++ b/backend/services/shared/request/settings.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package request import ( diff --git a/backend/services/shared/request/token.go b/backend/services/shared/request/token.go index 292d141..5e56082 100644 --- a/backend/services/shared/request/token.go +++ b/backend/services/shared/request/token.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package request import ( diff --git a/backend/services/shared/request/uninstall.go b/backend/services/shared/request/uninstall.go index 9722ebb..a163fd2 100644 --- a/backend/services/shared/request/uninstall.go +++ b/backend/services/shared/request/uninstall.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package request import "encoding/json" diff --git a/backend/services/shared/response/callback.go b/backend/services/shared/response/callback.go index 34bf788..d232529 100644 --- a/backend/services/shared/response/callback.go +++ b/backend/services/shared/response/callback.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package response import "encoding/json" diff --git a/backend/services/shared/response/command.go b/backend/services/shared/response/command.go index 582285f..13b7d7d 100644 --- a/backend/services/shared/response/command.go +++ b/backend/services/shared/response/command.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package response type BaseCommandResponse struct { diff --git a/backend/services/shared/response/config.go b/backend/services/shared/response/config.go index d0d8dae..fdfdb52 100644 --- a/backend/services/shared/response/config.go +++ b/backend/services/shared/response/config.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package response import ( diff --git a/backend/services/shared/response/error.go b/backend/services/shared/response/error.go index 610ae23..debb13f 100644 --- a/backend/services/shared/response/error.go +++ b/backend/services/shared/response/error.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package response import "encoding/json" diff --git a/backend/services/shared/response/generic.go b/backend/services/shared/response/generic.go index e38ff5a..8fcaf22 100644 --- a/backend/services/shared/response/generic.go +++ b/backend/services/shared/response/generic.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package response import "encoding/json" diff --git a/backend/services/shared/response/settings.go b/backend/services/shared/response/settings.go index 82995af..68167b4 100644 --- a/backend/services/shared/response/settings.go +++ b/backend/services/shared/response/settings.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package response import "encoding/json" diff --git a/backend/services/shared/response/user.go b/backend/services/shared/response/user.go index 0f6ed48..395553a 100644 --- a/backend/services/shared/response/user.go +++ b/backend/services/shared/response/user.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package response import "encoding/json" diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index dc4ac9e..81a4ac6 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + /* eslint-disable */ const path = require("path"); diff --git a/frontend/jest.config.js b/frontend/jest.config.js index a5df951..5468655 100644 --- a/frontend/jest.config.js +++ b/frontend/jest.config.js @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + module.exports = { verbose: true, preset: "ts-jest", diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js index 1ebcf24..8355cc0 100644 --- a/frontend/postcss.config.js +++ b/frontend/postcss.config.js @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + module.exports = { plugins: ["tailwindcss", "autoprefixer"], }; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c65a2ee..fb2f50a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; import { BrowserRouter as Router, diff --git a/frontend/src/__mocks__/fileMock.ts b/frontend/src/__mocks__/fileMock.ts index dbc304a..5a03ed1 100644 --- a/frontend/src/__mocks__/fileMock.ts +++ b/frontend/src/__mocks__/fileMock.ts @@ -1 +1,19 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export default "test-file-stub"; diff --git a/frontend/src/__mocks__/styleMock.ts b/frontend/src/__mocks__/styleMock.ts index ff8b4c5..62e444f 100644 --- a/frontend/src/__mocks__/styleMock.ts +++ b/frontend/src/__mocks__/styleMock.ts @@ -1 +1,19 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export default {}; diff --git a/frontend/src/components/button/Button.tsx b/frontend/src/components/button/Button.tsx index 56fe905..707f43d 100644 --- a/frontend/src/components/button/Button.tsx +++ b/frontend/src/components/button/Button.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; import cx from "classnames"; diff --git a/frontend/src/components/button/index.ts b/frontend/src/components/button/index.ts index d99df4c..df873cb 100644 --- a/frontend/src/components/button/index.ts +++ b/frontend/src/components/button/index.ts @@ -1 +1,19 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export { OnlyofficeButton } from "./Button"; diff --git a/frontend/src/components/divider/Divider.tsx b/frontend/src/components/divider/Divider.tsx index 187beef..f83eabd 100644 --- a/frontend/src/components/divider/Divider.tsx +++ b/frontend/src/components/divider/Divider.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; type DividerProps = { diff --git a/frontend/src/components/divider/index.ts b/frontend/src/components/divider/index.ts index 63e0b7b..a42b1ca 100644 --- a/frontend/src/components/divider/index.ts +++ b/frontend/src/components/divider/index.ts @@ -1 +1,19 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export { OnlyofficeDivider } from "./Divider"; diff --git a/frontend/src/components/drop/Drop.tsx b/frontend/src/components/drop/Drop.tsx index a2d77b1..5e04d6d 100644 --- a/frontend/src/components/drop/Drop.tsx +++ b/frontend/src/components/drop/Drop.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + /* eslint-disable react/jsx-props-no-spreading */ import React, { useState } from "react"; import { useDropzone, DropEvent, FileRejection } from "react-dropzone"; diff --git a/frontend/src/components/drop/index.ts b/frontend/src/components/drop/index.ts index 6db7797..5c6169b 100644 --- a/frontend/src/components/drop/index.ts +++ b/frontend/src/components/drop/index.ts @@ -1 +1,19 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export { OnlyofficeDragDrop } from "./Drop"; diff --git a/frontend/src/components/error/Error.tsx b/frontend/src/components/error/Error.tsx index b418384..99dc33f 100644 --- a/frontend/src/components/error/Error.tsx +++ b/frontend/src/components/error/Error.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; type ErrorProps = { diff --git a/frontend/src/components/error/index.ts b/frontend/src/components/error/index.ts index 6320c57..0f0c411 100644 --- a/frontend/src/components/error/index.ts +++ b/frontend/src/components/error/index.ts @@ -1 +1,19 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export { OnlyofficeError } from "./Error"; diff --git a/frontend/src/components/file/File.tsx b/frontend/src/components/file/File.tsx index 833cf93..643d5c6 100644 --- a/frontend/src/components/file/File.tsx +++ b/frontend/src/components/file/File.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React, { useState } from "react"; import DetailsIcon from "@assets/arrow-down.svg"; diff --git a/frontend/src/components/file/index.ts b/frontend/src/components/file/index.ts index 47d72fd..ad3ea22 100644 --- a/frontend/src/components/file/index.ts +++ b/frontend/src/components/file/index.ts @@ -1 +1,19 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export { OnlyofficeFile } from "./File"; diff --git a/frontend/src/components/header/Header.tsx b/frontend/src/components/header/Header.tsx index 97307b0..f272246 100644 --- a/frontend/src/components/header/Header.tsx +++ b/frontend/src/components/header/Header.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; import { OnlyofficeTitle, OnlyofficeSubtitle } from "@components/title"; diff --git a/frontend/src/components/header/index.ts b/frontend/src/components/header/index.ts index a4e0832..130ff07 100644 --- a/frontend/src/components/header/index.ts +++ b/frontend/src/components/header/index.ts @@ -1 +1,19 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export { OnlyofficeMainHeader } from "./Header"; diff --git a/frontend/src/components/info/Info.tsx b/frontend/src/components/info/Info.tsx index a41d878..d47d6b0 100644 --- a/frontend/src/components/info/Info.tsx +++ b/frontend/src/components/info/Info.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; import { OnlyofficeSubtitle, OnlyofficeTitle } from "@components/title"; diff --git a/frontend/src/components/info/index.ts b/frontend/src/components/info/index.ts index 225f6f0..e5c6b14 100644 --- a/frontend/src/components/info/index.ts +++ b/frontend/src/components/info/index.ts @@ -1 +1,19 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export { OnlyofficeFileInfo } from "./Info"; diff --git a/frontend/src/components/input/Input.tsx b/frontend/src/components/input/Input.tsx index 6b3b5d0..7512cda 100644 --- a/frontend/src/components/input/Input.tsx +++ b/frontend/src/components/input/Input.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + /* eslint-disable jsx-a11y/label-has-associated-control */ import React from "react"; import cx from "classnames"; diff --git a/frontend/src/components/input/index.ts b/frontend/src/components/input/index.ts index 5b0b6a8..028861e 100644 --- a/frontend/src/components/input/index.ts +++ b/frontend/src/components/input/index.ts @@ -1 +1,19 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export { OnlyofficeInput } from "./Input"; diff --git a/frontend/src/components/nofile/NoFile.tsx b/frontend/src/components/nofile/NoFile.tsx index f78cba3..c0167d9 100644 --- a/frontend/src/components/nofile/NoFile.tsx +++ b/frontend/src/components/nofile/NoFile.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; import Nofiles from "@assets/nofile.svg"; diff --git a/frontend/src/components/nofile/index.ts b/frontend/src/components/nofile/index.ts index a754832..aca4109 100644 --- a/frontend/src/components/nofile/index.ts +++ b/frontend/src/components/nofile/index.ts @@ -1 +1,19 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export { OnlyofficeNoFile } from "./NoFile"; diff --git a/frontend/src/components/search/Search.tsx b/frontend/src/components/search/Search.tsx index a75c210..d9ffb5d 100644 --- a/frontend/src/components/search/Search.tsx +++ b/frontend/src/components/search/Search.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; type SearchProps = { diff --git a/frontend/src/components/search/index.ts b/frontend/src/components/search/index.ts index 0052822..f1d989c 100644 --- a/frontend/src/components/search/index.ts +++ b/frontend/src/components/search/index.ts @@ -1 +1,19 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export { OnlyofficeSearchBar } from "./Search"; diff --git a/frontend/src/components/spinner/Spinner.tsx b/frontend/src/components/spinner/Spinner.tsx index e8276d4..8e1dbaf 100644 --- a/frontend/src/components/spinner/Spinner.tsx +++ b/frontend/src/components/spinner/Spinner.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; export const OnlyofficeSpinner: React.FC = () =>
; diff --git a/frontend/src/components/spinner/index.ts b/frontend/src/components/spinner/index.ts index c308183..6c1df4f 100644 --- a/frontend/src/components/spinner/index.ts +++ b/frontend/src/components/spinner/index.ts @@ -1 +1,19 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export { OnlyofficeSpinner } from "./Spinner"; diff --git a/frontend/src/components/tile/Tile.tsx b/frontend/src/components/tile/Tile.tsx index b097db7..66f9e42 100644 --- a/frontend/src/components/tile/Tile.tsx +++ b/frontend/src/components/tile/Tile.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; import cx from "classnames"; diff --git a/frontend/src/components/tile/index.ts b/frontend/src/components/tile/index.ts index 3f46063..da6f738 100644 --- a/frontend/src/components/tile/index.ts +++ b/frontend/src/components/tile/index.ts @@ -1 +1,19 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export { OnlyofficeTile } from "./Tile"; diff --git a/frontend/src/components/title/Subtitle.tsx b/frontend/src/components/title/Subtitle.tsx index 00e067f..ff597df 100644 --- a/frontend/src/components/title/Subtitle.tsx +++ b/frontend/src/components/title/Subtitle.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; import cx from "classnames"; diff --git a/frontend/src/components/title/Title.tsx b/frontend/src/components/title/Title.tsx index d0276a6..f25e736 100644 --- a/frontend/src/components/title/Title.tsx +++ b/frontend/src/components/title/Title.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; import cx from "classnames"; diff --git a/frontend/src/components/title/index.ts b/frontend/src/components/title/index.ts index 0165afc..d5bb383 100644 --- a/frontend/src/components/title/index.ts +++ b/frontend/src/components/title/index.ts @@ -1,2 +1,20 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export { OnlyofficeTitle } from "./Title"; export { OnlyofficeSubtitle } from "./Subtitle"; diff --git a/frontend/src/context/TokenContext.tsx b/frontend/src/context/TokenContext.tsx index c5b4f6e..ee75d4d 100644 --- a/frontend/src/context/TokenContext.tsx +++ b/frontend/src/context/TokenContext.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import AppExtensionsSDK from "@pipedrive/app-extensions-sdk"; import React, { useEffect } from "react"; import { proxy } from "valtio"; diff --git a/frontend/src/custom.d.ts b/frontend/src/custom.d.ts index 1a3dd3c..a45576f 100644 --- a/frontend/src/custom.d.ts +++ b/frontend/src/custom.d.ts @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + declare module "*.svg" { const content: any; export default content; diff --git a/frontend/src/hooks/useBuildConfig.tsx b/frontend/src/hooks/useBuildConfig.tsx index 3b89629..cba62c2 100644 --- a/frontend/src/hooks/useBuildConfig.tsx +++ b/frontend/src/hooks/useBuildConfig.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import { useQuery } from "react-query"; import { fetchConfig } from "@services/config"; diff --git a/frontend/src/hooks/useDeleteFile.tsx b/frontend/src/hooks/useDeleteFile.tsx index fed3d59..04fd1af 100644 --- a/frontend/src/hooks/useDeleteFile.tsx +++ b/frontend/src/hooks/useDeleteFile.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import { useMutation, useQueryClient } from "react-query"; import { deleteFile } from "@services/file"; diff --git a/frontend/src/hooks/useFileSearch.tsx b/frontend/src/hooks/useFileSearch.tsx index 0d5069c..ded3c4f 100644 --- a/frontend/src/hooks/useFileSearch.tsx +++ b/frontend/src/hooks/useFileSearch.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import { useInfiniteQuery } from "react-query"; import { fetchFiles } from "@services/file"; diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index df96116..a5be54c 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import i18n from "i18next"; import I18NextHttpBackend from "i18next-http-backend"; import LanguageDetector from "i18next-browser-languagedetector"; diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 6ec12fb..dc049a2 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; import ReactDOM from "react-dom/client"; import { QueryClient, QueryClientProvider } from "react-query"; diff --git a/frontend/src/layout/Banner.tsx b/frontend/src/layout/Banner.tsx index 4869dda..41c4809 100644 --- a/frontend/src/layout/Banner.tsx +++ b/frontend/src/layout/Banner.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; import { OnlyofficeTitle, OnlyofficeSubtitle } from "@components/title"; diff --git a/frontend/src/layout/ErrorBackground.tsx b/frontend/src/layout/ErrorBackground.tsx index f5e861c..0d3d548 100644 --- a/frontend/src/layout/ErrorBackground.tsx +++ b/frontend/src/layout/ErrorBackground.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; import { OnlyofficeButton } from "@components/button"; diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx index 3016ed9..a75063a 100644 --- a/frontend/src/pages/Creation/Creation.tsx +++ b/frontend/src/pages/Creation/Creation.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React, { useEffect, useState } from "react"; import md5 from "md5"; import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; diff --git a/frontend/src/pages/Creation/Upload.tsx b/frontend/src/pages/Creation/Upload.tsx index 56c0c5f..ad0a181 100644 --- a/frontend/src/pages/Creation/Upload.tsx +++ b/frontend/src/pages/Creation/Upload.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import { FileRejection, DropEvent } from "react-dropzone"; import React, { useEffect, useState } from "react"; import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; diff --git a/frontend/src/pages/Creation/index.tsx b/frontend/src/pages/Creation/index.tsx index d2c1f3c..b41469c 100644 --- a/frontend/src/pages/Creation/index.tsx +++ b/frontend/src/pages/Creation/index.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React, { useEffect, useState } from "react"; import AppExtensionsSDK from "@pipedrive/app-extensions-sdk"; import { Tabs, TabList, Tab, TabPanel } from "react-tabs"; diff --git a/frontend/src/pages/Editor/index.tsx b/frontend/src/pages/Editor/index.tsx index b1f28a9..67b9485 100644 --- a/frontend/src/pages/Editor/index.tsx +++ b/frontend/src/pages/Editor/index.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; import { useSearchParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; diff --git a/frontend/src/pages/Main/Actions.tsx b/frontend/src/pages/Main/Actions.tsx index d62667f..259be73 100644 --- a/frontend/src/pages/Main/Actions.tsx +++ b/frontend/src/pages/Main/Actions.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React, { useEffect, useState } from "react"; import md5 from "md5"; import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; diff --git a/frontend/src/pages/Main/Main.tsx b/frontend/src/pages/Main/Main.tsx index 2f731ed..e6a5e6d 100644 --- a/frontend/src/pages/Main/Main.tsx +++ b/frontend/src/pages/Main/Main.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React, { useCallback, useEffect, useRef, useState } from "react"; import AppExtensionsSDK, { Command, diff --git a/frontend/src/pages/Main/index.tsx b/frontend/src/pages/Main/index.tsx index b689278..a4ffdf4 100644 --- a/frontend/src/pages/Main/index.tsx +++ b/frontend/src/pages/Main/index.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React from "react"; import { useSnapshot } from "valtio"; import { useTranslation } from "react-i18next"; diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx index 8dad92c..1d7c934 100644 --- a/frontend/src/pages/Settings/index.tsx +++ b/frontend/src/pages/Settings/index.tsx @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import React, { useEffect, useState } from "react"; import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; import { useSnapshot } from "valtio"; diff --git a/frontend/src/services/config.ts b/frontend/src/services/config.ts index c4398a0..9a127c9 100644 --- a/frontend/src/services/config.ts +++ b/frontend/src/services/config.ts @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import axios from "axios"; import axiosRetry from "axios-retry"; diff --git a/frontend/src/services/file.ts b/frontend/src/services/file.ts index 7cd9344..73cf1d7 100644 --- a/frontend/src/services/file.ts +++ b/frontend/src/services/file.ts @@ -1,6 +1,25 @@ -import { AuthToken } from "@context/TokenContext"; +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import axios from "axios"; +import { AuthToken } from "@context/TokenContext"; + import { FileResponse } from "src/types/file"; export const fetchFiles = async ( diff --git a/frontend/src/services/me.ts b/frontend/src/services/me.ts index a76060f..bd10124 100644 --- a/frontend/src/services/me.ts +++ b/frontend/src/services/me.ts @@ -1,9 +1,29 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import axios from "axios"; import axiosRetry from "axios-retry"; import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; -import { PipedriveUserResponse, UserResponse } from "src/types/user"; + import { AuthToken } from "@context/TokenContext"; +import { PipedriveUserResponse, UserResponse } from "src/types/user"; + export const getMe = async (sdk: AppExtensionsSDK) => { const pctx = await sdk.execute(Command.GET_SIGNED_TOKEN); const client = axios.create({ baseURL: process.env.BACKEND_GATEWAY }); diff --git a/frontend/src/services/settings.ts b/frontend/src/services/settings.ts index d3513e0..52e604e 100644 --- a/frontend/src/services/settings.ts +++ b/frontend/src/services/settings.ts @@ -1,6 +1,25 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import axios from "axios"; import axiosRetry from "axios-retry"; import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; + import { SettingsResponse } from "src/types/settings"; export const postSettings = async ( diff --git a/frontend/src/services/user.ts b/frontend/src/services/user.ts index cae74f2..6185022 100644 --- a/frontend/src/services/user.ts +++ b/frontend/src/services/user.ts @@ -1,5 +1,25 @@ -import { AuthToken } from "@context/TokenContext"; +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import axios from "axios"; + +import { AuthToken } from "@context/TokenContext"; + import { PipedriveSearchUsersResponse } from "src/types/user"; export const fetchUsers = async ( diff --git a/frontend/src/setupTests.ts b/frontend/src/setupTests.ts index d0de870..a390a78 100644 --- a/frontend/src/setupTests.ts +++ b/frontend/src/setupTests.ts @@ -1 +1,19 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import "@testing-library/jest-dom"; diff --git a/frontend/src/types/config.ts b/frontend/src/types/config.ts index ee88429..dde2dd1 100644 --- a/frontend/src/types/config.ts +++ b/frontend/src/types/config.ts @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + type Permissions = { comment: boolean; copy: boolean; diff --git a/frontend/src/types/file.ts b/frontend/src/types/file.ts index 0b4af22..cad51b5 100644 --- a/frontend/src/types/file.ts +++ b/frontend/src/types/file.ts @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export type File = { id: string; user_id: number; diff --git a/frontend/src/types/settings.ts b/frontend/src/types/settings.ts index eb23002..c5c442c 100644 --- a/frontend/src/types/settings.ts +++ b/frontend/src/types/settings.ts @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export type SettingsResponse = { doc_address: string; doc_secret: string; diff --git a/frontend/src/types/user.ts b/frontend/src/types/user.ts index 70c6874..26d2a73 100644 --- a/frontend/src/types/user.ts +++ b/frontend/src/types/user.ts @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export type UserResponse = { id: string; access_token: string; diff --git a/frontend/src/utils/file.ts b/frontend/src/utils/file.ts index 3499370..7293b28 100644 --- a/frontend/src/utils/file.ts +++ b/frontend/src/utils/file.ts @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import Docx from "@assets/docx.svg"; import Pptx from "@assets/pptx.svg"; import Xlsx from "@assets/xlsx.svg"; diff --git a/frontend/src/utils/url.ts b/frontend/src/utils/url.ts index eb98e7c..e35c067 100644 --- a/frontend/src/utils/url.ts +++ b/frontend/src/utils/url.ts @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + export const getCurrentURL = () => { const url = window.location !== window.parent.location diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 36b6bd6..20cfdab 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + module.exports = { content: ["./src/**/*.{ts,tsx}"], theme: { diff --git a/frontend/webpack.common.js b/frontend/webpack.common.js index c1680b5..44314d5 100644 --- a/frontend/webpack.common.js +++ b/frontend/webpack.common.js @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + /* eslint-disable */ const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); diff --git a/frontend/webpack.development.js b/frontend/webpack.development.js index a9e1c38..9e7d0db 100644 --- a/frontend/webpack.development.js +++ b/frontend/webpack.development.js @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + /* eslint-disable */ const { merge } = require("webpack-merge"); const common = require("./webpack.common.js"); diff --git a/frontend/webpack.production.js b/frontend/webpack.production.js index 966045f..b68996a 100644 --- a/frontend/webpack.production.js +++ b/frontend/webpack.production.js @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + /* eslint-disable */ const { merge } = require("webpack-merge"); const webpack = require('webpack'); From ace25151fdafee104ed615a6dc3c6d552d32f3ba Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 29 Mar 2023 13:02:22 +0500 Subject: [PATCH 045/145] fix(logging): replace typos --- backend/services/callback/web/worker/callback.go | 2 +- backend/services/gateway/web/middleware/auth.go | 2 +- backend/services/shared/client/error.go | 2 +- backend/services/shared/client/model/error.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/services/callback/web/worker/callback.go b/backend/services/callback/web/worker/callback.go index 9989cf3..98d64d3 100644 --- a/backend/services/callback/web/worker/callback.go +++ b/backend/services/callback/web/worker/callback.go @@ -141,7 +141,7 @@ func (c callbackWorker) UploadFile(ctx context.Context, payload []byte) error { Scope: ures.Scope, ApiDomain: ures.ApiDomain, }); err != nil { - c.logger.Debugf("could not upload an onlyoffice file to zoom: %s", err.Error()) + c.logger.Debugf("could not upload an onlyoffice file to pipedrive: %s", err.Error()) return err } diff --git a/backend/services/gateway/web/middleware/auth.go b/backend/services/gateway/web/middleware/auth.go index fa7b8b3..ae2e60a 100644 --- a/backend/services/gateway/web/middleware/auth.go +++ b/backend/services/gateway/web/middleware/auth.go @@ -31,7 +31,7 @@ func BuildHandleAuthMiddleware( clientID, clientSecret string, logger log.Logger, ) func(next http.Handler) http.HandlerFunc { - logger.Debugf("zoom event middleware has been built with client_id %s and client_secret %s", clientID, clientSecret) + logger.Debugf("pipedrive event middleware has been built with client_id %s and client_secret %s", clientID, clientSecret) return func(next http.Handler) http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { signature := strings.ReplaceAll(r.Header.Get("Authorization"), "Basic ", "") diff --git a/backend/services/shared/client/error.go b/backend/services/shared/client/error.go index 23cb22c..91cd1c8 100644 --- a/backend/services/shared/client/error.go +++ b/backend/services/shared/client/error.go @@ -31,5 +31,5 @@ type UnexpectedStatusCodeError struct { } func (e *UnexpectedStatusCodeError) Error() string { - return fmt.Sprintf("could not perform zoom %s action. Status code: %d", e.Action, e.Code) + return fmt.Sprintf("could not perform pipedrive %s action. Status code: %d", e.Action, e.Code) } diff --git a/backend/services/shared/client/model/error.go b/backend/services/shared/client/model/error.go index 95e5c0d..24aec0a 100644 --- a/backend/services/shared/client/model/error.go +++ b/backend/services/shared/client/model/error.go @@ -20,4 +20,4 @@ package model import "errors" -var ErrInvalidTokenFormat error = errors.New("could not perform zoom action due to unexpected token format") +var ErrInvalidTokenFormat error = errors.New("could not perform pipedrive action due to unexpected token format") From 086779ce92c177a8d62808a257c66abf953bed7e Mon Sep 17 00:00:00 2001 From: Sergey Linnik Date: Wed, 29 Mar 2023 13:36:40 +0500 Subject: [PATCH 046/145] fix readme --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 7d29a48..019dd9e 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,6 @@ This app allows users to edit and collaborate on office documents right within P You can add the ONLYOFFICE app from the Pipedrive App Marketplace. To do it, you need to sign into your account and allow the requested app permissions. -*ONLYOFFICE app is using the connection to the special ONLYOFFICE Document Server instance to ensure document processing.* - ## App usage The app allows working with office documents directly within the Pipedrive frontend. From c2ce420a116dd8d2d8895041adb0db366968c1de Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 29 Mar 2023 18:21:06 +0500 Subject: [PATCH 047/145] feat(gateway): expose new files --- .gitmodules | 4 + backend/services/builder/web/handler/build.go | 2 +- backend/services/gateway/assets/assets | 1 + backend/services/gateway/assets/embed.go | 6 + .../services/gateway/web/controller/api.go | 125 ++----------- .../services/gateway/web/controller/file.go | 169 ++++++++++++++++++ backend/services/gateway/web/server.go | 8 +- 7 files changed, 200 insertions(+), 115 deletions(-) create mode 100644 .gitmodules create mode 160000 backend/services/gateway/assets/assets create mode 100644 backend/services/gateway/assets/embed.go create mode 100644 backend/services/gateway/web/controller/file.go diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..252a172 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "backend/services/gateway/embed/assets"] + path = backend/services/gateway/embed/assets + url = https://github.com/ONLYOFFICE/document-templates.git + branch = main/new diff --git a/backend/services/builder/web/handler/build.go b/backend/services/builder/web/handler/build.go index 43f046f..4b7c1cd 100644 --- a/backend/services/builder/web/handler/build.go +++ b/backend/services/builder/web/handler/build.go @@ -154,7 +154,7 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui Document: response.Document{ Key: req.DocKey, Title: req.Filename, - URL: fmt.Sprintf("%s/download?cid=%d&fid=%s&token=%s", c.gatewayURL, usr.CompanyID, req.FileID, tkn), + URL: fmt.Sprintf("%s/files/download?cid=%d&fid=%s&token=%s", c.gatewayURL, usr.CompanyID, req.FileID, tkn), }, EditorConfig: response.EditorConfig{ User: response.User{ diff --git a/backend/services/gateway/assets/assets b/backend/services/gateway/assets/assets new file mode 160000 index 0000000..4b96e28 --- /dev/null +++ b/backend/services/gateway/assets/assets @@ -0,0 +1 @@ +Subproject commit 4b96e283924e0481299b6400520829649634c23e diff --git a/backend/services/gateway/assets/embed.go b/backend/services/gateway/assets/embed.go new file mode 100644 index 0000000..0a2ef26 --- /dev/null +++ b/backend/services/gateway/assets/embed.go @@ -0,0 +1,6 @@ +package assets + +import "embed" + +//go:embed assets +var Files embed.FS diff --git a/backend/services/gateway/web/controller/api.go b/backend/services/gateway/web/controller/api.go index 6ca6a5e..91629b3 100644 --- a/backend/services/gateway/web/controller/api.go +++ b/backend/services/gateway/web/controller/api.go @@ -23,7 +23,6 @@ import ( "encoding/json" "errors" "fmt" - "io" "io/ioutil" "net/http" "strconv" @@ -38,32 +37,29 @@ import ( "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" "go-micro.dev/v4/client" - "golang.org/x/sync/semaphore" ) var _ErrNotAdmin = errors.New("no admin access") type apiController struct { - namespace string - client client.Client - apiClient pclient.PipedriveApiClient - commandClient pclient.CommandClient - jwtManager crypto.JwtManager - logger log.Logger - allowedDownloads int + namespace string + client client.Client + apiClient pclient.PipedriveApiClient + commandClient pclient.CommandClient + jwtManager crypto.JwtManager + logger log.Logger } func NewApiController( namespace string, client client.Client, - jwtManager crypto.JwtManager, allowedDownloads int, logger log.Logger) apiController { + jwtManager crypto.JwtManager, logger log.Logger) apiController { return apiController{ - namespace: namespace, - client: client, - apiClient: pclient.NewPipedriveApiClient(), - commandClient: pclient.NewCommandClient(jwtManager), - jwtManager: jwtManager, - logger: logger, - allowedDownloads: allowedDownloads, + namespace: namespace, + client: client, + apiClient: pclient.NewPipedriveApiClient(), + commandClient: pclient.NewCommandClient(jwtManager), + jwtManager: jwtManager, + logger: logger, } } @@ -358,98 +354,3 @@ func (c apiController) BuildGetConfig() http.HandlerFunc { rw.Write(resp.ToJSON()) } } - -func (c apiController) BuildGetFile() http.HandlerFunc { - sem := semaphore.NewWeighted(int64(c.allowedDownloads)) - return func(rw http.ResponseWriter, r *http.Request) { - if ok := sem.TryAcquire(1); !ok { - c.logger.Warn("too many download requests") - rw.WriteHeader(http.StatusTooManyRequests) - return - } - - defer sem.Release(1) - - fid, cid, token := strings.TrimSpace(r.URL.Query().Get("fid")), - strings.TrimSpace(r.URL.Query().Get("cid")), strings.TrimSpace(r.URL.Query().Get("token")) - - var pctx request.PipedriveTokenContext - if token == "" { - c.logger.Errorf("unauthorized access to an api endpoint") - rw.WriteHeader(http.StatusUnauthorized) - return - } - - var docs response.DocSettingsResponse - if err := c.client.Call(r.Context(), c.client.NewRequest(fmt.Sprintf("%s:settings", c.namespace), "SettingsSelectHandler.GetSettings", cid), &docs); err != nil { - c.logger.Debugf("could not document server settings: %s", err.Error()) - rw.WriteHeader(http.StatusBadRequest) - return - } - - var wg sync.WaitGroup - wg.Add(2) - errChan := make(chan error, 2) - - go func() { - defer wg.Done() - if err := c.jwtManager.Verify(docs.DocSecret, token, &pctx); err != nil { - c.logger.Errorf("could not verify X-Pipedrive-App-Context: %s", err.Error()) - errChan <- err - return - } - }() - - go func() { - defer wg.Done() - var tkn interface{} - if err := c.jwtManager.Verify(docs.DocSecret, strings.ReplaceAll(r.Header.Get(docs.DocHeader), "Bearer ", ""), &tkn); err != nil { - c.logger.Errorf("could not verify docs header: %s", err.Error()) - errChan <- err - return - } - }() - - wg.Wait() - - select { - case <-errChan: - rw.WriteHeader(http.StatusForbidden) - return - default: - } - - ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) - defer cancel() - - var ures response.UserResponse - if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", fmt.Sprint(pctx.UID+pctx.CID)), &ures); err != nil { - c.logger.Errorf("could not get user access info: %s", err.Error()) - if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { - rw.WriteHeader(http.StatusRequestTimeout) - return - } - - microErr := response.MicroError{} - if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { - rw.WriteHeader(http.StatusUnauthorized) - return - } - - rw.WriteHeader(microErr.Code) - return - } - - req, _ := http.NewRequest("GET", fmt.Sprintf("%s/files/%s/download", ures.ApiDomain, fid), nil) - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", ures.AccessToken)) - - resp, err := http.DefaultClient.Do(req) - if err != nil { - rw.WriteHeader(http.StatusBadRequest) - return - } - - defer resp.Body.Close() - io.Copy(rw, resp.Body) - } -} diff --git a/backend/services/gateway/web/controller/file.go b/backend/services/gateway/web/controller/file.go new file mode 100644 index 0000000..36d86c5 --- /dev/null +++ b/backend/services/gateway/web/controller/file.go @@ -0,0 +1,169 @@ +package controller + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "strings" + "sync" + "time" + + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/gateway/assets" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" + "go-micro.dev/v4/client" + "golang.org/x/sync/semaphore" +) + +type fileController struct { + namespace string + allowedDownloads int + client client.Client + jwtManager crypto.JwtManager + logger log.Logger +} + +func NewFileController( + namespace string, allowedDownloads int, client client.Client, + jwtManager crypto.JwtManager, logger log.Logger) fileController { + return fileController{ + namespace: namespace, + client: client, + jwtManager: jwtManager, + logger: logger, + allowedDownloads: allowedDownloads, + } +} + +func (c fileController) BuildGetFile() http.HandlerFunc { + return func(rw http.ResponseWriter, r *http.Request) { + lang, fileType := strings.TrimSpace(r.URL.Query().Get("lang")), + strings.TrimSpace(r.URL.Query().Get("type")) + if lang == "" { + rw.WriteHeader(http.StatusBadRequest) + return + } + + _, ok := r.Context().Value(request.PipedriveTokenContext{}).(request.PipedriveTokenContext) + if !ok { + rw.WriteHeader(http.StatusForbidden) + c.logger.Error("could not extract pipedrive context from the context") + return + } + + file, err := assets.Files.Open(fmt.Sprintf("assets/%s/new.%s", lang, fileType)) + if err != nil { + lang = "en-US" + file, err = assets.Files.Open(fmt.Sprintf("assets/%s/new.%s", lang, fileType)) + if err != nil { + rw.WriteHeader(http.StatusBadRequest) + c.logger.Errorf("could not get a new file: %s", err.Error()) + return + } + io.Copy(rw, file) + return + } + + io.Copy(rw, file) + } +} + +func (c fileController) BuildDownloadFile() http.HandlerFunc { + sem := semaphore.NewWeighted(int64(c.allowedDownloads)) + return func(rw http.ResponseWriter, r *http.Request) { + if ok := sem.TryAcquire(1); !ok { + c.logger.Warn("too many download requests") + rw.WriteHeader(http.StatusTooManyRequests) + return + } + + defer sem.Release(1) + + fid, cid, token := strings.TrimSpace(r.URL.Query().Get("fid")), + strings.TrimSpace(r.URL.Query().Get("cid")), strings.TrimSpace(r.URL.Query().Get("token")) + + var pctx request.PipedriveTokenContext + if token == "" { + c.logger.Errorf("unauthorized access to an api endpoint") + rw.WriteHeader(http.StatusUnauthorized) + return + } + + var docs response.DocSettingsResponse + if err := c.client.Call(r.Context(), c.client.NewRequest(fmt.Sprintf("%s:settings", c.namespace), "SettingsSelectHandler.GetSettings", cid), &docs); err != nil { + c.logger.Debugf("could not document server settings: %s", err.Error()) + rw.WriteHeader(http.StatusBadRequest) + return + } + + var wg sync.WaitGroup + wg.Add(2) + errChan := make(chan error, 2) + + go func() { + defer wg.Done() + if err := c.jwtManager.Verify(docs.DocSecret, token, &pctx); err != nil { + c.logger.Errorf("could not verify X-Pipedrive-App-Context: %s", err.Error()) + errChan <- err + return + } + }() + + go func() { + defer wg.Done() + var tkn interface{} + if err := c.jwtManager.Verify(docs.DocSecret, strings.ReplaceAll(r.Header.Get(docs.DocHeader), "Bearer ", ""), &tkn); err != nil { + c.logger.Errorf("could not verify docs header: %s", err.Error()) + errChan <- err + return + } + }() + + wg.Wait() + + select { + case <-errChan: + rw.WriteHeader(http.StatusForbidden) + return + default: + } + + ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) + defer cancel() + + var ures response.UserResponse + if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", fmt.Sprint(pctx.UID+pctx.CID)), &ures); err != nil { + c.logger.Errorf("could not get user access info: %s", err.Error()) + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + rw.WriteHeader(http.StatusRequestTimeout) + return + } + + microErr := response.MicroError{} + if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { + rw.WriteHeader(http.StatusUnauthorized) + return + } + + rw.WriteHeader(microErr.Code) + return + } + + req, _ := http.NewRequest("GET", fmt.Sprintf("%s/files/%s/download", ures.ApiDomain, fid), nil) + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", ures.AccessToken)) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + rw.WriteHeader(http.StatusBadRequest) + return + } + + defer resp.Body.Close() + io.Copy(rw, resp.Body) + } +} diff --git a/backend/services/gateway/web/server.go b/backend/services/gateway/web/server.go index f2db3d0..d742c66 100644 --- a/backend/services/gateway/web/server.go +++ b/backend/services/gateway/web/server.go @@ -98,7 +98,8 @@ func (s *PipedriveHTTPService) InitializeRoutes() { tokenMiddleware := middleware.BuildHandleContextMiddleware(s.clientSecret, jwtManager, s.logger) authController := controller.NewAuthController(s.namespace, s.redirectURI, s.client, pclient.NewPipedriveAuthClient(s.clientID, s.clientSecret), s.logger) - apiController := controller.NewApiController(s.namespace, s.client, jwtManager, s.allowedDownloads, s.logger) + apiController := controller.NewApiController(s.namespace, s.client, jwtManager, s.logger) + fileController := controller.NewFileController(s.namespace, s.allowedDownloads, s.client, jwtManager, s.logger) s.mux.Group(func(r chi.Router) { r.Use(chimiddleware.Recoverer) @@ -122,6 +123,9 @@ func (s *PipedriveHTTPService) InitializeRoutes() { cr.Get("/settings", apiController.BuildGetSettings()) }) - r.Get("/download", apiController.BuildGetFile()) + r.Route("/files", func(fr chi.Router) { + fr.Get("/download", fileController.BuildDownloadFile()) + fr.Get("/create", fileController.BuildGetFile()) + }) }) } From 7adfbcc7fd6c54ec6ac23122434599daa1b5b9ee Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 29 Mar 2023 18:23:54 +0500 Subject: [PATCH 048/145] fix(gateway): token middleware for create endpoint --- backend/services/gateway/web/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/services/gateway/web/server.go b/backend/services/gateway/web/server.go index d742c66..20e43a5 100644 --- a/backend/services/gateway/web/server.go +++ b/backend/services/gateway/web/server.go @@ -125,7 +125,7 @@ func (s *PipedriveHTTPService) InitializeRoutes() { r.Route("/files", func(fr chi.Router) { fr.Get("/download", fileController.BuildDownloadFile()) - fr.Get("/create", fileController.BuildGetFile()) + fr.Get("/create", tokenMiddleware(fileController.BuildGetFile())) }) }) } From b5203c510d76ba938700a734f90c83232a60597f Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 29 Mar 2023 19:18:26 +0500 Subject: [PATCH 049/145] fix: create new file --- .../services/gateway/web/controller/file.go | 68 +++++++++++++++++-- backend/services/shared/client/api.go | 21 ++++++ backend/services/shared/response/pipedrive.go | 18 +++++ frontend/src/pages/Creation/Creation.tsx | 43 +++++++----- 4 files changed, 126 insertions(+), 24 deletions(-) create mode 100644 backend/services/shared/response/pipedrive.go diff --git a/backend/services/gateway/web/controller/file.go b/backend/services/gateway/web/controller/file.go index 36d86c5..4d1eac0 100644 --- a/backend/services/gateway/web/controller/file.go +++ b/backend/services/gateway/web/controller/file.go @@ -13,6 +13,8 @@ import ( "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/gateway/assets" + pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client/model" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" @@ -24,6 +26,7 @@ type fileController struct { namespace string allowedDownloads int client client.Client + apiClient pclient.PipedriveApiClient jwtManager crypto.JwtManager logger log.Logger } @@ -34,6 +37,7 @@ func NewFileController( return fileController{ namespace: namespace, client: client, + apiClient: pclient.NewPipedriveApiClient(), jwtManager: jwtManager, logger: logger, allowedDownloads: allowedDownloads, @@ -42,20 +46,44 @@ func NewFileController( func (c fileController) BuildGetFile() http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { - lang, fileType := strings.TrimSpace(r.URL.Query().Get("lang")), - strings.TrimSpace(r.URL.Query().Get("type")) - if lang == "" { + query := r.URL.Query() + lang, fileType, dealID, filename := strings.TrimSpace(query.Get("lang")), + strings.TrimSpace(query.Get("type")), strings.TrimSpace(query.Get("deal")), + strings.TrimSpace(query.Get("filename")) + if lang == "" || fileType == "" || dealID == "" || filename == "" { rw.WriteHeader(http.StatusBadRequest) return } - _, ok := r.Context().Value(request.PipedriveTokenContext{}).(request.PipedriveTokenContext) + pctx, ok := r.Context().Value(request.PipedriveTokenContext{}).(request.PipedriveTokenContext) if !ok { rw.WriteHeader(http.StatusForbidden) c.logger.Error("could not extract pipedrive context from the context") return } + ctx, cancel := context.WithTimeout(r.Context(), 4*time.Second) + defer cancel() + var ures response.UserResponse + if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", fmt.Sprint(pctx.UID+pctx.CID)), &ures); err != nil { + c.logger.Errorf("could not get user access info: %s", err.Error()) + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + rw.WriteHeader(http.StatusRequestTimeout) + return + } + + microErr := response.MicroError{} + if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { + rw.WriteHeader(http.StatusUnauthorized) + c.logger.Errorf("could not get me info: %s", err.Error()) + return + } + + rw.WriteHeader(microErr.Code) + c.logger.Errorf("could not get me info: %s", microErr.Detail) + return + } + file, err := assets.Files.Open(fmt.Sprintf("assets/%s/new.%s", lang, fileType)) if err != nil { lang = "en-US" @@ -65,11 +93,39 @@ func (c fileController) BuildGetFile() http.HandlerFunc { c.logger.Errorf("could not get a new file: %s", err.Error()) return } - io.Copy(rw, file) + res, ferr := c.apiClient.CreateFile(ctx, dealID, filename, file, model.Token{ + AccessToken: ures.AccessToken, + RefreshToken: ures.AccessToken, + TokenType: ures.TokenType, + Scope: ures.Scope, + ApiDomain: ures.ApiDomain, + }) + + if ferr != nil { + rw.WriteHeader(http.StatusBadRequest) + c.logger.Errorf("could not upload a pipedrive file: %s", ferr.Error()) + return + } + + rw.Write(res.ToJSON()) + return + } + + res, ferr := c.apiClient.CreateFile(ctx, dealID, filename, file, model.Token{ + AccessToken: ures.AccessToken, + RefreshToken: ures.AccessToken, + TokenType: ures.TokenType, + Scope: ures.Scope, + ApiDomain: ures.ApiDomain, + }) + + if ferr != nil { + rw.WriteHeader(http.StatusBadRequest) + c.logger.Errorf("could not upload a pipedrive file: %s", ferr.Error()) return } - io.Copy(rw, file) + rw.Write(res.ToJSON()) } } diff --git a/backend/services/shared/client/api.go b/backend/services/shared/client/api.go index 8bec3d5..b289a51 100644 --- a/backend/services/shared/client/api.go +++ b/backend/services/shared/client/api.go @@ -29,6 +29,7 @@ import ( "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client/model" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" "github.com/go-resty/resty/v2" "github.com/mitchellh/mapstructure" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" @@ -175,3 +176,23 @@ func (p *PipedriveApiClient) UploadFile(ctx context.Context, url, deal, fileID, return nil } + +func (p *PipedriveApiClient) CreateFile(ctx context.Context, deal, filename string, file io.ReadCloser, token model.Token) (response.AddFileResponse, error) { + var body response.AddFileResponse + + _, err := p.client.R(). + SetResult(&body). + SetContext(ctx). + SetAuthToken(token.AccessToken). + SetFileReader("file", filename, file). + SetFormData(map[string]string{ + "deal_id": deal, + }). + Post(fmt.Sprintf("%s/api/v1/files", token.ApiDomain)) + + if err != nil { + return body, err + } + + return body, nil +} diff --git a/backend/services/shared/response/pipedrive.go b/backend/services/shared/response/pipedrive.go new file mode 100644 index 0000000..d340d28 --- /dev/null +++ b/backend/services/shared/response/pipedrive.go @@ -0,0 +1,18 @@ +package response + +import "encoding/json" + +type AddFileResponse struct { + Success bool `json:"success"` + Data struct { + ID int `json:"id"` + Filename string `json:"file_name"` + DealID int `json:"deal_id"` + UpdateTime string `json:"update_time"` + } `json:"data"` +} + +func (r AddFileResponse) ToJSON() []byte { + buf, _ := json.Marshal(r) + return buf +} diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx index a75063a..90ba631 100644 --- a/frontend/src/pages/Creation/Creation.tsx +++ b/frontend/src/pages/Creation/Creation.tsx @@ -17,18 +17,18 @@ */ import React, { useEffect, useState } from "react"; +import axios from "axios"; import md5 from "md5"; import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; import { useTranslation } from "react-i18next"; +import i18next from "i18next"; import { OnlyofficeButton } from "@components/button"; import { OnlyofficeInput } from "@components/input"; import { OnlyofficeTile } from "@components/tile"; import { OnlyofficeTitle } from "@components/title"; -import { uploadFile } from "@services/file"; - -import { getFileParts, getFileIcon, getMimeType } from "@utils/file"; +import { getFileIcon } from "@utils/file"; import { getCurrentURL } from "@utils/url"; import Redirect from "@assets/redirect.svg"; @@ -124,24 +124,31 @@ export const Creation: React.FC = () => { onClick={async () => { const token = await sdk?.execute(Command.GET_SIGNED_TOKEN); if (!token) return; - const { url, parameters } = getCurrentURL(); - const filename = `${file}.${fileType}`; - const binary = new File([], filename, { - type: getMimeType(filename), - }); + const { parameters } = getCurrentURL(); try { - const res = await uploadFile( - `${url}api/v1/files`, - parameters.get("selectedIds") || "", - binary - ); - const [name, ext] = getFileParts(res.data.name); + const fres = await axios({ + method: "GET", + url: `${process.env.BACKEND_GATEWAY}/files/create`, + headers: { + "X-Pipedrive-App-Context": token.token, + }, + params: { + lang: i18next.language, + type: fileType, + deal: parameters.get("selectedIds") || "", + filename: `${file.substring(0, 190)}.${fileType}`, + }, + }); window.open( - `/editor?token=${token.token}&id=${res.data.id}&deal_id=${ - res.data.deal_id + `/editor?token=${token.token}&id=${ + fres.data.data.id + }&deal_id=${ + fres.data.data.deal_id }&name=${`${encodeURIComponent( - name.substring(0, 190) - )}.${ext}`}&key=${md5(res.data.id + res.data.update_time)}` + file.substring(0, 190) + )}.${fileType}`}&key=${md5( + fres.data.data.id + fres.data.data.update_time + )}` ); } catch { await sdk?.execute(Command.SHOW_SNACKBAR, { From 17aecfe982139a1d903eacec806df25d66b849ac Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 29 Mar 2023 19:30:33 +0500 Subject: [PATCH 050/145] fix(actions): disable edit for unsupported and being deleted files --- frontend/src/pages/Main/Actions.tsx | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/frontend/src/pages/Main/Actions.tsx b/frontend/src/pages/Main/Actions.tsx index 259be73..40c7f05 100644 --- a/frontend/src/pages/Main/Actions.tsx +++ b/frontend/src/pages/Main/Actions.tsx @@ -39,6 +39,7 @@ export const OnlyofficeFileActions: React.FC = ({ file }) => { const { t } = useTranslation(); const { url, parameters } = getCurrentURL(); const [sdk, setSDK] = useState(); + const [disable, setDisable] = useState(false); const mutator = useDeleteFile(`${url}api/v1/files/${file.id}`); useEffect(() => { @@ -49,6 +50,7 @@ export const OnlyofficeFileActions: React.FC = ({ file }) => { }, []); const handleDelete = () => { + setDisable(true); mutator .mutateAsync() .then(async () => { @@ -61,6 +63,7 @@ export const OnlyofficeFileActions: React.FC = ({ file }) => { }); }) .catch(async () => { + setDisable(false); await sdk?.execute(Command.SHOW_SNACKBAR, { message: t( "snackbar.fileremoved.error", @@ -72,16 +75,18 @@ export const OnlyofficeFileActions: React.FC = ({ file }) => { }; const handleEditor = async () => { - const token = await sdk?.execute(Command.GET_SIGNED_TOKEN); - if (token) { - const [name, ext] = getFileParts(file.name); - window.open( - `/editor?token=${token.token}&deal_id=${ - parameters.get("selectedIds") || "1" - }&id=${file.id}&name=${`${encodeURIComponent( - name.substring(0, 190) - )}.${ext}`}&key=${md5(file.id + file.update_time)}` - ); + if (!disable && isFileSupported(file.name)) { + const token = await sdk?.execute(Command.GET_SIGNED_TOKEN); + if (token) { + const [name, ext] = getFileParts(file.name); + window.open( + `/editor?token=${token.token}&deal_id=${ + parameters.get("selectedIds") || "1" + }&id=${file.id}&name=${`${encodeURIComponent( + name.substring(0, 190) + )}.${ext}`}&key=${md5(file.id + file.update_time)}` + ); + } } }; @@ -91,7 +96,7 @@ export const OnlyofficeFileActions: React.FC = ({ file }) => { role="button" tabIndex={0} className={`${ - !isFileSupported(file.name) + !isFileSupported(file.name) || disable ? "hover:cursor-default opacity-50" : "hover:cursor-pointer" } mx-1`} From 503fe3343579ba2325d397beb0e029875c172ea7 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 29 Mar 2023 19:47:52 +0500 Subject: [PATCH 051/145] fix: sanitize filename --- backend/services/builder/web/handler/build.go | 10 ++++++---- frontend/src/pages/Creation/Creation.tsx | 5 ++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/backend/services/builder/web/handler/build.go b/backend/services/builder/web/handler/build.go index 4b7c1cd..f2f7c11 100644 --- a/backend/services/builder/web/handler/build.go +++ b/backend/services/builder/web/handler/build.go @@ -150,10 +150,12 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui downloadToken.ExpiresAt = time.Now().Add(4 * time.Minute).UnixMilli() tkn, _ := c.jwtManager.Sign(settings.DocSecret, downloadToken) + fileName := strings.ReplaceAll(req.Filename, "\\", ":") + fileName = strings.ReplaceAll(fileName, "/", ":") config = response.BuildConfigResponse{ Document: response.Document{ Key: req.DocKey, - Title: req.Filename, + Title: fileName, URL: fmt.Sprintf("%s/files/download?cid=%d&fid=%s&token=%s", c.gatewayURL, usr.CompanyID, req.FileID, tkn), }, EditorConfig: response.EditorConfig{ @@ -163,7 +165,7 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui }, CallbackURL: fmt.Sprintf( "%s/callback?cid=%d&did=%s&fid=%s&filename=%s", - c.gatewayURL, usr.CompanyID, req.Deal, req.FileID, url.QueryEscape(req.Filename), + c.gatewayURL, usr.CompanyID, req.Deal, req.FileID, url.QueryEscape(fileName), ), Customization: response.Customization{ Goback: response.Goback{ @@ -178,8 +180,8 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui ServerURL: settings.DocAddress, } - if strings.TrimSpace(req.Filename) != "" { - ext := strings.ReplaceAll(filepath.Ext(req.Filename), ".", "") + if strings.TrimSpace(fileName) != "" { + ext := strings.ReplaceAll(filepath.Ext(fileName), ".", "") fileType, err := constants.GetFileType(ext) if err != nil { return config, err diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx index 90ba631..dcd5e64 100644 --- a/frontend/src/pages/Creation/Creation.tsx +++ b/frontend/src/pages/Creation/Creation.tsx @@ -136,7 +136,10 @@ export const Creation: React.FC = () => { lang: i18next.language, type: fileType, deal: parameters.get("selectedIds") || "", - filename: `${file.substring(0, 190)}.${fileType}`, + filename: `${file + .replaceAll("/", ":") + .replaceAll("\\", ":") + .substring(0, 190)}.${fileType}`, }, }); window.open( From b123b9fb75a958b09cdfc0cad329bae87fbf3fc6 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 29 Mar 2023 20:30:01 +0500 Subject: [PATCH 052/145] fix(creation): disable actions during creation --- frontend/src/pages/Creation/Creation.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx index dcd5e64..3c7bbb0 100644 --- a/frontend/src/pages/Creation/Creation.tsx +++ b/frontend/src/pages/Creation/Creation.tsx @@ -35,13 +35,14 @@ import Redirect from "@assets/redirect.svg"; export const Creation: React.FC = () => { const { t } = useTranslation(); + const [creating, setCreating] = useState(false); const [sdk, setSDK] = useState(); const [file, setFile] = useState( t("document.new", "New Document") || "New Document" ); const [fileType, setFileType] = useState<"docx" | "pptx" | "xlsx">("docx"); const handleChangeFile = (newType: "docx" | "pptx" | "xlsx") => { - setFileType(newType); + if (!creating) setFileType(newType); }; useEffect(() => { @@ -72,6 +73,7 @@ export const Creation: React.FC = () => { } value={file} onChange={(e) => setFile(e.target.value)} + disabled={creating} />
@@ -113,15 +115,17 @@ export const Creation: React.FC = () => { onClick={async () => { await sdk?.execute(Command.CLOSE_MODAL); }} + disabled={creating} />
190} + disabled={file.length > 190 || creating} text={t("button.create", "Create document")} primary Icon={} onClick={async () => { + setCreating(true); const token = await sdk?.execute(Command.GET_SIGNED_TOKEN); if (!token) return; const { parameters } = getCurrentURL(); @@ -157,6 +161,8 @@ export const Creation: React.FC = () => { await sdk?.execute(Command.SHOW_SNACKBAR, { message: t("creation.error", "Could not create a new file"), }); + } finally { + setCreating(false); } }} /> From 9ece01b203eaa8676a0b5cde299b8a0a512afa06 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 30 Mar 2023 13:34:45 +0500 Subject: [PATCH 053/145] feat(auth): caching --- .../services/auth/web/core/adapter/mongo.go | 2 +- .../services/auth/web/core/service/user.go | 42 ++++++++++++++++--- .../auth/web/core/service/user_test.go | 4 +- .../services/auth/web/handler/select_test.go | 5 ++- backend/services/auth/web/server.go | 6 ++- 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/backend/services/auth/web/core/adapter/mongo.go b/backend/services/auth/web/core/adapter/mongo.go index b111dc9..e3a242d 100644 --- a/backend/services/auth/web/core/adapter/mongo.go +++ b/backend/services/auth/web/core/adapter/mongo.go @@ -53,7 +53,7 @@ type mongoUserAdapter struct { func NewMongoUserAdapter(url string) port.UserAccessServiceAdapter { if err := mgm.SetDefaultConfig( - &mgm.Config{CtxTimeout: 3 * time.Second}, "users", + &mgm.Config{CtxTimeout: 3 * time.Second}, "pipedrive-users", options.Client().ApplyURI(url), ); err != nil { log.Fatalf("mongo initialization error: %s", err.Error()) diff --git a/backend/services/auth/web/core/service/user.go b/backend/services/auth/web/core/service/user.go index 44c8eac..7d1670e 100644 --- a/backend/services/auth/web/core/service/user.go +++ b/backend/services/auth/web/core/service/user.go @@ -23,11 +23,14 @@ import ( "errors" "strings" "sync" + "time" plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/port" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" + "github.com/mitchellh/mapstructure" + "go-micro.dev/v4/cache" ) var _ErrOperationTimeout = errors.New("operation timeout") @@ -35,17 +38,20 @@ var _ErrOperationTimeout = errors.New("operation timeout") type userService struct { adapter port.UserAccessServiceAdapter encryptor crypto.Encryptor + cache cache.Cache logger plog.Logger } func NewUserService( adapter port.UserAccessServiceAdapter, encryptor crypto.Encryptor, + cache cache.Cache, logger plog.Logger, ) port.UserAccessService { return userService{ adapter: adapter, encryptor: encryptor, + cache: cache, logger: logger, } } @@ -125,9 +131,22 @@ func (s userService) GetUser(ctx context.Context, uid string) (domain.UserAccess atokenChan := make(chan string, 1) rtokenChan := make(chan string, 1) - user, err := s.adapter.SelectUserByID(ctx, id) - if err != nil { - return user, err + var user domain.UserAccess + var err error + if res, _, err := s.cache.Get(ctx, id); err == nil && res != nil { + s.logger.Debugf("found user %s in the cache", id) + if err := mapstructure.Decode(res, &user); err != nil { + s.logger.Errorf("could not decode from cache: %s", err.Error()) + } + } + + if user.AccessToken == "" { + user, err = s.adapter.SelectUserByID(ctx, id) + if err != nil { + return user, err + } + + s.cache.Put(ctx, id, user, time.Duration((user.ExpiresAt-time.Now().UnixMilli())*1e6/6)) } s.logger.Debugf("found a user: %v", user) @@ -212,8 +231,7 @@ func (s userService) UpdateUser(ctx context.Context, user domain.UserAccess) (do default: } - s.logger.Debugf("user %s is valid to perform an update action", user.ID) - if _, err := s.adapter.UpsertUser(ctx, domain.UserAccess{ + euser := domain.UserAccess{ ID: user.ID, AccessToken: <-atokenChan, RefreshToken: <-rtokenChan, @@ -221,7 +239,15 @@ func (s userService) UpdateUser(ctx context.Context, user domain.UserAccess) (do Scope: user.Scope, ExpiresAt: user.ExpiresAt, ApiDomain: user.ApiDomain, - }); err != nil { + } + + if err := s.cache.Put(ctx, euser.ID, euser, time.Duration((euser.ExpiresAt-time.Now().UnixMilli())*1e6/6)); err != nil { + s.logger.Warnf("could not populate cache with a user %s instance: %s", euser.ID, err.Error()) + s.cache.Delete(ctx, euser.ID) + } + + s.logger.Debugf("user %s is valid to perform an update action", user.ID) + if _, err := s.adapter.UpsertUser(ctx, euser); err != nil { return user, err } @@ -239,6 +265,10 @@ func (s userService) DeleteUser(ctx context.Context, uid string) error { } } + if err := s.cache.Delete(ctx, uid); err != nil { + return err + } + s.logger.Debugf("uid %s is valid to perform a delete action", id) return s.adapter.DeleteUserByID(ctx, uid) } diff --git a/backend/services/auth/web/core/service/user_test.go b/backend/services/auth/web/core/service/user_test.go index 1fdccab..31fe22c 100644 --- a/backend/services/auth/web/core/service/user_test.go +++ b/backend/services/auth/web/core/service/user_test.go @@ -23,6 +23,8 @@ import ( "testing" "time" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/cache" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" "github.com/stretchr/testify/assert" @@ -71,7 +73,7 @@ func (m mockAdapter) DeleteUserByID(ctx context.Context, uid string) error { } func TestUserService(t *testing.T) { - service := NewUserService(mockAdapter{}, mockEncryptor{}, log.NewEmptyLogger()) + service := NewUserService(mockAdapter{}, mockEncryptor{}, cache.NewCache(&config.CacheConfig{}), log.NewEmptyLogger()) t.Run("save user", func(t *testing.T) { assert.NoError(t, service.CreateUser(context.Background(), user)) diff --git a/backend/services/auth/web/handler/select_test.go b/backend/services/auth/web/handler/select_test.go index dae2336..efb60c1 100644 --- a/backend/services/auth/web/handler/select_test.go +++ b/backend/services/auth/web/handler/select_test.go @@ -23,6 +23,8 @@ import ( "testing" "time" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/cache" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/adapter" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" @@ -43,7 +45,8 @@ func (e mockEncryptor) Decrypt(ciphertext string) (string, error) { func TestSelectCaching(t *testing.T) { adapter := adapter.NewMemoryUserAdapter() - service := service.NewUserService(adapter, mockEncryptor{}, log.NewEmptyLogger()) + cache := cache.NewCache(&config.CacheConfig{}) + service := service.NewUserService(adapter, mockEncryptor{}, cache, log.NewEmptyLogger()) pclient := pclient.NewPipedriveAuthClient("clientID", "clientSecret") sel := NewUserSelectHandler(service, nil, pclient, log.NewEmptyLogger()) diff --git a/backend/services/auth/web/server.go b/backend/services/auth/web/server.go index 35b4218..c310f71 100644 --- a/backend/services/auth/web/server.go +++ b/backend/services/auth/web/server.go @@ -39,13 +39,15 @@ type AuthRPCServer struct { logger log.Logger } -func NewAuthRPCServer(persistenceConfig *config.PersistenceConfig, oauthConfig *config.OAuthCredentialsConfig, logger log.Logger) rpc.RPCEngine { +func NewAuthRPCServer( + persistenceConfig *config.PersistenceConfig, oauthConfig *config.OAuthCredentialsConfig, + cache cache.Cache, logger log.Logger) rpc.RPCEngine { adptr := adapter.NewMemoryUserAdapter() if persistenceConfig.Persistence.URL != "" { adptr = adapter.NewMongoUserAdapter(persistenceConfig.Persistence.URL) } - service := service.NewUserService(adptr, crypto.NewAesEncryptor([]byte(oauthConfig.Credentials.ClientSecret)), logger) + service := service.NewUserService(adptr, crypto.NewAesEncryptor([]byte(oauthConfig.Credentials.ClientSecret)), cache, logger) return AuthRPCServer{ service: service, pipedriveAuth: pclient.NewPipedriveAuthClient( From 0d59fe8ceede0da4715fbd7c2cd5cf27385bf237 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 30 Mar 2023 14:02:30 +0500 Subject: [PATCH 054/145] fix(caching): pass db as a config field --- backend/pkg/cache/cache.go | 2 +- backend/pkg/config/cache.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/pkg/cache/cache.go b/backend/pkg/cache/cache.go index 6c2cd38..ba07c43 100644 --- a/backend/pkg/cache/cache.go +++ b/backend/pkg/cache/cache.go @@ -60,7 +60,7 @@ func NewCache(config *config.CacheConfig) cache.Cache { } case 2: return &CustomCache{ - store: newRedis(config.Cache.Address, config.Cache.Password, 0), + store: newRedis(config.Cache.Address, config.Cache.Password, config.Cache.DB), name: "Redis", } default: diff --git a/backend/pkg/config/cache.go b/backend/pkg/config/cache.go index 854907b..6246368 100644 --- a/backend/pkg/config/cache.go +++ b/backend/pkg/config/cache.go @@ -33,6 +33,7 @@ type CacheConfig struct { Size int `yaml:"size" env:"CACHE_SIZE,overwrite"` Address string `yaml:"address" env:"CACHE_ADDRESS,overwrite"` Password string `yaml:"password" env:"CACHE_PASSWORD,overwrite"` + DB int `yaml:"db" env:"CACHE_DB,overwrite"` } `yaml:"cache"` } From 7920de6861b6559ca81239f5d3b023eb8c3a1bd1 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 30 Mar 2023 14:15:23 +0500 Subject: [PATCH 055/145] fix(builder): change collection name --- backend/services/builder/web/handler/build.go | 12 ++++++------ backend/services/shared/sanitizer.go | 9 +++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 backend/services/shared/sanitizer.go diff --git a/backend/services/builder/web/handler/build.go b/backend/services/builder/web/handler/build.go index f2f7c11..3c5813a 100644 --- a/backend/services/builder/web/handler/build.go +++ b/backend/services/builder/web/handler/build.go @@ -29,6 +29,7 @@ import ( "time" plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client/model" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/constants" @@ -150,12 +151,11 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui downloadToken.ExpiresAt = time.Now().Add(4 * time.Minute).UnixMilli() tkn, _ := c.jwtManager.Sign(settings.DocSecret, downloadToken) - fileName := strings.ReplaceAll(req.Filename, "\\", ":") - fileName = strings.ReplaceAll(fileName, "/", ":") + filename := shared.EscapeFilename(req.Filename) config = response.BuildConfigResponse{ Document: response.Document{ Key: req.DocKey, - Title: fileName, + Title: filename, URL: fmt.Sprintf("%s/files/download?cid=%d&fid=%s&token=%s", c.gatewayURL, usr.CompanyID, req.FileID, tkn), }, EditorConfig: response.EditorConfig{ @@ -165,7 +165,7 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui }, CallbackURL: fmt.Sprintf( "%s/callback?cid=%d&did=%s&fid=%s&filename=%s", - c.gatewayURL, usr.CompanyID, req.Deal, req.FileID, url.QueryEscape(fileName), + c.gatewayURL, usr.CompanyID, req.Deal, req.FileID, url.QueryEscape(filename), ), Customization: response.Customization{ Goback: response.Goback{ @@ -180,8 +180,8 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui ServerURL: settings.DocAddress, } - if strings.TrimSpace(fileName) != "" { - ext := strings.ReplaceAll(filepath.Ext(fileName), ".", "") + if strings.TrimSpace(filename) != "" { + ext := strings.ReplaceAll(filepath.Ext(filename), ".", "") fileType, err := constants.GetFileType(ext) if err != nil { return config, err diff --git a/backend/services/shared/sanitizer.go b/backend/services/shared/sanitizer.go new file mode 100644 index 0000000..ad5267f --- /dev/null +++ b/backend/services/shared/sanitizer.go @@ -0,0 +1,9 @@ +package shared + +import "strings" + +func EscapeFilename(filename string) string { + f := strings.ReplaceAll(filename, "\\", ":") + f = strings.ReplaceAll(f, "/", ":") + return f +} From 211e527db4064b5956115c9734a748033241fcf5 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 30 Mar 2023 14:24:46 +0500 Subject: [PATCH 056/145] chore(pkg): rename db field --- backend/pkg/cache/cache.go | 2 +- backend/pkg/config/cache.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/pkg/cache/cache.go b/backend/pkg/cache/cache.go index ba07c43..294b9eb 100644 --- a/backend/pkg/cache/cache.go +++ b/backend/pkg/cache/cache.go @@ -60,7 +60,7 @@ func NewCache(config *config.CacheConfig) cache.Cache { } case 2: return &CustomCache{ - store: newRedis(config.Cache.Address, config.Cache.Password, config.Cache.DB), + store: newRedis(config.Cache.Address, config.Cache.Password, config.Cache.Database), name: "Redis", } default: diff --git a/backend/pkg/config/cache.go b/backend/pkg/config/cache.go index 6246368..508d51c 100644 --- a/backend/pkg/config/cache.go +++ b/backend/pkg/config/cache.go @@ -33,7 +33,7 @@ type CacheConfig struct { Size int `yaml:"size" env:"CACHE_SIZE,overwrite"` Address string `yaml:"address" env:"CACHE_ADDRESS,overwrite"` Password string `yaml:"password" env:"CACHE_PASSWORD,overwrite"` - DB int `yaml:"db" env:"CACHE_DB,overwrite"` + Database int `yaml:"database" env:"CACHE_DATABASE,overwrite"` } `yaml:"cache"` } From 78f03f753a3b196a7ef325afc22da5c85ae05a9d Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 30 Mar 2023 14:34:17 +0500 Subject: [PATCH 057/145] feat(settings): caching --- .../settings/web/core/service/settings.go | 32 +++++++++++++++++-- .../settings/web/handler/select_test.go | 5 ++- backend/services/settings/web/server.go | 6 ++-- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/backend/services/settings/web/core/service/settings.go b/backend/services/settings/web/core/service/settings.go index 5054845..477dc2a 100644 --- a/backend/services/settings/web/core/service/settings.go +++ b/backend/services/settings/web/core/service/settings.go @@ -22,11 +22,14 @@ import ( "context" "errors" "strings" + "time" plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/port" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" + "github.com/mitchellh/mapstructure" + "go-micro.dev/v4/cache" ) var _ErrOperationTimeout = errors.New("operation timeout") @@ -34,17 +37,20 @@ var _ErrOperationTimeout = errors.New("operation timeout") type settingsService struct { adapter port.DocSettingsServiceAdapter encryptor crypto.Encryptor + cache cache.Cache logger plog.Logger } func NewSettingsService( adapter port.DocSettingsServiceAdapter, encryptor crypto.Encryptor, + cache cache.Cache, logger plog.Logger, ) port.DocSettingsService { return settingsService{ adapter: adapter, encryptor: encryptor, + cache: cache, logger: logger, } } @@ -84,9 +90,21 @@ func (s settingsService) GetSettings(ctx context.Context, cid string) (domain.Do } } - settings, err := s.adapter.SelectSettings(ctx, id) - if err != nil { - return settings, err + var settings domain.DocSettings + var err error + if res, _, err := s.cache.Get(ctx, id); err == nil && res != nil { + s.logger.Debugf("found settings %s in the cache", id) + if err := mapstructure.Decode(res, &settings); err != nil { + s.logger.Errorf("could not decode from cache: %s", err.Error()) + } + } + + if settings.CompanyID == "" { + settings, err = s.adapter.SelectSettings(ctx, id) + if err != nil { + return settings, err + } + s.cache.Put(ctx, id, settings, 1*time.Minute) } s.logger.Debugf("found settings: %v", settings) @@ -124,6 +142,10 @@ func (s settingsService) UpdateSettings(ctx context.Context, settings domain.Doc return settings, err } + if err := s.cache.Delete(ctx, settings.CompanyID); err != nil { + return settings, err + } + s.logger.Debugf("successfully persisted %s settings", settings.CompanyID) return settings, nil } @@ -139,6 +161,10 @@ func (s settingsService) DeleteSettings(ctx context.Context, cid string) error { } } + if err := s.cache.Delete(ctx, cid); err != nil { + return err + } + s.logger.Debugf("uid %s is valid to perform a delete action", id) return s.adapter.DeleteSettings(ctx, id) } diff --git a/backend/services/settings/web/handler/select_test.go b/backend/services/settings/web/handler/select_test.go index 9785545..13de2d0 100644 --- a/backend/services/settings/web/handler/select_test.go +++ b/backend/services/settings/web/handler/select_test.go @@ -22,6 +22,8 @@ import ( "context" "testing" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/cache" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/adapter" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" @@ -42,7 +44,7 @@ func (e mockEncryptor) Decrypt(ciphertext string) (string, error) { func TestSelectCaching(t *testing.T) { adapter := adapter.NewMemoryDocserverAdapter() - service := service.NewSettingsService(adapter, mockEncryptor{}, log.NewEmptyLogger()) + service := service.NewSettingsService(adapter, mockEncryptor{}, cache.NewCache(&config.CacheConfig{}), log.NewEmptyLogger()) sel := NewSettingsSelectHandler(service, nil, log.NewEmptyLogger()) @@ -50,6 +52,7 @@ func TestSelectCaching(t *testing.T) { CompanyID: "mock", DocAddress: "mock", DocSecret: "mock", + DocHeader: "mocj", }) t.Run("get settings", func(t *testing.T) { diff --git a/backend/services/settings/web/server.go b/backend/services/settings/web/server.go index 8897b25..fc60446 100644 --- a/backend/services/settings/web/server.go +++ b/backend/services/settings/web/server.go @@ -40,14 +40,16 @@ type DocserverRPCServer struct { func NewDocserverRPCServer( persistenceConfig *config.PersistenceConfig, credentialsConfig *config.OAuthCredentialsConfig, - logger log.Logger, + cache cache.Cache, logger log.Logger, ) rpc.RPCEngine { adptr := adapter.NewMemoryDocserverAdapter() if persistenceConfig.Persistence.URL != "" { adptr = adapter.NewMongoDocserverAdapter(persistenceConfig.Persistence.URL) } - service := service.NewSettingsService(adptr, crypto.NewAesEncryptor([]byte(credentialsConfig.Credentials.ClientSecret)), logger) + service := service.NewSettingsService( + adptr, crypto.NewAesEncryptor([]byte(credentialsConfig.Credentials.ClientSecret)), cache, logger, + ) return DocserverRPCServer{ service: service, logger: logger, From ce5b413d5d457afc2fee7aeaad65d271e942cb69 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 30 Mar 2023 15:26:44 +0500 Subject: [PATCH 058/145] fix(auth): caching user validation --- backend/services/auth/web/core/service/user.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/services/auth/web/core/service/user.go b/backend/services/auth/web/core/service/user.go index 7d1670e..39cdc6f 100644 --- a/backend/services/auth/web/core/service/user.go +++ b/backend/services/auth/web/core/service/user.go @@ -140,7 +140,7 @@ func (s userService) GetUser(ctx context.Context, uid string) (domain.UserAccess } } - if user.AccessToken == "" { + if user.Validate() == nil { user, err = s.adapter.SelectUserByID(ctx, id) if err != nil { return user, err From 3ae8bb2c718106163405f8791fc60ef3b7c7538c Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 30 Mar 2023 15:59:06 +0500 Subject: [PATCH 059/145] fix(auth): user validation checks --- backend/services/auth/web/core/service/user.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/services/auth/web/core/service/user.go b/backend/services/auth/web/core/service/user.go index 39cdc6f..96cb17c 100644 --- a/backend/services/auth/web/core/service/user.go +++ b/backend/services/auth/web/core/service/user.go @@ -140,7 +140,7 @@ func (s userService) GetUser(ctx context.Context, uid string) (domain.UserAccess } } - if user.Validate() == nil { + if user.Validate() != nil { user, err = s.adapter.SelectUserByID(ctx, id) if err != nil { return user, err From 3e510eb4fa106800d868f1f44c35cb1f0855b587 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 30 Mar 2023 16:07:12 +0500 Subject: [PATCH 060/145] refactor(gateway): separate methods --- .../services/gateway/web/controller/api.go | 65 +++++++++---------- .../services/gateway/web/controller/file.go | 56 +++++++--------- 2 files changed, 55 insertions(+), 66 deletions(-) diff --git a/backend/services/gateway/web/controller/api.go b/backend/services/gateway/web/controller/api.go index 91629b3..a2ab022 100644 --- a/backend/services/gateway/web/controller/api.go +++ b/backend/services/gateway/web/controller/api.go @@ -63,6 +63,25 @@ func NewApiController( } } +func (c *apiController) getUser(ctx context.Context, id string) (response.UserResponse, int, error) { + var ures response.UserResponse + if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", id), &ures); err != nil { + c.logger.Errorf("could not get user access info: %s", err.Error()) + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + return ures, http.StatusRequestTimeout, err + } + + microErr := response.MicroError{} + if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { + return ures, http.StatusUnauthorized, err + } + + return ures, microErr.Code, err + } + + return ures, http.StatusOK, nil +} + func (c *apiController) BuildGetMe() http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { pctx, ok := r.Context().Value(request.PipedriveTokenContext{}).(request.PipedriveTokenContext) @@ -75,23 +94,9 @@ func (c *apiController) BuildGetMe() http.HandlerFunc { ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) defer cancel() - var ures response.UserResponse - if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", fmt.Sprint(pctx.UID+pctx.CID)), &ures); err != nil { - c.logger.Errorf("could not get user access info: %s", err.Error()) - if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { - rw.WriteHeader(http.StatusRequestTimeout) - return - } - - microErr := response.MicroError{} - if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { - rw.WriteHeader(http.StatusUnauthorized) - c.logger.Errorf("could not get me info: %s", err.Error()) - return - } - - rw.WriteHeader(microErr.Code) - c.logger.Errorf("could not get me info: %s", microErr.Detail) + ures, status, _ := c.getUser(ctx, fmt.Sprint(pctx.UID+pctx.CID)) + if status != http.StatusOK { + rw.WriteHeader(status) return } @@ -152,9 +157,8 @@ func (c *apiController) BuildPostSettings() http.HandlerFunc { go func() { defer wg.Done() - var ures response.UserResponse - if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", fmt.Sprint(pctx.UID+pctx.CID)), &ures); err != nil { - c.logger.Errorf("could not get user access info: %s", err.Error()) + ures, _, err := c.getUser(ctx, fmt.Sprint(pctx.UID+pctx.CID)) + if err != nil { errChan <- err return } @@ -189,6 +193,9 @@ func (c *apiController) BuildPostSettings() http.HandlerFunc { case <-errChan: rw.WriteHeader(http.StatusForbidden) return + case <-ctx.Done(): + rw.WriteHeader(http.StatusRequestTimeout) + return default: } @@ -232,21 +239,9 @@ func (c apiController) BuildGetSettings() http.HandlerFunc { ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) defer cancel() - var ures response.UserResponse - if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", fmt.Sprint(pctx.UID+pctx.CID)), &ures); err != nil { - c.logger.Errorf("could not get user access info: %s", err.Error()) - if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { - rw.WriteHeader(http.StatusRequestTimeout) - return - } - - microErr := response.MicroError{} - if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { - rw.WriteHeader(http.StatusUnauthorized) - return - } - - rw.WriteHeader(microErr.Code) + ures, status, _ := c.getUser(ctx, fmt.Sprint(pctx.UID+pctx.CID)) + if status != http.StatusOK { + rw.WriteHeader(status) return } diff --git a/backend/services/gateway/web/controller/file.go b/backend/services/gateway/web/controller/file.go index 4d1eac0..6947627 100644 --- a/backend/services/gateway/web/controller/file.go +++ b/backend/services/gateway/web/controller/file.go @@ -44,6 +44,25 @@ func NewFileController( } } +func (c *fileController) getUser(ctx context.Context, id string) (response.UserResponse, int) { + var ures response.UserResponse + if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", id), &ures); err != nil { + c.logger.Errorf("could not get user access info: %s", err.Error()) + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + return ures, http.StatusRequestTimeout + } + + microErr := response.MicroError{} + if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { + return ures, http.StatusUnauthorized + } + + return ures, microErr.Code + } + + return ures, http.StatusOK +} + func (c fileController) BuildGetFile() http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { query := r.URL.Query() @@ -64,23 +83,10 @@ func (c fileController) BuildGetFile() http.HandlerFunc { ctx, cancel := context.WithTimeout(r.Context(), 4*time.Second) defer cancel() - var ures response.UserResponse - if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", fmt.Sprint(pctx.UID+pctx.CID)), &ures); err != nil { - c.logger.Errorf("could not get user access info: %s", err.Error()) - if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { - rw.WriteHeader(http.StatusRequestTimeout) - return - } - microErr := response.MicroError{} - if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { - rw.WriteHeader(http.StatusUnauthorized) - c.logger.Errorf("could not get me info: %s", err.Error()) - return - } - - rw.WriteHeader(microErr.Code) - c.logger.Errorf("could not get me info: %s", microErr.Detail) + ures, status := c.getUser(ctx, fmt.Sprint(pctx.UID+pctx.CID)) + if status != http.StatusOK { + rw.WriteHeader(status) return } @@ -192,21 +198,9 @@ func (c fileController) BuildDownloadFile() http.HandlerFunc { ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) defer cancel() - var ures response.UserResponse - if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", fmt.Sprint(pctx.UID+pctx.CID)), &ures); err != nil { - c.logger.Errorf("could not get user access info: %s", err.Error()) - if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { - rw.WriteHeader(http.StatusRequestTimeout) - return - } - - microErr := response.MicroError{} - if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { - rw.WriteHeader(http.StatusUnauthorized) - return - } - - rw.WriteHeader(microErr.Code) + ures, status := c.getUser(ctx, fmt.Sprint(pctx.UID+pctx.CID)) + if status != http.StatusOK { + rw.WriteHeader(status) return } From 4da827f1a2d067b45b7802d2dd2d4808e236e32e Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 30 Mar 2023 16:10:23 +0500 Subject: [PATCH 061/145] fix(settings): settings db name --- backend/services/settings/web/core/adapter/mongo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/services/settings/web/core/adapter/mongo.go b/backend/services/settings/web/core/adapter/mongo.go index 0ac31dd..5539d96 100644 --- a/backend/services/settings/web/core/adapter/mongo.go +++ b/backend/services/settings/web/core/adapter/mongo.go @@ -49,7 +49,7 @@ type mongoUserAdapter struct { func NewMongoDocserverAdapter(url string) port.DocSettingsServiceAdapter { if err := mgm.SetDefaultConfig( - &mgm.Config{CtxTimeout: 3 * time.Second}, "settings", + &mgm.Config{CtxTimeout: 3 * time.Second}, "pipedrive-settings", options.Client().ApplyURI(url), ); err != nil { log.Fatalf("mongo initialization error: %s", err.Error()) From 294ede4d642e4770388c3068c1b4322bc79e3dad Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 30 Mar 2023 16:14:15 +0500 Subject: [PATCH 062/145] chore: cleanup --- backend/services/shared/constants/session.go | 25 ---------------- backend/services/shared/request/session.go | 31 -------------------- backend/services/shared/sanitizer.go | 18 ++++++++++++ 3 files changed, 18 insertions(+), 56 deletions(-) delete mode 100644 backend/services/shared/constants/session.go delete mode 100644 backend/services/shared/request/session.go diff --git a/backend/services/shared/constants/session.go b/backend/services/shared/constants/session.go deleted file mode 100644 index e455545..0000000 --- a/backend/services/shared/constants/session.go +++ /dev/null @@ -1,25 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package constants - -const ( - SESSION_KEY = "ZMOFFICE" - SESSION_KEY_VERIFIER = "verifier" - SESSION_KEY_STATE = "state" -) diff --git a/backend/services/shared/request/session.go b/backend/services/shared/request/session.go deleted file mode 100644 index 0a73de3..0000000 --- a/backend/services/shared/request/session.go +++ /dev/null @@ -1,31 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package request - -import "encoding/json" - -type OwnerRemoveSessionRequest struct { - Uid string `json:"uid" mapstructure:"uid"` - Mid string `json:"mid" mapstructure:"mid"` -} - -func (r OwnerRemoveSessionRequest) ToJSON() []byte { - buf, _ := json.Marshal(r) - return buf -} diff --git a/backend/services/shared/sanitizer.go b/backend/services/shared/sanitizer.go index ad5267f..0e886a8 100644 --- a/backend/services/shared/sanitizer.go +++ b/backend/services/shared/sanitizer.go @@ -1,3 +1,21 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package shared import "strings" From 33b663a310250c0611659ff7ac96d18031ac4135 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 30 Mar 2023 16:14:33 +0500 Subject: [PATCH 063/145] fix(services): timeouts --- frontend/src/services/me.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/services/me.ts b/frontend/src/services/me.ts index bd10124..a9b3383 100644 --- a/frontend/src/services/me.ts +++ b/frontend/src/services/me.ts @@ -39,7 +39,7 @@ export const getMe = async (sdk: AppExtensionsSDK) => { "Content-Type": "application/json", "X-Pipedrive-App-Context": pctx.token, }, - timeout: 5000, + timeout: 3000, }); return { response: res.data }; @@ -53,7 +53,7 @@ export const getPipedriveMe = async (url: string) => { "Content-Type": "application/json", Authorization: `Bearer ${AuthToken.access_token}`, }, - timeout: 5000, + timeout: 3000, }); return res.data; }; From 93887e881db1786b9c26a2482ca23bd9f9182ddc Mon Sep 17 00:00:00 2001 From: Kseniya Fedoruk Date: Thu, 30 Mar 2023 14:54:26 +0300 Subject: [PATCH 064/145] Update README.md --- README.md | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 100 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 019dd9e..038ddc9 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,116 @@ # ONLYOFFICE App for Pipedrive -This app allows users to edit and collaborate on office documents right within Pipedrive deals. +This app allows users to edit and collaborate on office documents right within [Pipedrive](https://www.pipedrive.com/) deals using ONLYOFFICE Docs. -## App installation +## Installing ONLYOFFICE Docs -You can add the ONLYOFFICE app from the Pipedrive App Marketplace. +To be able to work with office files within Pipedrive, you will need an instance of ONLYOFFICE Docs. You can install the self-hosted version of the editors or opt for ONLYOFFICE Docs Cloud which doesn't require downloading and installation. + +**Self-hosted editors** + +You can install free Community version of ONLYOFFICE Docs or scalable Enterprise Edition. + +To install free Community version, use [Docker](https://github.com/onlyoffice/Docker-DocumentServer) (recommended) or follow [these instructions](https://helpcenter.onlyoffice.com/installation/docs-community-install-ubuntu.aspx) for Debian, Ubuntu, or derivatives. + +To install Enterprise Edition, follow the instructions [here](https://helpcenter.onlyoffice.com/installation/docs-enterprise-index.aspx). + +Community Edition vs Enterprise Edition comparison can be found [here](#onlyoffice-docs-editions). + +**ONLYOFFICE Docs Cloud** + +To get ONLYOFFICE Docs Cloud, get started [here](https://www.onlyoffice.com/docs-registration.aspx). + +## App installation and configuration + +You can add the ONLYOFFICE app from the [Pipedrive App Marketplace](https://www.pipedrive.com/en/marketplace). To do it, you need to sign into your account and allow the requested app permissions. +Once done, go to the ONLYOFFICE app settings page and enter the name of the server with ONLYOFFICE Docs installed in the *Document Server Address* field. + +Starting from version 7.2 of ONLYOFFICE Docs, JWT is enabled by default and the secret key is generated automatically to restrict the access to the editors and for security reasons and data integrity. You can specify your own *Document Server Secret* on the settings page. In the ONLYOFFICE Docs [config file](https://api.onlyoffice.com/editors/signature/), specify the same secret key to enable the validation. + ## App usage The app allows working with office documents directly within the Pipedrive frontend. -**Creating and editing files** - You can prepare and keep necessary materials such as text documents, spreadsheets, and presentations directly within your Pipedrive session. Just click the *Create with ONLYOFFICE* button on the main app screen. -To edit the created files, reach to the *ONLYOFFICE Documents* list on the main screen of the ONLYOFFICE app. +To edit the created files, reach to the *ONLYOFFICE Documents* list on the main screen of the ONLYOFFICE app. You can also collaborate on documents in real time together with your colleagues. + +## ONLYOFFICE Docs editions + +Self-hosted **ONLYOFFICE Docs** is packaged as Document Server: + +* Community Edition (`onlyoffice-documentserver` package) +* Enterprise Edition (`onlyoffice-documentserver-ee` package) + +The table below will help you make the right choice. + +| Pricing and licensing | Community Edition | Enterprise Edition | +| ------------- | ------------- | ------------- | +| | [Get it now](https://www.onlyoffice.com/download-docs.aspx#docs-community) | [Start Free Trial](https://www.onlyoffice.com/download-docs.aspx#docs-enterprise) | +| Cost | FREE | [Go to the pricing page](https://www.onlyoffice.com/docs-enterprise-prices.aspx) | +| Simultaneous connections | up to 20 maximum | As in chosen pricing plan | +| Number of users | up to 20 recommended | As in chosen pricing plan | +| License | GNU AGPL v.3 | Proprietary | +| **Support** | **Community Edition** | **Enterprise Edition** | +| Documentation | [Help Center](https://helpcenter.onlyoffice.com/installation/docs-community-index.aspx) | [Help Center](https://helpcenter.onlyoffice.com/installation/docs-enterprise-index.aspx) | +| Standard support | [GitHub](https://github.com/ONLYOFFICE/DocumentServer/issues) or paid | One year support included | +| Premium support | [Contact us](mailto:sales@onlyoffice.com) | [Contact us](mailto:sales@onlyoffice.com) | +| **Services** | **Community Edition** | **Enterprise Edition** | +| Conversion Service | + | + | +| Document Builder Service | + | + | +| **Interface** | **Community Edition** | **Enterprise Edition** | +| Tabbed interface | + | + | +| Dark theme | + | + | +| 125%, 150%, 175%, 200% scaling | + | + | +| White Label | - | - | +| Integrated test example (node.js) | + | + | +| Mobile web editors | - | +* | +| **Plugins & Macros** | **Community Edition** | **Enterprise Edition** | +| Plugins | + | + | +| Macros | + | + | +| **Collaborative capabilities** | **Community Edition** | **Enterprise Edition** | +| Two co-editing modes | + | + | +| Comments | + | + | +| Built-in chat | + | + | +| Review and tracking changes | + | + | +| Display modes of tracking changes | + | + | +| Version history | + | + | +| **Document Editor features** | **Community Edition** | **Enterprise Edition** | +| Font and paragraph formatting | + | + | +| Object insertion | + | + | +| Adding Content control | + | + | +| Editing Content control | + | + | +| Layout tools | + | + | +| Table of contents | + | + | +| Navigation panel | + | + | +| Mail Merge | + | + | +| Comparing Documents | + | + | +| **Spreadsheet Editor features** | **Community Edition** | **Enterprise Edition** | +| Font and paragraph formatting | + | + | +| Object insertion | + | + | +| Functions, formulas, equations | + | + | +| Table templates | + | + | +| Pivot tables | + | + | +| Data validation | + | + | +| Conditional formatting | + | + | +| Sparklines | + | + | +| Sheet Views | + | + | +| **Presentation Editor features** | **Community Edition** | **Enterprise Edition** | +| Font and paragraph formatting | + | + | +| Object insertion | + | + | +| Transitions | + | + | +| Presenter mode | + | + | +| Notes | + | + | +| **Form creator features** | **Community Edition** | **Enterprise Edition** | +| Adding form fields | + | + | +| Form preview | + | + | +| Saving as PDF | + | + | +| | [Get it now](https://www.onlyoffice.com/download-docs.aspx#docs-community) | [Start Free Trial](https://www.onlyoffice.com/download-docs.aspx#docs-enterprise) | + +\* If supported by DMS. ## Project info From 31508dcf4b6333c348b421408720c0fbbf8f2ca3 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 30 Mar 2023 17:14:10 +0500 Subject: [PATCH 065/145] fix(caching): pass username to redis adapter --- backend/pkg/cache/cache.go | 2 +- backend/pkg/cache/redis.go | 3 ++- backend/pkg/config/cache.go | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/pkg/cache/cache.go b/backend/pkg/cache/cache.go index 294b9eb..41f815d 100644 --- a/backend/pkg/cache/cache.go +++ b/backend/pkg/cache/cache.go @@ -60,7 +60,7 @@ func NewCache(config *config.CacheConfig) cache.Cache { } case 2: return &CustomCache{ - store: newRedis(config.Cache.Address, config.Cache.Password, config.Cache.Database), + store: newRedis(config.Cache.Address, config.Cache.Username, config.Cache.Password, config.Cache.Database), name: "Redis", } default: diff --git a/backend/pkg/cache/redis.go b/backend/pkg/cache/redis.go index 66700b6..1b74aae 100644 --- a/backend/pkg/cache/redis.go +++ b/backend/pkg/cache/redis.go @@ -25,8 +25,9 @@ import ( "github.com/go-redis/redis/v8" ) -func newRedis(address, password string, db int) *marshaler.Marshaler { +func newRedis(address, username, password string, db int) *marshaler.Marshaler { redisClient := redis.NewClient(&redis.Options{ + Username: username, Addr: address, Password: password, DB: db, diff --git a/backend/pkg/config/cache.go b/backend/pkg/config/cache.go index 508d51c..6862e28 100644 --- a/backend/pkg/config/cache.go +++ b/backend/pkg/config/cache.go @@ -32,6 +32,7 @@ type CacheConfig struct { Type int `yaml:"type" env:"CACHE_TYPE,overwrite"` Size int `yaml:"size" env:"CACHE_SIZE,overwrite"` Address string `yaml:"address" env:"CACHE_ADDRESS,overwrite"` + Username string `yaml:"username" env:"CACHE_USERNAME,overwrite"` Password string `yaml:"password" env:"CACHE_PASSWORD,overwrite"` Database int `yaml:"database" env:"CACHE_DATABASE,overwrite"` } `yaml:"cache"` From e265a8d2693fed6cb073b3a2fd7cca351014e1f6 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 30 Mar 2023 19:09:34 +0500 Subject: [PATCH 066/145] fix(services): me service retries --- frontend/src/services/me.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/services/me.ts b/frontend/src/services/me.ts index a9b3383..6657f67 100644 --- a/frontend/src/services/me.ts +++ b/frontend/src/services/me.ts @@ -28,7 +28,7 @@ export const getMe = async (sdk: AppExtensionsSDK) => { const pctx = await sdk.execute(Command.GET_SIGNED_TOKEN); const client = axios.create({ baseURL: process.env.BACKEND_GATEWAY }); axiosRetry(client, { - retries: 2, + retries: 4, retryCondition: (error) => error.status !== 200, retryDelay: (count) => count * 50, }); From cf84b6321063f6770714d357c892e6eb9c276010 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 31 Mar 2023 13:51:39 +0500 Subject: [PATCH 067/145] fix(services): log on error --- frontend/src/hooks/useFileSearch.tsx | 11 +++--- frontend/src/services/file.ts | 50 ++++++++++++++++++---------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/frontend/src/hooks/useFileSearch.tsx b/frontend/src/hooks/useFileSearch.tsx index ded3c4f..f57690a 100644 --- a/frontend/src/hooks/useFileSearch.tsx +++ b/frontend/src/hooks/useFileSearch.tsx @@ -34,7 +34,7 @@ export function useFileSearch(url: string, limit: number) { queryFn: ({ signal, pageParam }) => fetchFiles(url, pageParam, limit, signal), getNextPageParam: (lastPage) => - lastPage.pagination.more_items_in_collection + lastPage?.pagination?.more_items_in_collection ? lastPage.pagination.next_start : undefined, staleTime: 2000, @@ -43,10 +43,11 @@ export function useFileSearch(url: string, limit: number) { }); return { - files: data?.pages - .map((page) => page.response) - .filter(Boolean) - .flat(), + files: + data?.pages + .map((page) => page.response) + .filter(Boolean) + .flat() || [], isLoading, error, refetch, diff --git a/frontend/src/services/file.ts b/frontend/src/services/file.ts index 73cf1d7..3ca11a1 100644 --- a/frontend/src/services/file.ts +++ b/frontend/src/services/file.ts @@ -29,25 +29,39 @@ export const fetchFiles = async ( signal: AbortSignal | undefined = undefined, sort = "add_time ASC" ) => { - const res = await axios({ - method: "GET", - url, - params: { - start, - limit, - sort, - }, - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${AuthToken.access_token}`, - }, - signal, - }); + try { + const res = await axios({ + method: "GET", + url, + params: { + start, + limit, + sort, + }, + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${AuthToken.access_token}`, + }, + signal, + timeout: 4000, + }); - return { - response: res.data.data, - pagination: res.data.additional_data.pagination, - }; + return { + response: res.data.data, + pagination: res.data.additional_data.pagination, + }; + } catch (err) { + console.log(err); + return { + response: [], + pagination: { + start, + next_start: 0, + limit, + more_items_in_collection: false, + }, + }; + } }; export const deleteFile = async ( From 26321022c41d772b06b2d622ba9786dd38f278fb Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 31 Mar 2023 15:20:07 +0500 Subject: [PATCH 068/145] fix(services): use single database --- backend/services/auth/web/core/adapter/mongo.go | 2 +- backend/services/settings/web/core/adapter/mongo.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/services/auth/web/core/adapter/mongo.go b/backend/services/auth/web/core/adapter/mongo.go index e3a242d..72e9baa 100644 --- a/backend/services/auth/web/core/adapter/mongo.go +++ b/backend/services/auth/web/core/adapter/mongo.go @@ -53,7 +53,7 @@ type mongoUserAdapter struct { func NewMongoUserAdapter(url string) port.UserAccessServiceAdapter { if err := mgm.SetDefaultConfig( - &mgm.Config{CtxTimeout: 3 * time.Second}, "pipedrive-users", + &mgm.Config{CtxTimeout: 3 * time.Second}, "pipedrive", options.Client().ApplyURI(url), ); err != nil { log.Fatalf("mongo initialization error: %s", err.Error()) diff --git a/backend/services/settings/web/core/adapter/mongo.go b/backend/services/settings/web/core/adapter/mongo.go index 5539d96..3326252 100644 --- a/backend/services/settings/web/core/adapter/mongo.go +++ b/backend/services/settings/web/core/adapter/mongo.go @@ -49,7 +49,7 @@ type mongoUserAdapter struct { func NewMongoDocserverAdapter(url string) port.DocSettingsServiceAdapter { if err := mgm.SetDefaultConfig( - &mgm.Config{CtxTimeout: 3 * time.Second}, "pipedrive-settings", + &mgm.Config{CtxTimeout: 3 * time.Second}, "pipedrive", options.Client().ApplyURI(url), ); err != nil { log.Fatalf("mongo initialization error: %s", err.Error()) From 18e3ffacc9ad489423f6f32319361cef4d9e2702 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 31 Mar 2023 15:28:08 +0500 Subject: [PATCH 069/145] fix(submodules): path --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 252a172..4b48a40 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ -[submodule "backend/services/gateway/embed/assets"] - path = backend/services/gateway/embed/assets +[submodule "backend/services/gateway/assets/assets"] + path = backend/services/gateway/assets/assets url = https://github.com/ONLYOFFICE/document-templates.git branch = main/new From d924e413e92b9cf0f57c901cf773e97c2943d607 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 31 Mar 2023 17:37:25 +0500 Subject: [PATCH 070/145] fix(context): reset access token on error --- frontend/src/context/TokenContext.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/src/context/TokenContext.tsx b/frontend/src/context/TokenContext.tsx index ee75d4d..5802be7 100644 --- a/frontend/src/context/TokenContext.tsx +++ b/frontend/src/context/TokenContext.tsx @@ -17,6 +17,7 @@ */ import AppExtensionsSDK from "@pipedrive/app-extensions-sdk"; +import axios from "axios"; import React, { useEffect } from "react"; import { proxy } from "valtio"; @@ -49,8 +50,11 @@ export const TokenProvider: React.FC = ({ children }) => { const token = await getMe(sdk); AuthToken.access_token = token.response.access_token; AuthToken.expires_at = token.response.expires_at; - } catch { - AuthToken.error = true; + } catch (err) { + if (!axios.isAxiosError(err)) { + AuthToken.error = true; + AuthToken.access_token = ""; + } } } timerID = setTimeout(update, AuthToken.expires_at - Date.now() - 100); From 9421a2a7dcf6a76720f4d5ec35a65f78b21d34e2 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 31 Mar 2023 17:47:08 +0500 Subject: [PATCH 071/145] fix(context): check cancellation error --- frontend/src/context/TokenContext.tsx | 2 +- frontend/src/services/me.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/context/TokenContext.tsx b/frontend/src/context/TokenContext.tsx index 5802be7..6401707 100644 --- a/frontend/src/context/TokenContext.tsx +++ b/frontend/src/context/TokenContext.tsx @@ -51,7 +51,7 @@ export const TokenProvider: React.FC = ({ children }) => { AuthToken.access_token = token.response.access_token; AuthToken.expires_at = token.response.expires_at; } catch (err) { - if (!axios.isAxiosError(err)) { + if (!axios.isCancel(err)) { AuthToken.error = true; AuthToken.access_token = ""; } diff --git a/frontend/src/services/me.ts b/frontend/src/services/me.ts index 6657f67..ad2c5ed 100644 --- a/frontend/src/services/me.ts +++ b/frontend/src/services/me.ts @@ -28,7 +28,7 @@ export const getMe = async (sdk: AppExtensionsSDK) => { const pctx = await sdk.execute(Command.GET_SIGNED_TOKEN); const client = axios.create({ baseURL: process.env.BACKEND_GATEWAY }); axiosRetry(client, { - retries: 4, + retries: 0, retryCondition: (error) => error.status !== 200, retryDelay: (count) => count * 50, }); From 6d5876ec0a80a4db65add5854ffd9e8d9ab5dc08 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 3 Apr 2023 12:42:14 +0500 Subject: [PATCH 072/145] chore(config): config validation --- backend/pkg/config/persistence.go | 16 ++++++++++++++-- backend/pkg/config/registry.go | 22 +++++++++++++--------- backend/pkg/config/resilience.go | 3 +++ backend/pkg/registry/registry.go | 2 +- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/backend/pkg/config/persistence.go b/backend/pkg/config/persistence.go index 6a2e09c..ce2ae23 100644 --- a/backend/pkg/config/persistence.go +++ b/backend/pkg/config/persistence.go @@ -30,13 +30,25 @@ import ( type PersistenceConfig struct { Persistence struct { - URL string `yaml:"url" env:"PERSISTENCE_URL,overwrite"` + URL string `yaml:"url" env:"PERSISTENCE_URL,overwrite"` + Type int `yaml:"type" env:"PERSISTENCE_TYPE,overwrite"` } `yaml:"persistence"` } func (p *PersistenceConfig) Validate() error { p.Persistence.URL = strings.TrimSpace(p.Persistence.URL) - return nil + switch p.Persistence.Type { + case 2: + if p.Persistence.URL == "" { + return &InvalidConfigurationParameterError{ + Parameter: "URL", + Reason: "MongoDB driver expects a valid url", + } + } + return nil + default: + return nil + } } func BuildNewPersistenceConfig(path string) func() (*PersistenceConfig, error) { diff --git a/backend/pkg/config/registry.go b/backend/pkg/config/registry.go index 127a38d..75eed4a 100644 --- a/backend/pkg/config/registry.go +++ b/backend/pkg/config/registry.go @@ -29,21 +29,25 @@ import ( type RegistryConfig struct { Registry struct { - Addresses []string `yaml:"addresses" env:"REGISTRY_ADDRESSES,overwrite"` - CacheTTL time.Duration `yaml:"cache_duration" env:"REGISTRY_CACHE_DURATION,overwrite"` - RegistryType int `yaml:"type" env:"REGISTRY_TYPE,overwrite"` + Addresses []string `yaml:"addresses" env:"REGISTRY_ADDRESSES,overwrite"` + CacheTTL time.Duration `yaml:"cache_duration" env:"REGISTRY_CACHE_DURATION,overwrite"` + Type int `yaml:"type" env:"REGISTRY_TYPE,overwrite"` } `yaml:"registry"` } func (r *RegistryConfig) Validate() error { - if len(r.Registry.Addresses) <= 0 { - return &InvalidConfigurationParameterError{ - Parameter: "Addresses", - Reason: "Length should be greater than zero", + switch r.Registry.Type { + case 2: + return nil + default: + if len(r.Registry.Addresses) <= 0 { + return &InvalidConfigurationParameterError{ + Parameter: "Addresses", + Reason: "Length should be greater than zero", + } } + return nil } - - return nil } func BuildNewRegistryConfig(path string) func() (*RegistryConfig, error) { diff --git a/backend/pkg/config/resilience.go b/backend/pkg/config/resilience.go index 00a789c..63f70ad 100644 --- a/backend/pkg/config/resilience.go +++ b/backend/pkg/config/resilience.go @@ -37,6 +37,9 @@ type ResilienceConfig struct { func BuildNewResilienceConfig(path string) func() (*ResilienceConfig, error) { return func() (*ResilienceConfig, error) { var config ResilienceConfig + config.Resilience.RateLimiter.Limit = 3000 + config.Resilience.RateLimiter.IPLimit = 20 + config.Resilience.CircuitBreaker.Timeout = 5000 if path != "" { file, err := os.Open(path) if err != nil { diff --git a/backend/pkg/registry/registry.go b/backend/pkg/registry/registry.go index 9dcbb79..fd7e9f4 100644 --- a/backend/pkg/registry/registry.go +++ b/backend/pkg/registry/registry.go @@ -31,7 +31,7 @@ import ( // NewRegistry looks up envs and configures respective registries based on those variables. Defaults to memory func NewRegistry(config *config.RegistryConfig) registry.Registry { var r registry.Registry - switch config.Registry.RegistryType { + switch config.Registry.Type { case 1: r = kubernetes.NewRegistry( registry.Addrs(config.Registry.Addresses...), From 640551740e9f5124619bdb3e60e4def472073d58 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 3 Apr 2023 13:10:47 +0500 Subject: [PATCH 073/145] fix(pkg): persistence config type for mongo --- backend/pkg/config/persistence.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/pkg/config/persistence.go b/backend/pkg/config/persistence.go index ce2ae23..52adcd7 100644 --- a/backend/pkg/config/persistence.go +++ b/backend/pkg/config/persistence.go @@ -38,7 +38,7 @@ type PersistenceConfig struct { func (p *PersistenceConfig) Validate() error { p.Persistence.URL = strings.TrimSpace(p.Persistence.URL) switch p.Persistence.Type { - case 2: + case 1: if p.Persistence.URL == "" { return &InvalidConfigurationParameterError{ Parameter: "URL", From 663c3673e7260bf01ca22df494ef91a2c767aa4c Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 3 Apr 2023 13:11:35 +0500 Subject: [PATCH 074/145] fix(pkg): new app bootstrapper --- backend/pkg/bootstrap.go | 46 ++++++++++++++++++++++--- backend/services/auth/cmd/server.go | 37 +++++--------------- backend/services/builder/cmd/server.go | 40 ++++++--------------- backend/services/callback/cmd/server.go | 40 ++++++--------------- backend/services/gateway/cmd/server.go | 40 ++++++--------------- backend/services/settings/cmd/server.go | 35 ++++--------------- 6 files changed, 86 insertions(+), 152 deletions(-) diff --git a/backend/pkg/bootstrap.go b/backend/pkg/bootstrap.go index 31264be..b0d88a4 100644 --- a/backend/pkg/bootstrap.go +++ b/backend/pkg/bootstrap.go @@ -19,6 +19,10 @@ package pkg import ( + "context" + "net/http" + "os" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/cache" "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" @@ -27,12 +31,28 @@ import ( "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/repl" "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/trace" "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/worker" + "go-micro.dev/v4" "go.uber.org/fx" + "go.uber.org/fx/fxevent" + "golang.org/x/sync/errgroup" ) -func Bootstrap(path string) fx.Option { - return fx.Module( - "pkg", +func Bootstrap(path string, extras ...interface{}) *fx.App { + builder := config.BuildNewServerConfig(path) + sconf, err := builder() + if err != nil { + log.DefaultLogger{}.Fatal(err.Error()) + return nil + } + + var logger fx.Option = fx.NopLogger + if sconf.Debug { + logger = fx.WithLogger(func() fxevent.Logger { + return &fxevent.ConsoleLogger{W: os.Stdout} + }) + } + + return fx.New( fx.Provide(config.BuildNewCacheConfig(path)), fx.Provide(config.BuildNewCorsConfig(path)), fx.Provide(config.BuildNewCredentialsConfig(path)), @@ -41,7 +61,7 @@ func Bootstrap(path string) fx.Option { fx.Provide(config.BuildNewPersistenceConfig(path)), fx.Provide(config.BuildNewRegistryConfig(path)), fx.Provide(config.BuildNewResilienceConfig(path)), - fx.Provide(config.BuildNewServerConfig(path)), + fx.Provide(builder), fx.Provide(config.BuildNewTracerConfig(path)), fx.Provide(config.BuildNewWorkerConfig(path)), fx.Provide(cache.NewCache), @@ -52,5 +72,23 @@ func Bootstrap(path string) fx.Option { fx.Provide(worker.NewBackgroundWorker), fx.Provide(worker.NewBackgroundEnqueuer), fx.Provide(repl.NewService), + fx.Provide(extras...), + fx.Invoke(func(lifecycle fx.Lifecycle, service micro.Service, repl *http.Server, logger log.Logger) { + lifecycle.Append(fx.Hook{ + OnStart: func(ctx context.Context) error { + go repl.ListenAndServe() + go service.Run() + return nil + }, + OnStop: func(ctx context.Context) error { + g, gCtx := errgroup.WithContext(ctx) + g.Go(func() error { + return repl.Shutdown(gCtx) + }) + return g.Wait() + }, + }) + }), + logger, ) } diff --git a/backend/services/auth/cmd/server.go b/backend/services/auth/cmd/server.go index 2fff9b3..bc9ac80 100644 --- a/backend/services/auth/cmd/server.go +++ b/backend/services/auth/cmd/server.go @@ -19,17 +19,10 @@ package cmd import ( - "context" - "net/http" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/rpc" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web" "github.com/urfave/cli/v2" - "go-micro.dev/v4" - "go.uber.org/fx" - "golang.org/x/sync/errgroup" ) func Server() *cli.Command { @@ -55,28 +48,14 @@ func Server() *cli.Command { // ENVIRONMENT = c.String("environment") ) - fx.New( - pkg.Bootstrap(CONFIG_PATH), - fx.Provide(rpc.NewService), - fx.Provide(web.NewAuthRPCServer), - fx.Invoke(func(lifecycle fx.Lifecycle, service micro.Service, repl *http.Server, logger log.Logger) { - lifecycle.Append(fx.Hook{ - OnStart: func(ctx context.Context) error { - go repl.ListenAndServe() - go service.Run() - return nil - }, - OnStop: func(ctx context.Context) error { - g, gCtx := errgroup.WithContext(ctx) - g.Go(func() error { - return repl.Shutdown(gCtx) - }) - return g.Wait() - }, - }) - }), - fx.NopLogger, - ).Run() + app := pkg.Bootstrap( + CONFIG_PATH, rpc.NewService, web.NewAuthRPCServer, + ) + if err := app.Err(); err != nil { + return err + } + + app.Run() return nil }, diff --git a/backend/services/builder/cmd/server.go b/backend/services/builder/cmd/server.go index e1811d9..68960e5 100644 --- a/backend/services/builder/cmd/server.go +++ b/backend/services/builder/cmd/server.go @@ -19,18 +19,11 @@ package cmd import ( - "context" - "net/http" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/rpc" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/builder/web" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" "github.com/urfave/cli/v2" - "go-micro.dev/v4" - "go.uber.org/fx" - "golang.org/x/sync/errgroup" ) func Server() *cli.Command { @@ -56,29 +49,16 @@ func Server() *cli.Command { // ENVIRONMENT = c.String("environment") ) - fx.New( - pkg.Bootstrap(CONFIG_PATH), - fx.Provide(shared.BuildNewOnlyofficeConfig(CONFIG_PATH)), - fx.Provide(rpc.NewService), - fx.Provide(web.NewConfigRPCServer), - fx.Invoke(func(lifecycle fx.Lifecycle, service micro.Service, repl *http.Server, logger log.Logger) { - lifecycle.Append(fx.Hook{ - OnStart: func(ctx context.Context) error { - go repl.ListenAndServe() - go service.Run() - return nil - }, - OnStop: func(ctx context.Context) error { - g, gCtx := errgroup.WithContext(ctx) - g.Go(func() error { - return repl.Shutdown(gCtx) - }) - return g.Wait() - }, - }) - }), - fx.NopLogger, - ).Run() + app := pkg.Bootstrap( + CONFIG_PATH, rpc.NewService, web.NewConfigRPCServer, + shared.BuildNewOnlyofficeConfig(CONFIG_PATH), + ) + + if err := app.Err(); err != nil { + return err + } + + app.Run() return nil }, diff --git a/backend/services/callback/cmd/server.go b/backend/services/callback/cmd/server.go index eec28df..45a11c8 100644 --- a/backend/services/callback/cmd/server.go +++ b/backend/services/callback/cmd/server.go @@ -19,18 +19,11 @@ package cmd import ( - "context" - "net/http" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" chttp "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/http" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/callback/web" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" "github.com/urfave/cli/v2" - "go-micro.dev/v4" - "go.uber.org/fx" - "golang.org/x/sync/errgroup" ) func Server() *cli.Command { @@ -56,29 +49,16 @@ func Server() *cli.Command { // ENVIRONMENT = c.String("environment") ) - fx.New( - pkg.Bootstrap(CONFIG_PATH), - fx.Provide(shared.BuildNewOnlyofficeConfig(CONFIG_PATH)), - fx.Provide(chttp.NewService), - fx.Provide(web.NewServer), - fx.Invoke(func(lifecycle fx.Lifecycle, service micro.Service, repl *http.Server, logger log.Logger) { - lifecycle.Append(fx.Hook{ - OnStart: func(ctx context.Context) error { - go repl.ListenAndServe() - go service.Run() - return nil - }, - OnStop: func(ctx context.Context) error { - g, gCtx := errgroup.WithContext(ctx) - g.Go(func() error { - return repl.Shutdown(gCtx) - }) - return g.Wait() - }, - }) - }), - fx.NopLogger, - ).Run() + app := pkg.Bootstrap( + CONFIG_PATH, chttp.NewService, web.NewServer, + shared.BuildNewOnlyofficeConfig(CONFIG_PATH), + ) + + if err := app.Err(); err != nil { + return err + } + + app.Run() return nil }, diff --git a/backend/services/gateway/cmd/server.go b/backend/services/gateway/cmd/server.go index cdbae16..da25983 100644 --- a/backend/services/gateway/cmd/server.go +++ b/backend/services/gateway/cmd/server.go @@ -19,18 +19,11 @@ package cmd import ( - "context" - "net/http" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" chttp "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/http" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/gateway/web" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" "github.com/urfave/cli/v2" - "go-micro.dev/v4" - "go.uber.org/fx" - "golang.org/x/sync/errgroup" ) func Server() *cli.Command { @@ -56,29 +49,16 @@ func Server() *cli.Command { // ENVIRONMENT = c.String("environment") ) - fx.New( - pkg.Bootstrap(CONFIG_PATH), - fx.Provide(shared.BuildNewOnlyofficeConfig(CONFIG_PATH)), - fx.Provide(chttp.NewService), - fx.Provide(web.NewServer), - fx.Invoke(func(lifecycle fx.Lifecycle, service micro.Service, repl *http.Server, logger log.Logger) { - lifecycle.Append(fx.Hook{ - OnStart: func(ctx context.Context) error { - go repl.ListenAndServe() - go service.Run() - return nil - }, - OnStop: func(ctx context.Context) error { - g, gCtx := errgroup.WithContext(ctx) - g.Go(func() error { - return repl.Shutdown(gCtx) - }) - return g.Wait() - }, - }) - }), - fx.NopLogger, - ).Run() + app := pkg.Bootstrap( + CONFIG_PATH, chttp.NewService, web.NewServer, + shared.BuildNewOnlyofficeConfig(CONFIG_PATH), + ) + + if err := app.Err(); err != nil { + return err + } + + app.Run() return nil }, diff --git a/backend/services/settings/cmd/server.go b/backend/services/settings/cmd/server.go index 2577fba..8d5ff90 100644 --- a/backend/services/settings/cmd/server.go +++ b/backend/services/settings/cmd/server.go @@ -19,17 +19,10 @@ package cmd import ( - "context" - "net/http" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/rpc" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web" "github.com/urfave/cli/v2" - "go-micro.dev/v4" - "go.uber.org/fx" - "golang.org/x/sync/errgroup" ) func Server() *cli.Command { @@ -55,28 +48,12 @@ func Server() *cli.Command { // ENVIRONMENT = c.String("environment") ) - fx.New( - pkg.Bootstrap(CONFIG_PATH), - fx.Provide(rpc.NewService), - fx.Provide(web.NewDocserverRPCServer), - fx.Invoke(func(lifecycle fx.Lifecycle, service micro.Service, repl *http.Server, logger log.Logger) { - lifecycle.Append(fx.Hook{ - OnStart: func(ctx context.Context) error { - go repl.ListenAndServe() - go service.Run() - return nil - }, - OnStop: func(ctx context.Context) error { - g, gCtx := errgroup.WithContext(ctx) - g.Go(func() error { - return repl.Shutdown(gCtx) - }) - return g.Wait() - }, - }) - }), - fx.NopLogger, - ).Run() + app := pkg.Bootstrap(CONFIG_PATH, rpc.NewService, web.NewDocserverRPCServer) + if err := app.Err(); err != nil { + return err + } + + app.Run() return nil }, From 4d2d7119ec08862dd3997cd9be6d48a212153ebb Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 3 Apr 2023 13:55:38 +0500 Subject: [PATCH 075/145] fix(actions): disable buttons until file downloaded --- frontend/src/pages/Main/Actions.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/Main/Actions.tsx b/frontend/src/pages/Main/Actions.tsx index 40c7f05..d56e979 100644 --- a/frontend/src/pages/Main/Actions.tsx +++ b/frontend/src/pages/Main/Actions.tsx @@ -75,6 +75,7 @@ export const OnlyofficeFileActions: React.FC = ({ file }) => { }; const handleEditor = async () => { + setDisable(true); if (!disable && isFileSupported(file.name)) { const token = await sdk?.execute(Command.GET_SIGNED_TOKEN); if (token) { @@ -88,6 +89,8 @@ export const OnlyofficeFileActions: React.FC = ({ file }) => { ); } } + // temporary solution + setTimeout(() => setDisable(false), 10000); }; return ( @@ -108,7 +111,11 @@ export const OnlyofficeFileActions: React.FC = ({ file }) => {
handleDelete()} onKeyDown={() => handleDelete()} > From 99ea36282368f42ea469d65912b243080f6080a1 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 3 Apr 2023 15:02:43 +0500 Subject: [PATCH 076/145] fix(registry): do not validate kubernetes addresses --- backend/pkg/config/registry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/pkg/config/registry.go b/backend/pkg/config/registry.go index 75eed4a..4958617 100644 --- a/backend/pkg/config/registry.go +++ b/backend/pkg/config/registry.go @@ -37,7 +37,7 @@ type RegistryConfig struct { func (r *RegistryConfig) Validate() error { switch r.Registry.Type { - case 2: + case 1: return nil default: if len(r.Registry.Addresses) <= 0 { From 386a772925296c01b0770bd8beb2cb952a8953ee Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 4 Apr 2023 13:41:44 +0500 Subject: [PATCH 077/145] fix(builder): pass callback url: --- backend/services/builder/web/handler/build.go | 31 ++++++++++--------- backend/services/builder/web/server.go | 20 ++++++------ 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/backend/services/builder/web/handler/build.go b/backend/services/builder/web/handler/build.go index 3c5813a..9f94090 100644 --- a/backend/services/builder/web/handler/build.go +++ b/backend/services/builder/web/handler/build.go @@ -45,13 +45,14 @@ var _ErrNoSettingsFound = errors.New("could not find document server settings") var _ErrOperationTimeout = errors.New("operation timeout") type ConfigHandler struct { - namespace string - logger plog.Logger - client client.Client - apiClient pclient.PipedriveApiClient - jwtManager crypto.JwtManager - gatewayURL string - group singleflight.Group + namespace string + logger plog.Logger + client client.Client + apiClient pclient.PipedriveApiClient + jwtManager crypto.JwtManager + gatewayURL string + callbackURL string + group singleflight.Group } func NewConfigHandler( @@ -60,14 +61,16 @@ func NewConfigHandler( client client.Client, jwtManager crypto.JwtManager, gatewayURL string, + callbackURL string, ) ConfigHandler { return ConfigHandler{ - namespace: namespace, - logger: logger, - client: client, - apiClient: pclient.NewPipedriveApiClient(), - jwtManager: jwtManager, - gatewayURL: gatewayURL, + namespace: namespace, + logger: logger, + client: client, + apiClient: pclient.NewPipedriveApiClient(), + jwtManager: jwtManager, + gatewayURL: gatewayURL, + callbackURL: callbackURL, } } @@ -165,7 +168,7 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui }, CallbackURL: fmt.Sprintf( "%s/callback?cid=%d&did=%s&fid=%s&filename=%s", - c.gatewayURL, usr.CompanyID, req.Deal, req.FileID, url.QueryEscape(filename), + c.callbackURL, usr.CompanyID, req.Deal, req.FileID, url.QueryEscape(filename), ), Customization: response.Customization{ Goback: response.Goback{ diff --git a/backend/services/builder/web/server.go b/backend/services/builder/web/server.go index 59b0137..8b68ca4 100644 --- a/backend/services/builder/web/server.go +++ b/backend/services/builder/web/server.go @@ -31,10 +31,11 @@ import ( ) type ConfigRPCServer struct { - namespace string - jwtManager crypto.JwtManager - logger plog.Logger - gatewayURL string + namespace string + jwtManager crypto.JwtManager + logger plog.Logger + gatewayURL string + callbackURL string } func NewConfigRPCServer( @@ -45,10 +46,11 @@ func NewConfigRPCServer( ) rpc.RPCEngine { jwtManager := crypto.NewOnlyofficeJwtManager() return ConfigRPCServer{ - namespace: serverConfig.Namespace, - jwtManager: jwtManager, - logger: logger, - gatewayURL: onlyofficeConfig.Onlyoffice.Builder.GatewayURL, + namespace: serverConfig.Namespace, + jwtManager: jwtManager, + logger: logger, + gatewayURL: onlyofficeConfig.Onlyoffice.Builder.GatewayURL, + callbackURL: onlyofficeConfig.Onlyoffice.Builder.CallbackURL, } } @@ -58,6 +60,6 @@ func (a ConfigRPCServer) BuildMessageHandlers() []rpc.RPCMessageHandler { func (a ConfigRPCServer) BuildHandlers(c mclient.Client, cache cache.Cache) []interface{} { return []interface{}{ - handler.NewConfigHandler(a.namespace, a.logger, c, a.jwtManager, a.gatewayURL), + handler.NewConfigHandler(a.namespace, a.logger, c, a.jwtManager, a.gatewayURL, a.callbackURL), } } From 35e3fe045f9f2da2063edbfc833f9cc8a8f89fca Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 4 Apr 2023 14:08:20 +0500 Subject: [PATCH 078/145] fix(pkg): asynq config --- backend/pkg/worker/asynq.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/pkg/worker/asynq.go b/backend/pkg/worker/asynq.go index d43cb43..745330a 100644 --- a/backend/pkg/worker/asynq.go +++ b/backend/pkg/worker/asynq.go @@ -44,6 +44,7 @@ func newAsynqWorker(config *config.WorkerConfig, logger plog.Logger) BackgroundW Addr: config.Worker.RedisAddresses[0], Username: config.Worker.RedisUsername, Password: config.Worker.RedisPassword, + DB: config.Worker.RedisDatabase, ReadTimeout: 4 * time.Second, WriteTimeout: 7 * time.Second, } @@ -90,6 +91,7 @@ func newAsynqEnqueuer(config *config.WorkerConfig) BackgroundEnqueuer { Addr: config.Worker.RedisAddresses[0], Username: config.Worker.RedisUsername, Password: config.Worker.RedisPassword, + DB: config.Worker.RedisDatabase, ReadTimeout: 4 * time.Second, WriteTimeout: 7 * time.Second, } From 7e6bf158e3fe73a16c791f6f0279cf8b49dd9ae8 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 4 Apr 2023 17:26:16 +0500 Subject: [PATCH 079/145] fix(actions): do not disable delete for unsupported files --- frontend/src/pages/Main/Actions.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/src/pages/Main/Actions.tsx b/frontend/src/pages/Main/Actions.tsx index d56e979..49a3330 100644 --- a/frontend/src/pages/Main/Actions.tsx +++ b/frontend/src/pages/Main/Actions.tsx @@ -112,9 +112,7 @@ export const OnlyofficeFileActions: React.FC = ({ file }) => { role="button" tabIndex={0} className={`mx-1 ${ - !isFileSupported(file.name) || disable - ? "hover:cursor-default opacity-50" - : "hover:cursor-pointer" + disable ? "hover:cursor-default opacity-50" : "hover:cursor-pointer" }`} onClick={() => handleDelete()} onKeyDown={() => handleDelete()} From 7942a9a25124f72bad1a90940eebb6b6f515098d Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 4 Apr 2023 18:09:09 +0500 Subject: [PATCH 080/145] fix(settings): get me --- frontend/src/pages/Settings/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx index 1d7c934..6f07b7c 100644 --- a/frontend/src/pages/Settings/index.tsx +++ b/frontend/src/pages/Settings/index.tsx @@ -34,6 +34,7 @@ import { getPipedriveMe } from "@services/me"; import { AuthToken } from "@context/TokenContext"; import SettingsError from "@assets/settings-error.svg"; +import { getCurrentURL } from "@utils/url"; export const SettingsPage: React.FC = () => { const { t } = useTranslation(); @@ -57,9 +58,8 @@ export const SettingsPage: React.FC = () => { useEffect(() => { if (accessToken && !error && !!sdk) { - getPipedriveMe( - `${window.parent[0].location.ancestorOrigins[0]}/api/v1/users/me` - ) + const { url } = getCurrentURL(); + getPipedriveMe(`${url}api/v1/users/me`) .then(async (ures) => { try { if (ures.data.access.find((a) => a.app === "global" && a.admin)) { @@ -75,7 +75,7 @@ export const SettingsPage: React.FC = () => { setLoading(false); } }) - .catch(() => { + .catch((e) => { setLoading(false); }); } From 3ddab61a80e113d0368cb15243c44c0c0829cb70 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Tue, 4 Apr 2023 18:25:55 +0500 Subject: [PATCH 081/145] fix(icons): supported and unsupported files --- frontend/src/assets/supported.svg | 16 ++++++++++++++++ frontend/src/assets/unsupported.svg | 16 ++++++++-------- frontend/src/utils/file.ts | 13 ++++++++++--- 3 files changed, 34 insertions(+), 11 deletions(-) create mode 100644 frontend/src/assets/supported.svg diff --git a/frontend/src/assets/supported.svg b/frontend/src/assets/supported.svg new file mode 100644 index 0000000..d676e89 --- /dev/null +++ b/frontend/src/assets/supported.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/unsupported.svg b/frontend/src/assets/unsupported.svg index 2ee28b3..56f7098 100644 --- a/frontend/src/assets/unsupported.svg +++ b/frontend/src/assets/unsupported.svg @@ -1,12 +1,12 @@ - - - - - - - + + + + + + + - + diff --git a/frontend/src/utils/file.ts b/frontend/src/utils/file.ts index 7293b28..33ebad9 100644 --- a/frontend/src/utils/file.ts +++ b/frontend/src/utils/file.ts @@ -20,6 +20,7 @@ import Docx from "@assets/docx.svg"; import Pptx from "@assets/pptx.svg"; import Xlsx from "@assets/xlsx.svg"; import Unsupported from "@assets/unsupported.svg"; +import Supported from "@assets/supported.svg"; const DOCUMENT_EXTS = [ "doc", @@ -127,9 +128,15 @@ export const getMimeType = (filename: string) => { export const getFileIcon = (filename: string) => { const e = getFileExt(filename).toLowerCase(); - if (DOCUMENT_EXTS.includes(e)) return Docx; - if (SPREADSHEET_EXTS.includes(e)) return Xlsx; - if (PRESENTATION_EXTS.includes(e)) return Pptx; + if (e === "docx") return Docx; + if (e === "xlsx") return Xlsx; + if (e === "pptx") return Pptx; + if ( + DOCUMENT_EXTS.includes(e) || + SPREADSHEET_EXTS.includes(e) || + PRESENTATION_EXTS.includes(e) + ) + return Supported; return Unsupported; }; From c82a668a8fa510c7027eaddd95d5a5f57e1b3ba5 Mon Sep 17 00:00:00 2001 From: VyacheslavSemin Date: Tue, 4 Apr 2023 15:15:42 +0000 Subject: [PATCH 082/145] Add files for build --- Dockerfile | 84 +++++++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 37 ++++++++++++++++++++ hooks/build | 7 ++++ hooks/post_checkout | 3 ++ hooks/push | 4 +++ 5 files changed, 135 insertions(+) create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 hooks/build create mode 100644 hooks/post_checkout create mode 100644 hooks/push diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3aa7dbd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,84 @@ +FROM node:current-alpine AS build-frontend +LABEL maintainer Ascensio System SIA +ARG BACKEND_GATEWAY +ARG PIPEDRIVE_CREATE_MODAL_ID +ARG PIPEDRIVE_EDITOR_MODAL_ID +ENV BACKEND_GATEWAY=$BACKEND_GATEWAY \ + PIPEDRIVE_CREATE_MODAL_ID=$PIPEDRIVE_CREATE_MODAL_ID \ + PIPEDRIVE_EDITOR_MODAL_ID=$PIPEDRIVE_EDITOR_MODAL_ID +WORKDIR /usr/src/app +COPY ./frontend/package*.json ./ +RUN npm install +COPY frontend . +RUN npm run build + +FROM golang:alpine AS build-gateway +WORKDIR /usr/src/app +COPY backend . +RUN go build services/gateway/main.go + +FROM golang:alpine AS build-auth +WORKDIR /usr/src/app +COPY backend . +RUN go build services/auth/main.go + +FROM golang:alpine AS build-builder +WORKDIR /usr/src/app +COPY backend . +RUN go build services/builder/main.go + +FROM golang:alpine AS build-callback +WORKDIR /usr/src/app +COPY backend . +RUN go build services/callback/main.go + +FROM golang:alpine AS build-settings +WORKDIR /usr/src/app +COPY backend . +RUN go build services/settings/main.go + +FROM golang:alpine AS gateway +WORKDIR /usr/src/app +COPY --from=build-gateway \ + /usr/src/app/main \ + /usr/src/app/main +EXPOSE 4044 +CMD ["./main", "server"] + +FROM golang:alpine AS auth +WORKDIR /usr/src/app +COPY --from=build-auth \ + /usr/src/app/main \ + /usr/src/app/main +EXPOSE 5052 +CMD ["./main", "server"] + +FROM golang:alpine AS builder +WORKDIR /usr/src/app +COPY --from=build-builder \ + /usr/src/app/main \ + /usr/src/app/main +EXPOSE 6260 +CMD ["./main", "server"] + +FROM golang:alpine AS callback +WORKDIR /usr/src/app +COPY --from=build-callback \ + /usr/src/app/main \ + /usr/src/app/main +EXPOSE 5044 +CMD ["./main", "server"] + +FROM golang:alpine AS settings +WORKDIR /usr/src/app +COPY --from=build-settings \ + /usr/src/app/main \ + /usr/src/app/main +EXPOSE 5150 +CMD ["./main", "server"] + +FROM nginx:alpine AS frontend +COPY --from=build-frontend \ + /usr/src/app/build \ + /usr/share/nginx/html +EXPOSE 80 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..50cd433 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,37 @@ +version: '3.8' +services: + gateway: + build: + context: . + target: gateway + image: onlyoffice/pipedrive-gateway:${PRODUCT_VERSION} + + auth: + build: + context: . + target: auth + image: onlyoffice/pipedrive-auth:${PRODUCT_VERSION} + + builder: + build: + context: . + target: builder + image: onlyoffice/pipedrive-builder:${PRODUCT_VERSION} + + callback: + build: + context: . + target: callback + image: onlyoffice/pipedrive-callback:${PRODUCT_VERSION} + + settings: + build: + context: . + target: settings + image: onlyoffice/pipedrive-settings:${PRODUCT_VERSION} + + frontend: + build: + context: . + target: frontend + image: onlyoffice/pipedrive-frontend:${PRODUCT_VERSION} diff --git a/hooks/build b/hooks/build new file mode 100644 index 0000000..1e2994e --- /dev/null +++ b/hooks/build @@ -0,0 +1,7 @@ +#!/bin/bash + +export DOCKER_BUILDKIT=1 PRODUCT_VERSION=$DOCKER_TAG +docker-compose build \ + --build-arg BACKEND_GATEWAY=$BACKEND_GATEWAY \ + --build-arg PIPEDRIVE_CREATE_MODAL_ID=$PIPEDRIVE_CREATE_MODAL_ID \ + --build-arg PIPEDRIVE_EDITOR_MODAL_ID=$PIPEDRIVE_EDITOR_MODAL_ID diff --git a/hooks/post_checkout b/hooks/post_checkout new file mode 100644 index 0000000..2d1026c --- /dev/null +++ b/hooks/post_checkout @@ -0,0 +1,3 @@ +#!/bin/bash + +git submodule update --init diff --git a/hooks/push b/hooks/push new file mode 100644 index 0000000..8f6f369 --- /dev/null +++ b/hooks/push @@ -0,0 +1,4 @@ +#!/bin/bash + +export PRODUCT_VERSION=$DOCKER_TAG +docker-compose push From fd920a3048ca9d0032aaca11fbc7fec796f129b4 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 6 Apr 2023 13:19:31 +0500 Subject: [PATCH 083/145] fix(icons): add editor favicons --- frontend/package.json | 2 ++ frontend/src/assets/cell.ico | Bin 0 -> 8348 bytes frontend/src/assets/generic.ico | Bin 0 -> 147237 bytes frontend/src/assets/slide.ico | Bin 0 -> 8348 bytes frontend/src/assets/word.ico | Bin 0 -> 8348 bytes frontend/src/custom.d.ts | 5 +++++ frontend/src/pages/Editor/index.tsx | 12 ++++++++++++ frontend/src/public/index.html | 1 - frontend/src/utils/file.ts | 14 ++++++++++++++ frontend/yarn.lock | 29 +++++++++++++++++++++++++++- 10 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 frontend/src/assets/cell.ico create mode 100644 frontend/src/assets/generic.ico create mode 100644 frontend/src/assets/slide.ico create mode 100644 frontend/src/assets/word.ico diff --git a/frontend/package.json b/frontend/package.json index a0121d3..352d9ea 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -45,6 +45,7 @@ "@types/node": "^18.11.0", "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", + "@types/react-helmet": "^6.1.6", "@typescript-eslint/eslint-plugin": "^5.40.1", "@typescript-eslint/parser": "^5.40.1", "autoprefixer": "^10.4.12", @@ -94,6 +95,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", + "react-helmet": "^6.1.0", "react-i18next": "^12.1.5", "react-query": "^3.39.2", "react-router-dom": "^6.4.2", diff --git a/frontend/src/assets/cell.ico b/frontend/src/assets/cell.ico new file mode 100644 index 0000000000000000000000000000000000000000..05ecd8c040b0e8cf5c34412c400cc92ea29566b6 GIT binary patch literal 8348 zcmeI0K@Ng25JgAh0bFYYR>otv)rHqE;K7J`3_Xg`V`!(zPk;yt*jD3jLI&DSf9EkJ zVMGQplq3;j6IqN!<{~mS);^C!Hs*d|`pgtrnoIlZTD;$_C;9dk9kRREE6*;fJ>6$l zueyaegT!6X4;43cJ?Hw5z4N=H_HN#(eBE3ZV^u8|8cl0bzkD8*m5Wy8T=%TnKF6wB z)DO>}`By%Z$Wa%$iM3%}Xf&+}UD&3%F2s~RpE)~9`*+vM_pTtfbTs&;%#S0VNQ zZLJfSa)GT7G}Q^Teh}s9smmFe9n!P)@9`h>N&hzAR>WJ>SAX=wzUuon|92T6zHi$< w_66IPf1miF*~S+6weiDT>)-MG>A6*ZHWDBK5+DH*AOR8}0TLhq68Ic}2Py&n?EnA( literal 0 HcmV?d00001 diff --git a/frontend/src/assets/generic.ico b/frontend/src/assets/generic.ico new file mode 100644 index 0000000000000000000000000000000000000000..dc87d1a88394e3aec8f283642442d2a85fb7bec1 GIT binary patch literal 147237 zcmeHQ2V4_L7vIDnu?K7@AOQi_P|k)5L9o#6UBHH@hoB-s zMQH+eC}6>}ApwF^5wRDPZzd28OG0`G7=G+;v%9l1Z{GjSdvD&%&L9W|!6O6$0?tHa z84f|VBM73RQh&X(1A@E;_lU&$>qwApT~`Dlks7Xb5Tu_5f{@Ae*LoHRl5UA06iUPM zec*kNA(h(j9FIW`sA3TK2g_TkiXgZ8V-Y=Y4YCGz>hFUSLI&ju@@MK$WCIT}g$}dg z>(7`{N;Gn0xSBW?(@h*1bZpEe15*iO2dA3 zU?iG4GIBWOXY5!M2JVjmMcF55I(5QJn--9$Lkp;5Kj6KA5yxyzofuS5$3zqRB3pKb>d^ctx-@?}`#YR|!+iSF z0v8jbsZ30oc`s2XKG2nUaG*ygKA>7B-rrT17C;4Qs(+Og80M6)X>oZ9C9FJ|Qr&QP zO}OS`3(~OFE07NI2!VAVMu!k!IX*Wwkl}k}DU>=+2Yj|a(cP6vKP`mFs()pQ-58KQ zk<&X|?j^!^;QM9C6grnQ9BDwMO)O2Nkjqj9Dw~-vtV?MkZ;*EgxL=JbqY)kG z_gFk&o#}}bLTNIEipqx$@3EK1O3&@g%qN9HDM_I;k~41Ug}o2;3{Y(d2^>)Wz&{Gm zakBO|b7oyT{e@+nOzauT!<|ZcG@Lsk)HjIrM&BU?j5XiTRbG{9_o25@37#~d>iY^0vI^B2~omgMauTo&X zqnDNrGIe-q2kLyw#G&{VhkR?nJ*Y!VK%L<8&rQ<6a+tcbc!t@MmjeI~I>J&+9A3@^ zv^LSz4ex&eXNJkr!W5&`kB=B`xEDCW{n0@{=hX&0;PbjhhXdmSpn+y;U$_urXu9;} zY4AKB9ADYeeKv7oJTX~X5IbUHcF^GCk!yke^#Z!?xOSX>;J`DXtA}mA<7@0pzufRH zx}0KQOU@3OncLpTp}e>0(jq%h<{6OZLwFc1qeqauQ+@w96}AQF;gdkWmi*Ggz<$K} zm+8a>oEdmJblvdHncIz)6+{5cvOrl6z!7WYSmh$7koGO$-%Y?f&;Id#<^wMIbwNPB5MOOtpcS+yI<%k^F8RI(_p>3N zAn(ENL*REBIKKl2x}4A8UOK3g&w%UxX24m(g6j($(!uv?AWeqxJ9e#4*yx#X%Vld` zqP=fs@=~8|H`jR?#qR_(!Mp-s`Ay4-5A+*;_ppL)od0a_{17-<44hUPs$*Q2Y9vGh~~HPFvvGA$vzC^br&4m?9LnTEbVc!!-?=)ZhqF+ zeb)g$V}ov7-~_#decf67hZxiWJ{4VG()Yicj#FPG*`EUM-3P}fa5S=ET&_VnyZ>G$ z+g;2}c3A!}!J&6u9WV(Ef0Zc<`hfS`%91E&%TgCV6d+$z*^9q{cVCnzQKBn=eJf0z zrv!@t_L2tfmk6IPx{l0p=w-=v=bp!{RIEs{vjb%$fc#PP2!9RePb^JdJge^f%yuPL zoH~}JF3|!qF$u^>Bq-~Huw|8}Qc?ha7FHxvbbud#XJG)(ckenWKrBj9?5xUD7GDsc zoGO?e@MAWl6Xwf~FMAoRa>~*c_5w0L7L*YV>Qcq2KH%Bwirb5imZjPm6(=m}4td>N zzUXow?ILSr789!%dRc#R98n8$<9NX>HzPcUsZH_ zNna>;xw$jji<&RGKEhr@Ka2_NpV%T*}f(4 zCqO?p8rWfGKSW$*!hB$!4Rvkov!H#LhHJnN=og7hhw+jkN`t?|SGl0fM(H3OFkT=i zFGMXDO$YFifKSXj-eBy+@DioIP0MY}58&%HiF@#pDK2?d!IbJd>&{J1dS3pMh+U>pSafny*Y^*mvVH*u;HTb?jqz)v9SsMrsXUszxGTsuC% z29|*?7k$m5gPCW290<0>U+n@_E=40%1z!NW4e5$#!8_EsiC+^jx9F#mXdEN{ita6*Y(`=p~-c>ITFs{ZH zFKaB9P4a|vfEc1~{LZ0}7xjG{9`7`!gBkBb^AYfv+(1vWa?zNcz%bCC%xQd&8!drh z+O!>HST2Y=QGidC2>mJ;4}`xflI`l-i6C@cC`if947rK7NXgSrHU8cCaFRp%8BRZJnHX~CKq$RN= zkzxz-r%SL5UOcN7hnxvRKa??cxn#EQrg3gw^b7PHlv#}~7cGktqJv&>+io#?ebMEj zuOS^3sT5Zc<%3x+I&VR*O9dSh0plmIzJiv+D?KkdnAieZdF3POb0!^NY==2cR6kZE zkd76#{I>Kyj8nj|A__Qu#woyvUP#Ef=eDF(mqrNKaw{nPC2t$J~@37*0Vu{H}u^vLkbbiGM z*y9E0->|(j#BPM)X>;!wI?_qZIXCDsC3Fqjh&b9hlx;!cqfKO7#QF(a524$ni1c!+ zKR-GP%C@Z0-K0Jo+WN0H$s;bvFKxF5D-5D@hfJ$2F^drfw46rh+qD!2fzf# z2h=nC=~oWv;G99>;@gZm7^gP`bADL#@EdCcu_$IdoAn-)GlA;?u!eIGq5Ws&!*6^X z(hX&r^&Y?RZ*(lNNj;X;kKKW~a36QzS4Po1X{awd-QtR4!+NmC3^~dJpLDZ$$~rT3 zNW(Y-ya!|D;-VXk2Ra_lst2TjMUSv&rfh>bI}(ZCH;#eVq3ab+hh}7qG=N5 zx?wzkU1#xI7dZ9?s}d}%Nr8;oZ{?BT&2 z+XVM?nf8p^I1K;y*>x7Eqj>nuW$XlvH7Hw%@hC2Ji}TMBl_y|qIGTOzg%$S(V>mA7Tti~KgSFO94}~85B|!Ba@3e_EIL?c&iMq=(cdC4>8_UxfyNq|)Dh$X=HI!( zxIUzTTlu;)KPnvWWYNj%%*uz|H-WMZ%MxVFvI)Ih#{byIHd(Ymy~tiZ>%O38wl>La z?y6`Qu+@oEJp`qfm7Ya6zdRIoIymmZuCrKl@j5r?l4x<$BSLWCh1m(cjS zxMhisrW>t`WV9a4@qU(%fu@<;bt(96LYQwJ#VBhnGfzQOodd@HOC<0;C9io7qVgTe zc5xczXH1ftT!?yK+&YU_n-rB^ZtsiO)&>4nmm3|T-WRYQVri42(#-9Bt~yIXbC0;u zA?kf5CK(iBDL6#UOa8u`1CrtZoTo(w2NfKwd0L1CxRC$JIncr!V9r|s-7E`7>pQgD#g;y_?KeWa)^8 z^^;ljqv-^(6OQ<>(hGWq`EzR9+O!}qsmz~8>j*AwBs;)fp<)kYGts|%O!hTECQhX5?AEc(!c;G55AGz5KP8hT9HMDKM z*0L9tE~&rAMOU)w49_f`CUIR3*0-wvwt=}0HFJG`?YIE2Pl10l$F;A-DH|O4z^b?8 zF{z)c&(3r7Z`Ry+QNJDGSr;}wfVATI&W0;ayz7nT1H1-pSA5^&aHY+kE-7Bu%Xt0z zbL{nlbE?6-uxR)lPs5!4YWUp)yvP2%1?wG&@*xiFp>aKgB;Iz{^g9+fuMX7Ji=Xd1 zxP9kP&j(??2l~~AXxuJ%yv&FlWdI@Sa%L z_Nz#wkl;LaVd!b$cbe!*PPlTr_bSEC2GF0_!gUabj0nVsmey$!Fj>vZ^8$j-?l+Mh+!RTS(6i?zKttQ6>e`)iR&%?^|es-M*D=Vjt@YmLz>a`5cL}7 zDa(BUNE`I!M6D;U_hm^}LjOft^+xlhh50}S-?F<&18;P_(brP88Di0C=sJk&T4K5~ zm69WgJsQMSZ*)Fl<3lPX8s>%0zqMRL+J&(hEv3^W#aBLcCC_zSBt@UJ@@4V?$OQDU z!9K5E@PNNtvy4lNck*)N9FTKByc~e*L{Pwy1P<0Z5!8L)LjEV`fSdzz4#+tm5(i9| zmuRpbB4s9jnI{LpH)5>yCvw=nb3=dQmjl<;fN6PVC{Mr_2kO5aW;DlhGzRtG;DPUS z2G1H#b;9`v#tij!4#~Y__Kkwksq?ocXWF&aV(+( zEWF5U`}MUpC?x93aM0yVc_@Z9(h6;LOWRaQ;UMelRklgKb7*I>W~+g`$&@c65&4%v z99o}E6@xwCf&*MHR2udf`U7&lbHP!9wi@d8`nawPu20HACZ(l=IF=(u9DWy@%8`$v z;tFsO)@Nd_qwiQ8CK0+stye32pI@H|>ULR*%b@W$bp+!uV0;U%kq7ETW*sK>MYc@4 z1fDU|$S6kKaDennB<3zAK5$!Ck(5uz5-BJ*OhN zsRM(GUYm~_&pF)}YTZjQ$qYYysb8O1#$*KWozwc4ocPP{J=w*fh;HH}BR(Y`d~aBL z)k`}b?x7CXw_>i(1NN^E$YI`#^MmV+H9MYCuWO~SHZ*_j2>M7s_jBn}NaH?;xkEe3 zO_rJKsDX8y1zJCjZ5=Xh`NA?mz2Ue^i|JlgJ7?M}DbvEsrv6;N4)C(^`c~Z5z2b16 zPaGt?7I#y8q0B9#6Vb9E4VmDZ?_AfGwi)Pl zPHTv>%7bTdt*gzetw6gWZ5;UJJ3Bw%Z;@q!51ZOjYr*n^gTQN{v)d9Vb56w}ob(Ku z&%CY$#sTU#4r{2hm(x;wZ`wZmf&Ucfir%AvomPSLa)Zo4p0Vd4uHT?7QMmaH%y!1Z zmuaze&3V}fK5aF(x&uE_?ERMJFk_bqUi$5)FT;xmvhbXR18T1hc3!}`H~36opAq20 zg7^*lb7}r=uAQ|izH`8yxz=to{ki7qE=>&3Wl77IX{yVG^-Y_9kC&}R*T1E%WyhvA z&re}-gE(;7E2X9Q%__I8^SudPEE|E^tA)i6X`G>MmswwigD!8vLvf^O0UTPpO_dl9 zAofaZ#%B_>P00ElH4@>g5N$-)`Iv;IMDp*%(g3Y?NxNXbHb>9T%fJ&=_Y?VKQ=^4X`0>7 z1+K5wuy;WSScf-}xwbo8=bLrZ?`aSM*FJ}7Ibi_r0sA3^{kQnkXJDTyU0MKDO8l+k zTu#${t=M@7*CB;#7B=j`!PolF=(YIS^JD$4$3dL?Uh&Cuc3lkY8l-1He1Mzq`%Vc{ zZXlS`z#rd5U2_^@hu&j~g#kRbl#S4(`B9}$N4;)u&X<9_0z8WrmG2zhZ+5>D>EOWI zmy-+~yltu!_bK6t1G66i=G1Z4mqN66Fi#K0ts!sOWuDh>*cSsdh|-_o884S2-y7Qw zV7EE#3(3wC(QP$vxnnPn+i$_?2n>V$Q*QY&Z7QGpMsm<2jrqv`o&g-+bB!@iZ zjvH^9yU!a3>DW|BV8Ra$dY}&u_Y~&tt28fn&GA-{XN`R~-Zq8hLrQ{g63Vm5zMSm* zX=)>zmpk@%#rd0;J(0q`og8(F7%|~#tDE!X#K~(x@@g@gDxxiD2DijAuy3FsJZOou zZIJJc+ZXVefSvGaF1{gczRQv?(~SH!=A(%1gdbZiX&hRiO%*E!5Nn77IE!^(SYEiv z`ObAu$!2XeFTV16-UfUbvCBUfeJ55N+PqB_(H3yQ0AkH`UtV6a&fH5z`g_kyOFpGq zjv%%g>ULS%1HKvgk}4lXrK33<?-GR&{AIg({a{A>~&@{NO&b&=&>#caygRsbav~ zf?)u3co^){vg;Ia{Dye~ofIOs5uEXm6yMR<$!&Eb{?nlkF9~@^^BsN7!U41i`fq5f zg|ew=9*XN4v_0G3#8%4`-_iKBSz9e8Uk1%DNnJx6pgn5sdP!O~RZ=*xYy_wWoGZX@ zT&l&>wRXOv@oXhHw3si0=9i?dAr2L(6jvGZnPl3gN(ze>x7DeXoQm6aQrSC4Qalt# zzEZ~_l@blh6bJ2XDGy2eOl{4kN(y^%+G^-`$@wlEQX=BOw5cr-ml3YEvisIgp9%Cq zp*(ZzM=+m(G;*6NSu9I4C`4#0*pK2g3Q4l1%5#@=~}$9Nn-N zeNO$}=y0FnVmiDB_Zw?S)9hZqzX7}2L=}xGrNsLE)P&fJ+5zlQ>QoeBOli8GY@1+C z8?J<*Bb_A9y@$cRd~iQ$xlWLAnK5-JvT4xmP4|Xo-@{eN-*P#SO#v`o0{3neLdT0B z2gdfrzy18KmXro)+og>=umO$r3h)ttPVgdAB(o=ROY&D%%NEI}L*pl|Yq=aqTwH$P zj%tJA!cZLf$omXpmjmc`i_@2HqQk|3JBK{vzM^QN0ByS@eR;VYh=z}(-fnT9jze1( zldR;xK7-s>Y$`c2;mb=7cX^+oDdfyMzkE9B_1(qPmK$R#jO_fzeTHAS%djsm45q@o z1LXjWI{~?D^YLX^$JXe}OG{_8)jmU8@#W=mz{QYrx!s0+dC~1ZU$&x%4(B7Zrar@O z<;(Mhp%{FeR^TiC^yy#>R9bxb-O7+BQ^!^q%K<)} ziBoZiF`u~WT=geu(*nG}k*Grpr0dWE>Np>PbnSTmXlB|tf4=5pKp(->i9v0Zz9MM< zhK|LgR-*%;FE4;?*P;1SbZP$0;LS}A>hD8&=}!w>OpK-ycI8n|0P+*qB-!pW0DeNuA+4-GD8ELIV0{Exk^|_=H^?Z9 zPHyK0+`~j^?@e$=+X+GC0LBqysn5V?J8EekR}i0$g*#8|&{&Fm?H{Jir;pIU&lVdi zg+87jc9Ye{q%8-6;I17PJO=3bFzMso*k^z~0>5(50zN`CzxtDfIj1wsLt^uGfle}Y zU{Lw?<$+HJdEU~vqsx~iIS`~Bal)J1J+aC!pSFlai`acSk@bILJ@88&{OLOJfv)`c ziemP;nBNib+m2X#73M6890+2In6@48QldVcq~t(az9MKxCJx1J(!d=pD}u-`Qzr@N z%j*Ch{%Tj;+MA5umy{d`s6WxziTj$HuPBHuYB^tCS~4V9pMj&#C1bdw<)}dp>gQJQ z8J~r|ye#^3lF~gw`HBL>LeQAV>UHb!=_I2c1d#(-#ZVZ!es|oP>J@*!`wYVHTNd7d zwq2VRe*s=gQ4ZujoebfQmIWvWaeg603G^A{xXUo^XgL7a;_?->;{$A9UQKKdhca7R z8eK3Coc(BR^z%cnuyWAme7d-R9PPM(5N+BHa&!6-KInrMl?_FD$4DfH;OA{$Xh$+Cf8em>0$kFglNO|g#q5Q}K(}!wxybMUx z1NIX3gJoX^_{W{u)&cH<;LRg#BELGJo}ZxqDD%EzAf44Wf_@FNpCfa&0LEzis4O~p zwL{R}DFDYr9#{&b2T*zOEeEpgD{^j2P+Z85)Lkp&&g8FzHMYC+lMD^3OJg|=PlxTE9?`Uv3g;ztf3%`M|A3Lg)Vo*V>* z@$1V&+@Vj$^xaW-2;H7V%ttomKz#8K$>}iu^bxDQGjSIYD@n@;q>*1=QRlipN&NO! zFxv@vE6AVdI!rKqBmrMu6#q|t*3r){%7o#gaan~(!J?t+auH_=xD_8D5box}2( zwjIR8e+OT_30@M?2ZH*FEg=VTpH9TK&WG%<FEzS#7u-&?|299wR7CrI6)v(1!YipiT3GdlP*imjhAtY>U1RzeR*?8I7x;*9~!k z5V@b&I&=aPcRu3%g319LQ!GuUP~~zUlX5_p6z*s_k;{Qh;m`Ex#4+BF#+jdMd7q)h zWPvYVo*xXwkw)HUkdhn#U%n)TVk-{JB$0<)4kRN7(6$$+)yL~3ft$GVl=m6LBL~v* z>BNmcD?hofD3lz~VLuDDAr>E7`&ljre93`K`E-(!IeDLJbx&Po(jrAMJiYq zu652$`YnAr(NX7|Lo4zX<+yW(P%L+4Ru1I8yjbw%Hxp)`K?c56l>2o2A|{!1E#WHy z%&X-ZJ`O3Fn-xqye1iNwqXLB`sQoNIra&+JCr?7X7)8jBO_b{ReF)?WK;*K@Q% z8}EG`T5$bp;H};5ADrH(^`%Gbl;v-uqK+^7()*By-`9vUThm>>Q7R0to``ZJub!4P zKPk57>QA9d^w6v}3I{VmRvsB+P1jIX2xghhSnz|x} zK*S^MG58sW5L|dly47QI1WPqQuswAVOk_;W{j~%tLg`E;Z|gut2CdlqmpYD!sI(*6 z+F=RQj(rnrew^C2`o*e68zUB)tL=Y13>SX7#I^R@fPmZ!LzHVe-Hs(&RMeIFIe2;; zyf$|5M7*hXtc}{$fgYD1*XE~v-*fW(Uo;}3W8lTe_StCq>1xlD@l#R)uBb1hG5koc zhFeWGc~xZ?y(BJt(!GrL1Vkh1JIZO2x87)70YFRsr@PMokkjt1?yUzC<5UdgPEUf6P zOG>QeO68{~4jq}(&%x>Nd<=n_XM1#ZZv4At-dZk%P352MP17e=q#Y$--H(>;?~dKo z+u6q>_`gs02l?f8xx8tYf<=M1Qp89Mw&29Jj-6gzTauiswI{3OkDS;AGriYj;mOF| zc$0r0#ea+3jr;gExXi3P>xqRD>2Ao{^GArtInuRB#TzL(Gcz=C7yofN&D{{zEs(oU&t!JUu-R6=LhGtKv1Z-7Z(ecB=U^|N zAtIPRG5=~$P|*6SJIDGh$jM@eV?R83qOJ2D&6tDR_pCl*iGQPW(jf6tA5Em!hLoBi zWb4J2M|aLQb~)DXN$frXwX>Si{Tw7`;;dI{4rzD{iMTT<6f@%N-1rG<)di84{^x+F zwjXYMtNq_vdy4ibudE%zrDQQg0t!;Z|u94wLi}!(ib7d%yvq= znok=!HDU7T{HPQioin`<<6CP>BW=C&Os+>o<~=@oa2hg2>+6HadppO?KCYx!oo#*& zhj@=(+K;+5#G>Se<>iGDu3aZ;B5s>MFTIF6l>5iO^Bp&IGnj)RTaJ0G8Iqaa9q(C6 zG{%rA-ENg>dQ&vUcNvcqeD2qE(+KCz-Z9wi`JE80uWv4sI{0eh@;ib$*md0$>)}f+ z?RLsz01|W~>_0!ptGBy3q*$GwkaUN7qBqv<@~x#GOkP_UW+-|rRKhOmV15EiO5+C!PMSniretW#fRIis7WO6BJB2SFjJL}L{bp>hsCY{z!!s8=42mou$kf%1*DsU zi;DUL^Mb*cQq0&&kWasK1N@u;_Y(7K3ue8m&{=EaeRa%oERsQU>fQr~-HVS6(I2>b z<$PLC$6ko#dxLrA;dtyi?Dlj>g3@@a<3!R#DwMiap4COSI;gJ1P#H%lciysZcX9(EW-NJmDdju>VQGezo&v5TaaV<1?j=yt(4+;C%D6njp|7NhKA2^B z-(U1qie`9Q8~sNNPwTrC)NWH%=z@6H;_+2R2SYt~@2whL zbo9%kz$0V?mq1KPNDf-;^%dhc*yBEN1Qtgo+F*yQ%KLu2cy9j+EQauA2V(Tv^J|~D z+FAqrJS^5kxh%2|)&IxP(m4aq!{fpOanh-Cpp05?X$9lTf0}cGyeKN z#KQIjX6wZRdk~|;Crh%b>&o-vUuPvK;Vo=4r+QD>f0#V%(b*yXiBCe;27R{1sJKn^ zCHl|Wa|Xjupbrbtct6Az+c$WOLHk`1l|g19eS6fH`VzYgIgP<&;C!E1-tU#BXm-W3 z#%lf^kM^XCovbVfQOXQ^tqk4qHAGS{IpAEJf%BHI_H%VHDl^_ZdogD4gf+YVtZ*JQ zt}n1Q*0^D2o)J1prz84P4*eVXPiO3Kymd&~(Sj?(+Nnf+o`bhMpfrGF(CICe97Z*# z*z7po9iuXWsD%A+MiaZ;bfoz_0&&f&&r=Q{4_|FR-Y(FE@TWO(tmT+-6Tc8DtC|R_2i-5z8gsAzK`i9%LeDJpO)Z1YP#3Rk-+y)lkC zYkxLep}?WEAbHgNZKk(C;iDh)RnqfgDBi>+WqaShH!flYZqLpiS!Y~dJN%IzopgUdkRxf-pHZgs{vZxOFF=yMw#{$JM>hpK4M-DydYGs`}oz^)y&CJvJ zAgC4fUEl8bQ72s=R6oc;(qF3;c)0JqG%bIWPN!GpD(BZ=Nu3oB?knrOUpXQwhM{;B zx1sW*`yK7SzN@XL(G&h#^}L@y0sGwY=FptEmR_V;haKyz9{=yHXGYTVK1t83hur;i zYZ%QUWx2)4*S)NN9E#ld=jC?~mutkW=yv7Qq=_CCYaL#xJ=tDqwJ{sJ$&b-Jx?704 z?gxrhLg5v{h5<-s&5hpK(FS+UUi@Yk4^ecF>{RpSm)HePSf;f_OjUJxUA=~PaC#qR6qIOsWHy$OfO_u+3%lc zXqxwa9KK_3D$Uhv{fR$b%nXh<_|V&{{XzwrRf2nH=!li6X{nJ{w#Ql3y(Yyh{o3C! zICx&>S0WC$KS$2(Est5#p*Lpi^b5w>2N#^p${Jy_q%uzX%kGo$2JUmu&vzRW(K93V zJ455gr(5P9@6KJ*8#x#}#;NM1wWB8Pa7Jhpc1@R8PhH+0S~G9qp2@wFtDIM6Pey(e zetg$OcTmM>>a4*vA8kC}+`qW8-ICIzk@s_7?oX?+S^LD%dU?t;Z9}&z-;CwDC-)ad zjjGx@*wGD=GHLvgxq;smhbhkNRoj2&PJ;1a%#+Rs?y7Zb-(kV3JI6)^PF%a>a6*vt zz1Nve9kgRgQ~sy#^ZfkHSqe$(Gc>yQcZ(b19_wy?S;K?cAxj?*&|~NW(DEG1B1QN|Lhvl_mjr>h5h$jzmTjn=bBsXiuTJ5 z(kAb&ij1_z3a67xX+6I?jEa*l(sxPbzOhc5v!-V_G=>$-(FAL z6`nbc_~=kdJ}^mxWJ$?+L>R3ZnfOMLFxf0UvRPQfoQy_I_E zdiKpnb_&k_`=4ol#I5#yuhlxZ^9pB7TwWQU9P_n{r$^tIaw^@AkQ*P_KX>jft(b$`LVXe#OK=(3H~&>tuORqczmN0Uj!YmMRs%4Fwd=A@clW&?_4JNw*FBZ7&PGd~ zeSP_d&5OrX4`biY+4Jo5#%ceNai7cPm*%%KdNkuk?g(?0=@yG#sl|7Gb>?Q@>>k9o zQx6sos;m9@&S>oaQdIrb7bMqCZ)U4u4q62TR6Z&{qICaLnzB_DA;Qq}TXsrqA>*CKn|qaJ zo`zREqE`1L4fa2MXa3rNis@w^I!^r-kg;Q^ZYkoq?%Rz{hW^!LT%-+$A8RI%cT&UBD`=Tdr`q?vxt(z%+~w?DyVBV@pJ$m7iFK1^fdV1t-nLhc8)$%Y8O}sDB%f39f>}_Pt-|LcUmMRVUOh0U@efiOu z*V)Dy8nLTNg2+C^!i^Od-|M*$ZRf<;4VX<|67eG5YSYF1!{44(ZQEuNdPCo$LO<2f zb;68mc3U#WY{+}jFSNqOV$Vcm`|t~EL$CW{EDEairVsa-;}CZEqy6r0gQw27>X94$ zjnQd@#)OrZdX1w8+s_TwT9iIo>BqAL3ZHTx9}4r@7{52Y=Bg9IxZXwc&3cmcjBmw{ zz0EAvI1Sm*L#agJ!x1I5(b~@^PVyb;S?km7ll8o)(ZD1Zhk1U;n-=2iv(rFl#3ke{ zm8#^uA~iT4H++^dNiiizmc1v9p@GvQC0q!A~>Zf)PY zFB?$VcduH{s3#AX=#DoJJ+B@`+!KFQEbt%>MI`|fT&L4^arPtby?=(%BQnB;is=Emv)_Z-Ke<9>l#0u}#mnlxb z%97#?hF#YxaHiOty^-7zx&(IvV`p{PmsI1k5V!Q>q>|b1F<>xaCzol*MOSh=De|+?4=P<&%_)f3R84OZd z9CG@Gd(H8>XWhJmKVk2!ul-PXHq!T;`tB9+1nh!o$|*61&Zkr}ykg=;dA%;U)3apR zWZz!F^L0=6d-deUe?#->hR!P&ZA|D%4RKwtid2&sYgDGJxzqXEn`5sQeJ$N&zxalM zLI+KfPU+PD?NIuDyC!kNHoLuQxBrM z?H$U%Ozl6;YJJ_fHmC6KwbOd9QKF7e`ktp^w!p_6uQuoY#~*>coCPZjsJ zvx;eVYRaP#+jJI8zPXP;DJ5>hJkVaPa({hapNS7?eVpGCu$9G52D zimN8&P&#($!}!g_EB~B{?9k<)nc{yq@;PUt;J#P7jz5I_GbAMS;e5i@*jaz|e$pwc zCPV4U=;c|15uf2-Eoay)UqXL)?U;vMDd>*BFu7%8pf}&1et@9TiQFgmuLB0{E!XTIv)%c&&zEsP3`cqW;b;3+Pe*3MR zp5oxYdz6!6X7|a^Fz_w^+i*YPOSn%PMw;GZ$?^)rdHa>On!H{ZvhDH2P4jn;&8RC* zpA=vaHh=Y+^CQyY-{|;VBv|*PooLk?H1{KDsbs~Fd)Qwi2`~Ku5?~x}n z%@lW*eH*jv(dSp=)~t=Wupdud^JCH7^&P(;DPB$yL;muZFd@jJ(#6?!L()AvO1DW< zAD%E8O^?BAW8eI%I=f%emlsL1&RhpUim9&Gc2#Dno8F4LW0W^{MD3pLN*mq|7`moz zUvAc$8$HO~hV^@P=)bW&2N-Lgw^ACqch_D=weD9pY2C1nUH)zQf=5QUk8Ap+YN^(~ zNx!2(uzL|Uz~dlh?4xdLBMQ{kFSfNHS_fDsgzI#)R_|4Dd+;~q>1O_sYu`AocV#?U zm!UAg_*SR$12cO(E8Ssq@lXHLe-=@x%QaP2UiMHbuPptds`Plu#pr$$drXS{{-yhu zF|&jJm)B2C-<=fR*?$K1&EXHPPoWzhtqf6R z45G~W>)@8e&8L?AFD+N2dX3ljGnR-~KP9~Y0%c3}{qVld9j$JrIjFuVR6Y23!spx$ znqEg2UOHfsXW}+NIk3=XTfqKsfut$}~YoX;Er$NMElHDN&Q=4;k=$&Gi|;DCo?x^*GUWbwbT6 zv${*8mn9z#@$P4+KlFXc$q#?q5PRmPWzML4yJt@9 zz9OQjm%q$;et6&{19~Q7V8!k7F-B zBF^&i;1TbCYz+QRo_VfAq>g!j-m2Yiwx}N5ueT%2%yz&7k8CTt+|9+6R?LDzT38XM^9rZ2y+op)9Ol`zxqDI~Z z=heX_vo5()T-q16!*s#+QXBJ7nKEtqCHHVUOKk;=I(DGeSJN~TC93&}iTLZK&fB)B z;2@|IeI)*_?k-JW$KPBT zcq|7Qe_`_mMyB%a6~G+cUhvQKdDvdG(ABPRbb) zhDvZSP<`8ea)EJ-mD%3xveHM1DGHQ1_!#$H2cJ&0(Zs1)y`TKGI0)%%)Xz*&H^Q>t z;tr9Ich5b2xP);m;akBOq%)C>f7`EXWX>S(t=LVbTi(n=+N&pLdk0V09p{F{82_M7 zC19ykyTY-3yZmWRUU)auUdV7V&vAN{-~%g6>eY{ysS$e`oMv3!$oL#^|b2#AOnj|)E?y!bkZs@Mfgnpkj8 z7iSSzcJ%#~*x}TvDxgc^uwlkvn+c#6vn~W8ovUFF|G4Hf@7SJy)n*Qcz6(ZiZtT%zm2s7w`quhOx zmTgY#dOYGVFslb%==sz1f86oPx7mAUqB9Yh{&&!EAL_yrDIkvv=E$^rDXEbIiC^~M zf}LF)Mu_kX65~N?RsVO8(VEN?ygf zd)6`~-*B@#2?+3}XZC}Zx5txbZEgRBCGa&_<{JIf=#(B$|HWcrQtwCZn^>(zJn1^Z zX~oj5$P--`+fe1LnWF;zlR}g6s}Cw*3xcDj{(Gj2N=i*ox=*Dw0eS4(<<8I`!p>OV zlTofWza5>wU2j*GCI(L(8$Z&&>qom8xQ{Ci(Kq%XAf-F2W)P+%k@9u?<19Rg4}-U* z3|7Dr4!ua|v6b;*bkzcv{N;+z%Lc@*j@y^P8d9S?qEMpDis4NDr~aPUalF7 WZWFiWO#}mI$b@lIEH7G6w*DWe&UJwR literal 0 HcmV?d00001 diff --git a/frontend/src/assets/slide.ico b/frontend/src/assets/slide.ico new file mode 100644 index 0000000000000000000000000000000000000000..888c7a853a451ee93e11e9643d2fca648856b62f GIT binary patch literal 8348 zcmeI0K?;K~5Ji7W58x486_3$vyXYwt58583w95+O+GZ3bg364f3i1OQjej!nWujz& z1CEGexH?7>0cQY_tDE~1aCg^}#rTk-@hUC?9*X7dD+K*-C-qL|Ohr&yhEIyLzAt++20JZX6yBoc^eHD?K#aL(KnhhEw(-^Z(!ZpR#wkcX#im z{Qp$#OI_?z?BS!Wbpk0D^c8|%bpov)G0 j-f$VKe|*0`jnjBG5+DH*AOR8}0TLhq5+DH*2uI)rIKZ~m literal 0 HcmV?d00001 diff --git a/frontend/src/custom.d.ts b/frontend/src/custom.d.ts index a45576f..36c6844 100644 --- a/frontend/src/custom.d.ts +++ b/frontend/src/custom.d.ts @@ -20,3 +20,8 @@ declare module "*.svg" { const content: any; export default content; } + +declare module "*ico" { + const content: any; + export default content; +}; diff --git a/frontend/src/pages/Editor/index.tsx b/frontend/src/pages/Editor/index.tsx index 67b9485..8554c7e 100644 --- a/frontend/src/pages/Editor/index.tsx +++ b/frontend/src/pages/Editor/index.tsx @@ -20,6 +20,7 @@ import React from "react"; import { useSearchParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { DocumentEditor } from "@onlyoffice/document-editor-react"; +import { Helmet } from "react-helmet"; import { OnlyofficeButton } from "@components/button"; import { OnlyofficeError } from "@components/error"; @@ -27,6 +28,8 @@ import { OnlyofficeSpinner } from "@components/spinner"; import { useBuildConfig } from "@hooks/useBuildConfig"; +import { getFileFavicon } from "@utils/file"; + import Icon from "@assets/nofile.svg"; const onEditor = () => { @@ -57,6 +60,15 @@ export const OnlyofficeEditorPage: React.FC = () => { const validConfig = !error && !isLoading && data; return (
+ {!error && (
- ONLYOFFICE diff --git a/frontend/src/utils/file.ts b/frontend/src/utils/file.ts index 33ebad9..b3be61b 100644 --- a/frontend/src/utils/file.ts +++ b/frontend/src/utils/file.ts @@ -22,6 +22,11 @@ import Xlsx from "@assets/xlsx.svg"; import Unsupported from "@assets/unsupported.svg"; import Supported from "@assets/supported.svg"; +import wordIcon from "@assets/word.ico"; +import slideIcon from "@assets/slide.ico"; +import cellIcon from "@assets/cell.ico"; +import genericIcon from "@assets/generic.ico"; + const DOCUMENT_EXTS = [ "doc", "docx", @@ -167,3 +172,12 @@ export const formatBytes = (bytes: number, decimals = 2) => { return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`; }; + +export const getFileFavicon = (filename: string) => { + const e = getFileExt(filename).toLowerCase(); + if (DOCUMENT_EXTS.includes(e)) return wordIcon; + if (PRESENTATION_EXTS.includes(e)) return slideIcon; + if (SPREADSHEET_EXTS.includes(e)) return cellIcon; + + return genericIcon; +}; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 150b932..3df57d5 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1908,6 +1908,13 @@ dependencies: "@types/react" "*" +"@types/react-helmet@^6.1.6": + version "6.1.6" + resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.1.6.tgz#7d1afd8cbf099616894e8240e9ef70e3c6d7506d" + integrity sha512-ZKcoOdW/Tg+kiUbkFCBtvDw0k3nD4HJ/h/B9yWxN4uDO8OkRksWTO+EL+z/Qu3aHTeTll3Ro0Cc/8UhwBCMG5A== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@^18.0.21": version "18.0.27" resolved "https://registry.npmjs.org/@types/react/-/react-18.0.27.tgz" @@ -6245,7 +6252,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.5.0, prop-types@^15.8.1: +prop-types@^15.5.0, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -6343,6 +6350,21 @@ react-dropzone@^14.2.3: file-selector "^0.6.0" prop-types "^15.8.1" +react-fast-compare@^3.1.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.1.tgz#53933d9e14f364281d6cba24bfed7a4afb808b5f" + integrity sha512-xTYf9zFim2pEif/Fw16dBiXpe0hoy5PxcD8+OwBnTtNLfIm3g6WxhKNurY+6OmdH1u6Ta/W/Vl6vjbYP1MFnDg== + +react-helmet@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-6.1.0.tgz#a750d5165cb13cf213e44747502652e794468726" + integrity sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw== + dependencies: + object-assign "^4.1.1" + prop-types "^15.7.2" + react-fast-compare "^3.1.1" + react-side-effect "^2.1.0" + react-i18next@^12.1.5: version "12.1.5" resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-12.1.5.tgz#b65f5733dd2f96188a9359c009b7dbe27443f009" @@ -6390,6 +6412,11 @@ react-router@6.8.1: dependencies: "@remix-run/router" "1.3.2" +react-side-effect@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a" + integrity sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw== + react-tabs@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/react-tabs/-/react-tabs-6.0.0.tgz#328fd61ca534e0517d24f4927e37e99b29461880" From af7c0723b1ddca4b5c54a54b48f07ab576c0c850 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 6 Apr 2023 13:22:39 +0500 Subject: [PATCH 084/145] doc(license): helmet license --- frontend/3rd-Party.license | 4 ++++ frontend/licenses/helmet.license | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 frontend/licenses/helmet.license diff --git a/frontend/3rd-Party.license b/frontend/3rd-Party.license index 1fb379b..8b96fa1 100644 --- a/frontend/3rd-Party.license +++ b/frontend/3rd-Party.license @@ -203,3 +203,7 @@ License File: webpack-dev-server.license webpack-merge - Merge designed for webpack. (https://github.com/survivejs/webpack-merge/blob/develop/LICENSE) License: MIT License File: webpack-merge.license + +react-helmet - A document head manager for React. (https://github.com/nfl/react-helmet/blob/master/LICENSE) +License: MIT +License File: helmet.license diff --git a/frontend/licenses/helmet.license b/frontend/licenses/helmet.license new file mode 100644 index 0000000..423f8ac --- /dev/null +++ b/frontend/licenses/helmet.license @@ -0,0 +1,19 @@ +Copyright (c) 2015 NFL + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From bcc1f6ba3511e8d55cc8333be791fc24bedccadd Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 6 Apr 2023 13:33:23 +0500 Subject: [PATCH 085/145] fix(settings): input padding --- frontend/src/pages/Settings/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx index 6f07b7c..24cfd96 100644 --- a/frontend/src/pages/Settings/index.tsx +++ b/frontend/src/pages/Settings/index.tsx @@ -183,7 +183,7 @@ export const SettingsPage: React.FC = () => { type="password" />
-
+
Date: Thu, 6 Apr 2023 13:38:30 +0500 Subject: [PATCH 086/145] fix(settings): remove reload buttons --- frontend/src/pages/Settings/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx index 24cfd96..d5609c4 100644 --- a/frontend/src/pages/Settings/index.tsx +++ b/frontend/src/pages/Settings/index.tsx @@ -127,7 +127,6 @@ export const SettingsPage: React.FC = () => { "background.error.subtitle", "Could not fetch plugin settings. Something went wrong. Please reload the pipedrive window" )} - button={t("button.reload", "Reload") || "Reload"} onClick={() => window.location.reload()} /> )} @@ -139,7 +138,6 @@ export const SettingsPage: React.FC = () => { "background.access.subtitle", "Something went wrong or access denied" )} - button={t("button.reload", "Reload") || "Reload"} onClick={() => window.location.reload()} /> )} From 95bcd193721817a77b40431e4aee5ace900b6b83 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 6 Apr 2023 13:41:02 +0500 Subject: [PATCH 087/145] fix(editor): close creation page on editor --- frontend/src/pages/Creation/Creation.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx index 3c7bbb0..1a5f49c 100644 --- a/frontend/src/pages/Creation/Creation.tsx +++ b/frontend/src/pages/Creation/Creation.tsx @@ -157,6 +157,7 @@ export const Creation: React.FC = () => { fres.data.data.id + fres.data.data.update_time )}` ); + await sdk?.execute(Command.CLOSE_MODAL); } catch { await sdk?.execute(Command.SHOW_SNACKBAR, { message: t("creation.error", "Could not create a new file"), From c1501e57698d88087e1c304ea04e3f12ffb2d785 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 6 Apr 2023 13:53:58 +0500 Subject: [PATCH 088/145] fix(upload): show status snackbar --- .../src/assets/locales/en-US/translation.json | 2 ++ .../src/assets/locales/en/translation.json | 2 ++ .../src/assets/locales/ru-RU/translation.json | 2 ++ .../src/assets/locales/ru/translation.json | 2 ++ frontend/src/pages/Creation/Upload.tsx | 23 ++++++++++++++++++- 5 files changed, 30 insertions(+), 1 deletion(-) diff --git a/frontend/src/assets/locales/en-US/translation.json b/frontend/src/assets/locales/en-US/translation.json index 6bb6a02..a8d8b54 100644 --- a/frontend/src/assets/locales/en-US/translation.json +++ b/frontend/src/assets/locales/en-US/translation.json @@ -9,6 +9,8 @@ "files.info.size": "Size", "snackbar.fileremoved.ok": "File {{file}} has been removed", "snackbar.fileremoved.error": "Could not remove file {{file}}", + "snackbar.uploaded.ok": "File {{file}} has been uploaded", + "snackbar.uploaded.error": "Could not upload file {{file}}", "creation.title": "Create with ONLYOFFICE", "creation.inputs.title": "Title", "creation.inputs.error": "File name length should be less than 190 characters", diff --git a/frontend/src/assets/locales/en/translation.json b/frontend/src/assets/locales/en/translation.json index 6bb6a02..a8d8b54 100644 --- a/frontend/src/assets/locales/en/translation.json +++ b/frontend/src/assets/locales/en/translation.json @@ -9,6 +9,8 @@ "files.info.size": "Size", "snackbar.fileremoved.ok": "File {{file}} has been removed", "snackbar.fileremoved.error": "Could not remove file {{file}}", + "snackbar.uploaded.ok": "File {{file}} has been uploaded", + "snackbar.uploaded.error": "Could not upload file {{file}}", "creation.title": "Create with ONLYOFFICE", "creation.inputs.title": "Title", "creation.inputs.error": "File name length should be less than 190 characters", diff --git a/frontend/src/assets/locales/ru-RU/translation.json b/frontend/src/assets/locales/ru-RU/translation.json index 6bb6a02..a8d8b54 100644 --- a/frontend/src/assets/locales/ru-RU/translation.json +++ b/frontend/src/assets/locales/ru-RU/translation.json @@ -9,6 +9,8 @@ "files.info.size": "Size", "snackbar.fileremoved.ok": "File {{file}} has been removed", "snackbar.fileremoved.error": "Could not remove file {{file}}", + "snackbar.uploaded.ok": "File {{file}} has been uploaded", + "snackbar.uploaded.error": "Could not upload file {{file}}", "creation.title": "Create with ONLYOFFICE", "creation.inputs.title": "Title", "creation.inputs.error": "File name length should be less than 190 characters", diff --git a/frontend/src/assets/locales/ru/translation.json b/frontend/src/assets/locales/ru/translation.json index 6bb6a02..a8d8b54 100644 --- a/frontend/src/assets/locales/ru/translation.json +++ b/frontend/src/assets/locales/ru/translation.json @@ -9,6 +9,8 @@ "files.info.size": "Size", "snackbar.fileremoved.ok": "File {{file}} has been removed", "snackbar.fileremoved.error": "Could not remove file {{file}}", + "snackbar.uploaded.ok": "File {{file}} has been uploaded", + "snackbar.uploaded.error": "Could not upload file {{file}}", "creation.title": "Create with ONLYOFFICE", "creation.inputs.title": "Title", "creation.inputs.error": "File name length should be less than 190 characters", diff --git a/frontend/src/pages/Creation/Upload.tsx b/frontend/src/pages/Creation/Upload.tsx index ad0a181..fde3ae7 100644 --- a/frontend/src/pages/Creation/Upload.tsx +++ b/frontend/src/pages/Creation/Upload.tsx @@ -75,7 +75,28 @@ export const Upload: React.FC = () => { t("upload.subtext", "File size is limited") || "File size is limited" } - onDrop={onDrop} + onDrop={async (files, rejections, event) => { + try { + onDrop(files, rejections, event); + await sdk?.execute(Command.SHOW_SNACKBAR, { + message: t( + "snackbar.uploaded.ok", + "File {{file}} has been uploaded", + { file: files[0].name } + ), + }); + return Promise.resolve(); + } catch { + await sdk?.execute(Command.SHOW_SNACKBAR, { + message: t( + "snackbar.uploaded.error", + "Could not upload file {{file}}", + { file: files[0].name } + ), + }); + return Promise.reject(); + } + }} />
From 3905f7dd63c484f9841b8896644877d707c007a3 Mon Sep 17 00:00:00 2001 From: Sergey Linnik Date: Fri, 7 Apr 2023 18:53:37 +0500 Subject: [PATCH 089/145] update submodule --- backend/services/gateway/assets/assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/services/gateway/assets/assets b/backend/services/gateway/assets/assets index 4b96e28..cb2c06e 160000 --- a/backend/services/gateway/assets/assets +++ b/backend/services/gateway/assets/assets @@ -1 +1 @@ -Subproject commit 4b96e283924e0481299b6400520829649634c23e +Subproject commit cb2c06ed33655b0dd09a47b8852f7391edad18ac From c22925ce34b2d25dd40fdcce7b691cf29f6ea06a Mon Sep 17 00:00:00 2001 From: Maria-Sukhova Date: Wed, 26 Apr 2023 14:44:57 +0300 Subject: [PATCH 090/145] added translations de, es, fr, it, ja, pt-BR, ru --- .../src/assets/locales/de/translation.json | 50 ++++++++++ .../src/assets/locales/en-US/translation.json | 2 +- .../src/assets/locales/en/translation.json | 2 +- .../src/assets/locales/es/translation.json | 50 ++++++++++ .../src/assets/locales/fr/translation.json | 50 ++++++++++ .../src/assets/locales/it/translation.json | 50 ++++++++++ .../src/assets/locales/ja/translation.json | 50 ++++++++++ .../src/assets/locales/pt-BR/translation.json | 50 ++++++++++ .../src/assets/locales/ru-RU/translation.json | 94 +++++++++---------- .../src/assets/locales/ru/translation.json | 94 +++++++++---------- 10 files changed, 396 insertions(+), 96 deletions(-) create mode 100644 frontend/src/assets/locales/de/translation.json create mode 100644 frontend/src/assets/locales/es/translation.json create mode 100644 frontend/src/assets/locales/fr/translation.json create mode 100644 frontend/src/assets/locales/it/translation.json create mode 100644 frontend/src/assets/locales/ja/translation.json create mode 100644 frontend/src/assets/locales/pt-BR/translation.json diff --git a/frontend/src/assets/locales/de/translation.json b/frontend/src/assets/locales/de/translation.json new file mode 100644 index 0000000..90e5553 --- /dev/null +++ b/frontend/src/assets/locales/de/translation.json @@ -0,0 +1,50 @@ +{ + "banner.title": "ONLYOFFICE Docs Cloud", + "banner.subtitle": "Einfaches Starten der Editoren in der Cloud ohne Herunterladen und Installation", + "files.error.nofiles": "Pipedrive-Dokumente konnten nicht gefunden werden", + "files.info.workspace": "Arbeitsbereich", + "files.info.type": "Typ", + "files.info.modified": "Datum der Änderung", + "files.info.creation": "Erstellungsdatum", + "files.info.size": "Größe", + "snackbar.fileremoved.ok": "Die Datei {{file}} wurde entfernt", + "snackbar.fileremoved.error": "Die Datei {{file}} konnte nicht entfernt werden", + "snackbar.uploaded.ok": "Die Datei {{file}} wurde hochgeladen", + "snackbar.uploaded.error": "Die Datei {{file}} konnte nicht hochgeladen werden", + "creation.title": "Mit ONLYOFFICE erstellen", + "creation.inputs.title": "Titel", + "creation.inputs.error": "Die Länge des Dateinamens sollte weniger als 190 Zeichen betragen", + "creation.tiles.doc": "Dokument", + "creation.tiles.spreadsheet": "Tabelle", + "creation.tiles.presentation": "Präsentation", + "creation.error": "Es konnte keine neue Datei erstellt werden", + "upload.error": "Ihre Datei konnte nicht hochgeladen werden. Bitte wenden Sie sich an den ONLYOFFICE-Support.", + "upload.uploading": "Hochladevorgang...", + "upload.select": "Datei auswählen", + "upload.dragdrop": "oder hier ablegen", + "upload.subtext": "Dateigröße ist beschränkt", + "settings.title": "Einstellungen der ONLYOFFICE-App konfigurieren", + "settings.text": "Das Plugin, das es den Nutzern ermöglicht, Office-Dokumente von Pipedrive aus mit ONLYOFFICE Document Server zu bearbeiten, erlaubt es mehreren Nutzern, in Echtzeit zusammenzuarbeiten und diese Änderungen in Pipedrive zu speichern", + "settings.inputs.address": "Document Server-Adresse", + "settings.inputs.secret": "Document Server-Geheimnisschlüssel", + "settings.inputs.header": "Document Server-Header", + "settings.saving.ok": "ONLYOFFICE-Einstellungen wurden gespeichert", + "settings.saving.error": "ONLYOFFICE-Einstellungen konnten nicht gespeichert werden", + "editor.error": "Die Datei konnte nicht geöffnet werden. Etwas ist schief gelaufen", + "background.error.title": "Fehler", + "background.error.subtitle": "Plugin-Einstellungen konnten nicht abgerufen werden. Etwas ist schief gelaufen. Bitte laden Sie das Pipedrive-Fenster neu", + "background.access.title": "Zugriff verweigert", + "background.access.subtitle": "Etwas ist schief gelaufen oder der Zugriff wurde verweigert", + "background.reinstall.title": "Das Sicherheitstoken für das Dokument ist abgelaufen", + "background.reinstall.subtitle": "Es ist etwas schief gelaufen. Bitte öffnen oder installieren Sie die App erneut.", + "document.new": "Neues Dokument", + "button.upload": "Dokument erstellen oder hochladen", + "button.reload": "Neu laden", + "button.save": "Speichern", + "button.cancel": "Abbrechen", + "button.close": "Schließen", + "button.create": "Dokument erstellen", + "button.creation.create": "Erstellen", + "button.creation.upload": "Hochladen", + "button.getnow": "Jetzt erhalten" +} diff --git a/frontend/src/assets/locales/en-US/translation.json b/frontend/src/assets/locales/en-US/translation.json index a8d8b54..95cfd28 100644 --- a/frontend/src/assets/locales/en-US/translation.json +++ b/frontend/src/assets/locales/en-US/translation.json @@ -32,7 +32,7 @@ "settings.saving.error": "Could not save ONLYOFFICE settings", "editor.error": "Could not open the file. Something went wrong", "background.error.title": "Error", - "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the pipedrive window", + "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the Pipedrive window", "background.access.title": "Access denied", "background.access.subtitle": "Something went wrong or access denied", "background.reinstall.title": "The document security token has expired", diff --git a/frontend/src/assets/locales/en/translation.json b/frontend/src/assets/locales/en/translation.json index a8d8b54..95cfd28 100644 --- a/frontend/src/assets/locales/en/translation.json +++ b/frontend/src/assets/locales/en/translation.json @@ -32,7 +32,7 @@ "settings.saving.error": "Could not save ONLYOFFICE settings", "editor.error": "Could not open the file. Something went wrong", "background.error.title": "Error", - "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the pipedrive window", + "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the Pipedrive window", "background.access.title": "Access denied", "background.access.subtitle": "Something went wrong or access denied", "background.reinstall.title": "The document security token has expired", diff --git a/frontend/src/assets/locales/es/translation.json b/frontend/src/assets/locales/es/translation.json new file mode 100644 index 0000000..242c17b --- /dev/null +++ b/frontend/src/assets/locales/es/translation.json @@ -0,0 +1,50 @@ +{ + "banner.title": "ONLYOFFICE Docs Cloud", + "banner.subtitle": "Inicie fácilmente los editores en la nube sin descargarlos ni instalarlos", + "files.error.nofiles": "No se han podido encontrar los documentos de Pipedrive", + "files.info.workspace": "Área de trabajo", + "files.info.type": "Tipo", + "files.info.modified": "Fecha de modificación", + "files.info.creation": "Fecha de creación", + "files.info.size": "Tamaño", + "snackbar.fileremoved.ok": "El archivo {{file}} se ha eliminado", + "snackbar.fileremoved.error": "No se ha podido eliminar el archivo {{file}}", + "snackbar.uploaded.ok": "El archivo {{file}} se ha cargado", + "snackbar.uploaded.error": "No se ha podido cargar el archivo {{file}}", + "creation.title": "Crear con ONLYOFFICE", + "creation.inputs.title": "Nombre", + "creation.inputs.error": "El nombre del archivo debe tener menos de 190 caracteres", + "creation.tiles.doc": "Documento", + "creation.tiles.spreadsheet": "Hoja de cálculo", + "creation.tiles.presentation": "Presentación", + "creation.error": "No se ha podido crear un nuevo archivo", + "upload.error": "No se ha podido cargar su archivo. Por favor, póngase en contacto con el soporte de ONLYOFFICE.", + "upload.uploading": "Cargando....", + "upload.select": "Seleccione un archivo", + "upload.dragdrop": "o arrastre y coloque aquí", + "upload.subtext": "El tamaño del archivo está limitado", + "settings.title": "Configurar los ajustes de la aplicación ONLYOFFICE", + "settings.text": "El plugin que permite a los usuarios editar documentos ofimáticos desde Pipedrive usando el Servidor de Documentos ONLYOFFICE y permite a múltiples usuarios colaborar en tiempo real y guardar los cambios en Pipedrive.", + "settings.inputs.address": "Dirección del Servidor de documentos", + "settings.inputs.secret": "Secreto del Servidor de documentos", + "settings.inputs.header": "Encabezado del Servidor de documentos", + "settings.saving.ok": "Se han guardado los ajustes de ONLYOFFICE", + "settings.saving.error": "No se han podido guardar los ajustes de ONLYOFFICE", + "editor.error": "No se ha podido abrir el archivo. Se ha producido un error", + "background.error.title": "Error", + "background.error.subtitle": "No se han podido recuperar los ajustes del plugin. Se ha producido un error. Por favor, vuelva a cargar la ventana de Pipedrive", + "background.access.title": "Se ha denegado el acceso", + "background.access.subtitle": "Se ha producido un error o se ha denegado el acceso", + "background.reinstall.title": "El token de seguridad del documento ha expirado", + "background.reinstall.subtitle": "Se ha producido un error. Por favor, vuelva a cargar o reinstale la aplicación.", + "document.new": "Nuevo documento", + "button.upload": "Crear o cargar documento", + "button.reload": "Recargar", + "button.save": "Guardar", + "button.cancel": "Cancelar", + "button.close": "Cerrar", + "button.create": "Crear documento", + "button.creation.create": "Crear", + "button.creation.upload": "Cargar", + "button.getnow": "Obtener ahora" +} diff --git a/frontend/src/assets/locales/fr/translation.json b/frontend/src/assets/locales/fr/translation.json new file mode 100644 index 0000000..78a9557 --- /dev/null +++ b/frontend/src/assets/locales/fr/translation.json @@ -0,0 +1,50 @@ +{ + "banner.title": "ONLYOFFICE Docs Cloud", + "banner.subtitle": " Lancez facilement les éditeurs dans le cloud sans téléchargement ni installation", + "files.error.nofiles": "Impossible de trouver les documents Pipedrive", + "files.info.workspace": "Espace de travail", + "files.info.type": "Type", + "files.info.modified": "Date de modification", + "files.info.creation": "Date de création", + "files.info.size": "Taille", + "snackbar.fileremoved.ok": "Le fichier {{file}} a été supprimé", + "snackbar.fileremoved.error": "Impossible de supprimer le fichier {{file}}", + "snackbar.uploaded.ok": "Le fichier {{file}} a été chargé", + "snackbar.uploaded.error": "Impossible de charger le fichier {{file}}", + "creation.title": "Créer avec ONLYOFFICE", + "creation.inputs.title": "Titre", + "creation.inputs.error": "La longueur du nom de fichier doit être inférieure à 190 caractères", + "creation.tiles.doc": "Document", + "creation.tiles.spreadsheet": "Feuille de calcul", + "creation.tiles.presentation": "Présentation", + "creation.error": "Impossible de créer un nouveau fichier", + "upload.error": "Impossible de charger votre fichier. Veuillez contacter le service de support d'ONLYOFFICE.", + "upload.uploading": "Chargement...", + "upload.select": "Sélectionnez un fichier", + "upload.dragdrop": "ou glissez-déposez ici", + "upload.subtext": "La taille du fichier est limitée", + "settings.title": "Configurer les paramètres de l'application ONLYOFFICE", + "settings.text": "Le plugin qui amène à éditer des documents bureautiques à partir de Pipedrive en utilisant ONLYOFFICE Document Server, permet à plusieurs utilisateurs de collaborer en temps réel et de sauvegarder ces modifications sur Pipedrive", + "settings.inputs.address": "Adresse de Document Server", + "settings.inputs.secret": "Clé secrète de Document Server", + "settings.inputs.header": "En-tête de Document Server", + "settings.saving.ok": "Les paramètres ONLYOFFICE ont été sauvegardés", + "settings.saving.error": "Impossible de sauvegarder les paramètres ONLYOFFICE", + "editor.error": "Impossible d'ouvrir le fichier. Une erreur s'est produite.", + "background.error.title": "Erreur", + "background.error.subtitle": "Impossible de récupérer les paramètres du plugin. Une erreur s'est produite. Veuillez recharger la fenêtre Pipedrive", + "background.access.title": "Accès refusé", + "background.access.subtitle": "Une erreur s'est produite ou l'accès a été refusé", + "background.reinstall.title": "Le jeton de sécurité du document a expiré", + "background.reinstall.subtitle": "Une erreur s'est produite. Veuillez recharger ou réinstaller l'application", + "document.new": "Nouveau document", + "button.upload": "Créer ou charger un document", + "button.reload": "Recharger", + "button.save": "Enregistrer", + "button.cancel": "Annuler", + "button.close": "Fermer", + "button.create": "Créer document", + "button.creation.create": "Créer", + "button.creation.upload": "Charger", + "button.getnow": "Obtenir maintenant" +} diff --git a/frontend/src/assets/locales/it/translation.json b/frontend/src/assets/locales/it/translation.json new file mode 100644 index 0000000..2751f0c --- /dev/null +++ b/frontend/src/assets/locales/it/translation.json @@ -0,0 +1,50 @@ +{ + "banner.title": "ONLYOFFICE Docs Cloud", + "banner.subtitle": "Avvia facilmente gli editor nel cloud senza dover scaricare e installare", + "files.error.nofiles": "Impossibile trovare i documenti Pipedrive", + "files.info.workspace": "Spazio di lavoro", + "files.info.type": "Tipo", + "files.info.modified": "Data di modifica", + "files.info.creation": "Data di creazione", + "files.info.size": "Dimensione", + "snackbar.fileremoved.ok": "Il file {{file}} è stato rimosso", + "snackbar.fileremoved.error": "Impossibile rimuovere il file {{file}}", + "snackbar.uploaded.ok": "Il file {{file}} è stato caricato", + "snackbar.uploaded.error": "Impossibile caricare il file {{file}}", + "creation.title": "Crea con ONLYOFFICE", + "creation.inputs.title": "Titolo", + "creation.inputs.error": "La lunghezza del nome del file dovrebbe essere inferiore a 190 caratteri", + "creation.tiles.doc": "Documento", + "creation.tiles.spreadsheet": "Foglio di calcolo", + "creation.tiles.presentation": "Presentazione", + "creation.error": "Impossibile creare un nuovo file", + "upload.error": "Impossibile caricare il file. Ti preghiamo di contattare il team supporto di ONLYOFFICE.", + "upload.uploading": "Caricamento in corso ...", + "upload.select": "Seleziona un file", + "upload.dragdrop": "oppure trascina e rilascia qui", + "upload.subtext": "La dimensione del file è limitata", + "settings.title": "Configura le impostazioni dell'app ONLYOFFICE", + "settings.text": "Il plugin che consente agli utenti di modificare i documenti dell'ufficio da Pipedrive utilizzando ONLYOFFICE Document Server, permette a più utenti di collaborare in tempo reale e di salvare le modifiche apportate a Pipedrive", + "settings.inputs.address": "Indirizzo di Document Server", + "settings.inputs.secret": "Segreto di Document Server", + "settings.inputs.header": "Intestazione di Document Server", + "settings.saving.ok": "Le impostazioni di ONLYOFFICE sono state salvate", + "settings.saving.error": "Impossibile salvare le impostazioni di ONLYOFFICE", + "editor.error": "Impossibile aprire il file. Qualcosa è andato storto", + "background.error.title": "Errore", + "background.error.subtitle": "Impossibile recuperare le impostazioni del plugin. Qualcosa è andato storto. Ricarica la finestra di Pipedrive", + "background.access.title": "Accesso negato", + "background.access.subtitle": "Si è verificato un problema o l'accesso è stato negato", + "background.reinstall.title": "Il token di sicurezza del documento è scaduto", + "background.reinstall.subtitle": "Qualcosa è andato storto. Ricarica o reinstalla l'app.", + "document.new": "Nuovo documento", + "button.upload": "Crea o carica un documento", + "button.reload": "Ricarica", + "button.save": "Salva", + "button.cancel": "Annulla", + "button.close": "Chiudi", + "button.create": "Crea documento", + "button.creation.create": "Crea", + "button.creation.upload": "Carica", + "button.getnow": "Ottieni ora" +} diff --git a/frontend/src/assets/locales/ja/translation.json b/frontend/src/assets/locales/ja/translation.json new file mode 100644 index 0000000..4144d14 --- /dev/null +++ b/frontend/src/assets/locales/ja/translation.json @@ -0,0 +1,50 @@ +{ + "banner.title": "ONLYOFFICE Docs Cloud", + "banner.subtitle": "ダウンロードやインストールをせずに、クラウド上のエディターを簡単に起動できる", + "files.error.nofiles": "Pipedriveのドキュメントが見つかりませんでした", + "files.info.workspace": "ワークスペース", + "files.info.type": "タイプ", + "files.info.modified": "変更日", + "files.info.creation": "作成日", + "files.info.size": "サイズ", + "snackbar.fileremoved.ok": "{{file}}ファイルは削除されました", + "snackbar.fileremoved.error": "{{file}}ファイルを削除できませんでした", + "snackbar.uploaded.ok": "{{file}}ファイルがアップロードされました", + "snackbar.uploaded.error": "{{file}}ファイルをアップロードできませんでした", + "creation.title": "ONLYOFFICEで作成する", + "creation.inputs.title": "タイトル", + "creation.inputs.error": "ファイル名の長さは190文字以下であること", + "creation.tiles.doc": "ドキュメント", + "creation.tiles.spreadsheet": "スプレッドシート", + "creation.tiles.presentation": "プレゼンテーション", + "creation.error": "新しいファイルを作成できませんでした", + "upload.error": "ファイルをアップロードできませんでした。ONLYOFFICEのサポートまでご連絡ください。", + "upload.uploading": "アップロード中...", + "upload.select": "ファイルの選択", + "upload.dragdrop": "または、ここにドラッグ&ドロップ", + "upload.subtext": "ファイルサイズは制限されています", + "settings.title": "ONLYOFFICEアプリの設定を構成する", + "settings.text": "ONLYOFFICE Document Serverを利用して、Pipedriveからオフィス文書を編集できるプラグインで、複数のユーザーがリアルタイムで共同作業を行い、その内容をPipedriveに保存することが可能です", + "settings.inputs.address": "Document Serverのアドレス", + "settings.inputs.secret": "Document Serverのシークレット", + "settings.inputs.header": "Document Serverのヘッダー", + "settings.saving.ok": "ONLYOFFICEの設定が保存されました", + "settings.saving.error": "ONLYOFFICEの設定を保存できませんでした", + "editor.error": "ファイルを開くことに失敗しました。エラーが発生しました", + "background.error.title": "エラー", + "background.error.subtitle": "プラグイン設定を取得できませんでした。何らかのエラーが発生しました。pipedriveのウィンドウを再読み込みしてください", + "background.access.title": "アクセスが拒否されました", + "background.access.subtitle": "エラーが発生しました。或いは、この文書へのアクセス許可がありません", + "background.reinstall.title": "文書のセキュリティトークンの有効期限が切れています", + "background.reinstall.subtitle": "エラーが発生しました。アプリを再読み込みするか、再インストールしてください。", + "document.new": "新しい文書", + "button.upload": "文書を作成、またはアップロードする", + "button.reload": "再読み込み", + "button.save": "保存", + "button.cancel": "キャンセル", + "button.close": "閉じる", + "button.create": "文書を作成する", + "button.creation.create": "作成する", + "button.creation.upload": "アップロード", + "button.getnow": "今すぐ入手する" +} diff --git a/frontend/src/assets/locales/pt-BR/translation.json b/frontend/src/assets/locales/pt-BR/translation.json new file mode 100644 index 0000000..f3ba183 --- /dev/null +++ b/frontend/src/assets/locales/pt-BR/translation.json @@ -0,0 +1,50 @@ +{ + "banner.title": "ONLYOFFICE Docs Cloud", + "banner.subtitle": "Inicie facilmente os editores na nuvem sem download e instalação", + "files.error.nofiles": "Não foi possível encontrar os documentos do Pipedrive", + "files.info.workspace": "Área de trabalho", + "files.info.type": "Tipo", + "files.info.modified": "Data modificada", + "files.info.creation": "Data de criação", + "files.info.size": "Tamanho", + "snackbar.fileremoved.ok": "Arquivo {{arquivo}} foi removido", + "snackbar.fileremoved.error": "Não foi possível remover o arquivo {{arquivo}}", + "snackbar.uploaded.ok": "Arquivo {{arquivo}} foi carregado", + "snackbar.uploaded.error": "Não foi possível carregar o arquivo {{arquivo}}", + "creation.title": "Criar com o ONLYOFFICE", + "creation.inputs.title": "Título", + "creation.inputs.error": "O comprimento do nome do arquivo deve ser inferior a 190 caracteres", + "creation.tiles.doc": "Documento", + "creation.tiles.spreadsheet": "Planilha", + "creation.tiles.presentation": "Apresentação", + "creation.error": "Não foi possível criar um novo arquivo", + "upload.error": "Não foi possível enviar seu arquivo. Entre em contato com o suporte do ONLYOFFICE.", + "upload.uploading": "Enviando...", + "upload.select": "Selecione um arquivo", + "upload.dragdrop": "ou arraste e solte aqui", + "upload.subtext": "O tamanho do arquivo é limitado", + "settings.title": "Definir as configurações do aplicativo ONLYOFFICE", + "settings.text": "O plug-in que permite aos usuários editar documentos de escritório do Pipedrive usando o ONLYOFFICE Document Server, permite que vários usuários colaborem em tempo real e salvem essas alterações no Pipedrive", + "settings.inputs.address": "Endereço do Servidor de Documentos", + "settings.inputs.secret": "Segredo do Servidor de Documentos", + "settings.inputs.header": "Cabeçalho do Servidor de Documentos", + "settings.saving.ok": "As configurações do ONLYOFFICE foram salvas", + "settings.saving.error": "Could not save ONLYOFFICE settings", + "editor.error": "Não foi possível abrir o arquivo. Algo deu errado", + "background.error.title": "Erro", + "background.error.subtitle": "Não foi possível obter as configurações do plug-in. Algo deu errado. Recarregue a janela do Pipedrive", + "background.access.title": "Acesso negado", + "background.access.subtitle": "Algo deu errado ou acesso negado", + "background.reinstall.title": "O token de segurança do documento expirou", + "background.reinstall.subtitle": "Algo deu errado. Atualize ou reinstale o aplicativo.", + "document.new": "Novo Documento", + "button.upload": "Criar ou carregar documento", + "button.reload": "Recarregar", + "button.save": "Salvar", + "button.cancel": "Cancelar", + "button.close": "Fechar", + "button.create": "Criar documento", + "button.creation.create": "Criar", + "button.creation.upload": "Carregar", + "button.getnow": "Pegue agora" +} diff --git a/frontend/src/assets/locales/ru-RU/translation.json b/frontend/src/assets/locales/ru-RU/translation.json index a8d8b54..afd230c 100644 --- a/frontend/src/assets/locales/ru-RU/translation.json +++ b/frontend/src/assets/locales/ru-RU/translation.json @@ -1,50 +1,50 @@ { "banner.title": "ONLYOFFICE Docs Cloud", - "banner.subtitle": "Easily launch the editors in the cloud without downloading and installation", - "files.error.nofiles": "Could not find Pipedrive documents", - "files.info.workspace": "Workspace", - "files.info.type": "Type", - "files.info.modified": "Date modified", - "files.info.creation": "Creation date", - "files.info.size": "Size", - "snackbar.fileremoved.ok": "File {{file}} has been removed", - "snackbar.fileremoved.error": "Could not remove file {{file}}", - "snackbar.uploaded.ok": "File {{file}} has been uploaded", - "snackbar.uploaded.error": "Could not upload file {{file}}", - "creation.title": "Create with ONLYOFFICE", - "creation.inputs.title": "Title", - "creation.inputs.error": "File name length should be less than 190 characters", - "creation.tiles.doc": "Document", - "creation.tiles.spreadsheet": "Spreadsheet", - "creation.tiles.presentation": "Presentation", - "creation.error": "Could not create a new file", - "upload.error": "Could not upload your file. Please contact ONLYOFFICE support.", - "upload.uploading": "Uploading...", - "upload.select": "Select a file", - "upload.dragdrop": "or drag and drop here", - "upload.subtext": "File size is limited", - "settings.title": "Configure ONLYOFFICE app settings", - "settings.text": "The plugin which enables the users to edit office documents from Pipedrive using ONLYOFFICE Document Server, allows multiple users to collaborate in real time and to save back those changes to Pipedrive", - "settings.inputs.address": "Document Server Address", - "settings.inputs.secret": "Document Server Secret", - "settings.inputs.header": "Document Server Header", - "settings.saving.ok": "ONLYOFFICE settings have been saved", - "settings.saving.error": "Could not save ONLYOFFICE settings", - "editor.error": "Could not open the file. Something went wrong", - "background.error.title": "Error", - "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the pipedrive window", - "background.access.title": "Access denied", - "background.access.subtitle": "Something went wrong or access denied", - "background.reinstall.title": "The document security token has expired", - "background.reinstall.subtitle": "Something went wrong. Please reload or reinstall the app.", - "document.new": "New Document", - "button.upload": "Create or upload document", - "button.reload": "Reload", - "button.save": "Save", - "button.cancel": "Cancel", - "button.close": "Close", - "button.create": "Create document", - "button.creation.create": "Create", - "button.creation.upload": "Upload", - "button.getnow": "Get Now" + "banner.subtitle": " ", + "files.error.nofiles": " Pipedrive", + "files.info.workspace": " ", + "files.info.type": "", + "files.info.modified": "", + "files.info.creation": "", + "files.info.size": "", + "snackbar.fileremoved.ok": " {{file}} ", + "snackbar.fileremoved.error": " {{file}}", + "snackbar.uploaded.ok": " {{file}} ", + "snackbar.uploaded.error": " {{file}}", + "creation.title": " ONLYOFFICE", + "creation.inputs.title": "", + "creation.inputs.error": " 190 ", + "creation.tiles.doc": "", + "creation.tiles.spreadsheet": " ", + "creation.tiles.presentation": "", + "creation.error": " ", + "upload.error": " . , ONLYOFFICE.", + "upload.uploading": "...", + "upload.select": " ", + "upload.dragdrop": " ", + "upload.subtext": " ", + "settings.title": " ONLYOFFICE", + "settings.text": ", Pipedrive ONLYOFFICE, Pipedrive.", + "settings.inputs.address": " ", + "settings.inputs.secret": " ", + "settings.inputs.header": " ", + "settings.saving.ok": " ONLYOFFICE ", + "settings.saving.error": " ONLYOFFICE", + "editor.error": " . - ", + "background.error.title": "", + "background.error.subtitle": " . - . , Pipedrive", + "background.access.title": " ", + "background.access.subtitle": "- , ", + "background.reinstall.title": " ", + "background.reinstall.subtitle": "- . , .", + "document.new": " ", + "button.upload": " ", + "button.reload": "", + "button.save": "", + "button.cancel": "", + "button.close": "", + "button.create": " ", + "button.creation.create": "", + "button.creation.upload": "", + "button.getnow": " " } diff --git a/frontend/src/assets/locales/ru/translation.json b/frontend/src/assets/locales/ru/translation.json index a8d8b54..afd230c 100644 --- a/frontend/src/assets/locales/ru/translation.json +++ b/frontend/src/assets/locales/ru/translation.json @@ -1,50 +1,50 @@ { "banner.title": "ONLYOFFICE Docs Cloud", - "banner.subtitle": "Easily launch the editors in the cloud without downloading and installation", - "files.error.nofiles": "Could not find Pipedrive documents", - "files.info.workspace": "Workspace", - "files.info.type": "Type", - "files.info.modified": "Date modified", - "files.info.creation": "Creation date", - "files.info.size": "Size", - "snackbar.fileremoved.ok": "File {{file}} has been removed", - "snackbar.fileremoved.error": "Could not remove file {{file}}", - "snackbar.uploaded.ok": "File {{file}} has been uploaded", - "snackbar.uploaded.error": "Could not upload file {{file}}", - "creation.title": "Create with ONLYOFFICE", - "creation.inputs.title": "Title", - "creation.inputs.error": "File name length should be less than 190 characters", - "creation.tiles.doc": "Document", - "creation.tiles.spreadsheet": "Spreadsheet", - "creation.tiles.presentation": "Presentation", - "creation.error": "Could not create a new file", - "upload.error": "Could not upload your file. Please contact ONLYOFFICE support.", - "upload.uploading": "Uploading...", - "upload.select": "Select a file", - "upload.dragdrop": "or drag and drop here", - "upload.subtext": "File size is limited", - "settings.title": "Configure ONLYOFFICE app settings", - "settings.text": "The plugin which enables the users to edit office documents from Pipedrive using ONLYOFFICE Document Server, allows multiple users to collaborate in real time and to save back those changes to Pipedrive", - "settings.inputs.address": "Document Server Address", - "settings.inputs.secret": "Document Server Secret", - "settings.inputs.header": "Document Server Header", - "settings.saving.ok": "ONLYOFFICE settings have been saved", - "settings.saving.error": "Could not save ONLYOFFICE settings", - "editor.error": "Could not open the file. Something went wrong", - "background.error.title": "Error", - "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the pipedrive window", - "background.access.title": "Access denied", - "background.access.subtitle": "Something went wrong or access denied", - "background.reinstall.title": "The document security token has expired", - "background.reinstall.subtitle": "Something went wrong. Please reload or reinstall the app.", - "document.new": "New Document", - "button.upload": "Create or upload document", - "button.reload": "Reload", - "button.save": "Save", - "button.cancel": "Cancel", - "button.close": "Close", - "button.create": "Create document", - "button.creation.create": "Create", - "button.creation.upload": "Upload", - "button.getnow": "Get Now" + "banner.subtitle": " ", + "files.error.nofiles": " Pipedrive", + "files.info.workspace": " ", + "files.info.type": "", + "files.info.modified": "", + "files.info.creation": "", + "files.info.size": "", + "snackbar.fileremoved.ok": " {{file}} ", + "snackbar.fileremoved.error": " {{file}}", + "snackbar.uploaded.ok": " {{file}} ", + "snackbar.uploaded.error": " {{file}}", + "creation.title": " ONLYOFFICE", + "creation.inputs.title": "", + "creation.inputs.error": " 190 ", + "creation.tiles.doc": "", + "creation.tiles.spreadsheet": " ", + "creation.tiles.presentation": "", + "creation.error": " ", + "upload.error": " . , ONLYOFFICE.", + "upload.uploading": "...", + "upload.select": " ", + "upload.dragdrop": " ", + "upload.subtext": " ", + "settings.title": " ONLYOFFICE", + "settings.text": ", Pipedrive ONLYOFFICE, Pipedrive.", + "settings.inputs.address": " ", + "settings.inputs.secret": " ", + "settings.inputs.header": " ", + "settings.saving.ok": " ONLYOFFICE ", + "settings.saving.error": " ONLYOFFICE", + "editor.error": " . - ", + "background.error.title": "", + "background.error.subtitle": " . - . , Pipedrive", + "background.access.title": " ", + "background.access.subtitle": "- , ", + "background.reinstall.title": " ", + "background.reinstall.subtitle": "- . , .", + "document.new": " ", + "button.upload": " ", + "button.reload": "", + "button.save": "", + "button.cancel": "", + "button.close": "", + "button.create": " ", + "button.creation.create": "", + "button.creation.upload": "", + "button.getnow": " " } From 597629bfb3a946f3c4ce9b38aaf4409f7c67f857 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 27 Apr 2023 13:46:39 +0500 Subject: [PATCH 091/145] fix(pages): text wrap --- frontend/src/layout/Banner.tsx | 2 +- frontend/src/pages/Settings/index.tsx | 4 ++-- frontend/src/types/user.ts | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/src/layout/Banner.tsx b/frontend/src/layout/Banner.tsx index 41c4809..5e3a01d 100644 --- a/frontend/src/layout/Banner.tsx +++ b/frontend/src/layout/Banner.tsx @@ -61,7 +61,7 @@ export const Banner: React.FC = () => {
-
{actions}
+
{actions}
Date: Thu, 11 May 2023 21:05:56 +0500 Subject: [PATCH 116/145] fix(context): store tokens --- frontend/src/context/TokenContext.tsx | 74 ++++++++++++--------------- frontend/src/utils/storage.ts | 43 ++++++++++++++++ 2 files changed, 75 insertions(+), 42 deletions(-) create mode 100644 frontend/src/utils/storage.ts diff --git a/frontend/src/context/TokenContext.tsx b/frontend/src/context/TokenContext.tsx index c9a8a88..7ece207 100644 --- a/frontend/src/context/TokenContext.tsx +++ b/frontend/src/context/TokenContext.tsx @@ -25,7 +25,7 @@ import { proxy } from "valtio"; import { getMe, getPipedriveMe } from "@services/me"; import { getCurrentURL } from "@utils/url"; - +import { getWithExpiry, setWithExpiry } from "@utils/storage"; import { UserResponse } from "src/types/user"; export const AuthToken = proxy({ @@ -48,49 +48,39 @@ export const TokenProvider: React.FC = ({ children }) => { .then((sdk) => { const { url } = getCurrentURL(); timerID = setTimeout(async function update() { - if (AuthToken.error) return; - try { - let token; - const auth = localStorage.getItem("authorization"); - if (!auth) { - token = await getMe(sdk); - AuthToken.access_token = token.response.access_token; - AuthToken.expires_at = token.response.expires_at; - localStorage.setItem( - "authorization", - JSON.stringify({ - access_token: token.response.access_token, - expires_at: token.response.expires_at, - }) - ); - return; - } - - token = JSON.parse(auth) as UserResponse; - if (token.expires_at - 110 < Date.now()) { - localStorage.removeItem("authorization"); - const t = await getMe(sdk); - AuthToken.access_token = t.response.access_token; - AuthToken.expires_at = t.response.expires_at; - return; - } - - AuthToken.access_token = token.access_token; - AuthToken.expires_at = token.expires_at; - } catch (err) { - if (!axios.isCancel(err)) { - AuthToken.error = true; - AuthToken.access_token = ""; - localStorage.removeItem("authorization"); + if ( + !AuthToken.error && + (!AuthToken.access_token || + AuthToken.expires_at <= Date.now() - 1000 * 19) + ) { + try { + const val = getWithExpiry("authorization") as UserResponse; + if (val) { + AuthToken.access_token = val.access_token; + AuthToken.expires_at = val.expires_at; + } else { + const token = await getMe(sdk); + AuthToken.access_token = token.response.access_token; + AuthToken.expires_at = token.response.expires_at; + setWithExpiry( + "authorization", + token.response, + token.response.expires_at + ); + } + const resp = await getPipedriveMe(`${url}api/v1/users/me`); + await i18next.changeLanguage(resp.data.language.language_code); + } catch (err) { + if (!axios.isCancel(err)) { + AuthToken.error = true; + AuthToken.access_token = ""; + } } - } finally { - const resp = await getPipedriveMe(`${url}api/v1/users/me`); - await i18next.changeLanguage(resp.data.language.language_code); - timerID = setTimeout( - update, - AuthToken.expires_at - Date.now() - 100 - ); } + timerID = setTimeout( + update, + AuthToken.expires_at - Date.now() - 1000 * 19 + ); }, 0); }) .catch(() => null); diff --git a/frontend/src/utils/storage.ts b/frontend/src/utils/storage.ts new file mode 100644 index 0000000..33dd991 --- /dev/null +++ b/frontend/src/utils/storage.ts @@ -0,0 +1,43 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +export function setWithExpiry(key: string, value: any, expiry: number) { + localStorage.setItem( + key, + JSON.stringify({ + value, + expiry, + }) + ); +} + +export function getWithExpiry(key: string) { + const sitem = localStorage.getItem(key); + if (!sitem) { + return null; + } + + const item = JSON.parse(sitem); + const now = new Date(); + if (now.getTime() > item.expiry) { + localStorage.removeItem(key); + return null; + } + + return item.value; +} From 048928b080f4d84eed90d878ab61ec60efbae2ce Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 12 May 2023 12:04:23 +0500 Subject: [PATCH 117/145] chore: expiration --- backend/services/auth/web/handler/select.go | 2 +- frontend/src/hooks/useFileSearch.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/services/auth/web/handler/select.go b/backend/services/auth/web/handler/select.go index 531c1ef..a8d8791 100644 --- a/backend/services/auth/web/handler/select.go +++ b/backend/services/auth/web/handler/select.go @@ -61,7 +61,7 @@ func (u UserSelectHandler) GetUser(ctx context.Context, uid *string, res *domain return nil, err } - if user.ExpiresAt <= time.Now().Add(-20*time.Second).UnixMilli() { + if user.ExpiresAt <= time.Now().Add(-30*time.Second).UnixMilli() { u.logger.Debug("user token has expired. Trying to refresh!") token, terr := u.pipedriveAuth.RefreshAccessToken(ctx, user.RefreshToken) if terr != nil { diff --git a/frontend/src/hooks/useFileSearch.tsx b/frontend/src/hooks/useFileSearch.tsx index f57690a..c6aba62 100644 --- a/frontend/src/hooks/useFileSearch.tsx +++ b/frontend/src/hooks/useFileSearch.tsx @@ -37,9 +37,9 @@ export function useFileSearch(url: string, limit: number) { lastPage?.pagination?.more_items_in_collection ? lastPage.pagination.next_start : undefined, - staleTime: 2000, - cacheTime: 2500, - refetchInterval: 2000, + staleTime: 3500, + cacheTime: 4000, + refetchInterval: 3500, }); return { From 8f533db8cbd04ca15f830cb2acc7e0e35e653088 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 12 May 2023 12:26:59 +0500 Subject: [PATCH 118/145] fix(components): file name tooltip --- frontend/src/components/file/File.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/file/File.tsx b/frontend/src/components/file/File.tsx index bd4a233..88b16b9 100644 --- a/frontend/src/components/file/File.tsx +++ b/frontend/src/components/file/File.tsx @@ -63,6 +63,7 @@ export const OnlyofficeFile: React.FC = ({ supported && onClick ? "cursor-pointer" : "cursor-default" } text-left font-semibold font-sans md:text-sm text-xs px-2 w-[170px] h-[32px] overflow-hidden text-ellipsis whitespace-nowrap`} type="button" + title={name} onClick={onClick} > {name} From 0b1ce289abe509806dd3f9494ef8f47aa22746df Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 12 May 2023 12:34:03 +0500 Subject: [PATCH 119/145] fix: timeouts --- frontend/src/context/TokenContext.tsx | 6 +++--- frontend/src/services/file.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/context/TokenContext.tsx b/frontend/src/context/TokenContext.tsx index 7ece207..a9eca11 100644 --- a/frontend/src/context/TokenContext.tsx +++ b/frontend/src/context/TokenContext.tsx @@ -51,7 +51,7 @@ export const TokenProvider: React.FC = ({ children }) => { if ( !AuthToken.error && (!AuthToken.access_token || - AuthToken.expires_at <= Date.now() - 1000 * 19) + AuthToken.expires_at <= Date.now() - 1000 * 90) ) { try { const val = getWithExpiry("authorization") as UserResponse; @@ -65,7 +65,7 @@ export const TokenProvider: React.FC = ({ children }) => { setWithExpiry( "authorization", token.response, - token.response.expires_at + token.response.expires_at - 1000 * 90 ); } const resp = await getPipedriveMe(`${url}api/v1/users/me`); @@ -79,7 +79,7 @@ export const TokenProvider: React.FC = ({ children }) => { } timerID = setTimeout( update, - AuthToken.expires_at - Date.now() - 1000 * 19 + AuthToken.expires_at - Date.now() - 1000 * 90 ); }, 0); }) diff --git a/frontend/src/services/file.ts b/frontend/src/services/file.ts index 0f491e0..a4afd97 100644 --- a/frontend/src/services/file.ts +++ b/frontend/src/services/file.ts @@ -32,9 +32,9 @@ export const fetchFiles = async ( ) => { const client = axios.create(); axiosRetry(client, { - retries: 3, + retries: 6, retryCondition: (error) => error.status !== 200, - retryDelay: (count) => count * 50, + retryDelay: (count) => count * 100, shouldResetTimeout: true, }); From 4556289708e04754cb61a9e08957cbe06f49cb1a Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 12 May 2023 12:36:41 +0500 Subject: [PATCH 120/145] fix: 30 sec refresh interval --- frontend/src/context/TokenContext.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/context/TokenContext.tsx b/frontend/src/context/TokenContext.tsx index a9eca11..779832d 100644 --- a/frontend/src/context/TokenContext.tsx +++ b/frontend/src/context/TokenContext.tsx @@ -51,7 +51,7 @@ export const TokenProvider: React.FC = ({ children }) => { if ( !AuthToken.error && (!AuthToken.access_token || - AuthToken.expires_at <= Date.now() - 1000 * 90) + AuthToken.expires_at <= Date.now() - 1000 * 30) ) { try { const val = getWithExpiry("authorization") as UserResponse; @@ -65,7 +65,7 @@ export const TokenProvider: React.FC = ({ children }) => { setWithExpiry( "authorization", token.response, - token.response.expires_at - 1000 * 90 + token.response.expires_at - 1000 * 30 ); } const resp = await getPipedriveMe(`${url}api/v1/users/me`); @@ -79,7 +79,7 @@ export const TokenProvider: React.FC = ({ children }) => { } timerID = setTimeout( update, - AuthToken.expires_at - Date.now() - 1000 * 90 + AuthToken.expires_at - Date.now() - 1000 * 30 ); }, 0); }) From 2788baa588f28f48375a4e4f834bd61129092296 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 15 May 2023 12:03:53 +0500 Subject: [PATCH 121/145] fix(context): refresh tokens --- frontend/src/context/TokenContext.tsx | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/frontend/src/context/TokenContext.tsx b/frontend/src/context/TokenContext.tsx index 779832d..b385750 100644 --- a/frontend/src/context/TokenContext.tsx +++ b/frontend/src/context/TokenContext.tsx @@ -25,8 +25,6 @@ import { proxy } from "valtio"; import { getMe, getPipedriveMe } from "@services/me"; import { getCurrentURL } from "@utils/url"; -import { getWithExpiry, setWithExpiry } from "@utils/storage"; -import { UserResponse } from "src/types/user"; export const AuthToken = proxy({ access_token: "", @@ -54,20 +52,9 @@ export const TokenProvider: React.FC = ({ children }) => { AuthToken.expires_at <= Date.now() - 1000 * 30) ) { try { - const val = getWithExpiry("authorization") as UserResponse; - if (val) { - AuthToken.access_token = val.access_token; - AuthToken.expires_at = val.expires_at; - } else { - const token = await getMe(sdk); - AuthToken.access_token = token.response.access_token; - AuthToken.expires_at = token.response.expires_at; - setWithExpiry( - "authorization", - token.response, - token.response.expires_at - 1000 * 30 - ); - } + const token = await getMe(sdk); + AuthToken.access_token = token.response.access_token; + AuthToken.expires_at = token.response.expires_at; const resp = await getPipedriveMe(`${url}api/v1/users/me`); await i18next.changeLanguage(resp.data.language.language_code); } catch (err) { From a2b936e53bf2d22d626ab04b586b34c432491398 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 5 Jun 2023 16:17:57 +0500 Subject: [PATCH 122/145] fix(utils): remove getFileUrl --- frontend/src/utils/file.ts | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/frontend/src/utils/file.ts b/frontend/src/utils/file.ts index a26beba..f9b6f77 100644 --- a/frontend/src/utils/file.ts +++ b/frontend/src/utils/file.ts @@ -147,21 +147,6 @@ export const getFileIcon = (filename: string) => { return Unsupported; }; -export const getCreateFileUrl = ( - fileType: "docx" | "pptx" | "xlsx" | undefined -) => { - switch (fileType) { - case "docx": - return encodeURIComponent(process.env.WORD_FILE || ""); - case "pptx": - return encodeURIComponent(process.env.SLIDE_FILE || ""); - case "xlsx": - return encodeURIComponent(process.env.SPREADSHEET_FILE || ""); - default: - return encodeURIComponent(process.env.WORD_FILE || ""); - } -}; - export const formatBytes = (bytes: number, decimals = 2) => { if (!+bytes) return "0 Bytes"; From e2b3580b248887a2fbc3e6c412eec21d6e642cbe Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 5 Jun 2023 16:19:04 +0500 Subject: [PATCH 123/145] chore: remove unused webpack env variables --- frontend/webpack.production.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/webpack.production.js b/frontend/webpack.production.js index b68996a..46e878b 100644 --- a/frontend/webpack.production.js +++ b/frontend/webpack.production.js @@ -28,7 +28,6 @@ module.exports = merge(common, { new webpack.DefinePlugin({ 'process.env.BACKEND_GATEWAY': JSON.stringify(process.env.BACKEND_GATEWAY), 'process.env.PIPEDRIVE_CREATE_MODAL_ID': JSON.stringify(process.env.PIPEDRIVE_CREATE_MODAL_ID), - 'process.env.PIPEDRIVE_EDITOR_MODAL_ID': JSON.stringify(process.env.PIPEDRIVE_EDITOR_MODAL_ID), }), ], }); From a047dc678a3d47432081f59fc78083b104a460aa Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 5 Jun 2023 16:19:53 +0500 Subject: [PATCH 124/145] fix(pages): add reinstall button --- frontend/src/pages/Main/index.tsx | 5 +++++ frontend/yarn.lock | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/pages/Main/index.tsx b/frontend/src/pages/Main/index.tsx index a4ffdf4..c66d06e 100644 --- a/frontend/src/pages/Main/index.tsx +++ b/frontend/src/pages/Main/index.tsx @@ -54,6 +54,11 @@ export const MainPage: React.FC = () => { "background.reinstall.subtitle", "Something went wrong. Please reload or reinstall the app." )} + button={t( + "background.reinstall.button", + "Reinstall" + ) || "Reinstall"} + onClick={() => window.open(`${process.env.BACKEND_GATEWAY}/oauth/install`, "_blank")} /> )} {loaded &&
} diff --git a/frontend/yarn.lock b/frontend/yarn.lock index f17d16b..4dee530 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1842,11 +1842,6 @@ expect "^29.0.0" pretty-format "^29.0.0" -"@types/js-cookie@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-3.0.3.tgz#d6bfbbdd0c187354ca555213d1962f6d0691ff4e" - integrity sha512-Xe7IImK09HP1sv2M/aI+48a20VX+TdRJucfq4vfRVy6nWN8PYPOEnlMRSgxJAgYQIXJVL8dZ4/ilAM7dWNaOww== - "@types/jsdom@^20.0.0": version "20.0.1" resolved "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz" From 499b6d9f0c6000cc2adf7763566222e58da3d685 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 5 Jun 2023 16:28:09 +0500 Subject: [PATCH 125/145] refactor(services): auth service --- backend/services/auth/cmd/server.go | 26 ++++---- .../services/auth/config/config.example.yml | 17 ++--- .../insert.go => core/adapter/adapter.go} | 29 ++------- .../web/{message => core/adapter}/error.go | 7 +- .../services/auth/web/core/adapter/memory.go | 4 +- .../auth/web/core/adapter/memory_test.go | 6 +- .../services/auth/web/core/adapter/mongo.go | 12 ++-- .../auth/web/core/adapter/mongo_test.go | 14 ++-- backend/services/auth/web/core/port/input.go | 2 +- backend/services/auth/web/core/port/output.go | 4 +- .../services/auth/web/core/service/error.go | 7 +- .../services/auth/web/core/service/user.go | 51 +++++++-------- .../auth/web/core/service/user_test.go | 22 ++++--- backend/services/auth/web/handler/delete.go | 16 ++++- backend/services/auth/web/handler/handler.go | 23 +++++++ backend/services/auth/web/handler/insert.go | 64 +++++++++++++++++++ backend/services/auth/web/handler/select.go | 5 +- .../services/auth/web/handler/select_test.go | 21 ++++-- backend/services/auth/web/server.go | 53 ++++----------- 19 files changed, 226 insertions(+), 157 deletions(-) rename backend/services/auth/web/{message/insert.go => core/adapter/adapter.go} (51%) rename backend/services/auth/web/{message => core/adapter}/error.go (80%) create mode 100644 backend/services/auth/web/handler/handler.go create mode 100644 backend/services/auth/web/handler/insert.go diff --git a/backend/services/auth/cmd/server.go b/backend/services/auth/cmd/server.go index e007632..312d823 100644 --- a/backend/services/auth/cmd/server.go +++ b/backend/services/auth/cmd/server.go @@ -19,9 +19,14 @@ package cmd import ( - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/rpc" + pkg "github.com/ONLYOFFICE/onlyoffice-integration-adapters" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/service/rpc" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/adapter" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/service" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/handler" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" "github.com/urfave/cli/v2" ) @@ -36,21 +41,20 @@ func Server() *cli.Command { Usage: "sets custom configuration path", Aliases: []string{"config", "conf", "c"}, }, - &cli.StringFlag{ - Name: "environment", - Usage: "sets servers environment (development, testing, production)", - Aliases: []string{"env", "e"}, - }, }, Action: func(c *cli.Context) error { var ( CONFIG_PATH = c.String("config_path") - // ENVIRONMENT = c.String("environment") ) - app := pkg.Bootstrap( - CONFIG_PATH, rpc.NewService, web.NewAuthRPCServer, - ) + app := pkg.NewBootstrapper(CONFIG_PATH, pkg.WithModules( + shared.BuildNewIntegrationCredentialsConfig(CONFIG_PATH), + rpc.NewService, web.NewAuthRPCServer, + adapter.BuildNewUserAdapter, service.NewUserService, + handler.NewUserSelectHandler, handler.NewUserInsertHandler, + handler.NewUserDeleteHandler, + client.NewPipedriveAuthClient, + )).Bootstrap() if err := app.Err(); err != nil { return err diff --git a/backend/services/auth/config/config.example.yml b/backend/services/auth/config/config.example.yml index 80f3944..f7d4dfb 100644 --- a/backend/services/auth/config/config.example.yml +++ b/backend/services/auth/config/config.example.yml @@ -4,21 +4,15 @@ version: 0 address: ":5052" repl_address: ":5053" debug: false -persistence: +storage: url: "" + type: 1 registry: - addresses: ["127.0.0.1:8500"] + addresses: [""] type: 2 -messaging: - type: 1 - addresses: ["0.0.0.0:5672"] - durable: true - ack_on_success: true - requeue_on_error: true - disable_auto_ack: true tracer: enable: false - address: "http://127.0.0.1:9411/api/v2/spans" + address: "" type: 1 resilience: rate_limiter: @@ -29,6 +23,7 @@ logger: name: "auth-logger" level: 1 color: true -oauth: +credentials: client_id: "" client_secret: "" + redirect_url: "" \ No newline at end of file diff --git a/backend/services/auth/web/message/insert.go b/backend/services/auth/web/core/adapter/adapter.go similarity index 51% rename from backend/services/auth/web/message/insert.go rename to backend/services/auth/web/core/adapter/adapter.go index 2d2a556..2e2743d 100644 --- a/backend/services/auth/web/message/insert.go +++ b/backend/services/auth/web/core/adapter/adapter.go @@ -16,33 +16,18 @@ * */ -package message +package adapter import ( - "context" - - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/config" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/port" - "github.com/mitchellh/mapstructure" ) -type InsertMessageHandler struct { - service port.UserAccessService -} - -func BuildInsertMessageHandler(service port.UserAccessService) InsertMessageHandler { - return InsertMessageHandler{ - service: service, +func BuildNewUserAdapter(config *config.StorageConfig) port.UserAccessServiceAdapter { + adapter := NewMemoryUserAdapter() + if config.Storage.URL != "" { + adapter = NewMongoUserAdapter(config.Storage.URL) } -} -func (i InsertMessageHandler) GetHandler() func(context.Context, interface{}) error { - return func(ctx context.Context, payload interface{}) error { - var user domain.UserAccess - if err := mapstructure.Decode(payload, &user); err != nil { - return err - } - _, err := i.service.UpdateUser(ctx, user) - return err - } + return adapter } diff --git a/backend/services/auth/web/message/error.go b/backend/services/auth/web/core/adapter/error.go similarity index 80% rename from backend/services/auth/web/message/error.go rename to backend/services/auth/web/core/adapter/error.go index a10e624..8db21a5 100644 --- a/backend/services/auth/web/message/error.go +++ b/backend/services/auth/web/core/adapter/error.go @@ -16,8 +16,11 @@ * */ -package message +package adapter import "errors" -var _ErrInvalidHandlerPayload = errors.New("invalid handler payload") +var ( + ErrInvalidUserId error = errors.New("invalid uid format") + ErrUserAlreadyExists error = errors.New("user already exists") +) diff --git a/backend/services/auth/web/core/adapter/memory.go b/backend/services/auth/web/core/adapter/memory.go index 48be0f5..ee0a10b 100644 --- a/backend/services/auth/web/core/adapter/memory.go +++ b/backend/services/auth/web/core/adapter/memory.go @@ -53,7 +53,7 @@ func (m *memoryUserAdapter) InsertUser(ctx context.Context, user domain.UserAcce return m.save(user) } -func (m *memoryUserAdapter) SelectUserByID(ctx context.Context, uid string) (domain.UserAccess, error) { +func (m *memoryUserAdapter) SelectUser(ctx context.Context, uid string) (domain.UserAccess, error) { buffer, ok := m.kvs[uid] var user domain.UserAccess @@ -76,7 +76,7 @@ func (m *memoryUserAdapter) UpsertUser(ctx context.Context, user domain.UserAcce return user, nil } -func (m *memoryUserAdapter) DeleteUserByID(ctx context.Context, uid string) error { +func (m *memoryUserAdapter) DeleteUser(ctx context.Context, uid string) error { if _, ok := m.kvs[uid]; !ok { return errors.New("user with this id doesn't exist") } diff --git a/backend/services/auth/web/core/adapter/memory_test.go b/backend/services/auth/web/core/adapter/memory_test.go index 6347dca..11a2261 100644 --- a/backend/services/auth/web/core/adapter/memory_test.go +++ b/backend/services/auth/web/core/adapter/memory_test.go @@ -38,7 +38,7 @@ func TestMemoryAdapter(t *testing.T) { }) t.Run("get user by id", func(t *testing.T) { - u, err := adapter.SelectUserByID(context.Background(), "mock") + u, err := adapter.SelectUser(context.Background(), "mock") assert.NoError(t, err) assert.Equal(t, user, u) }) @@ -53,11 +53,11 @@ func TestMemoryAdapter(t *testing.T) { }) t.Run("delete user by id", func(t *testing.T) { - assert.NoError(t, adapter.DeleteUserByID(context.Background(), "mock")) + assert.NoError(t, adapter.DeleteUser(context.Background(), "mock")) }) t.Run("get invalid user", func(t *testing.T) { - _, err := adapter.SelectUserByID(context.Background(), "mock") + _, err := adapter.SelectUser(context.Background(), "mock") assert.Error(t, err) }) } diff --git a/backend/services/auth/web/core/adapter/mongo.go b/backend/services/auth/web/core/adapter/mongo.go index 72e9baa..bfaf4d6 100644 --- a/backend/services/auth/web/core/adapter/mongo.go +++ b/backend/services/auth/web/core/adapter/mongo.go @@ -20,7 +20,6 @@ package adapter import ( "context" - "errors" "log" "strings" "time" @@ -34,9 +33,6 @@ import ( "go.mongodb.org/mongo-driver/mongo/options" ) -var _ErrInvalidUserId error = errors.New("invalid uid format") -var _ErrUserAlreadyExists error = errors.New("user already exists") - type userAccessCollection struct { mgm.DefaultModel `bson:",inline"` UID string `json:"uid" bson:"uid"` @@ -107,11 +103,11 @@ func (m *mongoUserAdapter) InsertUser(ctx context.Context, user domain.UserAcces return m.save(ctx, user) } -func (m *mongoUserAdapter) SelectUserByID(ctx context.Context, uid string) (domain.UserAccess, error) { +func (m *mongoUserAdapter) SelectUser(ctx context.Context, uid string) (domain.UserAccess, error) { uid = strings.TrimSpace(uid) if uid == "" { - return domain.UserAccess{}, _ErrInvalidUserId + return domain.UserAccess{}, ErrInvalidUserId } user := &userAccessCollection{} @@ -135,11 +131,11 @@ func (m *mongoUserAdapter) UpsertUser(ctx context.Context, user domain.UserAcces return user, m.save(ctx, user) } -func (m *mongoUserAdapter) DeleteUserByID(ctx context.Context, uid string) error { +func (m *mongoUserAdapter) DeleteUser(ctx context.Context, uid string) error { uid = strings.TrimSpace(uid) if uid == "" { - return _ErrInvalidUserId + return ErrInvalidUserId } _, err := mgm.Coll(&userAccessCollection{}).DeleteMany(ctx, bson.M{"uid": bson.M{operator.Eq: uid}}) diff --git a/backend/services/auth/web/core/adapter/mongo_test.go b/backend/services/auth/web/core/adapter/mongo_test.go index 4d2215d..672fc3b 100644 --- a/backend/services/auth/web/core/adapter/mongo_test.go +++ b/backend/services/auth/web/core/adapter/mongo_test.go @@ -56,12 +56,12 @@ func TestMongoAdapter(t *testing.T) { t.Run("get user by id with timeout", func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 0*time.Second) defer cancel() - _, err := adapter.SelectUserByID(ctx, "mock") + _, err := adapter.SelectUser(ctx, "mock") assert.Error(t, err) }) t.Run("get user by id", func(t *testing.T) { - u, err := adapter.SelectUserByID(context.Background(), "mock") + u, err := adapter.SelectUser(context.Background(), "mock") assert.NoError(t, err) assert.Equal(t, user, u) }) @@ -69,15 +69,15 @@ func TestMongoAdapter(t *testing.T) { t.Run("delete user by id with timeout", func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 0*time.Second) defer cancel() - assert.Error(t, adapter.DeleteUserByID(ctx, "mock")) + assert.Error(t, adapter.DeleteUser(ctx, "mock")) }) t.Run("delete user by id", func(t *testing.T) { - assert.NoError(t, adapter.DeleteUserByID(context.Background(), "mock")) + assert.NoError(t, adapter.DeleteUser(context.Background(), "mock")) }) t.Run("get invalid user", func(t *testing.T) { - _, err := adapter.SelectUserByID(context.Background(), "mock") + _, err := adapter.SelectUser(context.Background(), "mock") assert.Error(t, err) }) @@ -116,10 +116,10 @@ func TestMongoAdapter(t *testing.T) { }) t.Run("get updated user", func(t *testing.T) { - u, err := adapter.SelectUserByID(context.Background(), "mock") + u, err := adapter.SelectUser(context.Background(), "mock") assert.NoError(t, err) assert.Equal(t, "BRuh", u.AccessToken) }) - adapter.DeleteUserByID(context.Background(), "mock") + adapter.DeleteUser(context.Background(), "mock") } diff --git a/backend/services/auth/web/core/port/input.go b/backend/services/auth/web/core/port/input.go index 294eb6d..62fb73f 100644 --- a/backend/services/auth/web/core/port/input.go +++ b/backend/services/auth/web/core/port/input.go @@ -28,5 +28,5 @@ type UserAccessService interface { CreateUser(ctx context.Context, user domain.UserAccess) error GetUser(ctx context.Context, uid string) (domain.UserAccess, error) UpdateUser(ctx context.Context, user domain.UserAccess) (domain.UserAccess, error) - DeleteUser(ctx context.Context, uid string) error + RemoveUser(ctx context.Context, uid string) error } diff --git a/backend/services/auth/web/core/port/output.go b/backend/services/auth/web/core/port/output.go index 0b252ee..f60d6ab 100644 --- a/backend/services/auth/web/core/port/output.go +++ b/backend/services/auth/web/core/port/output.go @@ -26,7 +26,7 @@ import ( type UserAccessServiceAdapter interface { InsertUser(ctx context.Context, user domain.UserAccess) error - SelectUserByID(ctx context.Context, uid string) (domain.UserAccess, error) + SelectUser(ctx context.Context, uid string) (domain.UserAccess, error) UpsertUser(ctx context.Context, user domain.UserAccess) (domain.UserAccess, error) - DeleteUserByID(ctx context.Context, uid string) error + DeleteUser(ctx context.Context, uid string) error } diff --git a/backend/services/auth/web/core/service/error.go b/backend/services/auth/web/core/service/error.go index 241bb98..79decfb 100644 --- a/backend/services/auth/web/core/service/error.go +++ b/backend/services/auth/web/core/service/error.go @@ -18,7 +18,12 @@ package service -import "fmt" +import ( + "errors" + "fmt" +) + +var ErrOperationTimeout = errors.New("operation timeout") type InvalidServiceParameterError struct { Name string diff --git a/backend/services/auth/web/core/service/user.go b/backend/services/auth/web/core/service/user.go index 96cb17c..bb6992a 100644 --- a/backend/services/auth/web/core/service/user.go +++ b/backend/services/auth/web/core/service/user.go @@ -20,39 +20,40 @@ package service import ( "context" - "errors" "strings" "sync" "time" - plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/crypto" + plog "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/port" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" "github.com/mitchellh/mapstructure" "go-micro.dev/v4/cache" + "golang.org/x/oauth2" ) -var _ErrOperationTimeout = errors.New("operation timeout") - type userService struct { - adapter port.UserAccessServiceAdapter - encryptor crypto.Encryptor - cache cache.Cache - logger plog.Logger + adapter port.UserAccessServiceAdapter + encryptor crypto.Encryptor + cache cache.Cache + credentials *oauth2.Config + logger plog.Logger } func NewUserService( adapter port.UserAccessServiceAdapter, encryptor crypto.Encryptor, cache cache.Cache, + credentials *oauth2.Config, logger plog.Logger, ) port.UserAccessService { return userService{ - adapter: adapter, - encryptor: encryptor, - cache: cache, - logger: logger, + adapter: adapter, + encryptor: encryptor, + cache: cache, + credentials: credentials, + logger: logger, } } @@ -70,7 +71,7 @@ func (s userService) CreateUser(ctx context.Context, user domain.UserAccess) err go func() { defer wg.Done() - aToken, err := s.encryptor.Encrypt(user.AccessToken) + aToken, err := s.encryptor.Encrypt(user.AccessToken, []byte(s.credentials.ClientSecret)) if err != nil { errChan <- err return @@ -80,7 +81,7 @@ func (s userService) CreateUser(ctx context.Context, user domain.UserAccess) err go func() { defer wg.Done() - rToken, err := s.encryptor.Encrypt(user.RefreshToken) + rToken, err := s.encryptor.Encrypt(user.RefreshToken, []byte(s.credentials.ClientSecret)) if err != nil { errChan <- err return @@ -94,7 +95,7 @@ func (s userService) CreateUser(ctx context.Context, user domain.UserAccess) err case err := <-errChan: return err case <-ctx.Done(): - return _ErrOperationTimeout + return ErrOperationTimeout default: } @@ -141,7 +142,7 @@ func (s userService) GetUser(ctx context.Context, uid string) (domain.UserAccess } if user.Validate() != nil { - user, err = s.adapter.SelectUserByID(ctx, id) + user, err = s.adapter.SelectUser(ctx, id) if err != nil { return user, err } @@ -153,7 +154,7 @@ func (s userService) GetUser(ctx context.Context, uid string) (domain.UserAccess go func() { defer wg.Done() - aToken, err := s.encryptor.Decrypt(user.AccessToken) + aToken, err := s.encryptor.Decrypt(user.AccessToken, []byte(s.credentials.ClientSecret)) if err != nil { errChan <- err return @@ -163,7 +164,7 @@ func (s userService) GetUser(ctx context.Context, uid string) (domain.UserAccess go func() { defer wg.Done() - rToken, err := s.encryptor.Decrypt(user.RefreshToken) + rToken, err := s.encryptor.Decrypt(user.RefreshToken, []byte(s.credentials.ClientSecret)) if err != nil { errChan <- err return @@ -177,7 +178,7 @@ func (s userService) GetUser(ctx context.Context, uid string) (domain.UserAccess case err := <-errChan: return domain.UserAccess{}, err case <-ctx.Done(): - return domain.UserAccess{}, _ErrOperationTimeout + return domain.UserAccess{}, ErrOperationTimeout default: return domain.UserAccess{ ID: user.ID, @@ -205,7 +206,7 @@ func (s userService) UpdateUser(ctx context.Context, user domain.UserAccess) (do go func() { defer wg.Done() - aToken, err := s.encryptor.Encrypt(user.AccessToken) + aToken, err := s.encryptor.Encrypt(user.AccessToken, []byte(s.credentials.ClientSecret)) if err != nil { errChan <- err return @@ -215,7 +216,7 @@ func (s userService) UpdateUser(ctx context.Context, user domain.UserAccess) (do go func() { defer wg.Done() - rToken, err := s.encryptor.Encrypt(user.RefreshToken) + rToken, err := s.encryptor.Encrypt(user.RefreshToken, []byte(s.credentials.ClientSecret)) if err != nil { errChan <- err return @@ -227,7 +228,7 @@ func (s userService) UpdateUser(ctx context.Context, user domain.UserAccess) (do case err := <-errChan: return user, err case <-ctx.Done(): - return user, _ErrOperationTimeout + return user, ErrOperationTimeout default: } @@ -254,7 +255,7 @@ func (s userService) UpdateUser(ctx context.Context, user domain.UserAccess) (do return user, nil } -func (s userService) DeleteUser(ctx context.Context, uid string) error { +func (s userService) RemoveUser(ctx context.Context, uid string) error { id := strings.TrimSpace(uid) s.logger.Debugf("validating uid %s to perform a delete action", id) @@ -270,5 +271,5 @@ func (s userService) DeleteUser(ctx context.Context, uid string) error { } s.logger.Debugf("uid %s is valid to perform a delete action", id) - return s.adapter.DeleteUserByID(ctx, uid) + return s.adapter.DeleteUser(ctx, uid) } diff --git a/backend/services/auth/web/core/service/user_test.go b/backend/services/auth/web/core/service/user_test.go index 31fe22c..b8fb1bb 100644 --- a/backend/services/auth/web/core/service/user_test.go +++ b/backend/services/auth/web/core/service/user_test.go @@ -23,20 +23,21 @@ import ( "testing" "time" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/cache" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/cache" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/config" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" "github.com/stretchr/testify/assert" + "golang.org/x/oauth2" ) type mockEncryptor struct{} -func (e mockEncryptor) Encrypt(text string) (string, error) { +func (e mockEncryptor) Encrypt(text string, key []byte) (string, error) { return string(text), nil } -func (e mockEncryptor) Decrypt(ciphertext string) (string, error) { +func (e mockEncryptor) Decrypt(ciphertext string, key []byte) (string, error) { return string(ciphertext), nil } @@ -57,7 +58,7 @@ func (m mockAdapter) InsertUser(ctx context.Context, user domain.UserAccess) err return nil } -func (m mockAdapter) SelectUserByID(ctx context.Context, uid string) (domain.UserAccess, error) { +func (m mockAdapter) SelectUser(ctx context.Context, uid string) (domain.UserAccess, error) { return user, nil } @@ -68,12 +69,15 @@ func (m mockAdapter) UpsertUser(ctx context.Context, user domain.UserAccess) (do }, nil } -func (m mockAdapter) DeleteUserByID(ctx context.Context, uid string) error { +func (m mockAdapter) DeleteUser(ctx context.Context, uid string) error { return nil } func TestUserService(t *testing.T) { - service := NewUserService(mockAdapter{}, mockEncryptor{}, cache.NewCache(&config.CacheConfig{}), log.NewEmptyLogger()) + service := NewUserService(mockAdapter{}, mockEncryptor{}, cache.NewCache(&config.CacheConfig{}), &oauth2.Config{ + ClientID: "mock", + ClientSecret: "mock", + }, log.NewEmptyLogger()) t.Run("save user", func(t *testing.T) { assert.NoError(t, service.CreateUser(context.Background(), user)) @@ -121,6 +125,6 @@ func TestUserService(t *testing.T) { }) t.Run("delete user", func(t *testing.T) { - assert.NoError(t, service.DeleteUser(context.Background(), "mock")) + assert.NoError(t, service.RemoveUser(context.Background(), "mock")) }) } diff --git a/backend/services/auth/web/handler/delete.go b/backend/services/auth/web/handler/delete.go index b8d4b08..2cd4f7a 100644 --- a/backend/services/auth/web/handler/delete.go +++ b/backend/services/auth/web/handler/delete.go @@ -20,8 +20,9 @@ package handler import ( "context" + "fmt" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/port" "go-micro.dev/v4/client" ) @@ -45,6 +46,15 @@ func NewUserDeleteHandler( } func (u UserDeleteHandler) DeleteUser(ctx context.Context, uid *string, res *interface{}) error { - u.logger.Debugf("removing user %s", *uid) - return u.service.DeleteUser(ctx, *uid) + _, err, _ := group.Do(fmt.Sprintf("remove-%s", *uid), func() (interface{}, error) { + u.logger.Debugf("removing user %s", *uid) + if err := u.service.RemoveUser(ctx, *uid); err != nil { + u.logger.Debugf("could not delete user %s: %s", *uid, err.Error()) + return nil, err + } + + return nil, nil + }) + + return err } diff --git a/backend/services/auth/web/handler/handler.go b/backend/services/auth/web/handler/handler.go new file mode 100644 index 0000000..0846da7 --- /dev/null +++ b/backend/services/auth/web/handler/handler.go @@ -0,0 +1,23 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package handler + +import "golang.org/x/sync/singleflight" + +var group singleflight.Group diff --git a/backend/services/auth/web/handler/insert.go b/backend/services/auth/web/handler/insert.go new file mode 100644 index 0000000..13827b6 --- /dev/null +++ b/backend/services/auth/web/handler/insert.go @@ -0,0 +1,64 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package handler + +import ( + "context" + "fmt" + + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/port" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" +) + +type UserInsertHandler struct { + service port.UserAccessService + logger log.Logger +} + +func NewUserInsertHandler(service port.UserAccessService, logger log.Logger) UserInsertHandler { + return UserInsertHandler{ + service: service, + logger: logger, + } +} + +func (i UserInsertHandler) InsertUser(ctx context.Context, req response.UserResponse, res *domain.UserAccess) error { + _, err, _ := group.Do(fmt.Sprintf("insert-%s", req.ID), func() (interface{}, error) { + usr, err := i.service.UpdateUser(ctx, domain.UserAccess{ + ID: req.ID, + AccessToken: req.AccessToken, + RefreshToken: req.RefreshToken, + TokenType: req.TokenType, + Scope: req.Scope, + ExpiresAt: req.ExpiresAt, + ApiDomain: req.ApiDomain, + }) + + if err != nil { + i.logger.Errorf("could not update user: %s", err.Error()) + return nil, err + } + + return usr, nil + }) + + return err +} diff --git a/backend/services/auth/web/handler/select.go b/backend/services/auth/web/handler/select.go index a8d8791..d19cbfd 100644 --- a/backend/services/auth/web/handler/select.go +++ b/backend/services/auth/web/handler/select.go @@ -22,16 +22,13 @@ import ( "context" "time" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/port" pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" "go-micro.dev/v4/client" - "golang.org/x/sync/singleflight" ) -var group singleflight.Group - type UserSelectHandler struct { service port.UserAccessService client client.Client diff --git a/backend/services/auth/web/handler/select_test.go b/backend/services/auth/web/handler/select_test.go index efb60c1..d666f4d 100644 --- a/backend/services/auth/web/handler/select_test.go +++ b/backend/services/auth/web/handler/select_test.go @@ -23,31 +23,38 @@ import ( "testing" "time" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/cache" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/cache" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/config" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/adapter" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/domain" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/service" pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" "github.com/stretchr/testify/assert" + "golang.org/x/oauth2" ) type mockEncryptor struct{} -func (e mockEncryptor) Encrypt(text string) (string, error) { +func (e mockEncryptor) Encrypt(text string, key []byte) (string, error) { return string(text), nil } -func (e mockEncryptor) Decrypt(ciphertext string) (string, error) { +func (e mockEncryptor) Decrypt(ciphertext string, key []byte) (string, error) { return string(ciphertext), nil } func TestSelectCaching(t *testing.T) { adapter := adapter.NewMemoryUserAdapter() cache := cache.NewCache(&config.CacheConfig{}) - service := service.NewUserService(adapter, mockEncryptor{}, cache, log.NewEmptyLogger()) - pclient := pclient.NewPipedriveAuthClient("clientID", "clientSecret") + service := service.NewUserService(adapter, mockEncryptor{}, cache, &oauth2.Config{ + ClientID: "mock", + ClientSecret: "mock", + }, log.NewEmptyLogger()) + pclient := pclient.NewPipedriveAuthClient(&oauth2.Config{ + ClientID: "mock", + ClientSecret: "mock", + }) sel := NewUserSelectHandler(service, nil, pclient, log.NewEmptyLogger()) diff --git a/backend/services/auth/web/server.go b/backend/services/auth/web/server.go index c310f71..f23cc6b 100644 --- a/backend/services/auth/web/server.go +++ b/backend/services/auth/web/server.go @@ -19,57 +19,32 @@ package web import ( - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/rpc" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/adapter" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/port" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/core/service" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/service/rpc" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/handler" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/auth/web/message" - pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" - "go-micro.dev/v4/cache" - mclient "go-micro.dev/v4/client" ) type AuthRPCServer struct { - service port.UserAccessService - pipedriveAuth pclient.PipedriveAuthClient - logger log.Logger + selectHandler handler.UserSelectHandler + insertHandler handler.UserInsertHandler + deleteHandler handler.UserDeleteHandler } func NewAuthRPCServer( - persistenceConfig *config.PersistenceConfig, oauthConfig *config.OAuthCredentialsConfig, - cache cache.Cache, logger log.Logger) rpc.RPCEngine { - adptr := adapter.NewMemoryUserAdapter() - if persistenceConfig.Persistence.URL != "" { - adptr = adapter.NewMongoUserAdapter(persistenceConfig.Persistence.URL) - } - - service := service.NewUserService(adptr, crypto.NewAesEncryptor([]byte(oauthConfig.Credentials.ClientSecret)), cache, logger) + selectHandler handler.UserSelectHandler, + insetHandler handler.UserInsertHandler, + deleteHandler handler.UserDeleteHandler, +) rpc.RPCEngine { return AuthRPCServer{ - service: service, - pipedriveAuth: pclient.NewPipedriveAuthClient( - oauthConfig.Credentials.ClientID, oauthConfig.Credentials.ClientSecret, - ), - logger: logger, + selectHandler: selectHandler, + insertHandler: insetHandler, + deleteHandler: deleteHandler, } } func (a AuthRPCServer) BuildMessageHandlers() []rpc.RPCMessageHandler { - return []rpc.RPCMessageHandler{ - { - Topic: "insert-auth", - Queue: "pipedrive-auth", - Handler: message.BuildInsertMessageHandler(a.service).GetHandler(), - }, - } + return nil } -func (a AuthRPCServer) BuildHandlers(client mclient.Client, cache cache.Cache) []interface{} { - return []interface{}{ - handler.NewUserSelectHandler(a.service, client, a.pipedriveAuth, a.logger), - handler.NewUserDeleteHandler(a.service, client, a.logger), - } +func (a AuthRPCServer) BuildHandlers() []interface{} { + return []interface{}{a.selectHandler, a.insertHandler, a.deleteHandler} } From 8754202af8c0a35ead23a2d85cf61fd7bb4719ca Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 5 Jun 2023 16:31:32 +0500 Subject: [PATCH 126/145] refactor(services): builder service --- backend/services/builder/cmd/server.go | 20 +++--- .../builder/config/config.example.yml | 20 +++--- backend/services/builder/web/handler/build.go | 67 ++++++++++--------- backend/services/builder/web/handler/error.go | 10 ++- .../services/builder/web/handler/handler.go | 23 +++++++ backend/services/builder/web/server.go | 33 ++------- 6 files changed, 89 insertions(+), 84 deletions(-) create mode 100644 backend/services/builder/web/handler/handler.go diff --git a/backend/services/builder/cmd/server.go b/backend/services/builder/cmd/server.go index 68960e5..e8fa78e 100644 --- a/backend/services/builder/cmd/server.go +++ b/backend/services/builder/cmd/server.go @@ -19,10 +19,12 @@ package cmd import ( - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/rpc" + pkg "github.com/ONLYOFFICE/onlyoffice-integration-adapters" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/service/rpc" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/builder/web" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/builder/web/handler" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" "github.com/urfave/cli/v2" ) @@ -37,22 +39,18 @@ func Server() *cli.Command { Usage: "sets custom configuration path", Aliases: []string{"config", "conf", "c"}, }, - &cli.StringFlag{ - Name: "environment", - Usage: "sets servers environment (development, testing, production)", - Aliases: []string{"env", "e"}, - }, }, Action: func(c *cli.Context) error { var ( CONFIG_PATH = c.String("config_path") - // ENVIRONMENT = c.String("environment") ) - app := pkg.Bootstrap( - CONFIG_PATH, rpc.NewService, web.NewConfigRPCServer, + app := pkg.NewBootstrapper(CONFIG_PATH, pkg.WithModules( + rpc.NewService, web.NewConfigRPCServer, + handler.NewConfigHandler, shared.BuildNewOnlyofficeConfig(CONFIG_PATH), - ) + client.NewPipedriveApiClient, + )).Bootstrap() if err := app.Err(); err != nil { return err diff --git a/backend/services/builder/config/config.example.yml b/backend/services/builder/config/config.example.yml index 51166da..91f15a5 100644 --- a/backend/services/builder/config/config.example.yml +++ b/backend/services/builder/config/config.example.yml @@ -1,21 +1,15 @@ namespace: "pipedrive" name: "builder" version: 0 -address: ":6060" +address: ":6260" repl_address: ":7979" debug: false -persistence: - url: "" registry: - addresses: ["127.0.0.1:8500"] + addresses: [""] type: 2 -messaging: - type: 1 - addresses: ["0.0.0.0:5672"] - durable: true tracer: enable: false - address: "http://127.0.0.1:9411/api/v2/spans" + address: "" type: 1 resilience: rate_limiter: @@ -26,10 +20,14 @@ logger: name: "builder-logger" level: 1 color: true -oauth: +credentials: client_id: "" client_secret: "" + redirect_url: "" onlyoffice: builder: - callback_url: "" + document_server_url: "" + document_server_secret: "" + document_server_header: "" gateway_url: "" + callback_url: "" \ No newline at end of file diff --git a/backend/services/builder/web/handler/build.go b/backend/services/builder/web/handler/build.go index 4093bcc..e2d3a9c 100644 --- a/backend/services/builder/web/handler/build.go +++ b/backend/services/builder/web/handler/build.go @@ -20,7 +20,6 @@ package handler import ( "context" - "errors" "fmt" "net/http" "net/url" @@ -28,49 +27,43 @@ import ( "strings" "sync" - plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/config" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/crypto" + plog "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client/model" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/constants" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" "github.com/mileusna/useragent" "go-micro.dev/v4/client" - "golang.org/x/sync/singleflight" ) -var _ErrNoSettingsFound = errors.New("could not find document server settings") -var _ErrOperationTimeout = errors.New("operation timeout") - type ConfigHandler struct { - namespace string - logger plog.Logger - client client.Client - apiClient pclient.PipedriveApiClient - jwtManager crypto.JwtManager - gatewayURL string - callbackURL string - group singleflight.Group + client client.Client + apiClient pclient.PipedriveApiClient + jwtManager crypto.JwtManager + config *config.ServerConfig + onlyoffice *shared.OnlyofficeConfig + logger plog.Logger } func NewConfigHandler( - namespace string, - logger plog.Logger, client client.Client, jwtManager crypto.JwtManager, - gatewayURL string, - callbackURL string, + apiClient pclient.PipedriveApiClient, + config *config.ServerConfig, + onlyoffice *shared.OnlyofficeConfig, + logger plog.Logger, ) ConfigHandler { return ConfigHandler{ - namespace: namespace, - logger: logger, - client: client, - apiClient: pclient.NewPipedriveApiClient(), - jwtManager: jwtManager, - gatewayURL: gatewayURL, - callbackURL: callbackURL, + client: client, + apiClient: apiClient, + jwtManager: jwtManager, + config: config, + onlyoffice: onlyoffice, + logger: logger, } } @@ -106,7 +99,15 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui go func() { defer wg.Done() var docs response.DocSettingsResponse - if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:settings", c.namespace), "SettingsSelectHandler.GetSettings", fmt.Sprint(req.CID)), &docs); err != nil { + if err := c.client.Call( + ctx, + c.client.NewRequest( + fmt.Sprintf("%s:settings", c.config.Namespace), + "SettingsSelectHandler.GetSettings", + fmt.Sprint(req.CID), + ), + &docs, + ); err != nil { c.logger.Debugf("could not document server settings: %s", err.Error()) errorsChan <- err return @@ -114,7 +115,7 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui if docs.DocAddress == "" || docs.DocSecret == "" || docs.DocHeader == "" { c.logger.Debugf("no settings found") - errorsChan <- _ErrNoSettingsFound + errorsChan <- ErrNoSettingsFound return } @@ -131,7 +132,7 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui case err := <-errorsChan: return config, err case <-ctx.Done(): - return config, _ErrOperationTimeout + return config, ErrOperationTimeout default: c.logger.Debugf("select default") } @@ -172,7 +173,9 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui }, CallbackURL: fmt.Sprintf( "%s/callback?cid=%d&did=%s&fid=%s&filename=%s", - c.callbackURL, usr.CompanyID, req.Deal, req.FileID, url.QueryEscape(filename), + c.onlyoffice.Onlyoffice.Builder.CallbackURL, + usr.CompanyID, req.Deal, req.FileID, + url.QueryEscape(filename), ), Customization: response.Customization{ Goback: response.Goback{ @@ -220,9 +223,9 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui func (c ConfigHandler) BuildConfig(ctx context.Context, payload request.BuildConfigRequest, res *response.BuildConfigResponse) error { c.logger.Debugf("processing a docs config: %s", payload.Filename) - config, err, _ := c.group.Do(fmt.Sprint(payload.UID+payload.CID), func() (interface{}, error) { + config, err, _ := group.Do(fmt.Sprint(payload.UID+payload.CID), func() (interface{}, error) { req := c.client.NewRequest( - fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", + fmt.Sprintf("%s:auth", c.config.Namespace), "UserSelectHandler.GetUser", fmt.Sprint(payload.UID+payload.CID), ) diff --git a/backend/services/builder/web/handler/error.go b/backend/services/builder/web/handler/error.go index 3e9d75d..7b9a794 100644 --- a/backend/services/builder/web/handler/error.go +++ b/backend/services/builder/web/handler/error.go @@ -20,6 +20,10 @@ package handler import "errors" -var ErrInvalidContextValue = errors.New("could not extract context value") -var ErrEmptyIdValue = errors.New("could not perform current action with an empty id") -var ErrUnauthorizedAccess = errors.New("unauthorized file access") +var ( + ErrInvalidContextValue = errors.New("could not extract context value") + ErrEmptyIdValue = errors.New("could not perform current action with an empty id") + ErrUnauthorizedAccess = errors.New("unauthorized file access") + ErrNoSettingsFound = errors.New("could not find document server settings") + ErrOperationTimeout = errors.New("operation timeout") +) diff --git a/backend/services/builder/web/handler/handler.go b/backend/services/builder/web/handler/handler.go new file mode 100644 index 0000000..0846da7 --- /dev/null +++ b/backend/services/builder/web/handler/handler.go @@ -0,0 +1,23 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package handler + +import "golang.org/x/sync/singleflight" + +var group singleflight.Group diff --git a/backend/services/builder/web/server.go b/backend/services/builder/web/server.go index 8b68ca4..a53652e 100644 --- a/backend/services/builder/web/server.go +++ b/backend/services/builder/web/server.go @@ -19,38 +19,19 @@ package web import ( - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" - plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/rpc" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/service/rpc" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/builder/web/handler" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" - "go-micro.dev/v4/cache" - mclient "go-micro.dev/v4/client" ) type ConfigRPCServer struct { - namespace string - jwtManager crypto.JwtManager - logger plog.Logger - gatewayURL string - callbackURL string + configHandler handler.ConfigHandler } func NewConfigRPCServer( - serverConfig *config.ServerConfig, - credentialsConfig *config.OAuthCredentialsConfig, - onlyofficeConfig *shared.OnlyofficeConfig, - logger log.Logger, + configHandler handler.ConfigHandler, ) rpc.RPCEngine { - jwtManager := crypto.NewOnlyofficeJwtManager() return ConfigRPCServer{ - namespace: serverConfig.Namespace, - jwtManager: jwtManager, - logger: logger, - gatewayURL: onlyofficeConfig.Onlyoffice.Builder.GatewayURL, - callbackURL: onlyofficeConfig.Onlyoffice.Builder.CallbackURL, + configHandler: configHandler, } } @@ -58,8 +39,6 @@ func (a ConfigRPCServer) BuildMessageHandlers() []rpc.RPCMessageHandler { return nil } -func (a ConfigRPCServer) BuildHandlers(c mclient.Client, cache cache.Cache) []interface{} { - return []interface{}{ - handler.NewConfigHandler(a.namespace, a.logger, c, a.jwtManager, a.gatewayURL, a.callbackURL), - } +func (a ConfigRPCServer) BuildHandlers() []interface{} { + return []interface{}{a.configHandler} } From beeb5d5ce81de392d0b0c03f7378fea227defeac Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 5 Jun 2023 16:34:25 +0500 Subject: [PATCH 127/145] refactor(services): callback service --- backend/services/callback/cmd/server.go | 20 +-- .../callback/config/config.example.yml | 26 +-- .../callback/web/controller/callback.go | 161 ++++++------------ backend/services/callback/web/server.go | 37 ++-- 4 files changed, 84 insertions(+), 160 deletions(-) diff --git a/backend/services/callback/cmd/server.go b/backend/services/callback/cmd/server.go index 45a11c8..ad69b7e 100644 --- a/backend/services/callback/cmd/server.go +++ b/backend/services/callback/cmd/server.go @@ -19,10 +19,12 @@ package cmd import ( - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg" - chttp "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/http" + pkg "github.com/ONLYOFFICE/onlyoffice-integration-adapters" + chttp "github.com/ONLYOFFICE/onlyoffice-integration-adapters/service/http" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/callback/web" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/callback/web/controller" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" "github.com/urfave/cli/v2" ) @@ -37,22 +39,18 @@ func Server() *cli.Command { Usage: "sets custom configuration path", Aliases: []string{"config", "conf", "c"}, }, - &cli.StringFlag{ - Name: "environment", - Usage: "sets servers environment (development, testing, production)", - Aliases: []string{"env", "e"}, - }, }, Action: func(c *cli.Context) error { var ( CONFIG_PATH = c.String("config_path") - // ENVIRONMENT = c.String("environment") ) - app := pkg.Bootstrap( - CONFIG_PATH, chttp.NewService, web.NewServer, + app := pkg.NewBootstrapper(CONFIG_PATH, pkg.WithModules( + chttp.NewService, web.NewServer, + controller.NewCallbackController, shared.BuildNewOnlyofficeConfig(CONFIG_PATH), - ) + client.NewPipedriveApiClient, + )).Bootstrap() if err := app.Err(); err != nil { return err diff --git a/backend/services/callback/config/config.example.yml b/backend/services/callback/config/config.example.yml index 9d5e7de..1e4de43 100644 --- a/backend/services/callback/config/config.example.yml +++ b/backend/services/callback/config/config.example.yml @@ -1,21 +1,15 @@ namespace: "pipedrive" name: "callback" version: 0 -address: ":5044" +address: ":5454" repl_address: ":3132" debug: false -persistence: - url: "" registry: - addresses: ["127.0.0.1:8500"] + addresses: [""] type: 2 -messaging: - type: 1 - addresses: ["0.0.0.0:5672"] - durable: true tracer: enable: false - address: "http://127.0.0.1:9411/api/v2/spans" + address: "" type: 1 resilience: rate_limiter: @@ -23,15 +17,21 @@ resilience: iplimit: 100 circuit_breaker: timeout: 2500 -cors: - origins: ["", ""] logger: name: "callback-logger" level: 1 color: true -worker: - addresses: ["0.0.0.0:6379"] +credentials: + client_id: "" + client_secret: "" + redirect_url: "" onlyoffice: + builder: + document_server_url: "" + document_server_secret: "" + document_server_header: "" + gateway_url: "" + callback_url: "" callback: max_size: 210000000000 upload_timeout: 120 diff --git a/backend/services/callback/web/controller/callback.go b/backend/services/callback/web/controller/callback.go index 61565f8..a07d4a3 100644 --- a/backend/services/callback/web/controller/callback.go +++ b/backend/services/callback/web/controller/callback.go @@ -23,124 +23,49 @@ import ( "encoding/json" "fmt" "net/http" - "strconv" "strings" - "sync" "time" - plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/config" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/crypto" + plog "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client/model" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/message" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" "go-micro.dev/v4/client" "go-micro.dev/v4/util/backoff" - "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) -type callbackController struct { - namespace string - maxSize int64 - uploadTimeout int - logger plog.Logger - client client.Client - pipedriveAPI pclient.PipedriveApiClient - jwtManager crypto.JwtManager +type CallbackController struct { + client client.Client + pipedriveAPI pclient.PipedriveApiClient + jwtManager crypto.JwtManager + config *config.ServerConfig + onlyoffice *shared.OnlyofficeConfig + logger plog.Logger } func NewCallbackController( - namespace string, - maxSize int64, - uploadTimeout int, - logger plog.Logger, client client.Client, -) *callbackController { - return &callbackController{ - namespace: namespace, - maxSize: maxSize, - uploadTimeout: uploadTimeout, - logger: logger, - client: client, - pipedriveAPI: pclient.NewPipedriveApiClient(), - jwtManager: crypto.NewOnlyofficeJwtManager(), - } -} - -func (c callbackController) UploadFile(ctx context.Context, msg message.JobMessage) error { - var wg sync.WaitGroup - wg.Add(2) - userChan := make(chan response.UserResponse, 1) - sizeChan := make(chan int64, 1) - errChan := make(chan error, 2) - - go func() { - defer wg.Done() - - c.logger.Debugf("trying to get an access token") - req := c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", msg.UID) - var ures response.UserResponse - if err := c.client.Call(ctx, req, &ures, client.WithRetries(3), client.WithBackoff(func(ctx context.Context, req client.Request, attempts int) (time.Duration, error) { - return backoff.Do(attempts), nil - })); err != nil { - errChan <- err - return - } - - c.logger.Debugf("populating user channel") - userChan <- ures - c.logger.Debugf("successfully populated user channel") - }() - - go func() { - defer wg.Done() - - headResp, err := otelhttp.Head(ctx, msg.Url) - if err != nil { - errChan <- err - return - } - - size, err := strconv.ParseInt(headResp.Header.Get("Content-Length"), 10, 64) - if err != nil { - errChan <- err - return - } - - c.logger.Debugf("populating file size channel") - sizeChan <- size - c.logger.Debugf("successfully populated file size channel") - }() - - c.logger.Debugf("callback is waiting for waitgroup") - wg.Wait() - c.logger.Debugf("callback waitgroup ok") - - select { - case err := <-errChan: - c.logger.Debugf("an error from the channel: %s", err.Error()) - return err - default: - c.logger.Debugf("select default") - } - - ures := <-userChan - if err := c.pipedriveAPI.UploadFile(ctx, msg.Url, msg.Deal, msg.FileID, msg.Filename, <-sizeChan, model.Token{ - AccessToken: ures.AccessToken, - RefreshToken: ures.RefreshToken, - TokenType: ures.TokenType, - Scope: ures.Scope, - ApiDomain: ures.ApiDomain, - }); err != nil { - c.logger.Debugf("could not upload an onlyoffice file to pipedrive: %s", err.Error()) - return err + pipedriveAPI pclient.PipedriveApiClient, + jwtManager crypto.JwtManager, + config *config.ServerConfig, + onlyoffice *shared.OnlyofficeConfig, + logger plog.Logger, +) *CallbackController { + return &CallbackController{ + client: client, + pipedriveAPI: pipedriveAPI, + jwtManager: jwtManager, + config: config, + onlyoffice: onlyoffice, + logger: logger, } - - return nil } -func (c callbackController) BuildPostHandleCallback() http.HandlerFunc { +func (c CallbackController) BuildPostHandleCallback() http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { query := r.URL.Query() cid, did, fid := strings.TrimSpace(query.Get("cid")), strings.TrimSpace(query.Get("did")), strings.TrimSpace(query.Get("fid")) @@ -174,7 +99,7 @@ func (c callbackController) BuildPostHandleCallback() http.HandlerFunc { return } - req := c.client.NewRequest(fmt.Sprintf("%s:settings", c.namespace), "SettingsSelectHandler.GetSettings", cid) + req := c.client.NewRequest(fmt.Sprintf("%s:settings", c.config.Namespace), "SettingsSelectHandler.GetSettings", cid) var res response.DocSettingsResponse if err := c.client.Call(r.Context(), req, &res); err != nil { c.logger.Errorf("could not extract doc server settings %s", cid) @@ -219,7 +144,8 @@ func (c callbackController) BuildPostHandleCallback() http.HandlerFunc { usr := body.Users[0] if usr != "" { - if err := c.pipedriveAPI.ValidateFileSize(ctx, c.maxSize, body.URL); err != nil { + size, err := c.pipedriveAPI.ValidateFileSize(ctx, c.onlyoffice.Onlyoffice.Callback.MaxSize, body.URL) + if err != nil { c.logger.Errorf("could not validate file %s: %s", filename, err.Error()) rw.WriteHeader(http.StatusBadRequest) rw.Write(response.CallbackResponse{ @@ -228,20 +154,35 @@ func (c callbackController) BuildPostHandleCallback() http.HandlerFunc { return } - uctx, cancel := context.WithTimeout(ctx, time.Duration(c.uploadTimeout)*time.Second) + uctx, cancel := context.WithTimeout(ctx, time.Duration(c.onlyoffice.Onlyoffice.Callback.UploadTimeout)*time.Second) defer cancel() - if err := c.UploadFile(uctx, message.JobMessage{ - UID: usr, - Deal: did, - FileID: fid, - Filename: filename, - Url: body.URL, + + req := c.client.NewRequest(fmt.Sprintf("%s:auth", c.config.Namespace), "UserSelectHandler.GetUser", usr) + var ures response.UserResponse + if err := c.client.Call(uctx, req, &ures, client.WithRetries(3), client.WithBackoff(func(ctx context.Context, req client.Request, attempts int) (time.Duration, error) { + return backoff.Do(attempts), nil + })); err != nil { + c.logger.Errorf("could not get user tokens: %s", err.Error()) + rw.WriteHeader(http.StatusBadRequest) + rw.Write(response.CallbackResponse{ + Error: 1, + }.ToJSON()) + return + } + + if err := c.pipedriveAPI.UploadFile(ctx, body.URL, did, fid, filename, size, model.Token{ + AccessToken: ures.AccessToken, + RefreshToken: ures.RefreshToken, + TokenType: ures.TokenType, + Scope: ures.Scope, + ApiDomain: ures.ApiDomain, }); err != nil { - c.logger.Errorf("could not upload file %s: %s", filename, err.Error()) + c.logger.Debugf("could not upload an onlyoffice file to pipedrive: %s", err.Error()) rw.WriteHeader(http.StatusBadRequest) rw.Write(response.CallbackResponse{ Error: 1, }.ToJSON()) + return } } } diff --git a/backend/services/callback/web/server.go b/backend/services/callback/web/server.go index 2e1eb91..5c4e1fd 100644 --- a/backend/services/callback/web/server.go +++ b/backend/services/callback/web/server.go @@ -21,25 +21,17 @@ package web import ( "net/http" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" - chttp "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/http" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" + chttp "github.com/ONLYOFFICE/onlyoffice-integration-adapters/service/http" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/callback/web/controller" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" "github.com/gin-gonic/gin" "github.com/go-chi/chi/v5" chimiddleware "github.com/go-chi/chi/v5/middleware" - "go-micro.dev/v4/cache" - "go-micro.dev/v4/client" ) type CallbackService struct { - namespace string - mux *chi.Mux - client client.Client - logger log.Logger - maxSize int64 - uploadTimeout int + mux *chi.Mux + callbackController *controller.CallbackController } // ApplyMiddleware useed to apply http server middlewares. @@ -49,46 +41,39 @@ func (s CallbackService) ApplyMiddleware(middlewares ...func(http.Handler) http. // NewService initializes http server with options. func NewServer( - serverConfig *config.ServerConfig, - workerConfig *config.WorkerConfig, - onlyofficeConfig *shared.OnlyofficeConfig, + callbackController *controller.CallbackController, logger log.Logger, ) chttp.ServerEngine { gin.SetMode(gin.ReleaseMode) service := CallbackService{ - namespace: serverConfig.Namespace, - mux: chi.NewRouter(), - logger: logger, - maxSize: onlyofficeConfig.Onlyoffice.Callback.MaxSize, - uploadTimeout: onlyofficeConfig.Onlyoffice.Callback.UploadTimeout, + mux: chi.NewRouter(), + callbackController: callbackController, } return service } // NewHandler returns http server engine. -func (s CallbackService) NewHandler(client client.Client, cache cache.Cache) interface { +func (s CallbackService) NewHandler() interface { ServeHTTP(w http.ResponseWriter, r *http.Request) } { - return s.InitializeServer(client) + return s.InitializeServer() } // InitializeServer sets all injected dependencies. -func (s *CallbackService) InitializeServer(c client.Client) *chi.Mux { - s.client = c +func (s *CallbackService) InitializeServer() *chi.Mux { s.InitializeRoutes() return s.mux } // InitializeRoutes builds all http routes. func (s *CallbackService) InitializeRoutes() { - callbackController := controller.NewCallbackController(s.namespace, s.maxSize, s.uploadTimeout, s.logger, s.client) s.mux.Group(func(r chi.Router) { r.Use(chimiddleware.Recoverer) r.NotFound(func(rw http.ResponseWriter, r *http.Request) { http.Redirect(rw, r.WithContext(r.Context()), "https://onlyoffice.com", http.StatusMovedPermanently) }) - r.Post("/callback", callbackController.BuildPostHandleCallback()) + r.Post("/callback", s.callbackController.BuildPostHandleCallback()) }) } From d63c475aa7a5eb294d9298d594476294cc2ac13c Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 5 Jun 2023 16:37:29 +0500 Subject: [PATCH 128/145] refactor(services): settings service --- backend/services/settings/cmd/server.go | 25 +++++--- .../settings/config/config.example.yml | 16 +++-- .../delete.go => core/adapter/adapter.go} | 26 +++----- .../web/{message => core/adapter}/error.go | 7 ++- .../settings/web/core/adapter/memory.go | 7 +-- .../settings/web/core/adapter/mongo.go | 7 +-- .../services/settings/web/core/port/input.go | 2 +- .../settings/web/core/service/error.go | 7 ++- .../settings/web/core/service/settings.go | 35 +++++------ .../services/settings/web/handler/delete.go | 60 +++++++++++++++++++ .../services/settings/web/handler/handler.go | 23 +++++++ .../web/{message => handler}/insert.go | 45 ++++++++------ .../services/settings/web/handler/select.go | 5 +- .../settings/web/handler/select_test.go | 21 ++++--- backend/services/settings/web/server.go | 54 ++++------------- 15 files changed, 202 insertions(+), 138 deletions(-) rename backend/services/settings/web/{message/delete.go => core/adapter/adapter.go} (57%) rename backend/services/settings/web/{message => core/adapter}/error.go (81%) create mode 100644 backend/services/settings/web/handler/delete.go create mode 100644 backend/services/settings/web/handler/handler.go rename backend/services/settings/web/{message => handler}/insert.go (52%) diff --git a/backend/services/settings/cmd/server.go b/backend/services/settings/cmd/server.go index 8d5ff90..dd64f33 100644 --- a/backend/services/settings/cmd/server.go +++ b/backend/services/settings/cmd/server.go @@ -19,9 +19,13 @@ package cmd import ( - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/rpc" + pkg "github.com/ONLYOFFICE/onlyoffice-integration-adapters" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/service/rpc" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/adapter" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/service" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/handler" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" "github.com/urfave/cli/v2" ) @@ -36,19 +40,22 @@ func Server() *cli.Command { Usage: "sets custom configuration path", Aliases: []string{"config", "conf", "c"}, }, - &cli.StringFlag{ - Name: "environment", - Usage: "sets servers environment (development, testing, production)", - Aliases: []string{"env", "e"}, - }, }, Action: func(c *cli.Context) error { var ( CONFIG_PATH = c.String("config_path") - // ENVIRONMENT = c.String("environment") ) - app := pkg.Bootstrap(CONFIG_PATH, rpc.NewService, web.NewDocserverRPCServer) + app := pkg.NewBootstrapper(CONFIG_PATH, pkg.WithModules( + rpc.NewService, web.NewDocserverRPCServer, + adapter.BuildNewSettingsAdapter, + service.NewSettingsService, + handler.NewSettingsSelectHandler, + handler.NewSettingsInsertHandler, + handler.NewSettingsDeleteHandler, + shared.BuildNewIntegrationCredentialsConfig(CONFIG_PATH), + )).Bootstrap() + if err := app.Err(); err != nil { return err } diff --git a/backend/services/settings/config/config.example.yml b/backend/services/settings/config/config.example.yml index 4710d56..e1a22e5 100644 --- a/backend/services/settings/config/config.example.yml +++ b/backend/services/settings/config/config.example.yml @@ -4,18 +4,15 @@ version: 0 address: ":5150" repl_address: ":8889" debug: false -# persistence: -# url: "" +storage: + url: "" + type: 1 registry: - addresses: ["127.0.0.1:8500"] + addresses: [""] type: 2 -messaging: - type: 1 - addresses: ["0.0.0.0:5672"] - durable: true tracer: enable: false - address: "http://127.0.0.1:9411/api/v2/spans" + address: "" type: 1 resilience: rate_limiter: @@ -26,6 +23,7 @@ logger: name: "settings-logger" level: 1 color: true -oauth: +credentials: client_id: "" client_secret: "" + redirect_url: "" \ No newline at end of file diff --git a/backend/services/settings/web/message/delete.go b/backend/services/settings/web/core/adapter/adapter.go similarity index 57% rename from backend/services/settings/web/message/delete.go rename to backend/services/settings/web/core/adapter/adapter.go index 62a5612..223fad7 100644 --- a/backend/services/settings/web/message/delete.go +++ b/backend/services/settings/web/core/adapter/adapter.go @@ -16,30 +16,18 @@ * */ -package message +package adapter import ( - "context" - + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/config" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/port" ) -type DeleteMessageHandler struct { - service port.DocSettingsService -} - -func BuildDeleteMessageHandler(service port.DocSettingsService) DeleteMessageHandler { - return DeleteMessageHandler{ - service: service, +func BuildNewSettingsAdapter(config *config.StorageConfig) port.DocSettingsServiceAdapter { + adapter := NewMemoryDocserverAdapter() + if config.Storage.URL != "" { + adapter = NewMongoDocserverAdapter(config.Storage.URL) } -} -func (i DeleteMessageHandler) GetHandler() func(context.Context, interface{}) error { - return func(ctx context.Context, payload interface{}) error { - if cid, ok := payload.(string); !ok { - return _ErrInvalidMessagePayload - } else { - return i.service.DeleteSettings(ctx, cid) - } - } + return adapter } diff --git a/backend/services/settings/web/message/error.go b/backend/services/settings/web/core/adapter/error.go similarity index 81% rename from backend/services/settings/web/message/error.go rename to backend/services/settings/web/core/adapter/error.go index 3b70cef..1856992 100644 --- a/backend/services/settings/web/message/error.go +++ b/backend/services/settings/web/core/adapter/error.go @@ -16,8 +16,11 @@ * */ -package message +package adapter import "errors" -var _ErrInvalidMessagePayload = errors.New("invalid message payload") +var ( + ErrNoCompanySettings = errors.New("no company settings") + ErrInvalidCompanyID = errors.New("invalid cid format") +) diff --git a/backend/services/settings/web/core/adapter/memory.go b/backend/services/settings/web/core/adapter/memory.go index 9aa3097..333bb6c 100644 --- a/backend/services/settings/web/core/adapter/memory.go +++ b/backend/services/settings/web/core/adapter/memory.go @@ -21,14 +21,11 @@ package adapter import ( "context" "encoding/json" - "errors" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/port" ) -var _ErrNoCompanySettings = errors.New("no company settings") - type memoryDocserverAdapter struct { kvs map[string][]byte } @@ -60,7 +57,7 @@ func (m *memoryDocserverAdapter) SelectSettings(ctx context.Context, cid string) var settings domain.DocSettings if !ok { - return settings, _ErrNoCompanySettings + return settings, ErrNoCompanySettings } if err := json.Unmarshal(buffer, &settings); err != nil { @@ -80,7 +77,7 @@ func (m *memoryDocserverAdapter) UpsertSettings(ctx context.Context, settings do func (m *memoryDocserverAdapter) DeleteSettings(ctx context.Context, cid string) error { if _, ok := m.kvs[cid]; !ok { - return _ErrNoCompanySettings + return ErrNoCompanySettings } delete(m.kvs, cid) diff --git a/backend/services/settings/web/core/adapter/mongo.go b/backend/services/settings/web/core/adapter/mongo.go index 3326252..bfda045 100644 --- a/backend/services/settings/web/core/adapter/mongo.go +++ b/backend/services/settings/web/core/adapter/mongo.go @@ -20,7 +20,6 @@ package adapter import ( "context" - "errors" "log" "strings" "time" @@ -34,8 +33,6 @@ import ( "go.mongodb.org/mongo-driver/mongo/options" ) -var _ErrInvalidCompanyID error = errors.New("invalid cid format") - type docSettingsCollection struct { mgm.DefaultModel `bson:",inline"` CompanyID string `json:"company_id" bson:"company_id"` @@ -101,7 +98,7 @@ func (m *mongoUserAdapter) SelectSettings(ctx context.Context, cid string) (doma cid = strings.TrimSpace(cid) if cid == "" { - return domain.DocSettings{}, _ErrInvalidCompanyID + return domain.DocSettings{}, ErrInvalidCompanyID } settings := &docSettingsCollection{} @@ -126,7 +123,7 @@ func (m *mongoUserAdapter) DeleteSettings(ctx context.Context, cid string) error cid = strings.TrimSpace(cid) if cid == "" { - return _ErrInvalidCompanyID + return ErrInvalidCompanyID } _, err := mgm.Coll(&docSettingsCollection{}).DeleteMany(ctx, bson.M{"company_id": bson.M{operator.Eq: cid}}) diff --git a/backend/services/settings/web/core/port/input.go b/backend/services/settings/web/core/port/input.go index cf663ab..6f8d910 100644 --- a/backend/services/settings/web/core/port/input.go +++ b/backend/services/settings/web/core/port/input.go @@ -28,5 +28,5 @@ type DocSettingsService interface { CreateSettings(ctx context.Context, settings domain.DocSettings) error GetSettings(ctx context.Context, cid string) (domain.DocSettings, error) UpdateSettings(ctx context.Context, settings domain.DocSettings) (domain.DocSettings, error) - DeleteSettings(ctx context.Context, cid string) error + RemoveSettings(ctx context.Context, cid string) error } diff --git a/backend/services/settings/web/core/service/error.go b/backend/services/settings/web/core/service/error.go index 241bb98..79decfb 100644 --- a/backend/services/settings/web/core/service/error.go +++ b/backend/services/settings/web/core/service/error.go @@ -18,7 +18,12 @@ package service -import "fmt" +import ( + "errors" + "fmt" +) + +var ErrOperationTimeout = errors.New("operation timeout") type InvalidServiceParameterError struct { Name string diff --git a/backend/services/settings/web/core/service/settings.go b/backend/services/settings/web/core/service/settings.go index 477dc2a..90c2062 100644 --- a/backend/services/settings/web/core/service/settings.go +++ b/backend/services/settings/web/core/service/settings.go @@ -20,38 +20,39 @@ package service import ( "context" - "errors" "strings" "time" - plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/crypto" + plog "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/port" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" "github.com/mitchellh/mapstructure" "go-micro.dev/v4/cache" + "golang.org/x/oauth2" ) -var _ErrOperationTimeout = errors.New("operation timeout") - type settingsService struct { - adapter port.DocSettingsServiceAdapter - encryptor crypto.Encryptor - cache cache.Cache - logger plog.Logger + adapter port.DocSettingsServiceAdapter + encryptor crypto.Encryptor + cache cache.Cache + credentials *oauth2.Config + logger plog.Logger } func NewSettingsService( adapter port.DocSettingsServiceAdapter, encryptor crypto.Encryptor, cache cache.Cache, + credentials *oauth2.Config, logger plog.Logger, ) port.DocSettingsService { return settingsService{ - adapter: adapter, - encryptor: encryptor, - cache: cache, - logger: logger, + adapter: adapter, + encryptor: encryptor, + cache: cache, + credentials: credentials, + logger: logger, } } @@ -61,7 +62,7 @@ func (s settingsService) CreateSettings(ctx context.Context, settings domain.Doc return err } - esecret, err := s.encryptor.Encrypt(settings.DocSecret) + esecret, err := s.encryptor.Encrypt(settings.DocSecret, []byte(s.credentials.ClientSecret)) if err != nil { return err } @@ -108,7 +109,7 @@ func (s settingsService) GetSettings(ctx context.Context, cid string) (domain.Do } s.logger.Debugf("found settings: %v", settings) - dsecret, err := s.encryptor.Decrypt(settings.DocSecret) + dsecret, err := s.encryptor.Decrypt(settings.DocSecret, []byte(s.credentials.ClientSecret)) if err != nil { return settings, err } @@ -127,7 +128,7 @@ func (s settingsService) UpdateSettings(ctx context.Context, settings domain.Doc return settings, err } - esecret, err := s.encryptor.Encrypt(settings.DocSecret) + esecret, err := s.encryptor.Encrypt(settings.DocSecret, []byte(s.credentials.ClientSecret)) if err != nil { return settings, err } @@ -150,7 +151,7 @@ func (s settingsService) UpdateSettings(ctx context.Context, settings domain.Doc return settings, nil } -func (s settingsService) DeleteSettings(ctx context.Context, cid string) error { +func (s settingsService) RemoveSettings(ctx context.Context, cid string) error { id := strings.TrimSpace(cid) s.logger.Debugf("validating cid %s to perform a delete action", id) diff --git a/backend/services/settings/web/handler/delete.go b/backend/services/settings/web/handler/delete.go new file mode 100644 index 0000000..cb34b89 --- /dev/null +++ b/backend/services/settings/web/handler/delete.go @@ -0,0 +1,60 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package handler + +import ( + "context" + "fmt" + + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/port" + "go-micro.dev/v4/client" +) + +type SettingsDeleteHandler struct { + service port.DocSettingsService + client client.Client + logger log.Logger +} + +func NewSettingsDeleteHandler( + service port.DocSettingsService, + client client.Client, + logger log.Logger, +) SettingsDeleteHandler { + return SettingsDeleteHandler{ + service: service, + client: client, + logger: logger, + } +} + +func (u SettingsDeleteHandler) DeleteSettings(ctx context.Context, cid *string, res *interface{}) error { + _, err, _ := group.Do(fmt.Sprintf("remove-%s", *cid), func() (interface{}, error) { + u.logger.Debugf("removing settings %s", *cid) + if err := u.service.RemoveSettings(ctx, *cid); err != nil { + u.logger.Debugf("could not delete settings %s: %s", *cid, err.Error()) + return nil, err + } + + return nil, nil + }) + + return err +} diff --git a/backend/services/settings/web/handler/handler.go b/backend/services/settings/web/handler/handler.go new file mode 100644 index 0000000..0846da7 --- /dev/null +++ b/backend/services/settings/web/handler/handler.go @@ -0,0 +1,23 @@ +/** + * + * (c) Copyright Ascensio System SIA 2023 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package handler + +import "golang.org/x/sync/singleflight" + +var group singleflight.Group diff --git a/backend/services/settings/web/message/insert.go b/backend/services/settings/web/handler/insert.go similarity index 52% rename from backend/services/settings/web/message/insert.go rename to backend/services/settings/web/handler/insert.go index a351c4f..6071859 100644 --- a/backend/services/settings/web/message/insert.go +++ b/backend/services/settings/web/handler/insert.go @@ -16,40 +16,49 @@ * */ -package message +package handler import ( "context" "fmt" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/port" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" - "github.com/mitchellh/mapstructure" ) -type InsertMessageHandler struct { +type SettingsInsertHandler struct { service port.DocSettingsService + logger log.Logger } -func BuildInsertMessageHandler(service port.DocSettingsService) InsertMessageHandler { - return InsertMessageHandler{ +func NewSettingsInsertHandler( + service port.DocSettingsService, + logger log.Logger, +) SettingsInsertHandler { + return SettingsInsertHandler{ service: service, + logger: logger, } } -func (i InsertMessageHandler) GetHandler() func(context.Context, interface{}) error { - return func(ctx context.Context, payload interface{}) error { - var settings request.DocSettings - if err := mapstructure.Decode(payload, &settings); err != nil { - return err - } - _, err := i.service.UpdateSettings(ctx, domain.DocSettings{ - CompanyID: fmt.Sprint(settings.CompanyID), - DocAddress: settings.DocAddress, - DocSecret: settings.DocSecret, - DocHeader: settings.DocHeader, +func (i SettingsInsertHandler) InsertSettings(ctx context.Context, req request.DocSettings, res *interface{}) error { + _, err, _ := group.Do(fmt.Sprintf("insert-%d", req.CompanyID), func() (interface{}, error) { + settings, err := i.service.UpdateSettings(ctx, domain.DocSettings{ + CompanyID: fmt.Sprint(req.CompanyID), + DocAddress: req.DocAddress, + DocHeader: req.DocHeader, + DocSecret: req.DocSecret, }) - return err - } + + if err != nil { + i.logger.Errorf("could not update settings: %s", err.Error()) + return nil, err + } + + return settings, nil + }) + + return err } diff --git a/backend/services/settings/web/handler/select.go b/backend/services/settings/web/handler/select.go index b951f87..154f164 100644 --- a/backend/services/settings/web/handler/select.go +++ b/backend/services/settings/web/handler/select.go @@ -21,16 +21,13 @@ package handler import ( "context" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/port" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" "go-micro.dev/v4/client" - "golang.org/x/sync/singleflight" ) -var group singleflight.Group - type SettingsSelectHandler struct { service port.DocSettingsService client client.Client diff --git a/backend/services/settings/web/handler/select_test.go b/backend/services/settings/web/handler/select_test.go index 13de2d0..eb3258d 100644 --- a/backend/services/settings/web/handler/select_test.go +++ b/backend/services/settings/web/handler/select_test.go @@ -22,29 +22,36 @@ import ( "context" "testing" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/cache" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/cache" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/config" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/adapter" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/domain" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/service" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" "github.com/stretchr/testify/assert" + "golang.org/x/oauth2" ) type mockEncryptor struct{} -func (e mockEncryptor) Encrypt(text string) (string, error) { +func (e mockEncryptor) Encrypt(text string, key []byte) (string, error) { return string(text), nil } -func (e mockEncryptor) Decrypt(ciphertext string) (string, error) { +func (e mockEncryptor) Decrypt(ciphertext string, key []byte) (string, error) { return string(ciphertext), nil } func TestSelectCaching(t *testing.T) { adapter := adapter.NewMemoryDocserverAdapter() - service := service.NewSettingsService(adapter, mockEncryptor{}, cache.NewCache(&config.CacheConfig{}), log.NewEmptyLogger()) + service := service.NewSettingsService( + adapter, mockEncryptor{}, cache.NewCache(&config.CacheConfig{}), + &oauth2.Config{ + ClientID: "mock", + ClientSecret: "mock", + }, log.NewEmptyLogger(), + ) sel := NewSettingsSelectHandler(service, nil, log.NewEmptyLogger()) @@ -52,7 +59,7 @@ func TestSelectCaching(t *testing.T) { CompanyID: "mock", DocAddress: "mock", DocSecret: "mock", - DocHeader: "mocj", + DocHeader: "mock", }) t.Run("get settings", func(t *testing.T) { diff --git a/backend/services/settings/web/server.go b/backend/services/settings/web/server.go index fc60446..9d88415 100644 --- a/backend/services/settings/web/server.go +++ b/backend/services/settings/web/server.go @@ -19,60 +19,32 @@ package web import ( - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/rpc" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/adapter" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/port" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/core/service" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/service/rpc" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/handler" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/settings/web/message" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" - "go-micro.dev/v4/cache" - mclient "go-micro.dev/v4/client" ) type DocserverRPCServer struct { - service port.DocSettingsService - logger log.Logger + selectHandler handler.SettingsSelectHandler + insertHandler handler.SettingsInsertHandler + deleteHandler handler.SettingsDeleteHandler } func NewDocserverRPCServer( - persistenceConfig *config.PersistenceConfig, - credentialsConfig *config.OAuthCredentialsConfig, - cache cache.Cache, logger log.Logger, + selectHandler handler.SettingsSelectHandler, + insertHandler handler.SettingsInsertHandler, + deleteHandler handler.SettingsDeleteHandler, ) rpc.RPCEngine { - adptr := adapter.NewMemoryDocserverAdapter() - if persistenceConfig.Persistence.URL != "" { - adptr = adapter.NewMongoDocserverAdapter(persistenceConfig.Persistence.URL) - } - - service := service.NewSettingsService( - adptr, crypto.NewAesEncryptor([]byte(credentialsConfig.Credentials.ClientSecret)), cache, logger, - ) return DocserverRPCServer{ - service: service, - logger: logger, + selectHandler: selectHandler, + insertHandler: insertHandler, + deleteHandler: deleteHandler, } } func (a DocserverRPCServer) BuildMessageHandlers() []rpc.RPCMessageHandler { - return []rpc.RPCMessageHandler{ - { - Topic: "insert-settings", - Queue: "pipedrive-docserver", - Handler: message.BuildInsertMessageHandler(a.service).GetHandler(), - }, - { - Topic: "delete-settings", - Queue: "pipedrive-docserver", - Handler: message.BuildDeleteMessageHandler(a.service).GetHandler(), - }, - } + return nil } -func (a DocserverRPCServer) BuildHandlers(client mclient.Client, cache cache.Cache) []interface{} { - return []interface{}{ - handler.NewSettingsSelectHandler(a.service, client, a.logger), - } +func (a DocserverRPCServer) BuildHandlers() []interface{} { + return []interface{}{a.selectHandler, a.insertHandler, a.deleteHandler} } From 0c87f5ebde4d42f8675e9981f1099ffda4525cef Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 5 Jun 2023 16:40:23 +0500 Subject: [PATCH 129/145] refactor(services): gateway refactoring --- backend/services/gateway/cmd/server.go | 28 +++-- .../gateway/config/config.example.yml | 21 ++-- .../services/gateway/web/controller/api.go | 105 ++++++++++------- .../services/gateway/web/controller/auth.go | 109 ++++++++++++------ .../services/gateway/web/controller/file.go | 59 ++++++---- .../services/gateway/web/middleware/auth.go | 57 +++++---- .../gateway/web/middleware/context.go | 58 ++++++---- backend/services/gateway/web/server.go | 89 +++++--------- 8 files changed, 304 insertions(+), 222 deletions(-) diff --git a/backend/services/gateway/cmd/server.go b/backend/services/gateway/cmd/server.go index da25983..b111652 100644 --- a/backend/services/gateway/cmd/server.go +++ b/backend/services/gateway/cmd/server.go @@ -19,10 +19,13 @@ package cmd import ( - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg" - chttp "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/http" + pkg "github.com/ONLYOFFICE/onlyoffice-integration-adapters" + chttp "github.com/ONLYOFFICE/onlyoffice-integration-adapters/service/http" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/gateway/web" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/gateway/web/controller" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/gateway/web/middleware" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" "github.com/urfave/cli/v2" ) @@ -37,22 +40,25 @@ func Server() *cli.Command { Usage: "sets custom configuration path", Aliases: []string{"config", "conf", "c"}, }, - &cli.StringFlag{ - Name: "environment", - Usage: "sets servers environment (development, testing, production)", - Aliases: []string{"env", "e"}, - }, }, Action: func(c *cli.Context) error { var ( CONFIG_PATH = c.String("config_path") - // ENVIRONMENT = c.String("environment") ) - app := pkg.Bootstrap( - CONFIG_PATH, chttp.NewService, web.NewServer, + app := pkg.NewBootstrapper(CONFIG_PATH, pkg.WithModules( + chttp.NewService, web.NewServer, + controller.NewApiController, + controller.NewAuthController, + controller.NewFileController, + middleware.BuildHandleAuthMiddleware, + middleware.BuildHandleContextMiddleware, + client.NewCommandClient, + client.NewPipedriveApiClient, + client.NewPipedriveAuthClient, + shared.BuildNewIntegrationCredentialsConfig(CONFIG_PATH), shared.BuildNewOnlyofficeConfig(CONFIG_PATH), - ) + )).Bootstrap() if err := app.Err(); err != nil { return err diff --git a/backend/services/gateway/config/config.example.yml b/backend/services/gateway/config/config.example.yml index f75d8a0..e9538d6 100644 --- a/backend/services/gateway/config/config.example.yml +++ b/backend/services/gateway/config/config.example.yml @@ -1,19 +1,15 @@ namespace: "pipedrive" name: "gateway" version: 0 -address: ":4044" +address: ":6044" repl_address: ":9999" debug: false registry: - addresses: ["127.0.0.1:8500"] + addresses: [""] type: 2 -messaging: - type: 1 - addresses: ["0.0.0.0:5672"] - durable: true tracer: enable: false - address: "http://127.0.0.1:9411/api/v2/spans" + address: "" type: 1 resilience: rate_limiter: @@ -21,15 +17,14 @@ resilience: iplimit: 100 circuit_breaker: timeout: 15000 -cors: - origins: [] logger: name: "gateway-logger" level: 1 color: true -worker: - addresses: ["0.0.0.0:6379"] -oauth: +credentials: client_id: "" client_secret: "" - redirect_uri: "https://" + redirect_url: "" +onlyoffice: + builder: + allowed_downloads: 10 \ No newline at end of file diff --git a/backend/services/gateway/web/controller/api.go b/backend/services/gateway/web/controller/api.go index 3dd6e7d..91c3854 100644 --- a/backend/services/gateway/web/controller/api.go +++ b/backend/services/gateway/web/controller/api.go @@ -23,49 +23,54 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" "net/http" "strconv" "strings" "sync" "time" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/config" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/crypto" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client/model" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" "go-micro.dev/v4/client" ) -var _ErrNotAdmin = errors.New("no admin access") +var ErrNotAdmin = errors.New("no admin access") -type apiController struct { - namespace string +type ApiController struct { client client.Client apiClient pclient.PipedriveApiClient commandClient pclient.CommandClient jwtManager crypto.JwtManager + config *config.ServerConfig logger log.Logger } func NewApiController( - namespace string, client client.Client, - jwtManager crypto.JwtManager, logger log.Logger) apiController { - return apiController{ - namespace: namespace, + client client.Client, + apiClient pclient.PipedriveApiClient, + commandClient pclient.CommandClient, + jwtManager crypto.JwtManager, + serverConfig *config.ServerConfig, + logger log.Logger, +) ApiController { + return ApiController{ client: client, - apiClient: pclient.NewPipedriveApiClient(), - commandClient: pclient.NewCommandClient(jwtManager), + apiClient: apiClient, + commandClient: commandClient, jwtManager: jwtManager, + config: serverConfig, logger: logger, } } -func (c *apiController) getUser(ctx context.Context, id string) (response.UserResponse, int, error) { +func (c *ApiController) getUser(ctx context.Context, id string) (response.UserResponse, int, error) { var ures response.UserResponse - if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", id), &ures); err != nil { + if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.config.Namespace), "UserSelectHandler.GetUser", id), &ures); err != nil { c.logger.Errorf("could not get user access info: %s", err.Error()) if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return ures, http.StatusRequestTimeout, err @@ -82,7 +87,7 @@ func (c *apiController) getUser(ctx context.Context, id string) (response.UserRe return ures, http.StatusOK, nil } -func (c *apiController) BuildGetMe() http.HandlerFunc { +func (c ApiController) BuildGetMe() http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { rw.Header().Set("Content-Type", "application/json") pctx, ok := r.Context().Value("X-Pipedrive-App-Context").(request.PipedriveTokenContext) @@ -109,7 +114,7 @@ func (c *apiController) BuildGetMe() http.HandlerFunc { } } -func (c *apiController) BuildPostSettings() http.HandlerFunc { +func (c ApiController) BuildPostSettings() http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { rw.Header().Set("Content-Type", "application/json") pctx, ok := r.Context().Value("X-Pipedrive-App-Context").(request.PipedriveTokenContext) @@ -126,8 +131,7 @@ func (c *apiController) BuildPostSettings() http.HandlerFunc { } var settings request.DocSettings - buf, _ := ioutil.ReadAll(r.Body) - if err := json.Unmarshal(buf, &settings); err != nil { + if err := json.NewDecoder(r.Body).Decode(&settings); err != nil { rw.WriteHeader(http.StatusBadRequest) c.logger.Errorf(err.Error()) return @@ -175,7 +179,7 @@ func (c *apiController) BuildPostSettings() http.HandlerFunc { for _, access := range urs.Access { if access.App == "global" && !access.Admin { - errChan <- _ErrNotAdmin + errChan <- ErrNotAdmin return } } @@ -201,15 +205,19 @@ func (c *apiController) BuildPostSettings() http.HandlerFunc { default: } - msg := c.client.NewMessage("insert-settings", request.DocSettings{ - CompanyID: <-cidChan, - DocAddress: settings.DocAddress, - DocSecret: settings.DocSecret, - DocHeader: settings.DocHeader, - }) - - if err := c.client.Publish(ctx, msg); err != nil { - c.logger.Errorf("could not insert settings: %s", err.Error()) + var sres interface{} + if err := c.client.Call( + ctx, c.client.NewRequest( + fmt.Sprintf("%s:settings", c.config.Namespace), + "SettingsInsertHandler.InsertSettings", + request.DocSettings{ + CompanyID: <-cidChan, + DocAddress: settings.DocAddress, + DocHeader: settings.DocHeader, + DocSecret: settings.DocSecret, + }, + ), &sres); err != nil { + c.logger.Errorf("could not get user access info: %s", err.Error()) if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { rw.WriteHeader(http.StatusRequestTimeout) return @@ -218,6 +226,7 @@ func (c *apiController) BuildPostSettings() http.HandlerFunc { microErr := response.MicroError{} if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { rw.WriteHeader(http.StatusUnauthorized) + c.logger.Errorf("could not post new settings: %s", err.Error()) return } @@ -229,7 +238,7 @@ func (c *apiController) BuildPostSettings() http.HandlerFunc { } } -func (c apiController) BuildGetSettings() http.HandlerFunc { +func (c ApiController) BuildGetSettings() http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { rw.Header().Set("Content-Type", "application/json") pctx, ok := r.Context().Value("X-Pipedrive-App-Context").(request.PipedriveTokenContext) @@ -264,7 +273,15 @@ func (c apiController) BuildGetSettings() http.HandlerFunc { } var docs response.DocSettingsResponse - if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:settings", c.namespace), "SettingsSelectHandler.GetSettings", fmt.Sprint(pctx.CID)), &docs); err != nil { + if err := c.client.Call( + ctx, + c.client.NewRequest( + fmt.Sprintf("%s:settings", c.config.Namespace), + "SettingsSelectHandler.GetSettings", + fmt.Sprint(pctx.CID), + ), + &docs, + ); err != nil { c.logger.Errorf("could not get settings: %s", err.Error()) if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { rw.WriteHeader(http.StatusRequestTimeout) @@ -285,7 +302,7 @@ func (c apiController) BuildGetSettings() http.HandlerFunc { } } -func (c apiController) BuildGetConfig() http.HandlerFunc { +func (c ApiController) BuildGetConfig() http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { rw.Header().Set("Content-Type", "application/json") @@ -322,15 +339,23 @@ func (c apiController) BuildGetConfig() http.HandlerFunc { defer cancel() var resp response.BuildConfigResponse - if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:builder", c.namespace), "ConfigHandler.BuildConfig", request.BuildConfigRequest{ - UID: pctx.UID, - CID: pctx.CID, - Deal: dealID, - UserAgent: r.UserAgent(), - Filename: filename, - FileID: id, - DocKey: key, - }), &resp); err != nil { + if err := c.client.Call( + ctx, + c.client.NewRequest( + fmt.Sprintf("%s:builder", c.config.Namespace), + "ConfigHandler.BuildConfig", + request.BuildConfigRequest{ + UID: pctx.UID, + CID: pctx.CID, + Deal: dealID, + UserAgent: r.UserAgent(), + Filename: filename, + FileID: id, + DocKey: key, + }, + ), + &resp, + ); err != nil { c.logger.Errorf("could not build onlyoffice config: %s", err.Error()) if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { rw.WriteHeader(http.StatusRequestTimeout) diff --git a/backend/services/gateway/web/controller/auth.go b/backend/services/gateway/web/controller/auth.go index 390acee..9248b6c 100644 --- a/backend/services/gateway/web/controller/auth.go +++ b/backend/services/gateway/web/controller/auth.go @@ -23,49 +23,67 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" "net/http" + "net/url" "strconv" "strings" "time" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/config" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" "go-micro.dev/v4/client" + "golang.org/x/oauth2" "golang.org/x/sync/singleflight" ) var group singleflight.Group -type authController struct { - namespace string - redirectURI string +type AuthController struct { client client.Client pipedriveAuth pclient.PipedriveAuthClient pipedriveAPI pclient.PipedriveApiClient + config *config.ServerConfig + credentials *oauth2.Config logger log.Logger } func NewAuthController( - namespace string, - redirectURI string, client client.Client, pipedriveAuth pclient.PipedriveAuthClient, + pipedriveAPI pclient.PipedriveApiClient, + config *config.ServerConfig, + credentials *oauth2.Config, logger log.Logger, -) *authController { - return &authController{ - namespace: namespace, - redirectURI: redirectURI, +) AuthController { + return AuthController{ client: client, pipedriveAuth: pipedriveAuth, - pipedriveAPI: pclient.NewPipedriveApiClient(), + pipedriveAPI: pipedriveAPI, + config: config, + credentials: credentials, logger: logger, } } -func (c authController) BuildGetAuth() http.HandlerFunc { +func (c AuthController) BuildGetInstall() http.HandlerFunc { + return func(rw http.ResponseWriter, r *http.Request) { + c.logger.Debug("a new install request") + http.Redirect( + rw, r, + fmt.Sprintf( + "https://oauth.pipedrive.com/oauth/authorize?client_id=%s&redirect_uri=%s", + c.credentials.ClientID, + url.QueryEscape(c.credentials.RedirectURL), + ), + http.StatusMovedPermanently, + ) + } +} + +func (c AuthController) BuildGetAuth() http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { c.logger.Debug("a new auth request") code := strings.TrimSpace(r.URL.Query().Get("code")) @@ -76,7 +94,7 @@ func (c authController) BuildGetAuth() http.HandlerFunc { } group.Do(code, func() (interface{}, error) { - token, err := c.pipedriveAuth.GetAccessToken(r.Context(), code, c.redirectURI) + token, err := c.pipedriveAuth.GetAccessToken(r.Context(), code, c.credentials.RedirectURL) if err != nil { rw.WriteHeader(http.StatusBadRequest) c.logger.Errorf("could not get pipedrive access token: %s", err.Error()) @@ -90,17 +108,37 @@ func (c authController) BuildGetAuth() http.HandlerFunc { return nil, err } - if err := c.client.Publish(r.Context(), client.NewMessage("insert-auth", response.UserResponse{ - ID: fmt.Sprint(usr.ID + usr.CompanyID), - AccessToken: token.AccessToken, - RefreshToken: token.RefreshToken, - TokenType: token.TokenType, - Scope: token.Scope, - ApiDomain: token.ApiDomain, - ExpiresAt: time.Now().Local().Add(time.Second * time.Duration(token.ExpiresIn-700)).UnixMilli(), - })); err != nil { - rw.WriteHeader(http.StatusInternalServerError) - c.logger.Errorf("insert user error: %s", err.Error()) + var ures response.UserResponse + if err := c.client.Call( + r.Context(), + c.client.NewRequest( + fmt.Sprintf("%s:auth", c.config.Namespace), + "UserInsertHandler.InsertUser", + response.UserResponse{ + ID: fmt.Sprint(usr.ID + usr.CompanyID), + AccessToken: token.AccessToken, + RefreshToken: token.RefreshToken, + TokenType: token.TokenType, + Scope: token.Scope, + ApiDomain: token.ApiDomain, + ExpiresAt: time.Now().Local().Add(time.Second * time.Duration(token.ExpiresIn-700)).UnixMilli(), + }, + ), + &ures, + ); err != nil { + c.logger.Errorf("could not get user access info: %s", err.Error()) + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + rw.WriteHeader(http.StatusRequestTimeout) + return nil, err + } + + microErr := response.MicroError{} + if err := json.Unmarshal([]byte(err.Error()), µErr); err != nil { + rw.WriteHeader(http.StatusUnauthorized) + return nil, err + } + + rw.WriteHeader(microErr.Code) return nil, err } @@ -111,7 +149,7 @@ func (c authController) BuildGetAuth() http.HandlerFunc { } } -func (c authController) BuildDeleteAuth() http.HandlerFunc { +func (c AuthController) BuildDeleteAuth() http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { c.logger.Debug("a new uninstall request") var ureq request.UninstallRequest @@ -122,21 +160,22 @@ func (c authController) BuildDeleteAuth() http.HandlerFunc { return } - buf, err := ioutil.ReadAll(r.Body) - if err != nil { - rw.WriteHeader(http.StatusBadRequest) - c.logger.Errorf("could not parse request body: %s", err.Error()) - return - } - - if err := json.Unmarshal(buf, &ureq); err != nil { + if err := json.NewDecoder(r.Body).Decode(&ureq); err != nil { rw.WriteHeader(http.StatusInternalServerError) c.logger.Errorf("could not unmarshal request body: %s", err.Error()) return } var res interface{} - if err := c.client.Call(r.Context(), c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserDeleteHandler.DeleteUser", fmt.Sprint(ureq.UserID+ureq.CompanyID)), &res); err != nil { + if err := c.client.Call( + r.Context(), + c.client.NewRequest( + fmt.Sprintf("%s:auth", c.config.Namespace), + "UserDeleteHandler.DeleteUser", + fmt.Sprint(ureq.UserID+ureq.CompanyID), + ), + &res, + ); err != nil { c.logger.Errorf("could not delete user: %s", err.Error()) if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { rw.WriteHeader(http.StatusRequestTimeout) diff --git a/backend/services/gateway/web/controller/file.go b/backend/services/gateway/web/controller/file.go index 1273023..6521924 100644 --- a/backend/services/gateway/web/controller/file.go +++ b/backend/services/gateway/web/controller/file.go @@ -9,41 +9,56 @@ import ( "strings" "time" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/config" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/crypto" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/gateway/assets" + "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client/model" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" "go-micro.dev/v4/client" ) -type fileController struct { - namespace string - allowedDownloads int - client client.Client - apiClient pclient.PipedriveApiClient - jwtManager crypto.JwtManager - logger log.Logger +type FileController struct { + client client.Client + apiClient pclient.PipedriveApiClient + jwtManager crypto.JwtManager + config *config.ServerConfig + onlyoffice *shared.OnlyofficeConfig + logger log.Logger } func NewFileController( - namespace string, allowedDownloads int, client client.Client, - jwtManager crypto.JwtManager, logger log.Logger) fileController { - return fileController{ - namespace: namespace, - client: client, - apiClient: pclient.NewPipedriveApiClient(), - jwtManager: jwtManager, - logger: logger, - allowedDownloads: allowedDownloads, + client client.Client, + apiClient pclient.PipedriveApiClient, + jwtManager crypto.JwtManager, + config *config.ServerConfig, + onlyoffice *shared.OnlyofficeConfig, + logger log.Logger, +) FileController { + return FileController{ + client: client, + apiClient: apiClient, + jwtManager: jwtManager, + config: config, + onlyoffice: onlyoffice, + logger: logger, } } -func (c *fileController) getUser(ctx context.Context, id string) (response.UserResponse, int) { +func (c *FileController) getUser(ctx context.Context, id string) (response.UserResponse, int) { var ures response.UserResponse - if err := c.client.Call(ctx, c.client.NewRequest(fmt.Sprintf("%s:auth", c.namespace), "UserSelectHandler.GetUser", id), &ures); err != nil { + if err := c.client.Call( + ctx, + c.client.NewRequest( + fmt.Sprintf("%s:auth", c.config.Namespace), + "UserSelectHandler.GetUser", + id, + ), + &ures, + ); err != nil { c.logger.Errorf("could not get user access info: %s", err.Error()) if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return ures, http.StatusRequestTimeout @@ -60,7 +75,7 @@ func (c *fileController) getUser(ctx context.Context, id string) (response.UserR return ures, http.StatusOK } -func (c fileController) BuildGetFile() http.HandlerFunc { +func (c FileController) BuildGetFile() http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { rw.Header().Set("Content-Type", "application/json") @@ -134,7 +149,7 @@ func (c fileController) BuildGetFile() http.HandlerFunc { } } -func (c fileController) BuildGetDownloadUrl() http.HandlerFunc { +func (c FileController) BuildGetDownloadUrl() http.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) { rw.Header().Set("Content-Type", "plain/text") query := r.URL.Query() diff --git a/backend/services/gateway/web/middleware/auth.go b/backend/services/gateway/web/middleware/auth.go index ae2e60a..b5934cd 100644 --- a/backend/services/gateway/web/middleware/auth.go +++ b/backend/services/gateway/web/middleware/auth.go @@ -24,32 +24,45 @@ import ( "net/http" "strings" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" + "go-micro.dev/v4/logger" + "golang.org/x/oauth2" ) +type AuthMiddleware struct { + credentials *oauth2.Config + logger log.Logger +} + func BuildHandleAuthMiddleware( - clientID, clientSecret string, + credentials *oauth2.Config, logger log.Logger, -) func(next http.Handler) http.HandlerFunc { - logger.Debugf("pipedrive event middleware has been built with client_id %s and client_secret %s", clientID, clientSecret) - return func(next http.Handler) http.HandlerFunc { - return func(rw http.ResponseWriter, r *http.Request) { - signature := strings.ReplaceAll(r.Header.Get("Authorization"), "Basic ", "") - if signature == "" { - logger.Errorf("an unauthorized access to deauth endpoint") - rw.WriteHeader(http.StatusUnauthorized) - return - } - - validSignature := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", clientID, clientSecret))) - if signature != validSignature { - logger.Errorf("invalid uninstall signature") - logger.Debugf("valid signature is %s whereas signature is %s", validSignature, signature) - rw.WriteHeader(http.StatusForbidden) - return - } - - next.ServeHTTP(rw, r) +) AuthMiddleware { + return AuthMiddleware{ + credentials: credentials, + logger: logger, + } +} + +func (m AuthMiddleware) Protect(next http.Handler) http.HandlerFunc { + m.logger.Debugf("pipedrive event middleware has been built with client_id %s and client_secret %s", m.credentials.ClientID, m.credentials.ClientSecret) + return func(rw http.ResponseWriter, r *http.Request) { + signature := strings.ReplaceAll(r.Header.Get("Authorization"), "Basic ", "") + if signature == "" { + m.logger.Errorf("an unauthorized access to deauth endpoint") + rw.WriteHeader(http.StatusUnauthorized) + return + } + + validSignature := base64.StdEncoding. + EncodeToString([]byte(fmt.Sprintf("%s:%s", m.credentials.ClientID, m.credentials.ClientSecret))) + if signature != validSignature { + logger.Errorf("invalid uninstall signature") + logger.Debugf("valid signature is %s whereas signature is %s", validSignature, signature) + rw.WriteHeader(http.StatusForbidden) + return } + + next.ServeHTTP(rw, r) } } diff --git a/backend/services/gateway/web/middleware/context.go b/backend/services/gateway/web/middleware/context.go index 71a44af..06c7c32 100644 --- a/backend/services/gateway/web/middleware/context.go +++ b/backend/services/gateway/web/middleware/context.go @@ -22,34 +22,48 @@ import ( "context" "net/http" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/crypto" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" + "go-micro.dev/v4/logger" + "golang.org/x/oauth2" ) +type ContextMiddleware struct { + jwtManager crypto.JwtManager + credentials *oauth2.Config + logger log.Logger +} + func BuildHandleContextMiddleware( - clientSecret string, jwtManager crypto.JwtManager, + credentials *oauth2.Config, logger log.Logger, -) func(next http.Handler) http.HandlerFunc { - logger.Debugf("pipedrive context middleware has been built with client_secret %s", clientSecret) - return func(next http.Handler) http.HandlerFunc { - return func(rw http.ResponseWriter, r *http.Request) { - var ctx request.PipedriveTokenContext - token := r.Header.Get("X-Pipedrive-App-Context") - if token == "" { - logger.Errorf("unauthorized access to an api endpoint") - rw.WriteHeader(http.StatusUnauthorized) - return - } - - if err := jwtManager.Verify(clientSecret, token, &ctx); err != nil { - logger.Errorf("could not verify X-Pipedrive-App-Context: %s", err.Error()) - rw.WriteHeader(http.StatusForbidden) - return - } - - next.ServeHTTP(rw, r.WithContext(context.WithValue(r.Context(), "X-Pipedrive-App-Context", ctx))) +) ContextMiddleware { + return ContextMiddleware{ + jwtManager: jwtManager, + credentials: credentials, + logger: logger, + } +} + +func (m ContextMiddleware) Protect(next http.Handler) http.HandlerFunc { + m.logger.Debugf("pipedrive context middleware has been built with client_secret %s", m.credentials.ClientSecret) + return func(rw http.ResponseWriter, r *http.Request) { + var ctx request.PipedriveTokenContext + token := r.Header.Get("X-Pipedrive-App-Context") + if token == "" { + logger.Errorf("unauthorized access to an api endpoint") + rw.WriteHeader(http.StatusUnauthorized) + return + } + + if err := m.jwtManager.Verify(m.credentials.ClientSecret, token, &ctx); err != nil { + logger.Errorf("could not verify X-Pipedrive-App-Context: %s", err.Error()) + rw.WriteHeader(http.StatusForbidden) + return } + + next.ServeHTTP(rw, r.WithContext(context.WithValue(r.Context(), "X-Pipedrive-App-Context", ctx))) } } diff --git a/backend/services/gateway/web/server.go b/backend/services/gateway/web/server.go index a59608f..9385922 100644 --- a/backend/services/gateway/web/server.go +++ b/backend/services/gateway/web/server.go @@ -21,55 +21,38 @@ package web import ( "net/http" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" - shttp "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/http" + shttp "github.com/ONLYOFFICE/onlyoffice-integration-adapters/service/http" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/gateway/web/controller" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/gateway/web/middleware" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared" - pclient "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" - "github.com/gin-gonic/gin" "github.com/go-chi/chi/v5" chimiddleware "github.com/go-chi/chi/v5/middleware" - "github.com/gorilla/sessions" - "go-micro.dev/v4/cache" - "go-micro.dev/v4/client" ) type PipedriveHTTPService struct { - namespace string - mux *chi.Mux - client client.Client - logger log.Logger - store sessions.Store - clientID string - clientSecret string - redirectURI string - allowedDownloads int + apiController controller.ApiController + authController controller.AuthController + fileController controller.FileController + authMiddleware middleware.AuthMiddleware + contextMiddleware middleware.ContextMiddleware + mux *chi.Mux } // NewService initializes http server with options. func NewServer( - serverConfig *config.ServerConfig, - credentialsConfig *config.OAuthCredentialsConfig, - onlyofficeConfig *shared.OnlyofficeConfig, - logger log.Logger, + apiController controller.ApiController, + authController controller.AuthController, + fileController controller.FileController, + authMiddleware middleware.AuthMiddleware, + contextMiddleware middleware.ContextMiddleware, ) shttp.ServerEngine { - gin.SetMode(gin.ReleaseMode) - - service := PipedriveHTTPService{ - namespace: serverConfig.Namespace, - mux: chi.NewRouter(), - logger: logger, - clientID: credentialsConfig.Credentials.ClientID, - clientSecret: credentialsConfig.Credentials.ClientSecret, - redirectURI: credentialsConfig.Credentials.RedirectURI, - store: sessions.NewCookieStore([]byte(credentialsConfig.Credentials.ClientSecret)), - allowedDownloads: onlyofficeConfig.Onlyoffice.Builder.AllowedDownloads, + return PipedriveHTTPService{ + apiController: apiController, + authController: authController, + fileController: fileController, + authMiddleware: authMiddleware, + contextMiddleware: contextMiddleware, + mux: chi.NewRouter(), } - - return service } // ApplyMiddleware useed to apply http server middlewares. @@ -78,29 +61,20 @@ func (s PipedriveHTTPService) ApplyMiddleware(middlewares ...func(http.Handler) } // NewHandler returns http server engine. -func (s PipedriveHTTPService) NewHandler(client client.Client, cache cache.Cache) interface { +func (s PipedriveHTTPService) NewHandler() interface { ServeHTTP(w http.ResponseWriter, r *http.Request) } { - return s.InitializeServer(client) + return s.InitializeServer() } // InitializeServer sets all injected dependencies. -func (s *PipedriveHTTPService) InitializeServer(c client.Client) *chi.Mux { - s.client = c +func (s *PipedriveHTTPService) InitializeServer() *chi.Mux { s.InitializeRoutes() return s.mux } // InitializeRoutes builds all http routes. func (s *PipedriveHTTPService) InitializeRoutes() { - jwtManager := crypto.NewOnlyofficeJwtManager() - authMiddleware := middleware.BuildHandleAuthMiddleware(s.clientID, s.clientSecret, s.logger) - tokenMiddleware := middleware.BuildHandleContextMiddleware(s.clientSecret, jwtManager, s.logger) - - authController := controller.NewAuthController(s.namespace, s.redirectURI, s.client, pclient.NewPipedriveAuthClient(s.clientID, s.clientSecret), s.logger) - apiController := controller.NewApiController(s.namespace, s.client, jwtManager, s.logger) - fileController := controller.NewFileController(s.namespace, s.allowedDownloads, s.client, jwtManager, s.logger) - s.mux.Group(func(r chi.Router) { r.Use(chimiddleware.Recoverer) r.NotFound(func(rw http.ResponseWriter, cr *http.Request) { @@ -109,23 +83,24 @@ func (s *PipedriveHTTPService) InitializeRoutes() { r.Route("/oauth", func(cr chi.Router) { cr.Use(chimiddleware.NoCache) - cr.Get("/auth", authController.BuildGetAuth()) - cr.Delete("/auth", authMiddleware(authController.BuildDeleteAuth())) + cr.Get("/install", s.authController.BuildGetInstall()) + cr.Get("/auth", s.authController.BuildGetAuth()) + cr.Delete("/auth", s.authMiddleware.Protect(s.authController.BuildDeleteAuth())) }) r.Route("/api", func(cr chi.Router) { cr.Use(func(h http.Handler) http.Handler { - return tokenMiddleware(h) + return s.contextMiddleware.Protect(h) }) - cr.Get("/me", apiController.BuildGetMe()) - cr.Get("/config", apiController.BuildGetConfig()) - cr.Post("/settings", apiController.BuildPostSettings()) - cr.Get("/settings", apiController.BuildGetSettings()) + cr.Get("/me", s.apiController.BuildGetMe()) + cr.Get("/config", s.apiController.BuildGetConfig()) + cr.Post("/settings", s.apiController.BuildPostSettings()) + cr.Get("/settings", s.apiController.BuildGetSettings()) }) r.Route("/files", func(fr chi.Router) { - fr.Get("/download", fileController.BuildGetDownloadUrl()) - fr.Get("/create", tokenMiddleware(fileController.BuildGetFile())) + fr.Get("/download", s.fileController.BuildGetDownloadUrl()) + fr.Get("/create", s.contextMiddleware.Protect(s.fileController.BuildGetFile())) }) }) } From 663a200f332e067bad1b09e95101ee503797642b Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 5 Jun 2023 16:47:03 +0500 Subject: [PATCH 130/145] refactor(services): shared package --- backend/go.mod | 118 ++++++------ backend/go.sum | 175 ++++++++---------- backend/services/shared/client/api.go | 15 +- backend/services/shared/client/auth.go | 11 +- backend/services/shared/client/command.go | 4 +- backend/services/shared/client/error.go | 5 +- backend/services/shared/config.go | 59 ++++++ backend/services/shared/crypto/aes.go | 95 ---------- backend/services/shared/crypto/aes_test.go | 67 ------- backend/services/shared/crypto/jwt.go | 89 --------- .../{message/job.go => request/error.go} | 22 +-- backend/services/shared/request/settings.go | 14 +- 12 files changed, 223 insertions(+), 451 deletions(-) delete mode 100644 backend/services/shared/crypto/aes.go delete mode 100644 backend/services/shared/crypto/aes_test.go delete mode 100644 backend/services/shared/crypto/jwt.go rename backend/services/shared/{message/job.go => request/error.go} (67%) diff --git a/backend/go.mod b/backend/go.mod index 401ed29..517e8a1 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -3,40 +3,16 @@ module github.com/ONLYOFFICE/onlyoffice-pipedrive go 1.19 require ( + github.com/ONLYOFFICE/onlyoffice-integration-adapters v0.1.1 github.com/gin-gonic/gin v1.8.2 github.com/go-chi/chi/v5 v5.0.8 - github.com/go-chi/cors v1.2.1 - github.com/go-micro/plugins/v4/broker/nats v1.2.0 - github.com/go-micro/plugins/v4/broker/rabbitmq v1.2.1 - github.com/go-micro/plugins/v4/registry/consul v1.2.0 - github.com/go-micro/plugins/v4/registry/etcd v1.2.0 - github.com/go-micro/plugins/v4/registry/kubernetes v1.1.1 - github.com/go-micro/plugins/v4/registry/mdns v1.2.0 - github.com/go-micro/plugins/v4/server/http v1.2.1 - github.com/go-micro/plugins/v4/wrapper/breaker/hystrix v1.2.0 - github.com/go-micro/plugins/v4/wrapper/ratelimiter/uber v1.2.0 - github.com/go-micro/plugins/v4/wrapper/select/roundrobin v1.2.0 - github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0 - github.com/google/uuid v1.3.0 - github.com/gorilla/sessions v1.2.1 - github.com/hellofresh/health-go/v5 v5.0.0 - github.com/justinas/alice v1.2.0 - github.com/mitchellh/mapstructure v1.3.3 - github.com/olivere/elastic/v7 v7.0.32 - github.com/prometheus/client_golang v1.14.0 - github.com/sethvargo/go-envconfig v0.8.3 - github.com/sethvargo/go-limiter v0.7.2 - github.com/stretchr/testify v1.8.1 + github.com/mitchellh/mapstructure v1.5.0 + github.com/sethvargo/go-envconfig v0.9.0 + github.com/stretchr/testify v1.8.2 github.com/urfave/cli/v2 v2.17.1 - go-micro.dev/v4 v4.9.0 + go-micro.dev/v4 v4.10.2 go.mongodb.org/mongo-driver v1.9.1 - go.opentelemetry.io/otel v1.12.0 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.12.0 - go.opentelemetry.io/otel/exporters/zipkin v1.12.0 - go.opentelemetry.io/otel/sdk v1.12.0 - go.uber.org/fx v1.19.2 - go.uber.org/ratelimit v0.2.0 - golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 + golang.org/x/sync v0.2.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -49,7 +25,7 @@ require ( github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bitly/go-simplejson v0.5.0 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cloudflare/circl v1.2.0 // indirect github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect @@ -59,13 +35,26 @@ require ( github.com/emirpasic/gods v1.18.1 // indirect github.com/fatih/color v1.9.0 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-chi/cors v1.2.1 // indirect github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/go-billy/v5 v5.3.1 // indirect github.com/go-git/go-git/v5 v5.4.2 // indirect - github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-micro/plugins/v4/broker/nats v1.2.0 // indirect + github.com/go-micro/plugins/v4/broker/rabbitmq v1.2.1 // indirect + github.com/go-micro/plugins/v4/registry/consul v1.2.0 // indirect + github.com/go-micro/plugins/v4/registry/etcd v1.2.0 // indirect + github.com/go-micro/plugins/v4/registry/kubernetes v1.1.1 // indirect + github.com/go-micro/plugins/v4/registry/mdns v1.2.0 // indirect + github.com/go-micro/plugins/v4/server/http v1.2.1 // indirect + github.com/go-micro/plugins/v4/store/memory v1.2.0 // indirect + github.com/go-micro/plugins/v4/wrapper/breaker/hystrix v1.2.0 // indirect + github.com/go-micro/plugins/v4/wrapper/ratelimiter/uber v1.2.0 // indirect + github.com/go-micro/plugins/v4/wrapper/select/roundrobin v1.2.0 // indirect + github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/validator/v10 v10.11.1 // indirect @@ -75,8 +64,8 @@ require ( github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/gopherjs/gopherjs v1.17.2 // indirect - github.com/gorilla/securecookie v1.1.1 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gookit/event v1.0.6 // indirect github.com/hashicorp/consul/api v1.9.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.1 // indirect github.com/hashicorp/go-hclog v0.12.0 // indirect @@ -84,11 +73,12 @@ require ( github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/golang-lru v0.5.1 // indirect github.com/hashicorp/serf v0.9.5 // indirect + github.com/hellofresh/health-go/v5 v5.1.1 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/jtolds/gls v4.20.0+incompatible // indirect + github.com/justinas/alice v1.2.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.15.11 // indirect github.com/leodido/go-urn v1.2.1 // indirect @@ -96,28 +86,29 @@ require ( github.com/mattn/go-isatty v0.0.17 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/miekg/dns v1.1.50 // indirect - github.com/minio/highwayhash v1.0.2 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/hashstructure v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/nats-io/jwt/v2 v2.3.0 // indirect github.com/nats-io/nats.go v1.16.0 // indirect - github.com/nats-io/nkeys v0.3.0 // indirect + github.com/nats-io/nkeys v0.4.4 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/onsi/gomega v1.20.0 // indirect + github.com/olivere/elastic/v7 v7.0.32 // indirect github.com/openzipkin/zipkin-go v0.4.1 // indirect github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect + github.com/redis/go-redis/v9 v9.0.4 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect + github.com/sethvargo/go-limiter v0.7.2 // indirect github.com/spf13/cast v1.3.1 // indirect github.com/streadway/amqp v1.0.0 // indirect github.com/ugorji/go/codec v1.2.7 // indirect @@ -131,46 +122,51 @@ require ( go.etcd.io/etcd/api/v3 v3.5.2 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.2 // indirect go.etcd.io/etcd/client/v3 v3.5.2 // indirect + go.opentelemetry.io/otel v1.15.1 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.15.1 // indirect + go.opentelemetry.io/otel/exporters/zipkin v1.15.1 // indirect go.opentelemetry.io/otel/metric v0.35.0 // indirect - go.opentelemetry.io/otel/trace v1.12.0 // indirect + go.opentelemetry.io/otel/sdk v1.15.1 // indirect + go.opentelemetry.io/otel/trace v1.15.1 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/dig v1.16.1 // indirect + go.uber.org/fx v1.19.3 // indirect go.uber.org/multierr v1.6.0 // indirect + go.uber.org/ratelimit v0.2.0 // indirect go.uber.org/zap v1.23.0 // indirect - golang.org/x/crypto v0.6.0 // indirect - golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 // indirect - golang.org/x/mod v0.6.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/crypto v0.7.0 // indirect + golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect + golang.org/x/mod v0.10.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.2.0 // indirect - google.golang.org/appengine v1.6.6 // indirect - google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e // indirect - google.golang.org/grpc v1.50.0 // indirect + golang.org/x/tools v0.9.1 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect + google.golang.org/grpc v1.53.0 // indirect google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( - github.com/coocood/freecache v1.2.3 - github.com/eko/gocache/lib/v4 v4.1.3 - github.com/eko/gocache/store/freecache/v4 v4.1.2 - github.com/eko/gocache/store/redis/v4 v4.1.2 - github.com/go-micro/plugins/v4/broker/memory v1.2.0 - github.com/go-redis/redis/v8 v8.11.5 + github.com/coocood/freecache v1.2.3 // indirect + github.com/eko/gocache/lib/v4 v4.1.3 // indirect + github.com/eko/gocache/store/freecache/v4 v4.1.2 // indirect + github.com/eko/gocache/store/redis/v4 v4.2.0 // indirect + github.com/go-micro/plugins/v4/broker/memory v1.2.0 // indirect github.com/go-resty/resty/v2 v2.7.0 github.com/golang-jwt/jwt/v5 v5.0.0 - github.com/hibiken/asynq v0.24.0 + github.com/hibiken/asynq v0.24.1 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kamva/mgm/v3 v3.5.0 github.com/mailru/easyjson v0.7.7 // indirect github.com/mileusna/useragent v1.2.1 - github.com/natefinch/lumberjack v2.0.0+incompatible + github.com/natefinch/lumberjack v2.0.0+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/sirupsen/logrus v1.9.0 + github.com/sirupsen/logrus v1.9.2 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.38.0 + golang.org/x/oauth2 v0.8.0 ) diff --git a/backend/go.sum b/backend/go.sum index 3fab6a9..0f0aad8 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -39,6 +39,8 @@ github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugX github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/ONLYOFFICE/onlyoffice-integration-adapters v0.1.1 h1:NHdWB94cCmXIDnkwZaicF47ariZfZfvTM4fauDa+7Ww= +github.com/ONLYOFFICE/onlyoffice-integration-adapters v0.1.1/go.mod h1:jO0msN5WaPR4y8Gfxf98asjdyvHzthH+xsrBGjzzzQU= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad h1:QeeqI2zxxgZVe11UrYFXXx6gVxPVF40ygekjBzEg4XY= github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= @@ -72,12 +74,17 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= +github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= +github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= +github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= +github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -106,8 +113,8 @@ github.com/eko/gocache/lib/v4 v4.1.3 h1:i7xVxWWhAESLAcHgp73piJtST63TS7ThFD7LE78v github.com/eko/gocache/lib/v4 v4.1.3/go.mod h1:7uSUlaDeRgr17IYCV8kSluhRDXq4SGutWWxTpxpwW3w= github.com/eko/gocache/store/freecache/v4 v4.1.2 h1:lWSmt0EVtCx6GY+i4oRYuPjgpRaH5RZgBykl1czaogU= github.com/eko/gocache/store/freecache/v4 v4.1.2/go.mod h1:UFvuSJgLkftoBEqOc7g/8UoDeW+3aQj6IQvvG+Cl5Js= -github.com/eko/gocache/store/redis/v4 v4.1.2 h1:PtpjQu8Q698kpC3H06As5As15s+hJofV8NtkddVX5aQ= -github.com/eko/gocache/store/redis/v4 v4.1.2/go.mod h1:P2HeqvNwqkdwajYro+qwBWquhdYpN0LgfH2rJ3jK7yg= +github.com/eko/gocache/store/redis/v4 v4.2.0 h1:YguIDZuMeVFlQFi0DiYQpL7NvHAckinYFjWxvr0qmLw= +github.com/eko/gocache/store/redis/v4 v4.2.0/go.mod h1:IP4GWCjca/pyruK5GweTZ7zzoFlgQABd3cVMJFEgHfs= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= @@ -116,7 +123,6 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch/v5 v5.5.0 h1:bAmFiUJ+o0o2B4OiTFeE3MqCOtyo+jjPP9iZ0VRxYUc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= @@ -124,10 +130,9 @@ github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBd github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -135,7 +140,6 @@ github.com/gin-gonic/gin v1.8.2 h1:UzKToD9/PoFj/V4rvlKqTRKnQYyz8Sc1MJlv4JHPtvY= github.com/gin-gonic/gin v1.8.2/go.mod h1:qw5AYuDrzRTnhvusDsrov+fDIxp9Dleuu12h8nfB398= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-acme/lego/v4 v4.4.0 h1:uHhU5LpOYQOdp3aDU+XY2bajseu8fuExphTL1Ss6/Fc= github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= @@ -161,8 +165,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-micro/plugins/v4/broker/memory v1.2.0 h1:T4jWRCSyiPzMF3ek2+3FY6yJTSLjoIvE9S9H61OKs1o= @@ -181,6 +185,8 @@ github.com/go-micro/plugins/v4/registry/mdns v1.2.0 h1:BsGnco+PgycvSX+HS0XbeUQEP github.com/go-micro/plugins/v4/registry/mdns v1.2.0/go.mod h1:re0JvO5F56n59WEDaAKj2jtboKa2dklAd6iWyz5xa54= github.com/go-micro/plugins/v4/server/http v1.2.1 h1:Cia924J90rgFT/4qWWvyLvN+XqEm5T9tiQyQ+GU4bOQ= github.com/go-micro/plugins/v4/server/http v1.2.1/go.mod h1:YuAjaSPxcn3LI8j2FUsqx0Rxunrj4YwDV41Ax76rLl0= +github.com/go-micro/plugins/v4/store/memory v1.2.0 h1:5VyItIsQ2xbUO+R62/ZHFS7P2L/7nYKc1tRLA5MdjeI= +github.com/go-micro/plugins/v4/store/memory v1.2.0/go.mod h1:v2nkqOB1JFldKdgNJrFzeaZsYcbWXWJjGEt73jFQM7o= github.com/go-micro/plugins/v4/wrapper/breaker/hystrix v1.2.0 h1:0GYwyoHJ3kfJHYEuzSUH+033Kg924Y6aBSJYbpHAKNg= github.com/go-micro/plugins/v4/wrapper/breaker/hystrix v1.2.0/go.mod h1:Ii9bwBo2ADZfQJ8zGMQGh6nNN+8OEBU4VvDbrJjwbsE= github.com/go-micro/plugins/v4/wrapper/ratelimiter/uber v1.2.0 h1:VaiAfA2F0tS0ORn0CVSY3UxWH7jjUkWiL7T7KBa7JoM= @@ -197,16 +203,10 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= -github.com/go-redis/redis/v8 v8.11.2/go.mod h1:DLomh7y2e3ggQXQLd1YgmvIfecPJoFl7WU5SOQ/r06M= -github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= -github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= -github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= -github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs= github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -280,13 +280,9 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gookit/event v1.0.6 h1:/U95T1tBzt9RSSi23pg4VR3B9VWkyM4xv8TXAGi60IQ= +github.com/gookit/event v1.0.6/go.mod h1:7Udf/q/HQcrK9XE4JZUvbqi46rI1V8r/Pvao2NbPajA= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= -github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= -github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= -github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.9.0 h1:T6dKIWcaihG2c21YUi0BMAHbJanVXiYuz+mPgqxY3N4= @@ -323,11 +319,10 @@ github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= -github.com/hellofresh/health-go/v5 v5.0.0 h1:jxjllHekqEU4VYIajKJtFoOxDp1YaaygNWwAoZwWFh0= -github.com/hellofresh/health-go/v5 v5.0.0/go.mod h1:9hFVIBdKkxrg1bJurUPlw1D/0FWhl47IVfGYPy4Op9o= -github.com/hibiken/asynq v0.24.0 h1:r1CiSVYCy1vGq9REKGI/wdB2D5n/QmtzihYHHXOuBUs= -github.com/hibiken/asynq v0.24.0/go.mod h1:FVnRfUTm6gcoDkM/EjF4OIh5/06ergCPUO6pS2B2y+w= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hellofresh/health-go/v5 v5.1.1 h1:Ss0EQekzT45NVgk1hSgbXAa1LBaagZhqJ4Xql8bxxBA= +github.com/hellofresh/health-go/v5 v5.1.1/go.mod h1:ZUymhy/viIPx3xmGT9EkWny0CUZq9GLxqasC4Y2xdSc= +github.com/hibiken/asynq v0.24.1 h1:+5iIEAyA9K/lcSPvx3qoPtsKJeKI5u9aOIvUmSsazEw= +github.com/hibiken/asynq v0.24.1/go.mod h1:u5qVeSbrnfT+vtG5Mq8ZPzQu/BmCKMHvTGb91uy9Tts= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= @@ -348,7 +343,6 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= @@ -403,7 +397,6 @@ github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7Xn github.com/mileusna/useragent v1.2.1 h1:p3RJWhi3LfuI6BHdddojREyK3p6qX67vIfOVMnUIVr0= github.com/mileusna/useragent v1.2.1/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= -github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -413,8 +406,8 @@ github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9km github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -427,30 +420,20 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= -github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI= -github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= +github.com/nats-io/jwt/v2 v2.4.1 h1:Y35W1dgbbz2SQUYDPCaclXcuqleVmpbRa7646Jf2EX4= github.com/nats-io/nats-server/v2 v2.3.1 h1:tR8rp+ohGbqPGHROuzlvNzvw4G5TTeA5jUkxnSTEYPE= github.com/nats-io/nats.go v1.16.0 h1:zvLE7fGBQYW6MWaFaRdsgm9qT39PJDQoju+DS8KsO1g= github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= -github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= +github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA= +github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E= github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= -github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= -github.com/onsi/gomega v1.20.0/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= github.com/openzipkin/zipkin-go v0.4.1 h1:kNd/ST2yLLWhaWrkgchya40TJabe8Hioj9udfPcEO5A= github.com/openzipkin/zipkin-go v0.4.1/go.mod h1:qY0VqDSN1pOBN94dBc6w2GJlWLiovAyg7Qt6/I9HecM= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= @@ -496,6 +479,9 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/redis/go-redis/v9 v9.0.3/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= +github.com/redis/go-redis/v9 v9.0.4 h1:FC82T+CHJ/Q/PdyLW++GeCO+Ol59Y4T7R4jbgjvktgc= +github.com/redis/go-redis/v9 v9.0.4/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -511,8 +497,8 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/sethvargo/go-envconfig v0.8.3 h1:dXyUrDCJvCm3ybP7yNpiux93qoSORvuH23bdsgFfiJ0= -github.com/sethvargo/go-envconfig v0.8.3/go.mod h1:Iz1Gy1Sf3T64TQlJSvee81qDhf7YIlt8GMUX6yyNFs0= +github.com/sethvargo/go-envconfig v0.9.0 h1:Q6FQ6hVEeTECULvkJZakq3dZMeBQ3JUpcKMfPQbKMDE= +github.com/sethvargo/go-envconfig v0.9.0/go.mod h1:Iz1Gy1Sf3T64TQlJSvee81qDhf7YIlt8GMUX6yyNFs0= github.com/sethvargo/go-limiter v0.7.2 h1:FgC4N7RMpV5gMrUdda15FaFTkQ/L4fEqM7seXMs4oO8= github.com/sethvargo/go-limiter v0.7.2/go.mod h1:C0kbSFbiriE5k2FFOe18M1YZbAR2Fiwf72uGu0CXCcU= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -520,8 +506,8 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= +github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfItdAd1r3cck= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= @@ -541,8 +527,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= @@ -570,8 +557,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go-micro.dev/v4 v4.9.0 h1:pd1CpqMT9hA47jSmX8mfdGK865PkMh95Rwj5RdfqPqE= -go-micro.dev/v4 v4.9.0/go.mod h1:Ju8HrZ5hQSF+QguZ2QUs9Kbe42MHP1tJa/fpP5g07Cs= +go-micro.dev/v4 v4.10.2 h1:GWQf1+FcAiMf1yca3P09RNjB31Xtk0C5HiKHSpq/2qA= +go-micro.dev/v4 v4.10.2/go.mod h1:RV2AolXjTAil9Xm82QCMo1gknuZwD61oMUH14wJpECk= go.etcd.io/etcd/api/v3 v3.5.2 h1:tXok5yLlKyuQ/SXSjtqHc4uzNaMqZi2XsoSPr/LlJXI= go.etcd.io/etcd/api/v3 v3.5.2/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.2 h1:4hzqQ6hIb3blLyQ8usCU4h3NghkqcsohEQ3o3VetYxE= @@ -588,24 +575,24 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.38.0 h1:rTxmym+VN9f6ajzNtITVgyvZrNbpLt3NHr3suLLHLEQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.38.0/go.mod h1:w6xNm+kC506KNs5cknSHal6dtdRnc4uema0uN9GSQc0= -go.opentelemetry.io/otel v1.12.0 h1:IgfC7kqQrRccIKuB7Cl+SRUmsKbEwSGPr0Eu+/ht1SQ= -go.opentelemetry.io/otel v1.12.0/go.mod h1:geaoz0L0r1BEOR81k7/n9W4TCXYCJ7bPO7K374jQHG0= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.12.0 h1:FXwvCIXsrMas/reQkSUTPZVCqud1yDy441acn4Fdu6w= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.12.0/go.mod h1:TTDkohPFSX4LMcbmNMYDh0hMXV8YigEjw5WwD+nDn6U= -go.opentelemetry.io/otel/exporters/zipkin v1.12.0 h1:0Pvqi4MAvQ28rGUFoH8uIaNKNCGVLNpn7TbRCkB1gP8= -go.opentelemetry.io/otel/exporters/zipkin v1.12.0/go.mod h1:zrxX/glbLVD/EFi01JCn2W1KdWNrJb3FkBHF+oRPlXs= +go.opentelemetry.io/otel v1.15.1 h1:3Iwq3lfRByPaws0f6bU3naAqOR1n5IeDWd9390kWHa8= +go.opentelemetry.io/otel v1.15.1/go.mod h1:mHHGEHVDLal6YrKMmk9LqC4a3sF5g+fHfrttQIB1NTc= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.15.1 h1:2PunuO5SbkN5MhCbuHCd3tC6qrcaj+uDAkX/qBU5BAs= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.15.1/go.mod h1:q8+Tha+5LThjeSU8BW93uUC5w5/+DnYHMKBMpRCsui0= +go.opentelemetry.io/otel/exporters/zipkin v1.15.1 h1:B6s/o48bx00ayJu7F+jIMJfhPTyxW+S8vthjTZMNBj0= +go.opentelemetry.io/otel/exporters/zipkin v1.15.1/go.mod h1:EjjV7/YfYXG+khxCOfG6PPeRGoOmtcSusyW66qPqpRQ= go.opentelemetry.io/otel/metric v0.35.0 h1:aPT5jk/w7F9zW51L7WgRqNKDElBdyRLGuBtI5MX34e8= go.opentelemetry.io/otel/metric v0.35.0/go.mod h1:qAcbhaTRFU6uG8QM7dDo7XvFsWcugziq/5YI065TokQ= -go.opentelemetry.io/otel/sdk v1.12.0 h1:8npliVYV7qc0t1FKdpU08eMnOjgPFMnriPhn0HH4q3o= -go.opentelemetry.io/otel/sdk v1.12.0/go.mod h1:WYcvtgquYvgODEvxOry5owO2y9MyciW7JqMz6cpXShE= -go.opentelemetry.io/otel/trace v1.12.0 h1:p28in++7Kd0r2d8gSt931O57fdjUyWxkVbESuILAeUc= -go.opentelemetry.io/otel/trace v1.12.0/go.mod h1:pHlgBynn6s25qJ2szD+Bv+iwKJttjHSI3lUAyf0GNuQ= +go.opentelemetry.io/otel/sdk v1.15.1 h1:5FKR+skgpzvhPQHIEfcwMYjCBr14LWzs3uSqKiQzETI= +go.opentelemetry.io/otel/sdk v1.15.1/go.mod h1:8rVtxQfrbmbHKfqzpQkT5EzZMcbMBwTzNAggbEAM0KA= +go.opentelemetry.io/otel/trace v1.15.1 h1:uXLo6iHJEzDfrNC0L0mNjItIp06SyaBQxu5t3xMlngY= +go.opentelemetry.io/otel/trace v1.15.1/go.mod h1:IWdQG/5N1x7f6YUlmdLeJvH9yxtuJAfc4VW5Agv9r/8= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/dig v1.16.1 h1:+alNIBsl0qfY0j6epRubp/9obgtrObRAc5aD+6jbWY8= go.uber.org/dig v1.16.1/go.mod h1:557JTAUZT5bUK0SvCwikmLPPtdQhfvLYtO5tJgQSbnk= -go.uber.org/fx v1.19.2 h1:SyFgYQFr1Wl0AYstE8vyYIzP4bFz2URrScjwC4cwUvY= -go.uber.org/fx v1.19.2/go.mod h1:43G1VcqSzbIv77y00p1DRAsyZS8WdzuYdhZXmEUkMyQ= +go.uber.org/fx v1.19.3 h1:YqMRE4+2IepTYCMOvXqQpRa+QAVdiSTnsHU4XNWBceA= +go.uber.org/fx v1.19.3/go.mod h1:w2HrQg26ql9fLK7hlBiZ6JsRUKV+Lj/atT1KCjT8YhM= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= @@ -632,8 +619,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -644,8 +631,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 h1:yZNXmy+j/JpX19vZkVktWqAo7Gny4PBWYYK3zskGpx4= -golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo= +golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -668,11 +655,10 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -697,13 +683,11 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -713,8 +697,8 @@ golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -722,6 +706,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -733,15 +719,13 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 h1:cu5kTvlzcw1Q5S9f5ip1/cpiB4nXvw1XYzFPGgzLUOY= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -752,7 +736,6 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -760,7 +743,6 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -783,7 +765,6 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -800,15 +781,15 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -817,8 +798,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -867,14 +848,13 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -900,8 +880,9 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -933,8 +914,8 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e h1:halCgTFuLWDRD61piiNSxPsARANGD3Xl16hPrLgLiIg= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -949,8 +930,8 @@ google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.50.0 h1:fPVVDxY9w++VjTZsYvXWqEf9Rqar/e+9zYfxKK+W+YU= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -973,9 +954,7 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= diff --git a/backend/services/shared/client/api.go b/backend/services/shared/client/api.go index b289a51..29648ef 100644 --- a/backend/services/shared/client/api.go +++ b/backend/services/shared/client/api.go @@ -20,14 +20,13 @@ package client import ( "context" - "errors" "fmt" "io" "net/http" "strconv" "time" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client/model" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" "github.com/go-resty/resty/v2" @@ -35,8 +34,6 @@ import ( "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) -var _ErrInvalidContentLength = errors.New("could not perform api actions due to exceeding content-length") - type PipedriveApiClient struct { client *resty.Client } @@ -122,20 +119,20 @@ func (p *PipedriveApiClient) UpdateFile(ctx context.Context, id, name string, to return nil } -func (c *PipedriveApiClient) ValidateFileSize(ctx context.Context, limit int64, url string) error { +func (c *PipedriveApiClient) ValidateFileSize(ctx context.Context, limit int64, url string) (int64, error) { headResp, err := c.client.R(). SetContext(ctx). Head(url) if err != nil { - return err + return 0, err } if val, err := strconv.ParseInt(headResp.Header().Get("Content-Length"), 10, 0); val > limit || err != nil { - return _ErrInvalidContentLength + return 0, ErrInvalidContentLength + } else { + return val, nil } - - return nil } func (p PipedriveApiClient) getFile(ctx context.Context, url string) (io.ReadCloser, error) { diff --git a/backend/services/shared/client/auth.go b/backend/services/shared/client/auth.go index c72348a..907df69 100644 --- a/backend/services/shared/client/auth.go +++ b/backend/services/shared/client/auth.go @@ -25,10 +25,11 @@ import ( "strings" "time" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/client/model" "github.com/go-resty/resty/v2" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + "golang.org/x/oauth2" ) type PipedriveAuthClient struct { @@ -37,7 +38,7 @@ type PipedriveAuthClient struct { clientSecret string } -func NewPipedriveAuthClient(clientID, clientSecret string) PipedriveAuthClient { +func NewPipedriveAuthClient(credentials *oauth2.Config) PipedriveAuthClient { otelClient := otelhttp.DefaultClient otelClient.Transport = otelhttp.NewTransport(&http.Transport{ Proxy: http.ProxyFromEnvironment, @@ -49,7 +50,7 @@ func NewPipedriveAuthClient(clientID, clientSecret string) PipedriveAuthClient { }) return PipedriveAuthClient{ client: resty.NewWithClient(otelClient). - SetHostURL("https://oauth.pipedrive.com"). + SetBaseURL("https://oauth.pipedrive.com"). SetRetryCount(0). SetRetryWaitTime(1000 * time.Millisecond). SetRetryMaxWaitTime(1500 * time.Millisecond). @@ -57,8 +58,8 @@ func NewPipedriveAuthClient(clientID, clientSecret string) PipedriveAuthClient { AddRetryCondition(func(r *resty.Response, err error) bool { return r.StatusCode() == http.StatusTooManyRequests }), - clientID: clientID, - clientSecret: clientSecret, + clientID: credentials.ClientID, + clientSecret: credentials.ClientSecret, } } diff --git a/backend/services/shared/client/command.go b/backend/services/shared/client/command.go index 0bb5541..8353237 100644 --- a/backend/services/shared/client/command.go +++ b/backend/services/shared/client/command.go @@ -25,8 +25,8 @@ import ( "net/http" "time" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/crypto" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/crypto" + "github.com/ONLYOFFICE/onlyoffice-integration-adapters/log" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/request" "github.com/ONLYOFFICE/onlyoffice-pipedrive/services/shared/response" "github.com/go-resty/resty/v2" diff --git a/backend/services/shared/client/error.go b/backend/services/shared/client/error.go index 91cd1c8..9a7c2cb 100644 --- a/backend/services/shared/client/error.go +++ b/backend/services/shared/client/error.go @@ -23,7 +23,10 @@ import ( "fmt" ) -var ErrInvalidUrlFormat error = errors.New("url is not valid") +var ( + ErrInvalidUrlFormat = errors.New("url is not valid") + ErrInvalidContentLength = errors.New("could not perform api actions due to exceeding content-length") +) type UnexpectedStatusCodeError struct { Action string diff --git a/backend/services/shared/config.go b/backend/services/shared/config.go index a506e5a..a48b004 100644 --- a/backend/services/shared/config.go +++ b/backend/services/shared/config.go @@ -24,9 +24,18 @@ import ( "time" "github.com/sethvargo/go-envconfig" + "golang.org/x/oauth2" "gopkg.in/yaml.v2" ) +type IntegrationCredentials struct { + Credentials struct { + ClientID string `yaml:"client_id" env:"CREDENTIALS_CLIENT_ID"` + ClientSecret string `yaml:"client_secret" env:"CREDENTIALS_CLIENT_SECRET"` + RedirectURL string `yaml:"redirect_url" env:"CREDENTIALS_REDIRECT_URL"` + Scopes []string `yaml:"scopes" env:"CREDENTIALS_SCOPES"` + } `yaml:"credentials"` +} type OnlyofficeConfig struct { Onlyoffice struct { Builder OnlyofficeBuilderConfig `yaml:"builder"` @@ -34,6 +43,56 @@ type OnlyofficeConfig struct { } `yaml:"onlyoffice"` } +func (ic *IntegrationCredentials) Validate() error { + if ic.Credentials.ClientID == "" { + return &InvalidConfigurationParameterError{ + Parameter: "ClientID", + Reason: "Should not be empty", + } + } + + if ic.Credentials.ClientSecret == "" { + return &InvalidConfigurationParameterError{ + Parameter: "Client Secret", + Reason: "Should not be empty", + } + } + + return nil +} + +func BuildNewIntegrationCredentialsConfig(path string) func() (*oauth2.Config, error) { + return func() (*oauth2.Config, error) { + var config IntegrationCredentials + if path != "" { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + decoder := yaml.NewDecoder(file) + + if err := decoder.Decode(&config); err != nil { + return nil, err + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) + defer cancel() + if err := envconfig.Process(ctx, &config); err != nil { + return nil, err + } + + return &oauth2.Config{ + ClientID: config.Credentials.ClientID, + ClientSecret: config.Credentials.ClientSecret, + RedirectURL: config.Credentials.RedirectURL, + Scopes: config.Credentials.Scopes, + }, nil + } +} + func (oc *OnlyofficeConfig) Validate() error { if err := oc.Onlyoffice.Builder.Validate(); err != nil { return err diff --git a/backend/services/shared/crypto/aes.go b/backend/services/shared/crypto/aes.go deleted file mode 100644 index 0c0705f..0000000 --- a/backend/services/shared/crypto/aes.go +++ /dev/null @@ -1,95 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package crypto - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "encoding/base64" - "errors" - "io" -) - -var _ErrInvalidNonceSize = errors.New("invalid nonce size") - -type Encryptor interface { - Encrypt(text string) (string, error) - Decrypt(ciphertext string) (string, error) -} - -type aesEncryptor struct { - key []byte -} - -func NewAesEncryptor(key []byte) Encryptor { - validKey := make([]byte, 32) - copy(validKey, key) - return aesEncryptor{ - key: validKey, - } -} - -func (e aesEncryptor) Encrypt(text string) (string, error) { - c, err := aes.NewCipher(e.key) - if err != nil { - return "", err - } - - gcm, err := cipher.NewGCM(c) - if err != nil { - return "", err - } - - nonce := make([]byte, gcm.NonceSize()) - if _, err := io.ReadFull(rand.Reader, nonce); err != nil { - return "", err - } - - result := gcm.Seal(nonce, nonce, []byte(text), nil) - - return base64.StdEncoding.EncodeToString(result), nil -} - -func (e aesEncryptor) Decrypt(text string) (string, error) { - c, err := aes.NewCipher(e.key) - if err != nil { - return "", err - } - - gcm, err := cipher.NewGCM(c) - if err != nil { - return "", err - } - - buf, err := base64.StdEncoding.DecodeString(text) - if err != nil { - return "", err - } - - nonceSize := gcm.NonceSize() - if len(buf) < nonceSize { - return "", _ErrInvalidNonceSize - } - - nonce, ciphertext := buf[:nonceSize], buf[nonceSize:] - plaintext, _ := gcm.Open(nil, nonce, ciphertext, nil) - - return string(plaintext), nil -} diff --git a/backend/services/shared/crypto/aes_test.go b/backend/services/shared/crypto/aes_test.go deleted file mode 100644 index 9c463f9..0000000 --- a/backend/services/shared/crypto/aes_test.go +++ /dev/null @@ -1,67 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package crypto - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -const ( - _AES_SECRET = "sec" - _AES_TEXT = "mock" - _AES_ENCRYPTED = "79xG0i1SV2Kxz7KB8FKwMQqozRm1ADf45zLDq2MHBd8=" -) - -func TestEncryptText(t *testing.T) { - type test struct { - text string - isErr bool - } - - t.Parallel() - encryptor := NewAesEncryptor([]byte(_AES_SECRET)) - - tests := []test{ - {text: _AES_TEXT, isErr: false}, - {text: "1235423523623", isErr: false}, - {text: "477bbd54-3475-4036-bb64-cafd07275632", isErr: false}, - {text: "b29b5cca7ea66fa4aaeda02238b652b5dad0f31ab52a6f81a785ca4b73c577e97dac14dbf0bc24f8a0371e891de6bd304bddda26bef10f921d7079df7e0a7ccca52b9ab4a47e170f3a2a2d3c3dffeae9", isErr: false}, - } - - for _, test := range tests { - cipher, err := encryptor.Encrypt(test.text) - if test.isErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.NotEmpty(t, cipher) - } - } -} - -func TestDecryptText(t *testing.T) { - t.Parallel() - encryptor := NewAesEncryptor([]byte(_AES_SECRET)) - - text, err := encryptor.Decrypt(_AES_ENCRYPTED) - assert.NoError(t, err) - assert.Equal(t, _AES_TEXT, text) -} diff --git a/backend/services/shared/crypto/jwt.go b/backend/services/shared/crypto/jwt.go deleted file mode 100644 index 9c8aa4f..0000000 --- a/backend/services/shared/crypto/jwt.go +++ /dev/null @@ -1,89 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package crypto - -import ( - "errors" - - "github.com/golang-jwt/jwt/v5" - "github.com/mitchellh/mapstructure" -) - -var ErrJwtManagerSigning = errors.New("could not generate a new jwt") -var ErrJwtManagerEmptyToken = errors.New("could not verify an empty jwt") -var ErrJwtManagerEmptySecret = errors.New("could not sign/verify witn an empty secret") -var ErrJwtManagerEmptyDecodingBody = errors.New("could not decode a jwt. Got empty interface") -var ErrJwtManagerInvalidSigningMethod = errors.New("unexpected jwt signing method") -var ErrJwtManagerCastOrInvalidToken = errors.New("could not cast claims or invalid jwt") - -type JwtManager interface { - Sign(secret string, payload jwt.Claims) (string, error) - Verify(secret, jwtToken string, body interface{}) error -} - -type onlyofficeJwtManager struct { - key []byte -} - -func NewOnlyofficeJwtManager() JwtManager { - return onlyofficeJwtManager{} -} - -func (j onlyofficeJwtManager) Sign(secret string, payload jwt.Claims) (string, error) { - token := jwt.NewWithClaims(jwt.SigningMethodHS256, payload) - ss, err := token.SignedString([]byte(secret)) - - if err != nil { - return "", ErrJwtManagerSigning - } - - return ss, nil -} - -func (j onlyofficeJwtManager) Verify(secret, jwtToken string, body interface{}) error { - if secret == "" { - return ErrJwtManagerEmptySecret - } - - if jwtToken == "" { - return ErrJwtManagerEmptyToken - } - - if body == nil { - return ErrJwtManagerEmptyDecodingBody - } - - token, err := jwt.Parse(jwtToken, func(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, ErrJwtManagerInvalidSigningMethod - } - - return []byte(secret), nil - }) - - if err != nil { - return err - } - - if claims, ok := token.Claims.(jwt.MapClaims); !ok || !token.Valid { - return ErrJwtManagerCastOrInvalidToken - } else { - return mapstructure.Decode(claims, body) - } -} diff --git a/backend/services/shared/message/job.go b/backend/services/shared/request/error.go similarity index 67% rename from backend/services/shared/message/job.go rename to backend/services/shared/request/error.go index 67d40c4..4aa773f 100644 --- a/backend/services/shared/message/job.go +++ b/backend/services/shared/request/error.go @@ -16,19 +16,13 @@ * */ -package message +package request -import "encoding/json" +import "errors" -type JobMessage struct { - UID string `json:"uid"` - Deal string `json:"deal"` - FileID string `json:"fileID"` - Filename string `json:"filename"` - Url string `json:"url"` -} - -func (s JobMessage) ToJSON() []byte { - buf, _ := json.Marshal(s) - return buf -} +var ( + ErrInvalidCompanyID = errors.New("invalid company id") + ErrInvalidDocAddress = errors.New("invalid doc server address") + ErrInvalidDocSecret = errors.New("invalid doc server secret") + ErrInvalidDocHeader = errors.New("invalid doc server header") +) diff --git a/backend/services/shared/request/settings.go b/backend/services/shared/request/settings.go index c16a121..a988a7c 100644 --- a/backend/services/shared/request/settings.go +++ b/backend/services/shared/request/settings.go @@ -20,15 +20,9 @@ package request import ( "encoding/json" - "errors" "strings" ) -var _ErrInvalidCompanyID = errors.New("invalid company id") -var _ErrInvalidDocAddress = errors.New("invalid doc server address") -var _ErrInvalidDocSecret = errors.New("invalid doc server secret") -var _ErrInvalidDocHeader = errors.New("invalid doc server header") - type DocSettings struct { CompanyID int `json:"company_id" mapstructure:"company_id"` DocAddress string `json:"doc_address" mapstructure:"doc_address"` @@ -46,19 +40,19 @@ func (c DocSettings) Validate() error { c.DocSecret = strings.TrimSpace(c.DocSecret) if c.CompanyID <= 0 { - return _ErrInvalidCompanyID + return ErrInvalidCompanyID } if c.DocAddress == "" { - return _ErrInvalidDocAddress + return ErrInvalidDocAddress } if c.DocSecret == "" { - return _ErrInvalidDocSecret + return ErrInvalidDocSecret } if c.DocHeader == "" { - return _ErrInvalidDocHeader + return ErrInvalidDocHeader } return nil From bae6ee7ff6d0e58e32a99e9d98d61ae111d1b4a7 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 5 Jun 2023 16:47:41 +0500 Subject: [PATCH 131/145] refactor: remove pkg module --- backend/pkg/bootstrap.go | 94 -------- backend/pkg/cache/cache.go | 72 ------ backend/pkg/cache/memory.go | 35 --- backend/pkg/cache/redis.go | 39 ---- backend/pkg/config/cache.go | 82 ------- backend/pkg/config/cors.go | 71 ------ backend/pkg/config/credentials.go | 85 ------- backend/pkg/config/error.go | 30 --- backend/pkg/config/logger.go | 93 -------- backend/pkg/config/messaging.go | 77 ------- backend/pkg/config/persistence.go | 79 ------- backend/pkg/config/registry.go | 79 ------- backend/pkg/config/resilience.go | 87 -------- backend/pkg/config/server.go | 102 --------- backend/pkg/config/trace.go | 70 ------ backend/pkg/config/worker.go | 80 ------- backend/pkg/log/error.go | 41 ---- backend/pkg/log/hook/elastic.go | 280 ------------------------ backend/pkg/log/logger.go | 142 ------------ backend/pkg/log/logrus.go | 188 ---------------- backend/pkg/messaging/broker.go | 77 ------- backend/pkg/middleware/cache.go | 35 --- backend/pkg/middleware/cors.go | 35 --- backend/pkg/middleware/log.go | 37 ---- backend/pkg/middleware/rate.go | 54 ----- backend/pkg/middleware/secure.go | 36 --- backend/pkg/middleware/timeout.go | 30 --- backend/pkg/middleware/trace.go | 56 ----- backend/pkg/middleware/version.go | 33 --- backend/pkg/middleware/wrapper/trace.go | 53 ----- backend/pkg/registry/registry.go | 58 ----- backend/pkg/resilience/circuit.go | 49 ----- backend/pkg/service/http/service.go | 149 ------------- backend/pkg/service/repl/service.go | 70 ------ backend/pkg/service/rpc/service.go | 154 ------------- backend/pkg/shared/env.go | 37 ---- backend/pkg/trace/error.go | 26 --- backend/pkg/trace/tracer.go | 74 ------- backend/pkg/trace/zipkin.go | 35 --- backend/pkg/worker/asynq.go | 144 ------------ backend/pkg/worker/enqueuer.go | 40 ---- backend/pkg/worker/option.go | 72 ------ backend/pkg/worker/worker.go | 40 ---- 43 files changed, 3220 deletions(-) delete mode 100644 backend/pkg/bootstrap.go delete mode 100644 backend/pkg/cache/cache.go delete mode 100644 backend/pkg/cache/memory.go delete mode 100644 backend/pkg/cache/redis.go delete mode 100644 backend/pkg/config/cache.go delete mode 100644 backend/pkg/config/cors.go delete mode 100644 backend/pkg/config/credentials.go delete mode 100644 backend/pkg/config/error.go delete mode 100644 backend/pkg/config/logger.go delete mode 100644 backend/pkg/config/messaging.go delete mode 100644 backend/pkg/config/persistence.go delete mode 100644 backend/pkg/config/registry.go delete mode 100644 backend/pkg/config/resilience.go delete mode 100644 backend/pkg/config/server.go delete mode 100644 backend/pkg/config/trace.go delete mode 100644 backend/pkg/config/worker.go delete mode 100644 backend/pkg/log/error.go delete mode 100644 backend/pkg/log/hook/elastic.go delete mode 100644 backend/pkg/log/logger.go delete mode 100644 backend/pkg/log/logrus.go delete mode 100644 backend/pkg/messaging/broker.go delete mode 100644 backend/pkg/middleware/cache.go delete mode 100644 backend/pkg/middleware/cors.go delete mode 100644 backend/pkg/middleware/log.go delete mode 100644 backend/pkg/middleware/rate.go delete mode 100644 backend/pkg/middleware/secure.go delete mode 100644 backend/pkg/middleware/timeout.go delete mode 100644 backend/pkg/middleware/trace.go delete mode 100644 backend/pkg/middleware/version.go delete mode 100644 backend/pkg/middleware/wrapper/trace.go delete mode 100644 backend/pkg/registry/registry.go delete mode 100644 backend/pkg/resilience/circuit.go delete mode 100644 backend/pkg/service/http/service.go delete mode 100644 backend/pkg/service/repl/service.go delete mode 100644 backend/pkg/service/rpc/service.go delete mode 100644 backend/pkg/shared/env.go delete mode 100644 backend/pkg/trace/error.go delete mode 100644 backend/pkg/trace/tracer.go delete mode 100644 backend/pkg/trace/zipkin.go delete mode 100644 backend/pkg/worker/asynq.go delete mode 100644 backend/pkg/worker/enqueuer.go delete mode 100644 backend/pkg/worker/option.go delete mode 100644 backend/pkg/worker/worker.go diff --git a/backend/pkg/bootstrap.go b/backend/pkg/bootstrap.go deleted file mode 100644 index b0d88a4..0000000 --- a/backend/pkg/bootstrap.go +++ /dev/null @@ -1,94 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package pkg - -import ( - "context" - "net/http" - "os" - - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/cache" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/messaging" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/registry" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/service/repl" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/trace" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/worker" - "go-micro.dev/v4" - "go.uber.org/fx" - "go.uber.org/fx/fxevent" - "golang.org/x/sync/errgroup" -) - -func Bootstrap(path string, extras ...interface{}) *fx.App { - builder := config.BuildNewServerConfig(path) - sconf, err := builder() - if err != nil { - log.DefaultLogger{}.Fatal(err.Error()) - return nil - } - - var logger fx.Option = fx.NopLogger - if sconf.Debug { - logger = fx.WithLogger(func() fxevent.Logger { - return &fxevent.ConsoleLogger{W: os.Stdout} - }) - } - - return fx.New( - fx.Provide(config.BuildNewCacheConfig(path)), - fx.Provide(config.BuildNewCorsConfig(path)), - fx.Provide(config.BuildNewCredentialsConfig(path)), - fx.Provide(config.BuildNewLoggerConfig(path)), - fx.Provide(config.BuildNewMessagingConfig(path)), - fx.Provide(config.BuildNewPersistenceConfig(path)), - fx.Provide(config.BuildNewRegistryConfig(path)), - fx.Provide(config.BuildNewResilienceConfig(path)), - fx.Provide(builder), - fx.Provide(config.BuildNewTracerConfig(path)), - fx.Provide(config.BuildNewWorkerConfig(path)), - fx.Provide(cache.NewCache), - fx.Provide(log.NewLogrusLogger), - fx.Provide(registry.NewRegistry), - fx.Provide(messaging.NewBroker), - fx.Provide(trace.NewTracer), - fx.Provide(worker.NewBackgroundWorker), - fx.Provide(worker.NewBackgroundEnqueuer), - fx.Provide(repl.NewService), - fx.Provide(extras...), - fx.Invoke(func(lifecycle fx.Lifecycle, service micro.Service, repl *http.Server, logger log.Logger) { - lifecycle.Append(fx.Hook{ - OnStart: func(ctx context.Context) error { - go repl.ListenAndServe() - go service.Run() - return nil - }, - OnStop: func(ctx context.Context) error { - g, gCtx := errgroup.WithContext(ctx) - g.Go(func() error { - return repl.Shutdown(gCtx) - }) - return g.Wait() - }, - }) - }), - logger, - ) -} diff --git a/backend/pkg/cache/cache.go b/backend/pkg/cache/cache.go deleted file mode 100644 index 41f815d..0000000 --- a/backend/pkg/cache/cache.go +++ /dev/null @@ -1,72 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package cache - -import ( - "context" - "time" - - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/eko/gocache/lib/v4/marshaler" - "github.com/eko/gocache/lib/v4/store" - "go-micro.dev/v4/cache" -) - -type CustomCache struct { - store *marshaler.Marshaler - name string -} - -func (c *CustomCache) Get(ctx context.Context, key string) (interface{}, time.Time, error) { - var result interface{} - _, err := c.store.Get(ctx, key, &result) - return result, time.Now(), err -} - -func (c *CustomCache) Put(ctx context.Context, key string, val interface{}, d time.Duration) error { - return c.store.Set(ctx, key, val, store.WithExpiration(d)) -} - -func (c *CustomCache) Delete(ctx context.Context, key string) error { - return c.store.Delete(ctx, key) -} - -func (c *CustomCache) String() string { - return c.name -} - -func NewCache(config *config.CacheConfig) cache.Cache { - switch config.Cache.Type { - case 1: - return &CustomCache{ - store: newMemory(config.Cache.Size), - name: "Freecache", - } - case 2: - return &CustomCache{ - store: newRedis(config.Cache.Address, config.Cache.Username, config.Cache.Password, config.Cache.Database), - name: "Redis", - } - default: - return &CustomCache{ - store: newMemory(config.Cache.Size), - name: "Freecache", - } - } -} diff --git a/backend/pkg/cache/memory.go b/backend/pkg/cache/memory.go deleted file mode 100644 index 653f25f..0000000 --- a/backend/pkg/cache/memory.go +++ /dev/null @@ -1,35 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package cache - -import ( - "time" - - "github.com/coocood/freecache" - "github.com/eko/gocache/lib/v4/cache" - "github.com/eko/gocache/lib/v4/marshaler" - "github.com/eko/gocache/lib/v4/store" - freecache_store "github.com/eko/gocache/store/freecache/v4" -) - -func newMemory(size int) *marshaler.Marshaler { - freecacheStore := freecache_store.NewFreecache(freecache.NewCache(size*1024*1024), store.WithExpiration(10*time.Second)) - cacheManage := cache.New[[]byte](freecacheStore) - return marshaler.New(cacheManage.GetCodec().GetStore()) -} diff --git a/backend/pkg/cache/redis.go b/backend/pkg/cache/redis.go deleted file mode 100644 index 1b74aae..0000000 --- a/backend/pkg/cache/redis.go +++ /dev/null @@ -1,39 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package cache - -import ( - "github.com/eko/gocache/lib/v4/cache" - "github.com/eko/gocache/lib/v4/marshaler" - redis_store "github.com/eko/gocache/store/redis/v4" - "github.com/go-redis/redis/v8" -) - -func newRedis(address, username, password string, db int) *marshaler.Marshaler { - redisClient := redis.NewClient(&redis.Options{ - Username: username, - Addr: address, - Password: password, - DB: db, - }) - redisStore := redis_store.NewRedis(redisClient) - cacheManager := cache.New[string](redisStore) - marshaller := marshaler.New(cacheManager.GetCodec().GetStore()) - return marshaller -} diff --git a/backend/pkg/config/cache.go b/backend/pkg/config/cache.go deleted file mode 100644 index 6862e28..0000000 --- a/backend/pkg/config/cache.go +++ /dev/null @@ -1,82 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package config - -import ( - "context" - "os" - "time" - - "github.com/sethvargo/go-envconfig" - "gopkg.in/yaml.v2" -) - -type CacheConfig struct { - Cache struct { - Type int `yaml:"type" env:"CACHE_TYPE,overwrite"` - Size int `yaml:"size" env:"CACHE_SIZE,overwrite"` - Address string `yaml:"address" env:"CACHE_ADDRESS,overwrite"` - Username string `yaml:"username" env:"CACHE_USERNAME,overwrite"` - Password string `yaml:"password" env:"CACHE_PASSWORD,overwrite"` - Database int `yaml:"database" env:"CACHE_DATABASE,overwrite"` - } `yaml:"cache"` -} - -func (b *CacheConfig) Validate() error { - switch b.Cache.Type { - case 2: - if b.Cache.Address == "" { - return &InvalidConfigurationParameterError{ - Parameter: "Address", - Reason: "Redis cache must have a valid address", - } - } - return nil - default: - return nil - } -} - -func BuildNewCacheConfig(path string) func() (*CacheConfig, error) { - return func() (*CacheConfig, error) { - var config CacheConfig - config.Cache.Size = 10 - if path != "" { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - decoder := yaml.NewDecoder(file) - - if err := decoder.Decode(&config); err != nil { - return nil, err - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) - defer cancel() - if err := envconfig.Process(ctx, &config); err != nil { - return nil, err - } - - return &config, config.Validate() - } -} diff --git a/backend/pkg/config/cors.go b/backend/pkg/config/cors.go deleted file mode 100644 index 918ef47..0000000 --- a/backend/pkg/config/cors.go +++ /dev/null @@ -1,71 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package config - -import ( - "context" - "os" - "time" - - "github.com/sethvargo/go-envconfig" - "gopkg.in/yaml.v2" -) - -type CORSConfig struct { - CORS struct { - AllowedOrigins []string `yaml:"origins" env:"ALLOWED_ORIGINS,overwrite"` - AllowedMethods []string `yaml:"methods" env:"ALLOWED_METHODS,overwrite"` - AllowedHeaders []string `yaml:"headers" env:"ALLOWED_HEADERS,overwrite"` - AllowCredentials bool `yaml:"credentials" env:"ALLOW_CREDENTIALS,overwrite"` - } `yaml:"cors"` -} - -func (cc *CORSConfig) Validate() error { - return nil -} - -func BuildNewCorsConfig(path string) func() (*CORSConfig, error) { - return func() (*CORSConfig, error) { - var config CORSConfig - config.CORS.AllowedOrigins = []string{"*"} - config.CORS.AllowedMethods = []string{"*"} - config.CORS.AllowedHeaders = []string{"*"} - if path != "" { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - decoder := yaml.NewDecoder(file) - - if err := decoder.Decode(&config); err != nil { - return nil, err - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) - defer cancel() - if err := envconfig.Process(ctx, &config); err != nil { - return nil, err - } - - return &config, config.Validate() - } -} diff --git a/backend/pkg/config/credentials.go b/backend/pkg/config/credentials.go deleted file mode 100644 index 6002f37..0000000 --- a/backend/pkg/config/credentials.go +++ /dev/null @@ -1,85 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package config - -import ( - "context" - "os" - "strings" - "time" - - "github.com/sethvargo/go-envconfig" - "gopkg.in/yaml.v2" -) - -type OAuthCredentialsConfig struct { - Credentials struct { - ClientID string `yaml:"client_id" env:"CLIENT_ID,overwrite"` - ClientSecret string `yaml:"client_secret" env:"CLIENT_SECRET,overwrite"` - RedirectURI string `yaml:"redirect_uri" env:"REDIRECT_URI,overwrite"` - } `yaml:"oauth"` -} - -func (zc *OAuthCredentialsConfig) Validate() error { - zc.Credentials.ClientID = strings.TrimSpace(zc.Credentials.ClientID) - zc.Credentials.ClientSecret = strings.TrimSpace(zc.Credentials.ClientSecret) - - if zc.Credentials.ClientID == "" { - return &InvalidConfigurationParameterError{ - Parameter: "ClientID", - Reason: "Should not be empty", - } - } - - if zc.Credentials.ClientSecret == "" { - return &InvalidConfigurationParameterError{ - Parameter: "ClientSecret", - Reason: "Should not be empty", - } - } - - return nil -} - -func BuildNewCredentialsConfig(path string) func() (*OAuthCredentialsConfig, error) { - return func() (*OAuthCredentialsConfig, error) { - var config OAuthCredentialsConfig - if path != "" { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - decoder := yaml.NewDecoder(file) - - if err := decoder.Decode(&config); err != nil { - return nil, err - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) - defer cancel() - if err := envconfig.Process(ctx, &config); err != nil { - return nil, err - } - - return &config, config.Validate() - } -} diff --git a/backend/pkg/config/error.go b/backend/pkg/config/error.go deleted file mode 100644 index 87151fb..0000000 --- a/backend/pkg/config/error.go +++ /dev/null @@ -1,30 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package config - -import "fmt" - -type InvalidConfigurationParameterError struct { - Parameter string - Reason string -} - -func (e *InvalidConfigurationParameterError) Error() string { - return fmt.Sprintf("invald configuration [%s] parameter. Reason: %s", e.Parameter, e.Reason) -} diff --git a/backend/pkg/config/logger.go b/backend/pkg/config/logger.go deleted file mode 100644 index d6b504e..0000000 --- a/backend/pkg/config/logger.go +++ /dev/null @@ -1,93 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package config - -import ( - "context" - "os" - "time" - - "github.com/sethvargo/go-envconfig" - "gopkg.in/yaml.v2" -) - -type LoggerConfig struct { - Logger struct { - Name string `yaml:"name" env:"LOGGER_NAME,overwrite"` - Level int `yaml:"level" env:"LOGGER_LEVEL,overwrite"` - Pretty bool `yaml:"pretty" env:"LOGGER_PRETTY,overwrite"` - Color bool `yaml:"color" env:"LOGGER_COLOR,overwrite"` - File FileLogConfig `yaml:"file"` - Elastic ElasticLogConfig `yaml:"elastic"` - } `yaml:"logger"` -} - -type ElasticLogConfig struct { - Address string `yaml:"address" env:"ELASTIC_ADDRESS,overwrite"` - Index string `yaml:"index" env:"ELASTIC_INDEX,overwrite"` - Level int `yaml:"level" env:"ELASTIC_LEVEL,overwrite"` - Bulk bool `yaml:"bulk" env:"ELASTIC_BULK,overwrite"` - Async bool `yaml:"async" env:"ELASTIC_ASYNC,overwrite"` - HealthcheckEnabled bool `yaml:"healthcheck" env:"ELASTIC_HEALTHCHECK,overwrite"` - BasicAuthUsername string `yaml:"username" env:"ELASTIC_AUTH_USERNAME,overwrite"` - BasicAuthPassword string `yaml:"password" env:"ELASTIC_AUTH_PASSWORD,overwrite"` - GzipEnabled bool `yaml:"gzip" env:"ELASTIC_GZIP_ENABLED,overwrite"` -} - -type FileLogConfig struct { - Filename string `yaml:"filename" env:"FILELOG_NAME,overwrite"` - MaxSize int `yaml:"maxsize" env:"FILELOG_MAX_SIZE,overwrite"` - MaxAge int `yaml:"maxage" env:"FILELOG_MAX_AGE,overwrite"` - MaxBackups int `yaml:"maxbackups" env:"FILELOG_MAX_BACKUPS,overwrite"` - LocalTime bool `yaml:"localtime"` - Compress bool `yaml:"compress" env:"FILELOG_COMPRESS,overwrite"` -} - -func (lc *LoggerConfig) Validate() error { - return nil -} - -func BuildNewLoggerConfig(path string) func() (*LoggerConfig, error) { - return func() (*LoggerConfig, error) { - var config LoggerConfig - config.Logger.Name = "unknown" - config.Logger.Level = 4 - if path != "" { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - decoder := yaml.NewDecoder(file) - - if err := decoder.Decode(&config); err != nil { - return nil, err - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) - defer cancel() - if err := envconfig.Process(ctx, &config); err != nil { - return nil, err - } - - return &config, config.Validate() - } -} diff --git a/backend/pkg/config/messaging.go b/backend/pkg/config/messaging.go deleted file mode 100644 index ae16c56..0000000 --- a/backend/pkg/config/messaging.go +++ /dev/null @@ -1,77 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package config - -import ( - "context" - "os" - "time" - - "github.com/sethvargo/go-envconfig" - "gopkg.in/yaml.v2" -) - -type BrokerConfig struct { - Messaging struct { - Addrs []string `yaml:"addresses" env:"BROKER_ADDRESSES,overwrite"` - Type int `yaml:"type" env:"BROKER_TYPE,overwrite"` - DisableAutoAck bool `yaml:"disable_auto_ack" env:"BROKER_DISABLE_AUTO_ACK,overwrite"` - Durable bool `yaml:"durable" env:"BROKER_DURABLE,overwrite"` - AckOnSuccess bool `yaml:"ack_on_success" env:"BROKER_ACK_ON_SUCCESS,overwrite"` - RequeueOnError bool `yaml:"requeue_on_error" env:"BROKER_REQUEUE_ON_ERROR,overwrite"` - } `yaml:"messaging"` -} - -func (b *BrokerConfig) Validate() error { - if len(b.Messaging.Addrs) == 0 { - return &InvalidConfigurationParameterError{ - Parameter: "Addrs", - Reason: "Invalid number of addresses", - } - } - - return nil -} - -func BuildNewMessagingConfig(path string) func() (*BrokerConfig, error) { - return func() (*BrokerConfig, error) { - var config BrokerConfig - if path != "" { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - decoder := yaml.NewDecoder(file) - - if err := decoder.Decode(&config); err != nil { - return nil, err - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) - defer cancel() - if err := envconfig.Process(ctx, &config); err != nil { - return nil, err - } - - return &config, config.Validate() - } -} diff --git a/backend/pkg/config/persistence.go b/backend/pkg/config/persistence.go deleted file mode 100644 index 52adcd7..0000000 --- a/backend/pkg/config/persistence.go +++ /dev/null @@ -1,79 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package config - -import ( - "context" - "os" - "strings" - "time" - - "github.com/sethvargo/go-envconfig" - "gopkg.in/yaml.v2" -) - -type PersistenceConfig struct { - Persistence struct { - URL string `yaml:"url" env:"PERSISTENCE_URL,overwrite"` - Type int `yaml:"type" env:"PERSISTENCE_TYPE,overwrite"` - } `yaml:"persistence"` -} - -func (p *PersistenceConfig) Validate() error { - p.Persistence.URL = strings.TrimSpace(p.Persistence.URL) - switch p.Persistence.Type { - case 1: - if p.Persistence.URL == "" { - return &InvalidConfigurationParameterError{ - Parameter: "URL", - Reason: "MongoDB driver expects a valid url", - } - } - return nil - default: - return nil - } -} - -func BuildNewPersistenceConfig(path string) func() (*PersistenceConfig, error) { - return func() (*PersistenceConfig, error) { - var config PersistenceConfig - if path != "" { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - decoder := yaml.NewDecoder(file) - - if err := decoder.Decode(&config); err != nil { - return nil, err - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) - defer cancel() - if err := envconfig.Process(ctx, &config); err != nil { - return nil, err - } - - return &config, config.Validate() - } -} diff --git a/backend/pkg/config/registry.go b/backend/pkg/config/registry.go deleted file mode 100644 index 4958617..0000000 --- a/backend/pkg/config/registry.go +++ /dev/null @@ -1,79 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package config - -import ( - "context" - "os" - "time" - - "github.com/sethvargo/go-envconfig" - "gopkg.in/yaml.v2" -) - -type RegistryConfig struct { - Registry struct { - Addresses []string `yaml:"addresses" env:"REGISTRY_ADDRESSES,overwrite"` - CacheTTL time.Duration `yaml:"cache_duration" env:"REGISTRY_CACHE_DURATION,overwrite"` - Type int `yaml:"type" env:"REGISTRY_TYPE,overwrite"` - } `yaml:"registry"` -} - -func (r *RegistryConfig) Validate() error { - switch r.Registry.Type { - case 1: - return nil - default: - if len(r.Registry.Addresses) <= 0 { - return &InvalidConfigurationParameterError{ - Parameter: "Addresses", - Reason: "Length should be greater than zero", - } - } - return nil - } -} - -func BuildNewRegistryConfig(path string) func() (*RegistryConfig, error) { - return func() (*RegistryConfig, error) { - var config RegistryConfig - config.Registry.CacheTTL = 10 * time.Second - if path != "" { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - decoder := yaml.NewDecoder(file) - - if err := decoder.Decode(&config); err != nil { - return nil, err - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) - defer cancel() - if err := envconfig.Process(ctx, &config); err != nil { - return nil, err - } - - return &config, config.Validate() - } -} diff --git a/backend/pkg/config/resilience.go b/backend/pkg/config/resilience.go deleted file mode 100644 index 63f70ad..0000000 --- a/backend/pkg/config/resilience.go +++ /dev/null @@ -1,87 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package config - -import ( - "context" - "os" - "time" - - "github.com/sethvargo/go-envconfig" - "gopkg.in/yaml.v2" -) - -type ResilienceConfig struct { - Resilience struct { - RateLimiter RateLimiterConfig `yaml:"rate_limiter"` - CircuitBreaker CircuitBreakerConfig `yaml:"circuit_breaker"` - } `yaml:"resilience"` -} - -func BuildNewResilienceConfig(path string) func() (*ResilienceConfig, error) { - return func() (*ResilienceConfig, error) { - var config ResilienceConfig - config.Resilience.RateLimiter.Limit = 3000 - config.Resilience.RateLimiter.IPLimit = 20 - config.Resilience.CircuitBreaker.Timeout = 5000 - if path != "" { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - decoder := yaml.NewDecoder(file) - - if err := decoder.Decode(&config); err != nil { - return nil, err - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) - defer cancel() - if err := envconfig.Process(ctx, &config); err != nil { - return nil, err - } - - return &config, config.Validate() - } -} - -type RateLimiterConfig struct { - Limit uint64 `yaml:"limit" env:"RATE_LIMIT,overwrite"` - IPLimit uint64 `yaml:"iplimit" env:"RATE_LIMIT_IP,overwrite"` -} - -type CircuitBreakerConfig struct { - // Timeout is how long to wait for command to complete, in milliseconds (default 1000) - Timeout int `yaml:"timeout" env:"CIRCUIT_TIMEOUT,overwrite"` - // MaxConcurrent is how many commands of the same type can run at the same time (default 10) - MaxConcurrent int `yaml:"max_concurrent" env:"CIRCUIT_MAX_CONCURRENT,overwrite"` - // VolumeThreshold is the minimum number of requests needed before a circuit can be tripped due to health (default 20) - VolumeThreshold int `yaml:"volume_threshold" env:"CIRCUIT_VOLUME_THRESHOLD,overwrite"` - // SleepWindow is how long, in milliseconds, to wait after a circuit opens before testing for recovery (default 5000) - SleepWindow int `yaml:"sleep_window" env:"CIRCUIT_SLEEP_WINDOW,overwrite"` - // ErrorPercentThreshold causes circuits to open once the rolling measure of errors exceeds this percent of requests (default 50) - ErrorPercentThreshold int `yaml:"error_percent_threshold" env:"CIRCUIT_ERROR_PERCENT_THRESHOLD,overwrite"` -} - -func (rc *ResilienceConfig) Validate() error { - return nil -} diff --git a/backend/pkg/config/server.go b/backend/pkg/config/server.go deleted file mode 100644 index a6abec0..0000000 --- a/backend/pkg/config/server.go +++ /dev/null @@ -1,102 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package config - -import ( - "context" - "os" - "strings" - "time" - - "github.com/sethvargo/go-envconfig" - "gopkg.in/yaml.v2" -) - -type ServerConfig struct { - Namespace string `yaml:"namespace" env:"SERVER_NAMESPACE,overwrite"` - Name string `yaml:"name" env:"SERVER_NAME,overwrite"` - Version int `yaml:"version" env:"SERVER_VERSION,overwrite"` - Address string `yaml:"address" env:"SERVER_ADDRESS,overwrite"` - ReplAddress string `yaml:"repl_address" env:"REPL_ADDRESS,overwrite"` - Debug bool `yaml:"debug" env:"SERVER_DEBUG,overwrite"` -} - -func (hs *ServerConfig) Validate() error { - hs.Namespace = strings.TrimSpace(hs.Namespace) - hs.Name = strings.TrimSpace(hs.Name) - hs.Address = strings.TrimSpace(hs.Address) - hs.ReplAddress = strings.TrimSpace(hs.ReplAddress) - - if hs.Namespace == "" { - return &InvalidConfigurationParameterError{ - Parameter: "Namespace", - Reason: "Should not be empty", - } - } - - if hs.Name == "" { - return &InvalidConfigurationParameterError{ - Parameter: "Name", - Reason: "Should not be empty", - } - } - - if hs.Address == "" { - return &InvalidConfigurationParameterError{ - Parameter: "Address", - Reason: "Should not be empty", - } - } - - if hs.ReplAddress == "" { - return &InvalidConfigurationParameterError{ - Parameter: "Repl Address", - Reason: "Should not be empty", - } - } - - return nil -} - -func BuildNewServerConfig(path string) func() (*ServerConfig, error) { - return func() (*ServerConfig, error) { - var config ServerConfig - if path != "" { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - decoder := yaml.NewDecoder(file) - - if err := decoder.Decode(&config); err != nil { - return nil, err - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) - defer cancel() - if err := envconfig.Process(ctx, &config); err != nil { - return nil, err - } - - return &config, config.Validate() - } -} diff --git a/backend/pkg/config/trace.go b/backend/pkg/config/trace.go deleted file mode 100644 index ce426b6..0000000 --- a/backend/pkg/config/trace.go +++ /dev/null @@ -1,70 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package config - -import ( - "context" - "os" - "time" - - "github.com/sethvargo/go-envconfig" - "gopkg.in/yaml.v2" -) - -type TracerConfig struct { - Tracer struct { - Name string `yaml:"name" env:"TRACER_NAME,overwrite"` - Enable bool `yaml:"enable" env:"TRACER_ENABLE,overwrite"` - Address string `yaml:"address" env:"TRACER_ADDRESS,overwrite"` - TracerType int `yaml:"type" env:"TRACER_TYPE,overwrite"` - FractionRatio float64 `yaml:"fraction" env:"TRACER_FRACTION_RATIO,overwrite"` - } `yaml:"tracer"` -} - -func (tc *TracerConfig) Validate() error { - return nil -} - -func BuildNewTracerConfig(path string) func() (*TracerConfig, error) { - return func() (*TracerConfig, error) { - var config TracerConfig - config.Tracer.FractionRatio = 1 - if path != "" { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - decoder := yaml.NewDecoder(file) - - if err := decoder.Decode(&config); err != nil { - return nil, err - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) - defer cancel() - if err := envconfig.Process(ctx, &config); err != nil { - return nil, err - } - - return &config, config.Validate() - } -} diff --git a/backend/pkg/config/worker.go b/backend/pkg/config/worker.go deleted file mode 100644 index c495678..0000000 --- a/backend/pkg/config/worker.go +++ /dev/null @@ -1,80 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package config - -import ( - "context" - "os" - "time" - - "github.com/sethvargo/go-envconfig" - "gopkg.in/yaml.v2" -) - -// Worker configuration -type WorkerConfig struct { - Worker struct { - Enable bool `yaml:"enable" env:"WORKER_ENABLE,overwrite"` - Type int `yaml:"type" env:"WORKER_TYPE,overwrite"` - MaxConcurrency int `yaml:"max_concurrency" env:"WORKER_MAX_CONCURRENCY,overwrite"` - RedisAddresses []string `yaml:"addresses" env:"WORKER_ADDRESS,overwrite"` - RedisUsername string `yaml:"username" env:"WORKER_USERNAME,overwrite"` - RedisPassword string `yaml:"password" env:"WORKER_PASSWORD,overwrite"` - RedisDatabase int `yaml:"database" env:"WORKER_DATABASE,overwrite"` - } `yaml:"worker"` -} - -func (wc *WorkerConfig) Validate() error { - if wc.Worker.Enable && len(wc.Worker.RedisAddresses) < 1 { - return &InvalidConfigurationParameterError{ - Parameter: "Worker address", - Reason: "Should not be empty", - } - } - - return nil -} - -func BuildNewWorkerConfig(path string) func() (*WorkerConfig, error) { - return func() (*WorkerConfig, error) { - var config WorkerConfig - config.Worker.MaxConcurrency = 3 - if path != "" { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - decoder := yaml.NewDecoder(file) - - if err := decoder.Decode(&config); err != nil { - return nil, err - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second) - defer cancel() - if err := envconfig.Process(ctx, &config); err != nil { - return nil, err - } - - return &config, config.Validate() - } -} diff --git a/backend/pkg/log/error.go b/backend/pkg/log/error.go deleted file mode 100644 index 979c88b..0000000 --- a/backend/pkg/log/error.go +++ /dev/null @@ -1,41 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package log - -import "fmt" - -// LogFileInitializationError fires if Lumberjack throws an error. -type LogFileInitializationError struct { - Path string - Cause error -} - -func (e *LogFileInitializationError) Error() string { - return fmt.Sprintf("could not open/create a log file with path: %s. Cause: %s", e.Path, e.Cause.Error()) -} - -// LogElasticInitializationError fires when an elastic client throws an error. -type LogElasticInitializationError struct { - Address string - Cause error -} - -func (e *LogElasticInitializationError) Error() string { - return fmt.Sprintf("could not initialize an elastic client/hook with address: %s. Cause: %s", e.Address, e.Cause.Error()) -} diff --git a/backend/pkg/log/hook/elastic.go b/backend/pkg/log/hook/elastic.go deleted file mode 100644 index 2f9b2c6..0000000 --- a/backend/pkg/log/hook/elastic.go +++ /dev/null @@ -1,280 +0,0 @@ -/* -MIT License - -# Copyright (c) 2018 Radomír Sohlich - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ -package hook - -import ( - "context" - "errors" - "strings" - "time" - - "github.com/google/uuid" - "github.com/olivere/elastic/v7" - "github.com/sirupsen/logrus" -) - -const mapping = ` -{ - "mappings":{ - "properties":{ - "Host":{ - "type":"text" - }, - "@timestamp":{ - "type":"text" - }, - "Message":{ - "type":"text" - }, - "Data":{ - "dynamic": true, - "properties": {} - }, - "Level":{ - "type":"text" - } - } - } -} -` - -var ( - // ErrCannotCreateIndex Fired if the index is not created - ErrCannotCreateIndex = errors.New("cannot create index") -) - -// IndexNameFunc get index name -type IndexNameFunc func() string - -type fireFunc func(entry *logrus.Entry, hook *ElasticHook) error - -// ElasticHook is a logrus -// hook for ElasticSearch -type ElasticHook struct { - client *elastic.Client - host string - index IndexNameFunc - levels []logrus.Level - ctx context.Context - ctxCancel context.CancelFunc - fireFunc fireFunc -} - -type message struct { - Host string `json:"Host,omitempty"` - Timestamp string `json:"@timestamp"` - File string `json:"File,omitempty"` - Func string `json:"Func,omitempty"` - Message string `json:"Message,omitempty"` - Data logrus.Fields - Level string `json:"Level,omitempty"` -} - -// NewElasticHook creates new hook. -// client - ElasticSearch client with specific es version (v5/v6/v7/...) -// host - host of system -// level - log level -// index - name of the index in ElasticSearch -func NewElasticHook(client *elastic.Client, host string, level logrus.Level, index string) (*ElasticHook, error) { - return NewElasticHookWithFunc(client, host, level, func() string { return index }) -} - -// NewAsyncElasticHook creates new hook with asynchronous log. -// client - ElasticSearch client with specific es version (v5/v6/v7/...) -// host - host of system -// level - log level -// index - name of the index in ElasticSearch -func NewAsyncElasticHook(client *elastic.Client, host string, level logrus.Level, index string) (*ElasticHook, error) { - return NewAsyncElasticHookWithFunc(client, host, level, func() string { return index }) -} - -// NewBulkProcessorElasticHook creates new hook that uses a bulk processor for indexing. -// client - ElasticSearch client with specific es version (v5/v6/v7/...) -// host - host of system -// level - log level -// index - name of the index in ElasticSearch -func NewBulkProcessorElasticHook(client *elastic.Client, host string, level logrus.Level, index string) (*ElasticHook, error) { - return NewBulkProcessorElasticHookWithFunc(client, host, level, func() string { return index }) -} - -// NewElasticHookWithFunc creates new hook with -// function that provides the index name. This is useful if the index name is -// somehow dynamic especially based on time. -// client - ElasticSearch client with specific es version (v5/v6/v7/...) -// host - host of system -// level - log level -// indexFunc - function providing the name of index -func NewElasticHookWithFunc(client *elastic.Client, host string, level logrus.Level, indexFunc IndexNameFunc) (*ElasticHook, error) { - return newHookFuncAndFireFunc(client, host, level, indexFunc, syncFireFunc) -} - -// NewAsyncElasticHookWithFunc creates new asynchronous hook with -// function that provides the index name. This is useful if the index name is -// somehow dynamic especially based on time. -// client - ElasticSearch client with specific es version (v5/v6/v7/...) -// host - host of system -// level - log level -// indexFunc - function providing the name of index -func NewAsyncElasticHookWithFunc(client *elastic.Client, host string, level logrus.Level, indexFunc IndexNameFunc) (*ElasticHook, error) { - return newHookFuncAndFireFunc(client, host, level, indexFunc, asyncFireFunc) -} - -// NewBulkProcessorElasticHookWithFunc creates new hook with -// function that provides the index name. This is useful if the index name is -// somehow dynamic especially based on time that uses a bulk processor for -// indexing. -// client - ElasticSearch client with specific es version (v5/v6/v7/...) -// host - host of system -// level - log level -// indexFunc - function providing the name of index -func NewBulkProcessorElasticHookWithFunc(client *elastic.Client, host string, level logrus.Level, indexFunc IndexNameFunc) (*ElasticHook, error) { - fireFunc, err := makeBulkFireFunc(client) - if err != nil { - return nil, err - } - return newHookFuncAndFireFunc(client, host, level, indexFunc, fireFunc) -} - -func newHookFuncAndFireFunc(client *elastic.Client, host string, level logrus.Level, indexFunc IndexNameFunc, fireFunc fireFunc) (*ElasticHook, error) { - var levels []logrus.Level - for _, l := range []logrus.Level{ - logrus.PanicLevel, - logrus.FatalLevel, - logrus.ErrorLevel, - logrus.WarnLevel, - logrus.InfoLevel, - logrus.DebugLevel, - logrus.TraceLevel, - } { - if l <= level { - levels = append(levels, l) - } - } - - ctx, cancel := context.WithCancel(context.TODO()) - - // Use the IndexExists service to check if a specified index exists. - exists, err := client.IndexExists(indexFunc()).Do(ctx) - if err != nil { - // Handle error - cancel() - return nil, err - } - if !exists { - createIndex, err := client.CreateIndex(indexFunc()).BodyString(mapping).Do(ctx) - if err != nil { - cancel() - return nil, err - } - if !createIndex.Acknowledged { - cancel() - return nil, ErrCannotCreateIndex - } - } - - return &ElasticHook{ - client: client, - host: host, - index: indexFunc, - levels: levels, - ctx: ctx, - ctxCancel: cancel, - fireFunc: fireFunc, - }, nil -} - -// Fire is required to implement -// Logrus hook -func (hook *ElasticHook) Fire(entry *logrus.Entry) error { - return hook.fireFunc(entry, hook) -} - -func asyncFireFunc(entry *logrus.Entry, hook *ElasticHook) error { - go syncFireFunc(entry, hook) - return nil -} - -func createMessage(entry *logrus.Entry, hook *ElasticHook) *message { - level := entry.Level.String() - - if e, ok := entry.Data[logrus.ErrorKey]; ok && e != nil { - if err, ok := e.(error); ok { - entry.Data[logrus.ErrorKey] = err.Error() - } - } - - var file string - var function string - if entry.HasCaller() { - file = entry.Caller.File - function = entry.Caller.Function - } - - return &message{ - hook.host, - entry.Time.UTC().Format(time.RFC3339Nano), - file, - function, - entry.Message, - entry.Data, - strings.ToUpper(level), - } -} - -func syncFireFunc(entry *logrus.Entry, hook *ElasticHook) error { - _, err := hook.client.Index(). - Index(hook.index()). - Id(uuid.NewString()). - BodyJson(*createMessage(entry, hook)). - Do(hook.ctx) - - return err -} - -// Create closure with bulk processor tied to fireFunc. -func makeBulkFireFunc(client *elastic.Client) (fireFunc, error) { - processor, err := client.BulkProcessor(). - Name("elogrus.v3.bulk.processor"). - Workers(2). - FlushInterval(time.Second). - Do(context.Background()) - - return func(entry *logrus.Entry, hook *ElasticHook) error { - r := elastic.NewBulkIndexRequest(). - Index(hook.index()). - Doc(*createMessage(entry, hook)) - processor.Add(r) - return nil - }, err -} - -// Levels Required for logrus hook implementation -func (hook *ElasticHook) Levels() []logrus.Level { - return hook.levels -} - -// Cancel all calls to elastic -func (hook *ElasticHook) Cancel() { - hook.ctxCancel() -} diff --git a/backend/pkg/log/logger.go b/backend/pkg/log/logger.go deleted file mode 100644 index 94c098e..0000000 --- a/backend/pkg/log/logger.go +++ /dev/null @@ -1,142 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package log - -import ( - "fmt" - "log" - "os" - - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" -) - -// Logger is a generic logger interface. -type Logger interface { - Debugf(format string, args ...interface{}) - Infof(format string, args ...interface{}) - Warnf(format string, args ...interface{}) - Errorf(format string, args ...interface{}) - Fatalf(format string, args ...interface{}) - Debug(args ...interface{}) - Info(args ...interface{}) - Warn(args ...interface{}) - Error(args ...interface{}) - Fatal(args ...interface{}) -} - -// EmptyLogger is an empty Logger implementation. -type EmptyLogger struct{} - -// NewEmptyLogger is an empty logger constructor. -func NewEmptyLogger() Logger { - return EmptyLogger{} -} - -func (l EmptyLogger) Debugf(format string, args ...interface{}) {} -func (l EmptyLogger) Infof(format string, args ...interface{}) {} -func (l EmptyLogger) Warnf(format string, args ...interface{}) {} -func (l EmptyLogger) Errorf(format string, args ...interface{}) {} -func (l EmptyLogger) Fatalf(format string, args ...interface{}) {} -func (l EmptyLogger) Debug(args ...interface{}) {} -func (l EmptyLogger) Info(args ...interface{}) {} -func (l EmptyLogger) Warn(args ...interface{}) {} -func (l EmptyLogger) Error(args ...interface{}) {} -func (l EmptyLogger) Fatal(args ...interface{}) {} - -// DefaultLogger is a golang log package Logger implementation. -type DefaultLogger struct { - debugL *log.Logger - infoL *log.Logger - warnL *log.Logger - errorL *log.Logger - fatalL *log.Logger - config config.LoggerConfig -} - -// NewDefaultLogger is a golang log package Logger constructor. -func NewDefaultLogger(config *config.LoggerConfig) Logger { - return DefaultLogger{ - debugL: log.New(os.Stdout, fmt.Sprintf("[DEBUG - Default %s]: ", config.Logger.Name), log.Ldate|log.Ltime|log.Llongfile), - infoL: log.New(os.Stdout, fmt.Sprintf("[INFO - Default %s]: ", config.Logger.Name), log.Ldate|log.Ltime|log.Lshortfile), - warnL: log.New(os.Stdout, fmt.Sprintf("[WARN - Default %s]: ", config.Logger.Name), log.Ldate|log.Ltime|log.Lshortfile), - errorL: log.New(os.Stdout, fmt.Sprintf("[ERROR - Default %s]: ", config.Logger.Name), log.Ldate|log.Ltime|log.Lshortfile), - fatalL: log.New(os.Stderr, fmt.Sprintf("[FATAL - Default %s]: ", config.Logger.Name), log.Ldate|log.Ltime|log.Llongfile), - config: *config, - } -} - -func (l DefaultLogger) Debugf(format string, args ...interface{}) { - if l.config.Logger.Level <= 2 { - l.debugL.Printf(format+"\n", args...) - } -} - -func (l DefaultLogger) Infof(format string, args ...interface{}) { - if l.config.Logger.Level <= 3 { - l.infoL.Printf(format+"\n", args...) - } -} - -func (l DefaultLogger) Warnf(format string, args ...interface{}) { - if l.config.Logger.Level <= 4 { - l.warnL.Printf(format+"\n", args...) - } -} - -func (l DefaultLogger) Errorf(format string, args ...interface{}) { - if l.config.Logger.Level <= 5 { - l.errorL.Printf(format+"\n", args...) - } -} - -func (l DefaultLogger) Fatalf(format string, args ...interface{}) { - if l.config.Logger.Level <= 6 { - l.fatalL.Fatalf(format+"\n", args...) - } -} - -func (l DefaultLogger) Debug(args ...interface{}) { - if l.config.Logger.Level <= 2 { - l.debugL.Println(args...) - } -} - -func (l DefaultLogger) Info(args ...interface{}) { - if l.config.Logger.Level <= 3 { - l.infoL.Println(args...) - } -} - -func (l DefaultLogger) Warn(args ...interface{}) { - if l.config.Logger.Level <= 4 { - l.warnL.Println(args...) - } -} - -func (l DefaultLogger) Error(args ...interface{}) { - if l.config.Logger.Level <= 5 { - l.errorL.Println(args...) - } -} - -func (l DefaultLogger) Fatal(args ...interface{}) { - if l.config.Logger.Level <= 6 { - l.fatalL.Fatalln(args...) - } -} diff --git a/backend/pkg/log/logrus.go b/backend/pkg/log/logrus.go deleted file mode 100644 index 93971f1..0000000 --- a/backend/pkg/log/logrus.go +++ /dev/null @@ -1,188 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package log - -import ( - "os" - - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log/hook" - "github.com/natefinch/lumberjack" - elastic "github.com/olivere/elastic/v7" - "github.com/sirupsen/logrus" -) - -type LogLevel int - -const ( - LEVEL_TRACE LogLevel = 1 - LEVEL_DEBUG LogLevel = 2 - LEVEL_INFO LogLevel = 3 - LEVEL_WARNING LogLevel = 4 - LEVEL_ERROR LogLevel = 5 - LEVEL_FATAL LogLevel = 6 -) - -var levels = map[LogLevel]logrus.Level{ - LEVEL_TRACE: logrus.TraceLevel, - LEVEL_DEBUG: logrus.DebugLevel, - LEVEL_INFO: logrus.InfoLevel, - LEVEL_WARNING: logrus.WarnLevel, - LEVEL_ERROR: logrus.ErrorLevel, - LEVEL_FATAL: logrus.FatalLevel, -} - -// LogrusLogger is a logrus logger wrapper. -type LogrusLogger struct { - logger *logrus.Logger - config config.LoggerConfig -} - -// createElasticHook opens a new elastic client and generates an elastic hook. -func createElasticHook(config config.ElasticLogConfig) (*hook.ElasticHook, error) { - client, err := elastic.NewClient( - elastic.SetURL(config.Address), - elastic.SetSniff(false), - elastic.SetBasicAuth(config.BasicAuthUsername, config.BasicAuthPassword), - elastic.SetHealthcheck(config.HealthcheckEnabled), - elastic.SetGzip(config.GzipEnabled), - ) - - if err != nil { - return nil, &LogElasticInitializationError{ - Address: config.Address, - Cause: err, - } - } - - if config.Bulk { - return hook.NewBulkProcessorElasticHook(client, config.Address, levels[LogLevel(config.Level)], config.Index) - } - - if config.Async { - return hook.NewAsyncElasticHook(client, config.Address, levels[LogLevel(config.Level)], config.Index) - } - - return hook.NewElasticHook(client, config.Address, levels[LogLevel(config.Level)], config.Index) -} - -// NewLogrusLogger creates a new logger compliant with the Logger interface. -func NewLogrusLogger(config *config.LoggerConfig) (Logger, error) { - log := logrus.New() - log.SetFormatter(&logrus.TextFormatter{ - DisableColors: !config.Logger.Color, - FullTimestamp: true, - }) - - if lvl, ok := levels[LogLevel(config.Logger.Level)]; ok { - log.SetLevel(lvl) - } - - log.SetReportCaller(true) - log.SetOutput(os.Stdout) - - if config.Logger.File.Filename != "" { - log.SetOutput(&lumberjack.Logger{ - Filename: config.Logger.File.Filename, - MaxSize: config.Logger.File.MaxSize, - MaxBackups: config.Logger.File.MaxBackups, - MaxAge: config.Logger.File.MaxAge, - LocalTime: config.Logger.File.LocalTime, - Compress: config.Logger.File.Compress, - }) - } - - if config.Logger.File.Filename == "" && config.Logger.Elastic.Address != "" && config.Logger.Elastic.Index != "" { - hook, err := createElasticHook(config.Logger.Elastic) - - if err != nil { - return nil, &LogElasticInitializationError{ - Address: config.Logger.Elastic.Address, - Cause: err, - } - } - - log.AddHook(hook) - } - - return LogrusLogger{ - logger: log, - config: *config, - }, nil -} - -func (l LogrusLogger) Debugf(format string, args ...interface{}) { - l.logger.WithFields(logrus.Fields{ - "name": l.config.Logger.Name, - }).Debugf(format, args...) -} - -func (l LogrusLogger) Infof(format string, args ...interface{}) { - l.logger.WithFields(logrus.Fields{ - "name": l.config.Logger.Name, - }).Infof(format, args...) -} - -func (l LogrusLogger) Warnf(format string, args ...interface{}) { - l.logger.WithFields(logrus.Fields{ - "name": l.config.Logger.Name, - }).Warnf(format, args...) -} - -func (l LogrusLogger) Errorf(format string, args ...interface{}) { - l.logger.WithFields(logrus.Fields{ - "name": l.config.Logger.Name, - }).Errorf(format, args...) -} - -func (l LogrusLogger) Fatalf(format string, args ...interface{}) { - l.logger.WithFields(logrus.Fields{ - "name": l.config.Logger.Name, - }).Fatalf(format, args...) -} - -func (l LogrusLogger) Debug(args ...interface{}) { - l.logger.WithFields(logrus.Fields{ - "name": l.config.Logger.Name, - }).Debug(args...) -} - -func (l LogrusLogger) Info(args ...interface{}) { - l.logger.WithFields(logrus.Fields{ - "name": l.config.Logger.Name, - }).Info(args...) -} - -func (l LogrusLogger) Warn(args ...interface{}) { - l.logger.WithFields(logrus.Fields{ - "name": l.config.Logger.Name, - }).Warn(args...) -} - -func (l LogrusLogger) Error(args ...interface{}) { - l.logger.WithFields(logrus.Fields{ - "name": l.config.Logger.Name, - }).Error(args...) -} - -func (l LogrusLogger) Fatal(args ...interface{}) { - l.logger.WithFields(logrus.Fields{ - "name": l.config.Logger.Name, - }).Fatal(args...) -} diff --git a/backend/pkg/messaging/broker.go b/backend/pkg/messaging/broker.go deleted file mode 100644 index f5d023f..0000000 --- a/backend/pkg/messaging/broker.go +++ /dev/null @@ -1,77 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package messaging - -import ( - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/go-micro/plugins/v4/broker/memory" - "github.com/go-micro/plugins/v4/broker/nats" - "github.com/go-micro/plugins/v4/broker/rabbitmq" - "go-micro.dev/v4/broker" - "go-micro.dev/v4/registry" -) - -type BrokerWithOptions struct { - Broker broker.Broker - SubOptions broker.SubscribeOptions -} - -// NewBroker create a broker instance based on BrokerType value -func NewBroker(registry registry.Registry, config *config.BrokerConfig) BrokerWithOptions { - bo := []broker.Option{ - broker.Addrs(config.Messaging.Addrs...), - broker.Registry(registry), - } - - var b broker.Broker - var subOpts broker.SubscribeOptions - - switch config.Messaging.Type { - case 1: - b = rabbitmq.NewBroker(bo...) - - opts := []broker.SubscribeOption{} - if config.Messaging.DisableAutoAck { - opts = append(opts, broker.DisableAutoAck()) - } - - if config.Messaging.AckOnSuccess { - opts = append(opts, rabbitmq.AckOnSuccess()) - } - - if config.Messaging.Durable { - opts = append(opts, rabbitmq.DurableQueue()) - } - - if config.Messaging.RequeueOnError { - opts = append(opts, rabbitmq.RequeueOnError()) - } - - subOpts = broker.NewSubscribeOptions(opts...) - case 2: - b = nats.NewBroker(bo...) - default: - b = memory.NewBroker(bo...) - } - - return BrokerWithOptions{ - Broker: b, - SubOptions: subOpts, - } -} diff --git a/backend/pkg/middleware/cache.go b/backend/pkg/middleware/cache.go deleted file mode 100644 index 5af62e6..0000000 --- a/backend/pkg/middleware/cache.go +++ /dev/null @@ -1,35 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package middleware - -import ( - "net/http" - "time" -) - -// NoCache sets no-cache headers. -func NoCache(next http.Handler) http.Handler { - return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - rw.Header().Set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value") - rw.Header().Set("Expires", "Thu, 01 Jan 1970 00:00:00 GMT") - rw.Header().Set("Last-Modified", time.Now().UTC().Format(http.TimeFormat)) - - next.ServeHTTP(rw, r) - }) -} diff --git a/backend/pkg/middleware/cors.go b/backend/pkg/middleware/cors.go deleted file mode 100644 index ea02410..0000000 --- a/backend/pkg/middleware/cors.go +++ /dev/null @@ -1,35 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package middleware - -import ( - "net/http" - - corsmiddleware "github.com/go-chi/cors" -) - -// Cors creates a new CORS middleware. -func Cors(AllowedOrigins, AllowedMethods, AllowedHeaders []string, AllowCredentials bool) func(http.Handler) http.Handler { - return corsmiddleware.Handler(corsmiddleware.Options{ - AllowedOrigins: AllowedOrigins, - AllowedMethods: AllowedMethods, - AllowedHeaders: AllowedHeaders, - AllowCredentials: AllowCredentials, - }) -} diff --git a/backend/pkg/middleware/log.go b/backend/pkg/middleware/log.go deleted file mode 100644 index db2956b..0000000 --- a/backend/pkg/middleware/log.go +++ /dev/null @@ -1,37 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package middleware - -import ( - "net/http" - - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" -) - -// Log creates a new debug logging middleware. -func Log(logger log.Logger) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if logger != nil { - logger.Debugf("calling [%s] route %s", r.Method, r.URL.Path) - } - next.ServeHTTP(w, r) - }) - } -} diff --git a/backend/pkg/middleware/rate.go b/backend/pkg/middleware/rate.go deleted file mode 100644 index 9628e5b..0000000 --- a/backend/pkg/middleware/rate.go +++ /dev/null @@ -1,54 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package middleware - -import ( - "net/http" - "time" - - "github.com/sethvargo/go-limiter/httplimit" - "github.com/sethvargo/go-limiter/memorystore" -) - -// Option defines a single option. -type Option func() httplimit.KeyFunc - -const _AllRequests = "ALL" - -// WithKeyFuncIP sets ratelimiter based on IP. -func WithKeyFuncIP() httplimit.KeyFunc { - return httplimit.IPKeyFunc("RemoteAddr", "X-Forwarded-For", "X-Real-IP") -} - -// WithKeyFuncAll sets global ratelimiter. -func WithKeyFuncAll() httplimit.KeyFunc { - return func(r *http.Request) (string, error) { - return _AllRequests, nil - } -} - -// NewRateLimiter creates a ratelimiter middleware. -func NewRateLimiter(limit uint64, exp time.Duration, keyFunc Option) func(next http.Handler) http.Handler { - store, _ := memorystore.New(&memorystore.Config{ - Tokens: limit, - Interval: exp, - }) - limiter, _ := httplimit.NewMiddleware(store, keyFunc()) - return limiter.Handle -} diff --git a/backend/pkg/middleware/secure.go b/backend/pkg/middleware/secure.go deleted file mode 100644 index 55d5eef..0000000 --- a/backend/pkg/middleware/secure.go +++ /dev/null @@ -1,36 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package middleware - -import ( - "net/http" -) - -// Secure creates a new CSP, X-Frame, X-Content-Type headers middleware. -func Secure(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains") - w.Header().Set("X-Frame-Options", "DENY") - w.Header().Set("Referrer-Policy", "same-origin") - w.Header().Set("Content-Security-Policy", "frame-ancestors 'none'") - w.Header().Set("X-Content-Type-Options", "nosniff") - w.Header().Set("Content-Type", "Desired Content Type; charset=utf-8") - next.ServeHTTP(w, r) - }) -} diff --git a/backend/pkg/middleware/timeout.go b/backend/pkg/middleware/timeout.go deleted file mode 100644 index 8886589..0000000 --- a/backend/pkg/middleware/timeout.go +++ /dev/null @@ -1,30 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package middleware - -import ( - "net/http" - "time" -) - -func Timeout(timeout time.Duration) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.TimeoutHandler(http.HandlerFunc(next.ServeHTTP), timeout, "request timeout") - } -} diff --git a/backend/pkg/middleware/trace.go b/backend/pkg/middleware/trace.go deleted file mode 100644 index d0f6d78..0000000 --- a/backend/pkg/middleware/trace.go +++ /dev/null @@ -1,56 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package middleware - -import ( - "net/http" - - "go-micro.dev/v4/metadata" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/propagation" -) - -const ( - InstrumentationName = "github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry" -) - -// TracePropagationMiddleware inject previous context into a new request -func TracePropagationMiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - carrier := propagation.MapCarrier{} - propagator := otel.GetTextMapPropagator() - ctx := propagator.Extract(r.Context(), carrier) - - propagator.Inject(ctx, carrier) - next.ServeHTTP(w, r.WithContext(metadata.NewContext(ctx, metadata.Metadata(carrier)))) - }) -} - -// LogTraceMiddleware starts tracing with spans -func LogTraceMiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - carrier := propagation.MapCarrier{} - - ctx, span := otel.GetTracerProvider().Tracer(InstrumentationName).Start(r.Context(), r.URL.Path) - defer span.End() - - otel.GetTextMapPropagator().Inject(ctx, carrier) - next.ServeHTTP(w, r.WithContext(metadata.NewContext(ctx, metadata.Metadata(carrier)))) - }) -} diff --git a/backend/pkg/middleware/version.go b/backend/pkg/middleware/version.go deleted file mode 100644 index d7f81b1..0000000 --- a/backend/pkg/middleware/version.go +++ /dev/null @@ -1,33 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package middleware - -import ( - "net/http" -) - -// Version creates a new X-Version header middleware. -func Version(version string) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - rw.Header().Set("X-Api-Version", version) - next.ServeHTTP(rw, r) - }) - } -} diff --git a/backend/pkg/middleware/wrapper/trace.go b/backend/pkg/middleware/wrapper/trace.go deleted file mode 100644 index cbb76a9..0000000 --- a/backend/pkg/middleware/wrapper/trace.go +++ /dev/null @@ -1,53 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package wrapper - -import ( - "context" - "strings" - - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/middleware" - "go-micro.dev/v4/metadata" - "go-micro.dev/v4/server" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/propagation" -) - -// TracePropagationHandlerWrapper wraps RPC handlers to trace rpc calls -func TracePropagationHandlerWrapper(hf server.HandlerFunc) server.HandlerFunc { - return func(ctx context.Context, req server.Request, rsp interface{}) error { - meta, _ := metadata.FromContext(ctx) - converted := make(map[string]string) - - for k, v := range meta { - converted[strings.ToLower(k)] = v - } - - ctx = otel.GetTextMapPropagator().Extract(ctx, propagation.MapCarrier(converted)) - - ctx, span := otel.GetTracerProvider().Tracer(middleware.InstrumentationName).Start(ctx, req.Endpoint()) - defer span.End() - - if err := hf(ctx, req, rsp); err != nil { - return err - } - - return nil - } -} diff --git a/backend/pkg/registry/registry.go b/backend/pkg/registry/registry.go deleted file mode 100644 index fd7e9f4..0000000 --- a/backend/pkg/registry/registry.go +++ /dev/null @@ -1,58 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package registry - -import ( - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/go-micro/plugins/v4/registry/consul" - "github.com/go-micro/plugins/v4/registry/etcd" - "github.com/go-micro/plugins/v4/registry/kubernetes" - "github.com/go-micro/plugins/v4/registry/mdns" - "go-micro.dev/v4/registry" - "go-micro.dev/v4/registry/cache" -) - -// NewRegistry looks up envs and configures respective registries based on those variables. Defaults to memory -func NewRegistry(config *config.RegistryConfig) registry.Registry { - var r registry.Registry - switch config.Registry.Type { - case 1: - r = kubernetes.NewRegistry( - registry.Addrs(config.Registry.Addresses...), - ) - case 2: - r = consul.NewRegistry( - registry.Addrs(config.Registry.Addresses...), - ) - case 3: - r = etcd.NewRegistry( - registry.Addrs(config.Registry.Addresses...), - ) - case 4: - r = mdns.NewRegistry( - registry.Addrs(config.Registry.Addresses...), - ) - default: - r = mdns.NewRegistry( - registry.Addrs(config.Registry.Addresses...), - ) - } - - return cache.New(r, cache.WithTTL(config.Registry.CacheTTL)) -} diff --git a/backend/pkg/resilience/circuit.go b/backend/pkg/resilience/circuit.go deleted file mode 100644 index 11f5859..0000000 --- a/backend/pkg/resilience/circuit.go +++ /dev/null @@ -1,49 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package resilience - -import ( - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/go-micro/plugins/v4/wrapper/breaker/hystrix" -) - -func BuildHystrixCommandConfig(resilienceConfig *config.ResilienceConfig) hystrix.CommandConfig { - var config hystrix.CommandConfig - if resilienceConfig.Resilience.CircuitBreaker.Timeout > 0 { - config.Timeout = resilienceConfig.Resilience.CircuitBreaker.Timeout - } - - if resilienceConfig.Resilience.CircuitBreaker.MaxConcurrent > 0 { - config.MaxConcurrentRequests = resilienceConfig.Resilience.CircuitBreaker.MaxConcurrent - } - - if resilienceConfig.Resilience.CircuitBreaker.VolumeThreshold > 0 { - config.RequestVolumeThreshold = resilienceConfig.Resilience.CircuitBreaker.VolumeThreshold - } - - if resilienceConfig.Resilience.CircuitBreaker.SleepWindow > 0 { - config.SleepWindow = resilienceConfig.Resilience.CircuitBreaker.SleepWindow - } - - if resilienceConfig.Resilience.CircuitBreaker.ErrorPercentThreshold > 0 { - config.ErrorPercentThreshold = resilienceConfig.Resilience.CircuitBreaker.ErrorPercentThreshold - } - - return config -} diff --git a/backend/pkg/service/http/service.go b/backend/pkg/service/http/service.go deleted file mode 100644 index a3e3c21..0000000 --- a/backend/pkg/service/http/service.go +++ /dev/null @@ -1,149 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package http - -import ( - "context" - "log" - "net/http" - "strconv" - "strings" - "time" - - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/messaging" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/middleware" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/resilience" - chimiddleware "github.com/go-chi/chi/v5/middleware" - mserver "github.com/go-micro/plugins/v4/server/http" - "github.com/go-micro/plugins/v4/wrapper/breaker/hystrix" - "github.com/go-micro/plugins/v4/wrapper/select/roundrobin" - "github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry" - "go-micro.dev/v4" - "go-micro.dev/v4/cache" - "go-micro.dev/v4/client" - "go-micro.dev/v4/registry" - "go-micro.dev/v4/server" - "go.opentelemetry.io/otel" - oteltrace "go.opentelemetry.io/otel/sdk/trace" -) - -type ServerEngine interface { - ApplyMiddleware(middlewares ...func(http.Handler) http.Handler) - NewHandler(client client.Client, c cache.Cache) interface { - ServeHTTP(w http.ResponseWriter, req *http.Request) - } -} - -// NewService Initializes an http service. -func NewService( - engine ServerEngine, - registry registry.Registry, - broker messaging.BrokerWithOptions, - cache cache.Cache, - tracer *oteltrace.TracerProvider, - logger plog.Logger, - serverConfig *config.ServerConfig, - resilienceConfig *config.ResilienceConfig, - corsConfig *config.CORSConfig, - tracerConfig *config.TracerConfig, -) micro.Service { - if err := broker.Broker.Init(); err != nil { - log.Fatalf("could not initialize a new broker instance: %s", err.Error()) - } - - if err := broker.Broker.Connect(); err != nil { - log.Fatalf("broker connection error: %s", err.Error()) - } - - hystrix.ConfigureDefault(resilience.BuildHystrixCommandConfig(resilienceConfig)) - - service := micro.NewService( - micro.Name(strings.Join([]string{serverConfig.Namespace, serverConfig.Name}, ":")), - micro.Version(strconv.Itoa(serverConfig.Version)), - micro.Context(context.Background()), - micro.Server(mserver.NewServer( - server.Name(strings.Join([]string{serverConfig.Namespace, serverConfig.Name}, ":")), - server.Address(serverConfig.Address), - )), - micro.Cache(cache), - micro.Registry(registry), - micro.Broker(broker.Broker), - micro.Client(client.NewClient( - client.Registry(registry), - client.Broker(broker.Broker), - )), - micro.WrapClient( - roundrobin.NewClientWrapper(), - hystrix.NewClientWrapper(), - ), - micro.WrapCall(opentelemetry.NewCallWrapper(opentelemetry.WithTraceProvider(otel.GetTracerProvider()))), - micro.RegisterTTL(30*time.Second), - micro.RegisterInterval(10*time.Second), - micro.AfterStop(func() error { - if tracer != nil { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - if err := tracer.Shutdown(ctx); err != nil { - return err - } - - return nil - } - - return nil - }), - ) - - if resilienceConfig.Resilience.RateLimiter.IPLimit > 0 { - engine.ApplyMiddleware(middleware.NewRateLimiter(resilienceConfig.Resilience.RateLimiter.IPLimit, 1*time.Second, middleware.WithKeyFuncIP)) - } - - if resilienceConfig.Resilience.RateLimiter.Limit > 0 { - engine.ApplyMiddleware(middleware.NewRateLimiter(resilienceConfig.Resilience.RateLimiter.Limit, 1*time.Second, middleware.WithKeyFuncAll)) - } - - engine.ApplyMiddleware( - middleware.Log(logger), - chimiddleware.RealIP, - chimiddleware.RequestID, - chimiddleware.StripSlashes, - middleware.Secure, - middleware.Version(strconv.Itoa(serverConfig.Version)), - middleware.Cors(corsConfig.CORS.AllowedOrigins, corsConfig.CORS.AllowedMethods, corsConfig.CORS.AllowedHeaders, corsConfig.CORS.AllowCredentials), - ) - - if tracerConfig.Tracer.Enable { - engine.ApplyMiddleware( - middleware.TracePropagationMiddleware, - middleware.LogTraceMiddleware, - ) - } - - if err := micro.RegisterHandler( - service.Server(), - engine.NewHandler(service.Options().Client, service.Options().Cache), - ); err != nil { - log.Fatal("could not register http handler: ", err) - } - - return service -} diff --git a/backend/pkg/service/repl/service.go b/backend/pkg/service/repl/service.go deleted file mode 100644 index f00fbb2..0000000 --- a/backend/pkg/service/repl/service.go +++ /dev/null @@ -1,70 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package repl - -import ( - "fmt" - "net/http" - "net/http/pprof" - "strconv" - "time" - - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/middleware" - chimiddleware "github.com/go-chi/chi/v5/middleware" - "github.com/hellofresh/health-go/v5" - "github.com/justinas/alice" - "github.com/prometheus/client_golang/prometheus/promhttp" -) - -// NewService Initializes repl service with options. -func NewService( - replConfig *config.ServerConfig, - corsConfig *config.CORSConfig, -) *http.Server { - mux := http.NewServeMux() - h, _ := health.New(health.WithComponent(health.Component{ - Name: fmt.Sprintf("%s:%s", replConfig.Namespace, replConfig.Name), - Version: fmt.Sprintf("v%d", replConfig.Version), - })) - - mux.Handle("/metrics", promhttp.Handler()) - mux.Handle("/health", h.Handler()) - - if replConfig.Debug { - mux.HandleFunc("/debug/pprof/", pprof.Index) - mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) - mux.HandleFunc("/debug/pprof/profile", pprof.Profile) - mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) - mux.HandleFunc("/debug/pprof/trace", pprof.Trace) - } - - return &http.Server{ - Addr: replConfig.ReplAddress, - Handler: alice.New( - chimiddleware.RealIP, - middleware.NewRateLimiter(1000, 1*time.Second, middleware.WithKeyFuncAll), - chimiddleware.RequestID, - middleware.Cors(corsConfig.CORS.AllowedOrigins, corsConfig.CORS.AllowedMethods, corsConfig.CORS.AllowedHeaders, corsConfig.CORS.AllowCredentials), - middleware.Secure, - middleware.NoCache, - middleware.Version(strconv.Itoa(replConfig.Version)), - ).Then(mux), - } -} diff --git a/backend/pkg/service/rpc/service.go b/backend/pkg/service/rpc/service.go deleted file mode 100644 index 80517a3..0000000 --- a/backend/pkg/service/rpc/service.go +++ /dev/null @@ -1,154 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package rpc - -import ( - "context" - "log" - "strconv" - "strings" - "time" - - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/messaging" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/middleware/wrapper" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/resilience" - "github.com/go-micro/plugins/v4/wrapper/breaker/hystrix" - rlimiter "github.com/go-micro/plugins/v4/wrapper/ratelimiter/uber" - "github.com/go-micro/plugins/v4/wrapper/select/roundrobin" - "github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry" - "go-micro.dev/v4" - "go-micro.dev/v4/cache" - gcache "go-micro.dev/v4/cache" - "go-micro.dev/v4/client" - "go-micro.dev/v4/registry" - "go-micro.dev/v4/server" - "go.opentelemetry.io/otel" - oteltrace "go.opentelemetry.io/otel/sdk/trace" - uber "go.uber.org/ratelimit" -) - -type RPCMessageHandler struct { - Topic string - Queue string - Handler interface{} -} - -type RPCEngine interface { - BuildMessageHandlers() []RPCMessageHandler - BuildHandlers(client client.Client, cache gcache.Cache) []interface{} -} - -// NewService Initializes an http service with options. -func NewService( - engine RPCEngine, - registry registry.Registry, - broker messaging.BrokerWithOptions, - cache cache.Cache, - tracer *oteltrace.TracerProvider, - rpcConfig *config.ServerConfig, - resilienceConfig *config.ResilienceConfig, - tracerConfig *config.TracerConfig, -) micro.Service { - var wrappers []server.HandlerWrapper = make([]server.HandlerWrapper, 0, 2) - - if err := broker.Broker.Init(); err != nil { - log.Fatalf("could not initialize a new broker instance: %s", err.Error()) - } - - if err := broker.Broker.Connect(); err != nil { - log.Fatalf("broker connection error: %s", err.Error()) - } - - if resilienceConfig.Resilience.RateLimiter.Limit > 0 { - wrappers = append(wrappers, rlimiter.NewHandlerWrapper(int(resilienceConfig.Resilience.RateLimiter.Limit), uber.Per(1*time.Second))) - } - - if tracerConfig.Tracer.Enable { - wrappers = append(wrappers, wrapper.TracePropagationHandlerWrapper) - } - - hystrix.ConfigureDefault(resilience.BuildHystrixCommandConfig(resilienceConfig)) - - service := micro.NewService( - micro.Name(strings.Join([]string{rpcConfig.Namespace, rpcConfig.Name}, ":")), - micro.Version(strconv.Itoa(rpcConfig.Version)), - micro.Context(context.Background()), - micro.Server(server.NewServer( - server.Name(strings.Join([]string{rpcConfig.Namespace, rpcConfig.Name}, ":")), - server.Address(rpcConfig.Address), - )), - micro.Cache(cache), - micro.Registry(registry), - micro.Broker(broker.Broker), - micro.Client(client.NewClient( - client.Registry(registry), - client.Broker(broker.Broker), - )), - micro.WrapClient( - roundrobin.NewClientWrapper(), - hystrix.NewClientWrapper(), - opentelemetry.NewClientWrapper(opentelemetry.WithTraceProvider(otel.GetTracerProvider())), - ), - micro.WrapSubscriber(opentelemetry.NewSubscriberWrapper(opentelemetry.WithTraceProvider(otel.GetTracerProvider()))), - micro.WrapHandler(wrappers...), - micro.RegisterTTL(30*time.Second), - micro.RegisterInterval(10*time.Second), - micro.AfterStop(func() error { - if tracer != nil { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - if err := tracer.Shutdown(ctx); err != nil { - return err - } - - return nil - } - - return nil - }), - ) - - if len(engine.BuildMessageHandlers()) > 0 { - for _, entry := range engine.BuildMessageHandlers() { - if entry.Handler != nil && entry.Topic != "" { - if entry.Queue != "" { - if err := micro.RegisterSubscriber( - entry.Topic, service.Server(), entry.Handler, server.SubscriberContext(broker.SubOptions.Context), server.SubscriberQueue(entry.Queue), - ); err != nil { - log.Fatalf("could not register a new subscriber with topic %s. Reason: %s", entry.Topic, err.Error()) - } - } else { - if err := micro.RegisterSubscriber(entry.Topic, service.Server(), entry.Handler, server.SubscriberContext(broker.SubOptions.Context)); err != nil { - log.Fatalf("could not register a new subscriber with topic %s. Reason: %s", entry.Topic, err.Error()) - } - } - } - } - } - - for _, handler := range engine.BuildHandlers(service.Client(), service.Options().Cache) { - if err := micro.RegisterHandler(service.Server(), handler); err != nil { - log.Fatalf("could not initialize rpc handlers: %s", err.Error()) - } - } - - return service -} diff --git a/backend/pkg/shared/env.go b/backend/pkg/shared/env.go deleted file mode 100644 index 9b428be..0000000 --- a/backend/pkg/shared/env.go +++ /dev/null @@ -1,37 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package shared - -const ( - DEV = "development" - TEST = "testing" - PROD = "production" -) - -var SUPPORTED_ENVIRONMENTS = map[string]string{ - "development": DEV, - "testing": TEST, - "production": PROD, - "dev": DEV, - "test": TEST, - "prod": PROD, - "d": DEV, - "t": TEST, - "p": PROD, -} diff --git a/backend/pkg/trace/error.go b/backend/pkg/trace/error.go deleted file mode 100644 index fdfc2da..0000000 --- a/backend/pkg/trace/error.go +++ /dev/null @@ -1,26 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package trace - -import ( - "errors" -) - -var ErrTracerInvalidNameInitialization = errors.New("could not initialize a tracer with an empty name") -var ErrTracerInvalidAddressInitialization = errors.New("could not initialize a tracer without a URL") diff --git a/backend/pkg/trace/tracer.go b/backend/pkg/trace/tracer.go deleted file mode 100644 index bafc042..0000000 --- a/backend/pkg/trace/tracer.go +++ /dev/null @@ -1,74 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package trace - -import ( - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" - "go.opentelemetry.io/otel/propagation" - "go.opentelemetry.io/otel/sdk/resource" - "go.opentelemetry.io/otel/sdk/trace" - semconv "go.opentelemetry.io/otel/semconv/v1.4.0" -) - -type TracerType int - -var ( - Default TracerType = 0 - Zipkin TracerType = 1 -) - -// NewTracer initializes a new tracer. -func NewTracer(config *config.TracerConfig) (*trace.TracerProvider, error) { - var exporter trace.SpanExporter - - if config.Tracer.Name == "" { - config.Tracer.Name = "default-tracer" - } - - switch config.Tracer.TracerType { - case 1: - if config.Tracer.Address == "" { - return nil, ErrTracerInvalidAddressInitialization - } - exporter = NewZipkinExporter(config.Tracer.Address) - default: - exporter, _ = stdouttrace.New() - } - - provider := trace.NewTracerProvider( - trace.WithSampler(trace.ParentBased(trace.TraceIDRatioBased(config.Tracer.FractionRatio))), - trace.WithBatcher(exporter), - trace.WithResource(resource.NewWithAttributes( - semconv.SchemaURL, - semconv.ServiceNameKey.String(config.Tracer.Name), - )), - ) - - otel.SetTracerProvider(provider) - otel.SetTextMapPropagator( - propagation.NewCompositeTextMapPropagator( - propagation.TraceContext{}, - propagation.Baggage{}, - ), - ) - - return provider, nil -} diff --git a/backend/pkg/trace/zipkin.go b/backend/pkg/trace/zipkin.go deleted file mode 100644 index 4c50c68..0000000 --- a/backend/pkg/trace/zipkin.go +++ /dev/null @@ -1,35 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package trace - -import ( - "log" - - "go.opentelemetry.io/otel/exporters/zipkin" - "go.opentelemetry.io/otel/sdk/trace" -) - -// NewZipkingExporter creates a new zipkin exporter. -func NewZipkinExporter(url string, opts ...zipkin.Option) trace.SpanExporter { - exporter, err := zipkin.New(url, opts...) - if err != nil { - log.Fatalln("could not initialize a new zipkin exporter: ", err) - } - return exporter -} diff --git a/backend/pkg/worker/asynq.go b/backend/pkg/worker/asynq.go deleted file mode 100644 index 745330a..0000000 --- a/backend/pkg/worker/asynq.go +++ /dev/null @@ -1,144 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package worker - -import ( - "context" - "log" - "time" - - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - plog "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" - "github.com/hibiken/asynq" -) - -type asynqWorker struct { - enabled bool - srv *asynq.Server - mux *asynq.ServeMux -} - -type asynqEnqueuer struct { - enabled bool - client *asynq.Client -} - -func newAsynqWorker(config *config.WorkerConfig, logger plog.Logger) BackgroundWorker { - var workerOpts asynq.RedisConnOpt = asynq.RedisClientOpt{ - Addr: config.Worker.RedisAddresses[0], - Username: config.Worker.RedisUsername, - Password: config.Worker.RedisPassword, - DB: config.Worker.RedisDatabase, - ReadTimeout: 4 * time.Second, - WriteTimeout: 7 * time.Second, - } - if len(config.Worker.RedisAddresses) > 1 { - workerOpts = asynq.RedisClusterClientOpt{ - Addrs: config.Worker.RedisAddresses, - Username: config.Worker.RedisUsername, - Password: config.Worker.RedisPassword, - ReadTimeout: 4 * time.Second, - WriteTimeout: 7 * time.Second, - } - } - - return asynqWorker{ - enabled: config.Worker.Enable, - srv: asynq.NewServer(workerOpts, asynq.Config{ - Concurrency: config.Worker.MaxConcurrency, - Logger: logger, - }), - mux: asynq.NewServeMux(), - } -} - -func (w asynqWorker) Register(pattern string, handler func(ctx context.Context, payload []byte) error) { - if w.enabled { - w.mux.Handle(pattern, asynq.HandlerFunc(func(ctx context.Context, t *asynq.Task) error { - return handler(ctx, t.Payload()) - })) - } -} - -func (w asynqWorker) Run() { - if w.enabled { - go func() { - if err := w.srv.Run(w.mux); err != nil { - log.Fatal(err.Error()) - } - }() - } -} - -func newAsynqEnqueuer(config *config.WorkerConfig) BackgroundEnqueuer { - var enqOpts asynq.RedisConnOpt = asynq.RedisClientOpt{ - Addr: config.Worker.RedisAddresses[0], - Username: config.Worker.RedisUsername, - Password: config.Worker.RedisPassword, - DB: config.Worker.RedisDatabase, - ReadTimeout: 4 * time.Second, - WriteTimeout: 7 * time.Second, - } - if len(config.Worker.RedisAddresses) > 1 { - enqOpts = asynq.RedisClusterClientOpt{ - Addrs: config.Worker.RedisAddresses, - Username: config.Worker.RedisUsername, - Password: config.Worker.RedisPassword, - ReadTimeout: 4 * time.Second, - WriteTimeout: 7 * time.Second, - } - } - - return asynqEnqueuer{ - enabled: config.Worker.Enable, - client: asynq.NewClient(enqOpts), - } -} - -func (e asynqEnqueuer) Enqueue(pattern string, task []byte, opts ...EnqueuerOption) error { - if e.enabled { - options := NewEnqueuerOptions(opts...) - t := asynq.NewTask(pattern, task) - - _, err := e.client.Enqueue(t, asynq.MaxRetry(options.MaxRetry), asynq.Timeout(options.Timeout)) - return err - } - - return nil -} - -func (e asynqEnqueuer) EnqueueContext(ctx context.Context, pattern string, task []byte, opts ...EnqueuerOption) error { - if e.enabled { - options := NewEnqueuerOptions(opts...) - t := asynq.NewTask(pattern, task) - - _, err := e.client.EnqueueContext(ctx, t, asynq.MaxRetry(options.MaxRetry), asynq.Timeout(options.Timeout)) - return err - } - - return nil -} - -func (e asynqEnqueuer) Close() error { - if e.enabled { - return e.Close() - } - - return nil -} diff --git a/backend/pkg/worker/enqueuer.go b/backend/pkg/worker/enqueuer.go deleted file mode 100644 index 38e0986..0000000 --- a/backend/pkg/worker/enqueuer.go +++ /dev/null @@ -1,40 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package worker - -import ( - "context" - - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" -) - -type BackgroundEnqueuer interface { - Enqueue(pattern string, task []byte, opts ...EnqueuerOption) error - EnqueueContext(ctx context.Context, pattern string, task []byte, opts ...EnqueuerOption) error - Close() error -} - -func NewBackgroundEnqueuer(config *config.WorkerConfig) BackgroundEnqueuer { - switch config.Worker.Type { - case 0: - return newAsynqEnqueuer(config) - default: - return newAsynqEnqueuer(config) - } -} diff --git a/backend/pkg/worker/option.go b/backend/pkg/worker/option.go deleted file mode 100644 index 1418e48..0000000 --- a/backend/pkg/worker/option.go +++ /dev/null @@ -1,72 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package worker - -import ( - "time" -) - -type WorkerType int - -var ( - Asynq WorkerType = 0 -) - -type WorkerRedisCredentials struct { - Addresses []string - Username string - Password string - Database int - ReadTimeout time.Duration - WriteTimeout time.Duration -} - -type EnqueuerOption func(*EnqueuerOptions) - -type EnqueuerOptions struct { - MaxRetry int - Timeout time.Duration -} - -func NewEnqueuerOptions(opts ...EnqueuerOption) EnqueuerOptions { - opt := EnqueuerOptions{ - MaxRetry: 3, - Timeout: 0 * time.Second, - } - - for _, o := range opts { - o(&opt) - } - - return opt -} - -func WithMaxRetry(val int) EnqueuerOption { - return func(eo *EnqueuerOptions) { - if val > 0 { - eo.MaxRetry = val - } - } -} - -func WithTimeout(val time.Duration) EnqueuerOption { - return func(eo *EnqueuerOptions) { - eo.Timeout = val - } -} diff --git a/backend/pkg/worker/worker.go b/backend/pkg/worker/worker.go deleted file mode 100644 index 367162a..0000000 --- a/backend/pkg/worker/worker.go +++ /dev/null @@ -1,40 +0,0 @@ -/** - * - * (c) Copyright Ascensio System SIA 2023 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package worker - -import ( - "context" - - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/config" - "github.com/ONLYOFFICE/onlyoffice-pipedrive/pkg/log" -) - -type BackgroundWorker interface { - Register(pattern string, handler func(ctx context.Context, payload []byte) error) - Run() -} - -func NewBackgroundWorker(config *config.WorkerConfig, logger log.Logger) BackgroundWorker { - switch config.Worker.Type { - case 0: - return newAsynqWorker(config, logger) - default: - return newAsynqWorker(config, logger) - } -} From b9926f0fa58f8269117581adc1aa42bde268b992 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 7 Jun 2023 14:59:00 +0500 Subject: [PATCH 132/145] fix(pages): upload page on drop delay --- frontend/src/pages/Creation/Upload.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/pages/Creation/Upload.tsx b/frontend/src/pages/Creation/Upload.tsx index fde3ae7..604dd26 100644 --- a/frontend/src/pages/Creation/Upload.tsx +++ b/frontend/src/pages/Creation/Upload.tsx @@ -77,6 +77,7 @@ export const Upload: React.FC = () => { } onDrop={async (files, rejections, event) => { try { + await new Promise(resolve => setTimeout(resolve, 1000)); onDrop(files, rejections, event); await sdk?.execute(Command.SHOW_SNACKBAR, { message: t( From ec1cb476561cefdd8ef87f9f8b80dfb291ee202b Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 7 Jun 2023 15:03:05 +0500 Subject: [PATCH 133/145] fix(services): settings service caching --- backend/services/settings/web/core/service/settings.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/backend/services/settings/web/core/service/settings.go b/backend/services/settings/web/core/service/settings.go index 90c2062..27eeb8b 100644 --- a/backend/services/settings/web/core/service/settings.go +++ b/backend/services/settings/web/core/service/settings.go @@ -143,9 +143,7 @@ func (s settingsService) UpdateSettings(ctx context.Context, settings domain.Doc return settings, err } - if err := s.cache.Delete(ctx, settings.CompanyID); err != nil { - return settings, err - } + s.cache.Delete(ctx, settings.CompanyID) s.logger.Debugf("successfully persisted %s settings", settings.CompanyID) return settings, nil @@ -162,9 +160,7 @@ func (s settingsService) RemoveSettings(ctx context.Context, cid string) error { } } - if err := s.cache.Delete(ctx, cid); err != nil { - return err - } + s.cache.Delete(ctx, cid) s.logger.Debugf("uid %s is valid to perform a delete action", id) return s.adapter.DeleteSettings(ctx, id) From 71bd43ba9b27e72888319909fa3e8ee2f5960af4 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 14 Jun 2023 16:33:25 +0500 Subject: [PATCH 134/145] fix(i18n): create new files --- .../locales/{de => de-DE}/translation.json | 0 .../locales/{es => es-ES}/translation.json | 0 .../locales/{fr => fr-FR}/translation.json | 0 .../locales/{it => it-IT}/translation.json | 0 .../locales/{ja => ja-JP}/translation.json | 0 .../src/assets/locales/ru/translation.json | 50 ------------------- frontend/src/context/TokenContext.tsx | 2 +- 7 files changed, 1 insertion(+), 51 deletions(-) rename frontend/src/assets/locales/{de => de-DE}/translation.json (100%) rename frontend/src/assets/locales/{es => es-ES}/translation.json (100%) rename frontend/src/assets/locales/{fr => fr-FR}/translation.json (100%) rename frontend/src/assets/locales/{it => it-IT}/translation.json (100%) rename frontend/src/assets/locales/{ja => ja-JP}/translation.json (100%) delete mode 100644 frontend/src/assets/locales/ru/translation.json diff --git a/frontend/src/assets/locales/de/translation.json b/frontend/src/assets/locales/de-DE/translation.json similarity index 100% rename from frontend/src/assets/locales/de/translation.json rename to frontend/src/assets/locales/de-DE/translation.json diff --git a/frontend/src/assets/locales/es/translation.json b/frontend/src/assets/locales/es-ES/translation.json similarity index 100% rename from frontend/src/assets/locales/es/translation.json rename to frontend/src/assets/locales/es-ES/translation.json diff --git a/frontend/src/assets/locales/fr/translation.json b/frontend/src/assets/locales/fr-FR/translation.json similarity index 100% rename from frontend/src/assets/locales/fr/translation.json rename to frontend/src/assets/locales/fr-FR/translation.json diff --git a/frontend/src/assets/locales/it/translation.json b/frontend/src/assets/locales/it-IT/translation.json similarity index 100% rename from frontend/src/assets/locales/it/translation.json rename to frontend/src/assets/locales/it-IT/translation.json diff --git a/frontend/src/assets/locales/ja/translation.json b/frontend/src/assets/locales/ja-JP/translation.json similarity index 100% rename from frontend/src/assets/locales/ja/translation.json rename to frontend/src/assets/locales/ja-JP/translation.json diff --git a/frontend/src/assets/locales/ru/translation.json b/frontend/src/assets/locales/ru/translation.json deleted file mode 100644 index afd230c..0000000 --- a/frontend/src/assets/locales/ru/translation.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "banner.title": "ONLYOFFICE Docs Cloud", - "banner.subtitle": " ", - "files.error.nofiles": " Pipedrive", - "files.info.workspace": " ", - "files.info.type": "", - "files.info.modified": "", - "files.info.creation": "", - "files.info.size": "", - "snackbar.fileremoved.ok": " {{file}} ", - "snackbar.fileremoved.error": " {{file}}", - "snackbar.uploaded.ok": " {{file}} ", - "snackbar.uploaded.error": " {{file}}", - "creation.title": " ONLYOFFICE", - "creation.inputs.title": "", - "creation.inputs.error": " 190 ", - "creation.tiles.doc": "", - "creation.tiles.spreadsheet": " ", - "creation.tiles.presentation": "", - "creation.error": " ", - "upload.error": " . , ONLYOFFICE.", - "upload.uploading": "...", - "upload.select": " ", - "upload.dragdrop": " ", - "upload.subtext": " ", - "settings.title": " ONLYOFFICE", - "settings.text": ", Pipedrive ONLYOFFICE, Pipedrive.", - "settings.inputs.address": " ", - "settings.inputs.secret": " ", - "settings.inputs.header": " ", - "settings.saving.ok": " ONLYOFFICE ", - "settings.saving.error": " ONLYOFFICE", - "editor.error": " . - ", - "background.error.title": "", - "background.error.subtitle": " . - . , Pipedrive", - "background.access.title": " ", - "background.access.subtitle": "- , ", - "background.reinstall.title": " ", - "background.reinstall.subtitle": "- . , .", - "document.new": " ", - "button.upload": " ", - "button.reload": "", - "button.save": "", - "button.cancel": "", - "button.close": "", - "button.create": " ", - "button.creation.create": "", - "button.creation.upload": "", - "button.getnow": " " -} diff --git a/frontend/src/context/TokenContext.tsx b/frontend/src/context/TokenContext.tsx index b385750..6fa9366 100644 --- a/frontend/src/context/TokenContext.tsx +++ b/frontend/src/context/TokenContext.tsx @@ -56,7 +56,7 @@ export const TokenProvider: React.FC = ({ children }) => { AuthToken.access_token = token.response.access_token; AuthToken.expires_at = token.response.expires_at; const resp = await getPipedriveMe(`${url}api/v1/users/me`); - await i18next.changeLanguage(resp.data.language.language_code); + await i18next.changeLanguage(`${resp.data.language.language_code}-${resp.data.language.country_code}`); } catch (err) { if (!axios.isCancel(err)) { AuthToken.error = true; From 60cc5a3217a0f77a436287db88ce95d4a37a93d7 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 14 Jun 2023 16:45:57 +0500 Subject: [PATCH 135/145] fix(constants): add oxps and xml to the supported list --- backend/services/shared/constants/file.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/services/shared/constants/file.go b/backend/services/shared/constants/file.go index 17601c9..4aeaf15 100644 --- a/backend/services/shared/constants/file.go +++ b/backend/services/shared/constants/file.go @@ -79,6 +79,8 @@ var OnlyofficeFileExtensions map[string]string = map[string]string{ "fb2": _OnlyofficeWordType, "epub": _OnlyofficeWordType, "xps": _OnlyofficeWordType, + "oxps": _OnlyofficeWordType, + "xml": _OnlyofficeWordType, } func IsExtensionSupported(fileExt string) bool { From d132983150dd7f03e279a8a2d4ed7f195b1c8f9f Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 14 Jun 2023 17:24:54 +0500 Subject: [PATCH 136/145] fix(builder): file type to lower case --- backend/services/builder/web/handler/build.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/services/builder/web/handler/build.go b/backend/services/builder/web/handler/build.go index e2d3a9c..197c0d3 100644 --- a/backend/services/builder/web/handler/build.go +++ b/backend/services/builder/web/handler/build.go @@ -196,7 +196,7 @@ func (c ConfigHandler) processConfig(user response.UserResponse, req request.Bui if err != nil { return config, err } - config.Document.FileType = ext + config.Document.FileType = strings.ToLower(ext) config.Document.Permissions = response.Permissions{ Edit: constants.IsExtensionEditable(ext), Comment: true, From 0ccfda0eeaf049437129c7db55ca97c730d1a3d2 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 14 Jun 2023 18:21:05 +0500 Subject: [PATCH 137/145] fix(services): settings service persist docs header --- backend/services/settings/web/core/adapter/mongo.go | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/services/settings/web/core/adapter/mongo.go b/backend/services/settings/web/core/adapter/mongo.go index bfda045..ab7448d 100644 --- a/backend/services/settings/web/core/adapter/mongo.go +++ b/backend/services/settings/web/core/adapter/mongo.go @@ -76,6 +76,7 @@ func (m *mongoUserAdapter) save(ctx context.Context, settings domain.DocSettings u.CompanyID = settings.CompanyID u.DocAddress = settings.DocAddress u.DocSecret = settings.DocSecret + u.DocHeader = settings.DocHeader u.UpdatedAt = time.Now() if err := collection.UpdateWithCtx(ctx, u); err != nil { From 5d33519c56fb5999a8f4a55142f0e74a5cf4d292 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Wed, 14 Jun 2023 18:51:54 +0500 Subject: [PATCH 138/145] fix(i18n): russian translation --- .../src/assets/locales/ru-RU/translation.json | 94 +++++++++---------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/frontend/src/assets/locales/ru-RU/translation.json b/frontend/src/assets/locales/ru-RU/translation.json index afd230c..0c71ddc 100644 --- a/frontend/src/assets/locales/ru-RU/translation.json +++ b/frontend/src/assets/locales/ru-RU/translation.json @@ -1,50 +1,50 @@ { "banner.title": "ONLYOFFICE Docs Cloud", - "banner.subtitle": " ", - "files.error.nofiles": " Pipedrive", - "files.info.workspace": " ", - "files.info.type": "", - "files.info.modified": "", - "files.info.creation": "", - "files.info.size": "", - "snackbar.fileremoved.ok": " {{file}} ", - "snackbar.fileremoved.error": " {{file}}", - "snackbar.uploaded.ok": " {{file}} ", - "snackbar.uploaded.error": " {{file}}", - "creation.title": " ONLYOFFICE", - "creation.inputs.title": "", - "creation.inputs.error": " 190 ", - "creation.tiles.doc": "", - "creation.tiles.spreadsheet": " ", - "creation.tiles.presentation": "", - "creation.error": " ", - "upload.error": " . , ONLYOFFICE.", - "upload.uploading": "...", - "upload.select": " ", - "upload.dragdrop": " ", - "upload.subtext": " ", - "settings.title": " ONLYOFFICE", - "settings.text": ", Pipedrive ONLYOFFICE, Pipedrive.", - "settings.inputs.address": " ", - "settings.inputs.secret": " ", - "settings.inputs.header": " ", - "settings.saving.ok": " ONLYOFFICE ", - "settings.saving.error": " ONLYOFFICE", - "editor.error": " . - ", - "background.error.title": "", - "background.error.subtitle": " . - . , Pipedrive", - "background.access.title": " ", - "background.access.subtitle": "- , ", - "background.reinstall.title": " ", - "background.reinstall.subtitle": "- . , .", - "document.new": " ", - "button.upload": " ", - "button.reload": "", - "button.save": "", - "button.cancel": "", - "button.close": "", - "button.create": " ", - "button.creation.create": "", - "button.creation.upload": "", - "button.getnow": " " + "banner.subtitle": "Легко запускайте редакторы в облаке без скачивания и установки", + "files.error.nofiles": "Не удалось найти документы Pipedrive", + "files.info.workspace": "Рабочая область", + "files.info.type": "Тип", + "files.info.modified": "Изменено", + "files.info.creation": "Создано", + "files.info.size": "Размер", + "snackbar.fileremoved.ok": "Файл {{file}} удален", + "snackbar.fileremoved.error": "Не удалось удалить файл {{file}}", + "snackbar.uploaded.ok": "Файл {{file}} загружен", + "snackbar.uploaded.error": "Не удалось загрузить файл {{file}}", + "creation.title": "Создать в ONLYOFFICE", + "creation.inputs.title": "Заголовок", + "creation.inputs.error": "Имя файла должно содержать менее 190 символов", + "creation.tiles.doc": "Документ", + "creation.tiles.spreadsheet": "Электронная таблица", + "creation.tiles.presentation": "Презентация", + "creation.error": "Не удалось создать новый файл", + "upload.error": "Не удалось загрузить файл. Пожалуйста, обратитесь в службу поддержки ONLYOFFICE.", + "upload.uploading": "Загрузка...", + "upload.select": "Выберите файл", + "upload.dragdrop": "или перетащите сюда", + "upload.subtext": "Размер файла ограничен", + "settings.title": "Настройте параметры приложения ONLYOFFICE", + "settings.text": "Плагин, который позволяет пользователям редактировать офисные документы из Pipedrive с помощью сервера документов ONLYOFFICE, а также позволяет нескольким пользователям совместно работать в режиме реального времени и сохранять изменения в Pipedrive.", + "settings.inputs.address": "Адрес сервера документов", + "settings.inputs.secret": "Секретный ключ сервера документов", + "settings.inputs.header": "Заголовок сервера документов", + "settings.saving.ok": "Настройки ONLYOFFICE сохранены", + "settings.saving.error": "Не удалось сохранить настройки ONLYOFFICE", + "editor.error": "Не удалось открыть файл. Что-то пошло не так", + "background.error.title": "Ошибка", + "background.error.subtitle": "Не удалось получить настройки плагина. Что-то пошло не так. Пожалуйста, перезагрузите окно Pipedrive", + "background.access.title": "Доступ запрещен", + "background.access.subtitle": "Что-то пошло не так, или доступ запрещен", + "background.reinstall.title": "Истек срок действия токена безопасности документа", + "background.reinstall.subtitle": "Что-то пошло не так. Пожалуйста, перезагрузите или переустановите приложение.", + "document.new": "Новый документ", + "button.upload": "Создать или загрузить документ", + "button.reload": "Перезагрузить", + "button.save": "Сохранить", + "button.cancel": "Отменить", + "button.close": "Закрыть", + "button.create": "Создать документ", + "button.creation.create": "Создать", + "button.creation.upload": "Загрузить", + "button.getnow": "Получить сейчас" } From 144921699d7755280fc295d1250cf9b467500b58 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 15 Jun 2023 15:38:31 +0500 Subject: [PATCH 139/145] fix(components): no file break text --- frontend/src/components/nofile/NoFile.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/nofile/NoFile.tsx b/frontend/src/components/nofile/NoFile.tsx index c0167d9..324d221 100644 --- a/frontend/src/components/nofile/NoFile.tsx +++ b/frontend/src/components/nofile/NoFile.tsx @@ -27,7 +27,7 @@ type NoFileProps = { export const OnlyofficeNoFile: React.FC = ({ title }) => (
- + {title}
From fec467034e0686a32ea27f2a9d7e615ac61d83f5 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Thu, 15 Jun 2023 16:50:33 +0500 Subject: [PATCH 140/145] fix(i18n): spinner on loading translations --- frontend/package.json | 2 + frontend/src/context/TokenContext.tsx | 4 +- frontend/src/i18n.ts | 14 +++- frontend/src/pages/Creation/index.tsx | 99 +++++++++++++++------------ frontend/src/services/me.ts | 4 +- frontend/yarn.lock | 29 ++++++++ 6 files changed, 105 insertions(+), 47 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index bd9fb80..fa1478c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -89,7 +89,9 @@ "axios-retry": "^3.4.0", "classnames": "^2.3.2", "i18next": "^22.4.9", + "i18next-chained-backend": "^4.4.0", "i18next-http-backend": "^2.1.1", + "i18next-localstorage-backend": "^4.1.1", "md5": "^2.3.0", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/frontend/src/context/TokenContext.tsx b/frontend/src/context/TokenContext.tsx index 6fa9366..e593bf2 100644 --- a/frontend/src/context/TokenContext.tsx +++ b/frontend/src/context/TokenContext.tsx @@ -53,10 +53,10 @@ export const TokenProvider: React.FC = ({ children }) => { ) { try { const token = await getMe(sdk); + const resp = await getPipedriveMe(`${url}api/v1/users/me`, token.response.access_token); + await i18next.changeLanguage(`${resp.data.language.language_code}-${resp.data.language.country_code}`); AuthToken.access_token = token.response.access_token; AuthToken.expires_at = token.response.expires_at; - const resp = await getPipedriveMe(`${url}api/v1/users/me`); - await i18next.changeLanguage(`${resp.data.language.language_code}-${resp.data.language.country_code}`); } catch (err) { if (!axios.isCancel(err)) { AuthToken.error = true; diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index f936cd0..ac4c184 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -17,11 +17,13 @@ */ import i18n from "i18next"; +import ChainedBackend from "i18next-chained-backend"; import I18NextHttpBackend from "i18next-http-backend"; +import LocalStorageBackend from "i18next-localstorage-backend"; import { initReactI18next } from "react-i18next"; i18n - .use(I18NextHttpBackend) + .use(ChainedBackend) .use(initReactI18next) .init({ fallbackLng: "en-US", @@ -29,6 +31,16 @@ i18n interpolation: { escapeValue: false, }, + backend: { + backends: [ + LocalStorageBackend, + I18NextHttpBackend, + ], + backendOptions: [{ + expirationTime: 24 * 60 * 60 * 1000 + } + ] + } }); export default i18n; diff --git a/frontend/src/pages/Creation/index.tsx b/frontend/src/pages/Creation/index.tsx index b41469c..6965377 100644 --- a/frontend/src/pages/Creation/index.tsx +++ b/frontend/src/pages/Creation/index.tsx @@ -18,15 +18,24 @@ import React, { useEffect, useState } from "react"; import AppExtensionsSDK from "@pipedrive/app-extensions-sdk"; +import { useSnapshot } from "valtio"; import { Tabs, TabList, Tab, TabPanel } from "react-tabs"; import { useTranslation } from "react-i18next"; +import { OnlyofficeSpinner } from "@components/spinner"; + +import { AuthToken } from "@context/TokenContext"; + import { Creation } from "./Creation"; import { Upload } from "./Upload"; export const CreatePage: React.FC = () => { const { t } = useTranslation(); + const { access_token: accessToken, error } = useSnapshot(AuthToken); const [selected, setSelected] = useState(0); + const loadingError = !accessToken && error; + const loaded = accessToken && !error; + useEffect(() => { new AppExtensionsSDK().initialize({ size: { @@ -35,55 +44,61 @@ export const CreatePage: React.FC = () => { }, }); }); - + return (
- setSelected(index)} - > - + +
+ ) : ( + setSelected(index)} > - + + {t("button.creation.create", "Create")} + + + {t("button.creation.upload", "Upload")} + + + - {t("button.creation.create", "Create")} - - + + - {t("button.creation.upload", "Upload")} - - - - - - - - - + + + + )}
); }; diff --git a/frontend/src/services/me.ts b/frontend/src/services/me.ts index e271b39..b73710b 100644 --- a/frontend/src/services/me.ts +++ b/frontend/src/services/me.ts @@ -47,7 +47,7 @@ export const getMe = async (sdk: AppExtensionsSDK) => { return { response: res.data }; }; -export const getPipedriveMe = async (url: string) => { +export const getPipedriveMe = async (url: string, access_token?: string) => { const client = axios.create(); axiosRetry(client, { retries: 3, @@ -61,7 +61,7 @@ export const getPipedriveMe = async (url: string) => { url, headers: { "Content-Type": "application/json", - Authorization: `Bearer ${AuthToken.access_token}`, + Authorization: `Bearer ${access_token ? access_token : AuthToken.access_token}`, }, timeout: 3000, }); diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 4dee530..d526e7f 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1056,6 +1056,13 @@ dependencies: regenerator-runtime "^0.13.11" +"@babel/runtime@^7.21.5", "@babel/runtime@^7.22.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" + integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA== + dependencies: + regenerator-runtime "^0.13.11" + "@babel/runtime@^7.8.4": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" @@ -4515,6 +4522,14 @@ human-signals@^2.1.0: resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +i18next-chained-backend@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/i18next-chained-backend/-/i18next-chained-backend-4.4.0.tgz#e81ab29bd62ba3ad68f448de506aab8ceb1fa7b1" + integrity sha512-I+mG0Bly9lbUk8kPZYbJuV7zPY0QDueOJ9yiUqz9o/RX2Mb7eC8RrfwtTX1QhVZmT9KSs9Oq5VDVLxPOeXBIgg== + dependencies: + "@babel/runtime" "^7.22.3" + i18next-resources-to-backend "1.1.4" + i18next-http-backend@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/i18next-http-backend/-/i18next-http-backend-2.1.1.tgz#72a21d61c2e96eea9ad45ba1b9dd0090e119709a" @@ -4522,6 +4537,20 @@ i18next-http-backend@^2.1.1: dependencies: cross-fetch "3.1.5" +i18next-localstorage-backend@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/i18next-localstorage-backend/-/i18next-localstorage-backend-4.1.1.tgz#a27d38b3e933f911c03bfabc03ebd889b90eaf2b" + integrity sha512-gz3OP6m0jygF2SAzarDSFpaziYzF8mS8DP/pEUze+kDquzHTz2sy/eshcSkmFEYyGXMC037xUtlT97bNGlpZPA== + dependencies: + "@babel/runtime" "^7.20.6" + +i18next-resources-to-backend@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/i18next-resources-to-backend/-/i18next-resources-to-backend-1.1.4.tgz#d139ca0cacc270dcc90b7926e192f4cd5aa4db60" + integrity sha512-hMyr9AOmIea17AOaVe1srNxK/l3mbk81P7Uf3fdcjlw3ehZy3UNTd0OP3EEi6yu4J02kf9jzhCcjokz6AFlEOg== + dependencies: + "@babel/runtime" "^7.21.5" + i18next@^22.4.9: version "22.4.9" resolved "https://registry.yarnpkg.com/i18next/-/i18next-22.4.9.tgz#98c8384c6bd41ff937da98b1e809ba03d3b41053" From 94c4f1a50c301a4d734b0a6a5d9f876ebb9387fe Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 16 Jun 2023 16:47:22 +0500 Subject: [PATCH 141/145] fix(pages): settings page reinstall error handling --- .../src/assets/locales/de-DE/translation.json | 1 + .../src/assets/locales/en-US/translation.json | 1 + .../src/assets/locales/en/translation.json | 1 + .../src/assets/locales/es-ES/translation.json | 1 + .../src/assets/locales/fr-FR/translation.json | 1 + .../src/assets/locales/it-IT/translation.json | 1 + .../src/assets/locales/ja-JP/translation.json | 1 + .../src/assets/locales/pt-BR/translation.json | 1 + .../src/assets/locales/ru-RU/translation.json | 1 + frontend/src/context/TokenContext.tsx | 11 +++++++-- frontend/src/pages/Settings/index.tsx | 23 +++++++++++++++---- 11 files changed, 36 insertions(+), 7 deletions(-) diff --git a/frontend/src/assets/locales/de-DE/translation.json b/frontend/src/assets/locales/de-DE/translation.json index 806a274..610a35f 100644 --- a/frontend/src/assets/locales/de-DE/translation.json +++ b/frontend/src/assets/locales/de-DE/translation.json @@ -34,6 +34,7 @@ "editor.error": "Die Datei konnte nicht geöffnet werden. Etwas ist schief gelaufen", "background.error.title": "Fehler", "background.error.subtitle": "Plugin-Einstellungen konnten nicht abgerufen werden. Etwas ist schief gelaufen. Bitte laden Sie das Pipedrive-Fenster neu", + "background.error.subtitle.token": "Could not fetch plugin settings. Something went wrong with your access token. Please reinstall the app", "background.access.title": "Zugriff verweigert", "background.access.subtitle": "Etwas ist schief gelaufen oder der Zugriff wurde verweigert", "background.reinstall.title": "Das Sicherheitstoken für das Dokument ist abgelaufen", diff --git a/frontend/src/assets/locales/en-US/translation.json b/frontend/src/assets/locales/en-US/translation.json index e660f96..eb06b63 100644 --- a/frontend/src/assets/locales/en-US/translation.json +++ b/frontend/src/assets/locales/en-US/translation.json @@ -34,6 +34,7 @@ "editor.error": "Could not open the file. Something went wrong", "background.error.title": "Error", "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the Pipedrive window", + "background.error.subtitle.token": "Could not fetch plugin settings. Something went wrong with your access token. Please reinstall the app", "background.access.title": "Access denied", "background.access.subtitle": "Something went wrong or access denied", "background.reinstall.title": "The document security token has expired", diff --git a/frontend/src/assets/locales/en/translation.json b/frontend/src/assets/locales/en/translation.json index e660f96..eb06b63 100644 --- a/frontend/src/assets/locales/en/translation.json +++ b/frontend/src/assets/locales/en/translation.json @@ -34,6 +34,7 @@ "editor.error": "Could not open the file. Something went wrong", "background.error.title": "Error", "background.error.subtitle": "Could not fetch plugin settings. Something went wrong. Please reload the Pipedrive window", + "background.error.subtitle.token": "Could not fetch plugin settings. Something went wrong with your access token. Please reinstall the app", "background.access.title": "Access denied", "background.access.subtitle": "Something went wrong or access denied", "background.reinstall.title": "The document security token has expired", diff --git a/frontend/src/assets/locales/es-ES/translation.json b/frontend/src/assets/locales/es-ES/translation.json index 0b151e7..672cfd0 100644 --- a/frontend/src/assets/locales/es-ES/translation.json +++ b/frontend/src/assets/locales/es-ES/translation.json @@ -34,6 +34,7 @@ "editor.error": "No se ha podido abrir el archivo. Se ha producido un error", "background.error.title": "Error", "background.error.subtitle": "No se han podido recuperar los ajustes del plugin. Se ha producido un error. Por favor, vuelva a cargar la ventana de Pipedrive", + "background.error.subtitle.token": "Could not fetch plugin settings. Something went wrong with your access token. Please reinstall the app", "background.access.title": "Se ha denegado el acceso", "background.access.subtitle": "Se ha producido un error o se ha denegado el acceso", "background.reinstall.title": "El token de seguridad del documento ha expirado", diff --git a/frontend/src/assets/locales/fr-FR/translation.json b/frontend/src/assets/locales/fr-FR/translation.json index 4beadb6..63ef52b 100644 --- a/frontend/src/assets/locales/fr-FR/translation.json +++ b/frontend/src/assets/locales/fr-FR/translation.json @@ -34,6 +34,7 @@ "editor.error": "Impossible d'ouvrir le fichier. Une erreur s'est produite.", "background.error.title": "Erreur", "background.error.subtitle": "Impossible de récupérer les paramètres du plugin. Une erreur s'est produite. Veuillez recharger la fenêtre Pipedrive", + "background.error.subtitle.token": "Could not fetch plugin settings. Something went wrong with your access token. Please reinstall the app", "background.access.title": "Accès refusé", "background.access.subtitle": "Une erreur s'est produite ou l'accès a été refusé", "background.reinstall.title": "Le jeton de sécurité du document a expiré", diff --git a/frontend/src/assets/locales/it-IT/translation.json b/frontend/src/assets/locales/it-IT/translation.json index 24cf486..95ac236 100644 --- a/frontend/src/assets/locales/it-IT/translation.json +++ b/frontend/src/assets/locales/it-IT/translation.json @@ -34,6 +34,7 @@ "editor.error": "Impossibile aprire il file. Qualcosa è andato storto", "background.error.title": "Errore", "background.error.subtitle": "Impossibile recuperare le impostazioni del plugin. Qualcosa è andato storto. Ricarica la finestra di Pipedrive", + "background.error.subtitle.token": "Could not fetch plugin settings. Something went wrong with your access token. Please reinstall the app", "background.access.title": "Accesso negato", "background.access.subtitle": "Si è verificato un problema o l'accesso è stato negato", "background.reinstall.title": "Il token di sicurezza del documento è scaduto", diff --git a/frontend/src/assets/locales/ja-JP/translation.json b/frontend/src/assets/locales/ja-JP/translation.json index e91cc34..8d28360 100644 --- a/frontend/src/assets/locales/ja-JP/translation.json +++ b/frontend/src/assets/locales/ja-JP/translation.json @@ -34,6 +34,7 @@ "editor.error": "ファイルを開くことに失敗しました。エラーが発生しました", "background.error.title": "エラー", "background.error.subtitle": "プラグイン設定を取得できませんでした。何らかのエラーが発生しました。pipedriveのウィンドウを再読み込みしてください", + "background.error.subtitle.token": "Could not fetch plugin settings. Something went wrong with your access token. Please reinstall the app", "background.access.title": "アクセスが拒否されました", "background.access.subtitle": "エラーが発生しました。或いは、この文書へのアクセス許可がありません", "background.reinstall.title": "文書のセキュリティトークンの有効期限が切れています", diff --git a/frontend/src/assets/locales/pt-BR/translation.json b/frontend/src/assets/locales/pt-BR/translation.json index 59dec83..34bcb95 100644 --- a/frontend/src/assets/locales/pt-BR/translation.json +++ b/frontend/src/assets/locales/pt-BR/translation.json @@ -34,6 +34,7 @@ "editor.error": "Não foi possível abrir o arquivo. Algo deu errado", "background.error.title": "Erro", "background.error.subtitle": "Não foi possível obter as configurações do plug-in. Algo deu errado. Recarregue a janela do Pipedrive", + "background.error.subtitle.token": "Could not fetch plugin settings. Something went wrong with your access token. Please reinstall the app", "background.access.title": "Acesso negado", "background.access.subtitle": "Algo deu errado ou acesso negado", "background.reinstall.title": "O token de segurança do documento expirou", diff --git a/frontend/src/assets/locales/ru-RU/translation.json b/frontend/src/assets/locales/ru-RU/translation.json index 0c71ddc..82858c0 100644 --- a/frontend/src/assets/locales/ru-RU/translation.json +++ b/frontend/src/assets/locales/ru-RU/translation.json @@ -33,6 +33,7 @@ "editor.error": "Не удалось открыть файл. Что-то пошло не так", "background.error.title": "Ошибка", "background.error.subtitle": "Не удалось получить настройки плагина. Что-то пошло не так. Пожалуйста, перезагрузите окно Pipedrive", + "background.error.subtitle.token": "Could not fetch plugin settings. Something went wrong with your access token. Please reinstall the app", "background.access.title": "Доступ запрещен", "background.access.subtitle": "Что-то пошло не так, или доступ запрещен", "background.reinstall.title": "Истек срок действия токена безопасности документа", diff --git a/frontend/src/context/TokenContext.tsx b/frontend/src/context/TokenContext.tsx index e593bf2..2190794 100644 --- a/frontend/src/context/TokenContext.tsx +++ b/frontend/src/context/TokenContext.tsx @@ -18,7 +18,7 @@ import AppExtensionsSDK from "@pipedrive/app-extensions-sdk"; import i18next from "i18next"; -import axios from "axios"; +import axios, { AxiosError } from "axios"; import React, { useEffect } from "react"; import { proxy } from "valtio"; @@ -30,6 +30,7 @@ export const AuthToken = proxy({ access_token: "", expires_at: Date.now(), error: false, + status: 200, }); type ProviderProps = { @@ -58,7 +59,13 @@ export const TokenProvider: React.FC = ({ children }) => { AuthToken.access_token = token.response.access_token; AuthToken.expires_at = token.response.expires_at; } catch (err) { - if (!axios.isCancel(err)) { + if (axios.isAxiosError(err) && !axios.isCancel(err)) { + const aerr = err as AxiosError; + if (aerr.response && aerr.response.status) { + AuthToken.status = aerr.response.status; + } else { + AuthToken.status = 500; + } AuthToken.error = true; AuthToken.access_token = ""; } diff --git a/frontend/src/pages/Settings/index.tsx b/frontend/src/pages/Settings/index.tsx index ab5dba5..e8bfe18 100644 --- a/frontend/src/pages/Settings/index.tsx +++ b/frontend/src/pages/Settings/index.tsx @@ -39,7 +39,7 @@ import { getCurrentURL } from "@utils/url"; export const SettingsPage: React.FC = () => { const { t } = useTranslation(); const [sdk, setSDK] = useState(); - const { access_token: accessToken, error } = useSnapshot(AuthToken); + const { access_token: accessToken, error, status } = useSnapshot(AuthToken); const [admin, setAdmin] = useState(false); const [loading, setLoading] = useState(true); const [address, setAddress] = useState(undefined); @@ -124,10 +124,24 @@ export const SettingsPage: React.FC = () => { Icon={} title={t("background.error.title", "Error")} subtitle={t( - "background.error.subtitle", - "Could not fetch plugin settings. Something went wrong. Please reload the pipedrive window" + status !== 401 + ? "background.error.subtitle" + : "background.error.subtitle.token", + status !== 401 + ? "Could not fetch plugin settings. Something went wrong. Please reload the pipedrive window" + : "Could not fetch plugin settings. Something went wrong with your access token. Please reinstall the app" )} - onClick={() => window.location.reload()} + button={ + status === 401 && t("background.reinstall.button", "Reinstall") || + "Reinstall" + } + onClick={status === 401 ? () => { + if (status === 401) + window.open( + `${getCurrentURL().url}settings/marketplace`, + "_blank" + ); + } : undefined} /> )} {!loading && !error && !admin && ( @@ -138,7 +152,6 @@ export const SettingsPage: React.FC = () => { "background.access.subtitle", "Something went wrong or access denied" )} - onClick={() => window.location.reload()} /> )} {!loading && !error && admin && ( From 92c1a77c5edaca16033a5e9d37861ffcc4dee568 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Fri, 16 Jun 2023 16:59:00 +0500 Subject: [PATCH 142/145] fix(pages): main page token error handling --- .../src/assets/locales/de-DE/translation.json | 3 +- .../src/assets/locales/en-US/translation.json | 3 +- .../src/assets/locales/en/translation.json | 3 +- .../src/assets/locales/es-ES/translation.json | 3 +- .../src/assets/locales/fr-FR/translation.json | 3 +- .../src/assets/locales/it-IT/translation.json | 3 +- .../src/assets/locales/ja-JP/translation.json | 3 +- .../src/assets/locales/pt-BR/translation.json | 3 +- .../src/assets/locales/ru-RU/translation.json | 3 +- frontend/src/pages/Main/index.tsx | 29 ++++++++++++------- 10 files changed, 37 insertions(+), 19 deletions(-) diff --git a/frontend/src/assets/locales/de-DE/translation.json b/frontend/src/assets/locales/de-DE/translation.json index 610a35f..ae60cf8 100644 --- a/frontend/src/assets/locales/de-DE/translation.json +++ b/frontend/src/assets/locales/de-DE/translation.json @@ -38,7 +38,8 @@ "background.access.title": "Zugriff verweigert", "background.access.subtitle": "Etwas ist schief gelaufen oder der Zugriff wurde verweigert", "background.reinstall.title": "Das Sicherheitstoken für das Dokument ist abgelaufen", - "background.reinstall.subtitle": "Es ist etwas schief gelaufen. Bitte öffnen oder installieren Sie die App erneut.", + "background.reinstall.subtitle": "Something went wrong. Please reload the app.", + "background.reinstall.subtitle.token": "Something went wrong. Please reinstall the app.", "document.new": "Neues Dokument", "button.upload": "Dokument erstellen oder hochladen", "button.reload": "Neu laden", diff --git a/frontend/src/assets/locales/en-US/translation.json b/frontend/src/assets/locales/en-US/translation.json index eb06b63..ae31459 100644 --- a/frontend/src/assets/locales/en-US/translation.json +++ b/frontend/src/assets/locales/en-US/translation.json @@ -38,7 +38,8 @@ "background.access.title": "Access denied", "background.access.subtitle": "Something went wrong or access denied", "background.reinstall.title": "The document security token has expired", - "background.reinstall.subtitle": "Something went wrong. Please reload or reinstall the app.", + "background.reinstall.subtitle": "Something went wrong. Please reload the app.", + "background.reinstall.subtitle.token": "Something went wrong. Please reinstall the app.", "document.new": "New Document", "button.upload": "Create or upload document", "button.reload": "Reload", diff --git a/frontend/src/assets/locales/en/translation.json b/frontend/src/assets/locales/en/translation.json index eb06b63..ae31459 100644 --- a/frontend/src/assets/locales/en/translation.json +++ b/frontend/src/assets/locales/en/translation.json @@ -38,7 +38,8 @@ "background.access.title": "Access denied", "background.access.subtitle": "Something went wrong or access denied", "background.reinstall.title": "The document security token has expired", - "background.reinstall.subtitle": "Something went wrong. Please reload or reinstall the app.", + "background.reinstall.subtitle": "Something went wrong. Please reload the app.", + "background.reinstall.subtitle.token": "Something went wrong. Please reinstall the app.", "document.new": "New Document", "button.upload": "Create or upload document", "button.reload": "Reload", diff --git a/frontend/src/assets/locales/es-ES/translation.json b/frontend/src/assets/locales/es-ES/translation.json index 672cfd0..cf7fe51 100644 --- a/frontend/src/assets/locales/es-ES/translation.json +++ b/frontend/src/assets/locales/es-ES/translation.json @@ -38,7 +38,8 @@ "background.access.title": "Se ha denegado el acceso", "background.access.subtitle": "Se ha producido un error o se ha denegado el acceso", "background.reinstall.title": "El token de seguridad del documento ha expirado", - "background.reinstall.subtitle": "Se ha producido un error. Por favor, vuelva a cargar o reinstale la aplicación.", + "background.reinstall.subtitle": "Something went wrong. Please reload the app.", + "background.reinstall.subtitle.token": "Something went wrong. Please reinstall the app.", "document.new": "Nuevo documento", "button.upload": "Crear o cargar documento", "button.reload": "Recargar", diff --git a/frontend/src/assets/locales/fr-FR/translation.json b/frontend/src/assets/locales/fr-FR/translation.json index 63ef52b..ae2300f 100644 --- a/frontend/src/assets/locales/fr-FR/translation.json +++ b/frontend/src/assets/locales/fr-FR/translation.json @@ -38,7 +38,8 @@ "background.access.title": "Accès refusé", "background.access.subtitle": "Une erreur s'est produite ou l'accès a été refusé", "background.reinstall.title": "Le jeton de sécurité du document a expiré", - "background.reinstall.subtitle": "Une erreur s'est produite. Veuillez recharger ou réinstaller l'application", + "background.reinstall.subtitle": "Something went wrong. Please reload the app.", + "background.reinstall.subtitle.token": "Something went wrong. Please reinstall the app.", "document.new": "Nouveau document", "button.upload": "Créer ou charger un document", "button.reload": "Recharger", diff --git a/frontend/src/assets/locales/it-IT/translation.json b/frontend/src/assets/locales/it-IT/translation.json index 95ac236..85cdfff 100644 --- a/frontend/src/assets/locales/it-IT/translation.json +++ b/frontend/src/assets/locales/it-IT/translation.json @@ -38,7 +38,8 @@ "background.access.title": "Accesso negato", "background.access.subtitle": "Si è verificato un problema o l'accesso è stato negato", "background.reinstall.title": "Il token di sicurezza del documento è scaduto", - "background.reinstall.subtitle": "Qualcosa è andato storto. Ricarica o reinstalla l'app.", + "background.reinstall.subtitle": "Something went wrong. Please reload the app.", + "background.reinstall.subtitle.token": "Something went wrong. Please reinstall the app.", "document.new": "Nuovo documento", "button.upload": "Crea o carica un documento", "button.reload": "Ricarica", diff --git a/frontend/src/assets/locales/ja-JP/translation.json b/frontend/src/assets/locales/ja-JP/translation.json index 8d28360..5535707 100644 --- a/frontend/src/assets/locales/ja-JP/translation.json +++ b/frontend/src/assets/locales/ja-JP/translation.json @@ -38,7 +38,8 @@ "background.access.title": "アクセスが拒否されました", "background.access.subtitle": "エラーが発生しました。或いは、この文書へのアクセス許可がありません", "background.reinstall.title": "文書のセキュリティトークンの有効期限が切れています", - "background.reinstall.subtitle": "エラーが発生しました。アプリを再読み込みするか、再インストールしてください。", + "background.reinstall.subtitle": "Something went wrong. Please reload the app.", + "background.reinstall.subtitle.token": "Something went wrong. Please reinstall the app.", "document.new": "新しい文書", "button.upload": "文書を作成、またはアップロードする", "button.reload": "再読み込み", diff --git a/frontend/src/assets/locales/pt-BR/translation.json b/frontend/src/assets/locales/pt-BR/translation.json index 34bcb95..a78bffe 100644 --- a/frontend/src/assets/locales/pt-BR/translation.json +++ b/frontend/src/assets/locales/pt-BR/translation.json @@ -38,7 +38,8 @@ "background.access.title": "Acesso negado", "background.access.subtitle": "Algo deu errado ou acesso negado", "background.reinstall.title": "O token de segurança do documento expirou", - "background.reinstall.subtitle": "Algo deu errado. Atualize ou reinstale o aplicativo.", + "background.reinstall.subtitle": "Something went wrong. Please reload the app.", + "background.reinstall.subtitle.token": "Something went wrong. Please reinstall the app.", "document.new": "Novo Documento", "button.upload": "Criar ou carregar documento", "button.reload": "Recarregar", diff --git a/frontend/src/assets/locales/ru-RU/translation.json b/frontend/src/assets/locales/ru-RU/translation.json index 82858c0..d90a6d6 100644 --- a/frontend/src/assets/locales/ru-RU/translation.json +++ b/frontend/src/assets/locales/ru-RU/translation.json @@ -37,7 +37,8 @@ "background.access.title": "Доступ запрещен", "background.access.subtitle": "Что-то пошло не так, или доступ запрещен", "background.reinstall.title": "Истек срок действия токена безопасности документа", - "background.reinstall.subtitle": "Что-то пошло не так. Пожалуйста, перезагрузите или переустановите приложение.", + "background.reinstall.subtitle": "Something went wrong. Please reload the app.", + "background.reinstall.subtitle.token": "Something went wrong. Please reinstall the app.", "document.new": "Новый документ", "button.upload": "Создать или загрузить документ", "button.reload": "Перезагрузить", diff --git a/frontend/src/pages/Main/index.tsx b/frontend/src/pages/Main/index.tsx index c66d06e..d91acf0 100644 --- a/frontend/src/pages/Main/index.tsx +++ b/frontend/src/pages/Main/index.tsx @@ -26,13 +26,15 @@ import { OnlyofficeBackgroundError } from "@layouts/ErrorBackground"; import { AuthToken } from "@context/TokenContext"; +import { getCurrentURL } from "@utils/url"; + import TokenError from "@assets/token-error.svg"; import { Main } from "./Main"; export const MainPage: React.FC = () => { const { t } = useTranslation(); - const { access_token: accessToken, error } = useSnapshot(AuthToken); + const { access_token: accessToken, error, status } = useSnapshot(AuthToken); const loading = !accessToken && !error; const loadingError = !accessToken && error; const loaded = accessToken && !error; @@ -47,18 +49,25 @@ export const MainPage: React.FC = () => { } title={t( - "background.reinstall.title", - "The document security token has expired" + status === 401 ? "background.reinstall.title" : "background.error.title", + status === 401 ? "The document security token has expired" : "Error" )} subtitle={t( - "background.reinstall.subtitle", - "Something went wrong. Please reload or reinstall the app." + status === 401 ? "background.reinstall.subtitle.token" : "background.reinstall.subtitle", + status === 401 + ? "Something went wrong. Please reinstall the app." + : "Something went wrong. Please reload the app." )} - button={t( - "background.reinstall.button", - "Reinstall" - ) || "Reinstall"} - onClick={() => window.open(`${process.env.BACKEND_GATEWAY}/oauth/install`, "_blank")} + button={t("background.reinstall.button", "Reinstall") || "Reinstall"} + onClick={ + status === 401 + ? () => + window.open( + `${getCurrentURL().url}settings/marketplace`, + "_blank" + ) + : undefined + } /> )} {loaded &&
} From c25d08dc948485e8f73bf3e2fa7ca31c27fbdd87 Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 19 Jun 2023 12:06:43 +0500 Subject: [PATCH 143/145] fix(i18n): translate editor loading page --- frontend/package.json | 1 + frontend/src/i18n.ts | 2 ++ frontend/src/pages/Creation/Creation.tsx | 2 +- frontend/src/pages/Main/Actions.tsx | 3 ++- frontend/yarn.lock | 9 ++++++++- 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index fa1478c..37140fd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -89,6 +89,7 @@ "axios-retry": "^3.4.0", "classnames": "^2.3.2", "i18next": "^22.4.9", + "i18next-browser-languagedetector": "^7.0.2", "i18next-chained-backend": "^4.4.0", "i18next-http-backend": "^2.1.1", "i18next-localstorage-backend": "^4.1.1", diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts index ac4c184..ed5ac57 100644 --- a/frontend/src/i18n.ts +++ b/frontend/src/i18n.ts @@ -17,12 +17,14 @@ */ import i18n from "i18next"; +import LanguageDetector from 'i18next-browser-languagedetector'; import ChainedBackend from "i18next-chained-backend"; import I18NextHttpBackend from "i18next-http-backend"; import LocalStorageBackend from "i18next-localstorage-backend"; import { initReactI18next } from "react-i18next"; i18n + .use(LanguageDetector) .use(ChainedBackend) .use(initReactI18next) .init({ diff --git a/frontend/src/pages/Creation/Creation.tsx b/frontend/src/pages/Creation/Creation.tsx index 1a5f49c..aca23c2 100644 --- a/frontend/src/pages/Creation/Creation.tsx +++ b/frontend/src/pages/Creation/Creation.tsx @@ -155,7 +155,7 @@ export const Creation: React.FC = () => { file.substring(0, 190) )}.${fileType}`}&key=${md5( fres.data.data.id + fres.data.data.update_time - )}` + )}&lng=${i18next.language}` ); await sdk?.execute(Command.CLOSE_MODAL); } catch { diff --git a/frontend/src/pages/Main/Actions.tsx b/frontend/src/pages/Main/Actions.tsx index eed75e1..f3c99a8 100644 --- a/frontend/src/pages/Main/Actions.tsx +++ b/frontend/src/pages/Main/Actions.tsx @@ -17,6 +17,7 @@ */ import React, { useEffect, useState } from "react"; +import i18next from "i18next"; import md5 from "md5"; import AppExtensionsSDK, { Command } from "@pipedrive/app-extensions-sdk"; import { useTranslation } from "react-i18next"; @@ -91,7 +92,7 @@ export const OnlyofficeFileActions: React.FC = ({ file }) => { parameters.get("selectedIds") || "1" }&id=${file.id}&name=${`${encodeURIComponent( name.substring(0, 190) - )}.${ext}`}&key=${md5(file.id + file.update_time)}`; + )}.${ext}`}&key=${md5(file.id + file.update_time)}&lng=${i18next.language}`; } } // temporary solution diff --git a/frontend/yarn.lock b/frontend/yarn.lock index d526e7f..d147fd0 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1056,7 +1056,7 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/runtime@^7.21.5", "@babel/runtime@^7.22.3": +"@babel/runtime@^7.19.4", "@babel/runtime@^7.21.5", "@babel/runtime@^7.22.3": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA== @@ -4522,6 +4522,13 @@ human-signals@^2.1.0: resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +i18next-browser-languagedetector@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.2.tgz#22d8ed48411750c1a1828ac2031c816a5108e55f" + integrity sha512-5ViaK+gikxfqZ9M3jJ7gJkUzzu/p3HwiqfLoL1bdiL7CUb0IylcTyVLdPaTU3pH5VFWFCiGFuJDg3VkLUikWgg== + dependencies: + "@babel/runtime" "^7.19.4" + i18next-chained-backend@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/i18next-chained-backend/-/i18next-chained-backend-4.4.0.tgz#e81ab29bd62ba3ad68f448de506aab8ceb1fa7b1" From f2939d23545df0dee2d20779477b4f590f96e5dc Mon Sep 17 00:00:00 2001 From: Dmitrii Vershinin Date: Mon, 19 Jun 2023 12:07:20 +0500 Subject: [PATCH 144/145] doc(licenses): update licenses --- backend/3rd-Party.license | 24 +++ backend/licenses/jwt.license | 8 + backend/licenses/mgm.license | 190 ++++++++++++++++++ backend/licenses/oauth.license | 27 +++ backend/licenses/resty.license | 21 ++ backend/licenses/singleflight.license | 27 +++ backend/licenses/useragent.license | 21 ++ frontend/3rd-Party.license | 10 +- .../licenses/i18next-chained-backend.license | 21 ++ .../i18next-localstorage-backend.license | 21 ++ 10 files changed, 369 insertions(+), 1 deletion(-) create mode 100644 backend/licenses/jwt.license create mode 100644 backend/licenses/mgm.license create mode 100644 backend/licenses/oauth.license create mode 100644 backend/licenses/resty.license create mode 100644 backend/licenses/singleflight.license create mode 100644 backend/licenses/useragent.license create mode 100644 frontend/licenses/i18next-chained-backend.license create mode 100644 frontend/licenses/i18next-localstorage-backend.license diff --git a/backend/3rd-Party.license b/backend/3rd-Party.license index 3c70862..8d8abfd 100644 --- a/backend/3rd-Party.license +++ b/backend/3rd-Party.license @@ -68,10 +68,18 @@ mongo-driver - The MongoDB supported driver for Go. (https://pkg.go.dev/go.mong License: Apache-2.0 License File: mongo-driver.license +mgm - The Mongo ODM for Go. (https://github.com/Kamva/mgm/blob/master/LICENSE) +License: Apache-2.0 +License File: mgm.license + opentelemetry-go - OpenTelemetry-Go is the Go implementation of OpenTelemetry. It provides a set of APIs to directly measure performance and behavior of your software and send this data to observability platforms. (https://pkg.go.dev/go.opentelemetry.io/otel?tab=licenses) License: Apache-2.0 License File: opentelemetry-go.license +oauth2 - oauth2 package contains a client implementation for OAuth 2.0 spec. (https://pkg.go.dev/golang.org/x/oauth2@v0.8.0?tab=licenses) +License: BSD 3-Clause +License File: oauth.license + fx - Fx is a dependency injection system for Go. (https://pkg.go.dev/go.uber.org/fx?tab=licenses) License: MIT License File: fx.license @@ -84,6 +92,22 @@ sync - This repository provides Go concurrency primitives in addition t License: BSD 3-Clause License File: sync.license +singleflight - Package singleflight provides a duplicate function call suppression mechanism. (https://pkg.go.dev/golang.org/x/sync@v0.2.0/singleflight?tab=licenses) +License: BSD 3-Clause +License File: singleflight.license + yaml - YAML support for the Go language. (https://github.com/go-yaml/yaml/blob/v3/LICENSE) License: MIT License File: yaml.license + +useragent - Go/Golang package for parsing user agent strings. (https://pkg.go.dev/github.com/mileusna/useragent@v1.2.1?tab=licenses) +License: MIT +License File: useragent.license + +resty - Simple HTTP and REST client library for Go. (https://github.com/go-resty/resty/blob/master/LICENSE) +License: MIT +License File: resty.license + +jwt - Community maintained clone of https://github.com/dgrijalva/jwt-go. (https://github.com/golang-jwt/jwt/blob/main/LICENSE) +License: MIT +License File: jwt.license \ No newline at end of file diff --git a/backend/licenses/jwt.license b/backend/licenses/jwt.license new file mode 100644 index 0000000..7889d0f --- /dev/null +++ b/backend/licenses/jwt.license @@ -0,0 +1,8 @@ +Copyright (c) 2012 Dave Grijalva +Copyright (c) 2021 golang-jwt maintainers + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/licenses/mgm.license b/backend/licenses/mgm.license new file mode 100644 index 0000000..77dfd60 --- /dev/null +++ b/backend/licenses/mgm.license @@ -0,0 +1,190 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2019 Kamva. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/backend/licenses/oauth.license b/backend/licenses/oauth.license new file mode 100644 index 0000000..ea5ea89 --- /dev/null +++ b/backend/licenses/oauth.license @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/backend/licenses/resty.license b/backend/licenses/resty.license new file mode 100644 index 0000000..64a9300 --- /dev/null +++ b/backend/licenses/resty.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2023 Jeevanandam M., https://myjeeva.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/backend/licenses/singleflight.license b/backend/licenses/singleflight.license new file mode 100644 index 0000000..ea5ea89 --- /dev/null +++ b/backend/licenses/singleflight.license @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/backend/licenses/useragent.license b/backend/licenses/useragent.license new file mode 100644 index 0000000..2da0046 --- /dev/null +++ b/backend/licenses/useragent.license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Miloš Mileusnić + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/frontend/3rd-Party.license b/frontend/3rd-Party.license index 8b96fa1..3e99b79 100644 --- a/frontend/3rd-Party.license +++ b/frontend/3rd-Party.license @@ -24,7 +24,7 @@ i18next - i18next: learn once - translate everywhere. (https://github.com/ License: MIT License File: i18next.license -i18next-browser - languagedetector - language detector used in browser environment for i18next. (https://github.com/i18next/i18next-browser-languageDetector/blob/master/LICENSE) +i18next-browser-languagedetector - language detector used in browser environment for i18next. (https://github.com/i18next/i18next-browser-languageDetector/blob/master/LICENSE) License: MIT License File: i18next-detector.license @@ -32,6 +32,14 @@ i18next-http-backend - i18next-http-backend is a backend layer for i18next using License: MIT License File: i18next-backend.license +i18next-chained-backend - An i18next backend to chain multiple backends (add fallbacks, caches, ...). (https://github.com/i18next/i18next-chained-backend/blob/master/LICENSE) +License: MIT +License File: i18next-chained-backend.license + +i18next-localstorage-backend.license - This is a i18next cache layer to be used in the browser. It will load and cache resources from localStorage and can be used in combination with the chained backend. (https://github.com/i18next/i18next-localstorage-backend/blob/master/LICENSE) +License: MIT +License File: i18next-localstorage-backend.license + md5 - a JavaScript function for hashing messages with MD5. (https://github.com/pvorb/node-md5/blob/master/LICENSE) License: BSD 3-Clause License File: md5.license diff --git a/frontend/licenses/i18next-chained-backend.license b/frontend/licenses/i18next-chained-backend.license new file mode 100644 index 0000000..7f8f3b2 --- /dev/null +++ b/frontend/licenses/i18next-chained-backend.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 i18next + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/frontend/licenses/i18next-localstorage-backend.license b/frontend/licenses/i18next-localstorage-backend.license new file mode 100644 index 0000000..793bc4f --- /dev/null +++ b/frontend/licenses/i18next-localstorage-backend.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 i18next + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file From 9697e2ed056a0d3250192e1acd2e4523e9dc0b8a Mon Sep 17 00:00:00 2001 From: Sergey Linnik Date: Mon, 24 Jul 2023 14:49:07 +0300 Subject: [PATCH 145/145] 1.0.0 --- CHANGELOG.md | 2 +- frontend/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d00fa3c..458715c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## +## 1.0.0 ## Added - configuration page - collaboration editing for DOCX, XLSX, PPTX diff --git a/frontend/package.json b/frontend/package.json index 37140fd..43ba5fd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,7 +6,7 @@ "Ascensio System SIA (https://www.onlyoffice.com)" ], "license": "Apache-2.0", - "version": "0.0.1", + "version": "1.0.0", "private": true, "repository": { "type": "git",