diff --git a/.github/workflows/bump_version.yml b/.github/workflows/bump_version.yml index c98a4f2d8b..83e96a4456 100644 --- a/.github/workflows/bump_version.yml +++ b/.github/workflows/bump_version.yml @@ -115,6 +115,7 @@ jobs: git config user.email "dev@prosopo.io" # checkout the src branch + git fetch --all git checkout ${{ github.event.inputs.src }} INTERIM="${{ github.event.inputs.interim}}" diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index 0afa1cb1f6..be0970efc8 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -5,7 +5,7 @@ name: cypress on: pull_request: branches: [main, dev, staging, release/*] - types: + types: - opened # when a PR is opened - synchronize # when a PR is pushed to - reopened # when a PR is reopened @@ -106,6 +106,7 @@ jobs: cp demos/client-example-server/env.development demos/client-example-server/.env.test cp demos/client-example/env.development demos/client-example/.env.test + cp demos/client-bundle-example/env.development demos/client-example/.env.test cp dev/scripts/env.test .env.test cp dev/scripts/env.test dev/scripts/.env.test cp dev/scripts/env.test packages/cli/.env.test diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b6711b617c..fd7691f4f6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -128,6 +128,7 @@ jobs: cp demos/client-example-server/env.development demos/client-example-server/.env.test cp demos/client-example/env.development demos/client-example/.env.test + cp demos/client-bundle-example/env.development demos/client-bundle-example/.env.test cp dev/scripts/env.test .env.test cp dev/scripts/env.test dev/scripts/.env.test cp dev/scripts/env.test packages/cli/.env.test diff --git a/demos/client-bundle-example/README.md b/demos/client-bundle-example/README.md index 1356131751..0e6b84d53e 100644 --- a/demos/client-bundle-example/README.md +++ b/demos/client-bundle-example/README.md @@ -15,7 +15,7 @@ docker compose --file ./docker/docker-compose.development.yml up -d && \ npm i && \ npm run build:all && \ npm run setup:all && \ -NODE_ENV=production npm -w @prosopo/procaptcha-bundle run bundle && \ +NODE_ENV=development npm -w @prosopo/procaptcha-bundle run bundle && \ npm run start:all ``` diff --git a/demos/client-bundle-example/env.development b/demos/client-bundle-example/env.development new file mode 100644 index 0000000000..71d0f393f9 --- /dev/null +++ b/demos/client-bundle-example/env.development @@ -0,0 +1,2 @@ +PROSOPO_SITE_KEY=5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw +PROSOPO_SERVER_URL=http://localhost:9228 diff --git a/demos/client-bundle-example/package.json b/demos/client-bundle-example/package.json index 0aa5929887..e152ea809d 100644 --- a/demos/client-bundle-example/package.json +++ b/demos/client-bundle-example/package.json @@ -1,6 +1,7 @@ { "name": "@prosopo/client-bundle-example", "main": "index.js", + "type": "module", "engines": { "node": ">=20", "npm": ">=9" @@ -12,8 +13,9 @@ "start": "vite serve ./src --port 9232 --config vite.config.ts", "clean": "echo 'nothing to clean'" }, - "version": "2.1.3", + "version": "2.1.4", "devDependencies": { + "@prosopo/dotenv": "2.1.4", "@types/node": "22.5.5", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", diff --git a/demos/client-bundle-example/src/frictionless.html b/demos/client-bundle-example/src/frictionless.html index c9d098ea49..a84248844f 100644 --- a/demos/client-bundle-example/src/frictionless.html +++ b/demos/client-bundle-example/src/frictionless.html @@ -9,7 +9,7 @@ // Get the Element using elementId const captchaContainer = document.getElementById('procaptcha-container') render(captchaContainer, { - siteKey: '5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw', + siteKey: import.meta.env.PROSOPO_SITE_KEY, theme: 'dark', callback: onCaptchaVerified, captchaType: 'frictionless', diff --git a/demos/client-bundle-example/src/index.html b/demos/client-bundle-example/src/index.html index e9136f9765..b51781f642 100644 --- a/demos/client-bundle-example/src/index.html +++ b/demos/client-bundle-example/src/index.html @@ -1,53 +1,168 @@ - - - Procaptcha demo: Simple page - - - -
-
- Login -
- - -
-
- - -
-
-
-
-
- -
-
- -
-
- + + + + + + +
+
+

Example Login Form

+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +
+
+ + +
+
+ - + // Get the Element using elementId + const captchaContainer = document.getElementById('procaptcha-container') + // Render the CAPTCHA explicitly on a container with id "procaptcha-container" + render(captchaContainer, { + siteKey: import.meta.env.PROSOPO_SITE_KEY, + theme: 'dark', + callback: onCaptchaVerified, + "failed-callback": function () { + console.log('The user failed the captcha') + } + }) + + diff --git a/demos/client-bundle-example/src/jsBundleTest.html b/demos/client-bundle-example/src/jsBundleTest.html index 5ef37bdc11..dd4a516008 100644 --- a/demos/client-bundle-example/src/jsBundleTest.html +++ b/demos/client-bundle-example/src/jsBundleTest.html @@ -15,7 +15,7 @@
diff --git a/demos/client-bundle-example/src/urlParams.html b/demos/client-bundle-example/src/urlParams.html index 8d6f59f718..da37cc5fba 100644 --- a/demos/client-bundle-example/src/urlParams.html +++ b/demos/client-bundle-example/src/urlParams.html @@ -9,7 +9,7 @@ // Get the Element using elementId const captchaContainer = document.getElementById('procaptcha-container') render(captchaContainer, { - siteKey: '5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw', + siteKey: import.meta.env.PROSOPO_SITE_KEY, theme: 'dark', callback: onCaptchaVerified, }) diff --git a/demos/client-bundle-example/tsconfig.json b/demos/client-bundle-example/tsconfig.json index f748a511f0..233b9a2d74 100644 --- a/demos/client-bundle-example/tsconfig.json +++ b/demos/client-bundle-example/tsconfig.json @@ -8,5 +8,9 @@ "sourceMap": false }, "include": ["**/*.ts"], - "references": [] + "references": [ + { + "path": "../../packages/dotenv" + } + ] } diff --git a/demos/client-bundle-example/vite.config.ts b/demos/client-bundle-example/vite.config.ts index 0dc52fa56f..98310ec1b5 100644 --- a/demos/client-bundle-example/vite.config.ts +++ b/demos/client-bundle-example/vite.config.ts @@ -11,12 +11,26 @@ // WITHOUT 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 { - watch: false, - mode: "development", - server: { - https: false, - host: true, - cors: true, - }, -}; + +import { loadEnv } from "@prosopo/dotenv"; +import { type UserConfig, defineConfig } from "vite"; + +export default defineConfig(({ command, mode }) => { + loadEnv(); + return { + watch: false, + mode: "development", + server: { + host: true, + cors: true, + }, + define: { + "import.meta.env.PROSOPO_SITE_KEY": JSON.stringify( + process.env.PROSOPO_SITE_KEY, + ), + "import.meta.env.PROSOPO_SERVER_URL": JSON.stringify( + process.env.PROSOPO_SERVER_URL, + ), + }, + } as UserConfig; +}); diff --git a/demos/client-example-server/package.json b/demos/client-example-server/package.json index 1f6793ef7c..4947a03feb 100644 --- a/demos/client-example-server/package.json +++ b/demos/client-example-server/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/client-example-server", - "version": "2.1.3", + "version": "2.1.4", "description": "Backend for client-example", "main": "dist/app.js", "type": "module", @@ -32,12 +32,12 @@ "@noble/hashes": "1.5.0", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/contract": "2.1.3", - "@prosopo/dotenv": "2.1.3", - "@prosopo/server": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/contract": "2.1.4", + "@prosopo/dotenv": "2.1.4", + "@prosopo/server": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "@typegoose/auto-increment": "4.6.0", "cors": "2.8.5", "express": "4.21.0", @@ -47,7 +47,7 @@ "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@types/jsonwebtoken": "9.0.6", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", diff --git a/demos/client-example-server/tsconfig.json b/demos/client-example-server/tsconfig.json index 1d09bdf09d..b22c35ed6f 100644 --- a/demos/client-example-server/tsconfig.json +++ b/demos/client-example-server/tsconfig.json @@ -8,25 +8,22 @@ "include": ["src", "src/**/*.json"], "references": [ { - "path": "../../packages/contract" - }, - { - "path": "../../packages/server" + "path": "../../packages/common" }, { - "path": "../../packages/types" + "path": "../../packages/contract" }, { - "path": "../../packages/common" + "path": "../../packages/dotenv" }, { - "path": "../../packages/util" + "path": "../../packages/server" }, { - "path": "../../packages/server" + "path": "../../packages/types" }, { - "path": "../../packages/dotenv" + "path": "../../packages/util" } ] } diff --git a/demos/client-example/package.json b/demos/client-example/package.json index 9d5cb6118e..aeebc0f4fa 100644 --- a/demos/client-example/package.json +++ b/demos/client-example/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/client-example", - "version": "2.1.3", + "version": "2.1.4", "private": true, "type": "module", "engines": { @@ -13,14 +13,14 @@ "@mui/material": "5.9.1", "@polkadot/extension-dapp": "0.46.9", "@polkadot/extension-inject": "0.46.9", - "@prosopo/common": "2.1.3", - "@prosopo/locale-browser": "2.1.3", - "@prosopo/procaptcha-frictionless": "2.1.3", - "@prosopo/procaptcha-pow": "2.1.3", - "@prosopo/procaptcha-react": "2.1.3", - "@prosopo/server": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/vite-plugin-watch-workspace": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/locale-browser": "2.1.4", + "@prosopo/procaptcha-frictionless": "2.1.4", + "@prosopo/procaptcha-pow": "2.1.4", + "@prosopo/procaptcha-react": "2.1.4", + "@prosopo/server": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/vite-plugin-watch-workspace": "2.1.4", "@types/react-dom": "18.3.0", "@vitejs/plugin-react": "4.3.1", "react": "18.3.1", @@ -40,8 +40,8 @@ } }, "devDependencies": { - "@prosopo/config": "2.1.3", - "@prosopo/dotenv": "2.1.3", + "@prosopo/config": "2.1.4", + "@prosopo/dotenv": "2.1.4", "@types/node": "22.5.5", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", diff --git a/demos/client-example/src/Captcha.tsx b/demos/client-example/src/Captcha.tsx index b8264412d1..d3a491c8c6 100644 --- a/demos/client-example/src/Captcha.tsx +++ b/demos/client-example/src/Captcha.tsx @@ -38,24 +38,28 @@ export function Captcha(props: CaptchProps) { props.setProcaptchaToken(procaptchaToken); }; + const onFailed = () => { + console.log("The user failed the captcha"); + }; + return (
{props.captchaType === "frictionless" ? ( ) : props.captchaType === "pow" ? ( ) : ( )} diff --git a/demos/cypress-shared/cypress/e2e/correct.captcha.cy.ts b/demos/cypress-shared/cypress/e2e/correct.captcha.cy.ts index 2f424aaea4..7b97f9ea05 100644 --- a/demos/cypress-shared/cypress/e2e/correct.captcha.cy.ts +++ b/demos/cypress-shared/cypress/e2e/correct.captcha.cy.ts @@ -64,6 +64,33 @@ describe("Captchas", () => { }); }); + it("Selecting the incorrect images fails the captcha", () => { + cy.window() + .its("console") + .then((console) => { + cy.spy(console, "log").as("log"); + }); + cy.clickIAmHuman().then(() => { + // Make sure the images are loaded + cy.captchaImages().then(() => { + cy.get("@captchas").each((captcha: Captcha) => { + cy.log("in each function"); + // Click correct images and submit the solution + cy.clickNextButton(); + }); + }); + cy.get("input[type='checkbox']").then((checkboxes) => { + cy.wrap(checkboxes).first().should("not.be.checked"); + }); + }); + + // check the logs by going through all recorded calls + cy.get("@log").should( + "have.been.calledWith", + "The user failed the captcha", + ); + }); + it("Selecting the correct images passes the captcha", () => { cy.clickIAmHuman().then(() => { // Make sure the images are loaded diff --git a/demos/cypress-shared/cypress/e2e/correct.captcha.signup.cy.ts b/demos/cypress-shared/cypress/e2e/correct.captcha.signup.cy.ts index e40b6335dd..293ebe37dd 100644 --- a/demos/cypress-shared/cypress/e2e/correct.captcha.signup.cy.ts +++ b/demos/cypress-shared/cypress/e2e/correct.captcha.signup.cy.ts @@ -64,36 +64,48 @@ describe("Captchas", () => { }); }); - it("Selecting the correct images passes the captcha", () => { - cy.get('button[type="button"]').eq(1).click(); + it("Selecting the correct images passes the captcha and signs up the user", () => { + cy.get("button").as("button"); + expect("@button").to.have.length.gte(1); + cy.elementExists("button[type='button']:nth-of-type(2)").then( + (confirmBtn) => { + if (confirmBtn) { + cy.wrap(confirmBtn).click(); + } + }, + ); + // puts the client-example demo in the signup state. Does not exist in the client-bundle-example cy.clickIAmHuman().then(() => { // Make sure the images are loaded cy.captchaImages().then(() => { // Solve the captchas - cy.get("@captchas") - .each((captcha: Captcha) => { - cy.log("in each function"); - // Click correct images and submit the solution - cy.clickCorrectCaptchaImages(captcha); - }) - .then(() => { - // Get inputs of type checkbox - cy.get("input[type='checkbox']").then((checkboxes) => { - cy.wrap(checkboxes).first().should("be.checked"); - }); - }); - const uniqueId = Cypress._.uniqueId("test"); + cy.get("@captchas").each((captcha: Captcha) => { + cy.log("in each function"); + // Click correct images and submit the solution + cy.clickCorrectCaptchaImages(captcha); + }); + + // wait for solution http request to complete + cy.wait("@postSolution"); + + // Get checked checkboxes + cy.get("input[type='checkbox']:checked").should("have.length.gte", 1); + + const uniqueId = `test${Cypress._.random(0, 1e6)}`; cy.get('input[type="password"]').type("password"); cy.get('input[id="email"]').type(`${uniqueId}@prosopo.io`); cy.get('input[id="name"]').type("test"); - cy.get('button[type="button"]').first().click(); - cy.contains("user created").should("be.visible"); + cy.intercept("POST", "/signup").as("signup"); - // reloading the page and checking the box again should not require a captcha to be solved - cy.reload(); + cy.get('button[type="button"]').first().click(); - cy.get(checkboxClass, { timeout: 12000 }).first().click(); + cy.wait("@signup").then((interception) => { + const body = interception.response?.body; + console.log("body", body); + const { message } = body; + expect(message).to.equal("user created"); + }); }); }); }); diff --git a/demos/cypress-shared/cypress/support/commands.ts b/demos/cypress-shared/cypress/support/commands.ts index e67a0a2732..752e66fc93 100644 --- a/demos/cypress-shared/cypress/support/commands.ts +++ b/demos/cypress-shared/cypress/support/commands.ts @@ -28,6 +28,7 @@ declare global { ): Chainable>; getSelectors(captcha: Captcha): Cypress.Chainable; clickNextButton(): Cypress.Chainable; + elementExists(element: string): Chainable; } } } @@ -136,10 +137,17 @@ function clickNextButton() { cy.wait(0); } +function elementExists(selector: string) { + return cy + .window() + .then(($window) => $window.document.querySelector(selector)); +} + Cypress.Commands.addAll({ clickIAmHuman, captchaImages, clickCorrectCaptchaImages, getSelectors, clickNextButton, + elementExists, }); diff --git a/demos/cypress-shared/package.json b/demos/cypress-shared/package.json index f2dd9a09eb..0dfefbe10c 100644 --- a/demos/cypress-shared/package.json +++ b/demos/cypress-shared/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/cypress-shared", - "version": "2.1.3", + "version": "2.1.4", "private": true, "type": "module", "engines": { @@ -8,14 +8,14 @@ "npm": ">=9" }, "dependencies": { - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3" + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4" }, "devDependencies": { "@cypress/xpath": "2.0.3", - "@prosopo/common": "2.1.3", - "@prosopo/datasets": "2.1.3", - "@prosopo/types-database": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/datasets": "2.1.4", + "@prosopo/types-database": "2.1.4", "@types/node": "22.5.5", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", @@ -40,7 +40,7 @@ "cypress:open:client-example:frictionless": "CYPRESS_BASE_URL='http://0.0.0.0:9230' cypress open --env default_page='/frictionless'", "cypress:run:client-example:frictionless": "CYPRESS_BASE_URL='http://0.0.0.0:9230' cypress run --env default_page='/frictionless'", "cypress:open:client-bundle-example": "CYPRESS_BASE_URL='http://localhost:9232' cypress open", - "cypress:run:client-bundle-example": "CYPRESS_BASE_URL='http://localhost:9232' cypress run --spec 'cypress/e2e/captcha.cy.ts,cypress/e2e/correct.captcha.cy.ts'", + "cypress:run:client-bundle-example": "CYPRESS_BASE_URL='http://localhost:9232' cypress run", "cypress:open:client-bundle-example:explicit": "CYPRESS_BASE_URL='http://localhost:9232' cypress open --env default_page='/urlParams.html'", "cypress:run:client-bundle-example:explicit": "CYPRESS_BASE_URL='http://localhost:9232' cypress run --env default_page='/urlParams.html' --spec 'cypress/e2e/captcha.cy.ts'", "cypress:open:client-bundle-example:frictionless": "CYPRESS_BASE_URL='http://localhost:9232' cypress open --env default_page='/frictionless.html'", diff --git a/demos/provider-mock/package.json b/demos/provider-mock/package.json index b41bf490b4..ba54d57f4f 100644 --- a/demos/provider-mock/package.json +++ b/demos/provider-mock/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/provider-mock", - "version": "2.1.3", + "version": "2.1.4", "author": "PROSOPO LIMITED ", "license": "Apache-2.0", "main": "./dist/index.js", @@ -16,9 +16,9 @@ "build:cjs": "echo 'no cjs build'" }, "dependencies": { - "@prosopo/common": "2.1.3", - "@prosopo/provider": "2.1.3", - "@prosopo/types": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/provider": "2.1.4", + "@prosopo/types": "2.1.4", "cors": "2.8.5", "express": "4.21.0" }, diff --git a/demos/provider-mock/tsconfig.json b/demos/provider-mock/tsconfig.json index 23c30b5689..e02a2fad42 100644 --- a/demos/provider-mock/tsconfig.json +++ b/demos/provider-mock/tsconfig.json @@ -8,10 +8,10 @@ "include": ["src", "src/**/*.json"], "references": [ { - "path": "../../packages/provider" + "path": "../../packages/common" }, { - "path": "../../packages/common" + "path": "../../packages/provider" }, { "path": "../../packages/types" diff --git a/dev/config/package.json b/dev/config/package.json index 098f2038c2..3f8d9af11a 100644 --- a/dev/config/package.json +++ b/dev/config/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/config", - "version": "2.1.3", + "version": "2.1.4", "description": "Prosopo config library", "main": "./dist/index.js", "type": "module", @@ -36,9 +36,9 @@ "@babel/plugin-transform-react-jsx": "7.25.2", "@babel/plugin-transform-runtime": "7.25.4", "@babel/preset-env": "7.25.4", - "@prosopo/common": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "@rollup/plugin-alias": "5.1.0", "@rollup/plugin-babel": "6.0.4", "@rollup/plugin-commonjs": "26.0.1", diff --git a/dev/config/src/vite/vite.frontend.config.ts b/dev/config/src/vite/vite.frontend.config.ts index 5466a7db02..b4289d60cc 100644 --- a/dev/config/src/vite/vite.frontend.config.ts +++ b/dev/config/src/vite/vite.frontend.config.ts @@ -39,6 +39,17 @@ export default async function ( tsConfigPaths?: string[], workspaceRoot?: string, ): Promise { + // Only development and production modes are allowed + if (mode !== "production") { + mode = "development"; + } + // We don't have a default environment of, for example, `test` so we use the `mode` as the default + const allowedEnvironments = ["development", "staging", "production"]; + const defaultEnvironment = + process.env.NODE_ENV && allowedEnvironments.includes(process.env.NODE_ENV) + ? process.env.NODE_ENV + : mode; + logger.info(`Running at ${dir} in ${mode} mode`); const isProduction = mode === "production"; // NODE_ENV must be wrapped in quotes. @@ -53,9 +64,8 @@ export default async function ( "process.env.WS_NO_BUFFER_UTIL": JSON.stringify("true"), "process.env.WS_NO_UTF_8_VALIDATE": JSON.stringify("true"), "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV), - "process.env.PROSOPO_DEFAULT_ENVIRONMENT": JSON.stringify( - process.env.PROSOPO_DEFAULT_ENVIRONMENT || process.env.NODE_ENV || mode, - ), + "process.env.PROSOPO_DEFAULT_ENVIRONMENT": + JSON.stringify(defaultEnvironment), "process.env.PROSOPO_SERVER_URL": JSON.stringify( process.env.PROSOPO_SERVER_URL, ), diff --git a/dev/config/src/webpack/webpack.config.ts b/dev/config/src/webpack/webpack.config.ts index c507b9729f..2c85e79cdc 100644 --- a/dev/config/src/webpack/webpack.config.ts +++ b/dev/config/src/webpack/webpack.config.ts @@ -27,7 +27,7 @@ export default (mode: string) => { resolve: { extensions: [".js", ".jsx", ".ts", ".tsx"], extensionAlias: { - ".js": [".js", ".ts"], + ".js": [".js", ".jsx", ".ts", ".tsx"], }, modules: moduleDirs, fullySpecified: false, diff --git a/dev/flux/package.json b/dev/flux/package.json index b03f271d25..e0e3a772aa 100644 --- a/dev/flux/package.json +++ b/dev/flux/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/flux", - "version": "2.1.3", + "version": "2.1.4", "description": "Tools for managing Flux deployment", "main": "dist/index.js", "bin": { @@ -24,10 +24,10 @@ "@noble/curves": "1.6.0", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/config": "2.1.3", - "@prosopo/dotenv": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/config": "2.1.4", + "@prosopo/dotenv": "2.1.4", + "@prosopo/util": "2.1.4", "openpgp": "5.11.2", "qs": "6.13.0", "socket.io-client": "4.7.5", diff --git a/dev/gh-actions/package.json b/dev/gh-actions/package.json index 6adc5f41d5..60ff9774b9 100644 --- a/dev/gh-actions/package.json +++ b/dev/gh-actions/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/gh-actions", - "version": "2.1.3", + "version": "2.1.4", "description": "", "private": true, "scripts": { diff --git a/dev/lint/package.json b/dev/lint/package.json index ea4d91ebf7..c15f79d759 100644 --- a/dev/lint/package.json +++ b/dev/lint/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/lint", - "version": "2.1.3", + "version": "2.1.4", "description": "", "private": true, "scripts": { @@ -38,7 +38,7 @@ "npm": ">=9" }, "dependencies": { - "@prosopo/util": "2.1.3", + "@prosopo/util": "2.1.4", "fast-glob": "3.3.2", "zod": "3.23.8" } diff --git a/dev/prosoponator-bot/package.json b/dev/prosoponator-bot/package.json index 1d60514512..d9cc3d2e06 100644 --- a/dev/prosoponator-bot/package.json +++ b/dev/prosoponator-bot/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/prosoponator-bot", - "version": "2.1.3", + "version": "2.1.4", "description": "", "private": true, "scripts": { diff --git a/dev/scripts/env.development b/dev/scripts/env.development index 86efecee34..6c2c6adaf0 100644 --- a/dev/scripts/env.development +++ b/dev/scripts/env.development @@ -18,12 +18,13 @@ PROSOPO_DEFAULT_ENVIRONMENT=development PROSOPO_LOG_LEVEL=debug PROSOPO_MONGO_CAPTCHA_URI= PROSOPO_MONGO_EVENTS_URI=mongodb+srv:///frictionless_events -PROSOPO_PACKAGE_VERSION=2.1.3 +PROSOPO_PACKAGE_VERSION=2.1.4 PROSOPO_PROVIDER_ACCOUNT_PASSWORD= PROSOPO_PROVIDER_ADDRESS=5EjTA28bKSbFPPyMbUjNtArxyqjwq38r1BapVmLZShaqEedV PROSOPO_PROVIDER_JSON= PROSOPO_PROVIDER_MNEMONIC=puppy cream effort carbon despair leg pyramid cotton endorse immense drill peasant PROSOPO_PROXY_COUNT= +PROSOPO_SERVER_URL=http://localhost:9228 PROSOPO_SITE_KEY=5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw WATCHTOWER_LABEL_ENABLE=true WATCHTOWER_LOG_FORMAT=JSON @@ -31,3 +32,4 @@ WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL= WATCHTOWER_NOTIFICATIONS= WATCHTOWER_POLL_INTERVAL=30 WATCHTOWER_REMOVE_VOLUMES=true + diff --git a/dev/scripts/env.production b/dev/scripts/env.production index f3af20c016..ced0c46c17 100644 --- a/dev/scripts/env.production +++ b/dev/scripts/env.production @@ -16,13 +16,14 @@ PROSOPO_DEFAULT_ENVIRONMENT=production PROSOPO_LOG_LEVEL=info PROSOPO_MONGO_CAPTCHA_URI= PROSOPO_MONGO_EVENTS_URI=mongodb+srv:///frictionless_events -PROSOPO_PACKAGE_VERSION=2.1.3 +PROSOPO_PACKAGE_VERSION=2.1.4 PROSOPO_PROVIDER_ACCOUNT_PASSWORD= PROSOPO_PROVIDER_ADDRESS= PROSOPO_PROVIDER_JSON= PROSOPO_PROVIDER_MNEMONIC= PROSOPO_PROXY_COUNT= PROSOPO_SITE_KEY= +PROSOPO_SERVER_URL= WATCHTOWER_LABEL_ENABLE=true WATCHTOWER_LOG_FORMAT=JSON WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL= diff --git a/dev/scripts/env.staging b/dev/scripts/env.staging index 4172aa73e3..d7f0a45686 100644 --- a/dev/scripts/env.staging +++ b/dev/scripts/env.staging @@ -21,13 +21,14 @@ PROSOPO_DEFAULT_ENVIRONMENT=staging PROSOPO_LOG_LEVEL=info PROSOPO_MONGO_CAPTCHA_URI= PROSOPO_MONGO_EVENTS_URI=mongodb+srv:///frictionless_events -PROSOPO_PACKAGE_VERSION=2.1.3 +PROSOPO_PACKAGE_VERSION=2.1.4 PROSOPO_PROVIDER_ACCOUNT_PASSWORD= PROSOPO_PROVIDER_ADDRESS= PROSOPO_PROVIDER_JSON= PROSOPO_PROVIDER_MNEMONIC= PROSOPO_PROXY_COUNT= PROSOPO_SITE_KEY= +PROSOPO_SERVER_URL= WATCHTOWER_LABEL_ENABLE=true WATCHTOWER_LOG_FORMAT=JSON WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL= diff --git a/dev/scripts/env.test b/dev/scripts/env.test index 7acd6e8efd..5b7f9aa724 100644 --- a/dev/scripts/env.test +++ b/dev/scripts/env.test @@ -21,12 +21,13 @@ PROSOPO_DEFAULT_ENVIRONMENT=development PROSOPO_LOG_LEVEL=info PROSOPO_MONGO_CAPTCHA_URI=mongodb://root:root@localhost:27017/captchastorage?authSource=admin PROSOPO_MONGO_EVENTS_URI=mongodb+srv:///frictionless_events -PROSOPO_PACKAGE_VERSION=2.1.3 +PROSOPO_PACKAGE_VERSION=2.1.4 PROSOPO_PROVIDER_ACCOUNT_PASSWORD= PROSOPO_PROVIDER_ADDRESS=5EjTA28bKSbFPPyMbUjNtArxyqjwq38r1BapVmLZShaqEedV PROSOPO_PROVIDER_JSON= PROSOPO_PROVIDER_MNEMONIC=puppy cream effort carbon despair leg pyramid cotton endorse immense drill peasant PROSOPO_PROXY_COUNT= +PROSOPO_SERVER_URL=http://localhost:9228 PROSOPO_SITE_KEY=5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw WATCHTOWER_LABEL_ENABLE=true WATCHTOWER_LOG_FORMAT=JSON diff --git a/dev/scripts/package.json b/dev/scripts/package.json index bb311ec1d5..543e580b95 100644 --- a/dev/scripts/package.json +++ b/dev/scripts/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/scripts", - "version": "2.1.3", + "version": "2.1.4", "description": "Dev scripts for working with prosopo packages", "main": "dist/index.js", "type": "module", @@ -26,18 +26,18 @@ "@polkadot/keyring": "12.6.2", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/cli": "2.1.3", - "@prosopo/common": "2.1.3", - "@prosopo/config": "2.1.3", - "@prosopo/contract": "2.1.3", - "@prosopo/datasets": "2.1.3", - "@prosopo/dotenv": "2.1.3", - "@prosopo/env": "2.1.3", - "@prosopo/provider": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/types-database": "2.1.3", - "@prosopo/types-env": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/cli": "2.1.4", + "@prosopo/common": "2.1.4", + "@prosopo/config": "2.1.4", + "@prosopo/contract": "2.1.4", + "@prosopo/datasets": "2.1.4", + "@prosopo/dotenv": "2.1.4", + "@prosopo/env": "2.1.4", + "@prosopo/provider": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/types-database": "2.1.4", + "@prosopo/types-env": "2.1.4", + "@prosopo/util": "2.1.4", "dotenv": "16.4.5", "fast-glob": "3.3.2", "fs-extra": "11.2.0", diff --git a/dev/scripts/src/setup/dapp.ts b/dev/scripts/src/setup/dapp.ts new file mode 100644 index 0000000000..7c10a020e7 --- /dev/null +++ b/dev/scripts/src/setup/dapp.ts @@ -0,0 +1,26 @@ +// Copyright 2021-2024 Prosopo (UK) 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. + +import type { ProviderEnvironment } from "@prosopo/env"; +import { Tasks } from "@prosopo/provider"; + +export async function registerSiteKey( + env: ProviderEnvironment, + siteKey: string, +): Promise { + const logger = env.logger; + const tasks = new Tasks(env); + logger.info(" - siteKeyRegister"); + await tasks.clientTaskManager.registerSiteKey(siteKey as string); +} diff --git a/dev/scripts/src/setup/setup.ts b/dev/scripts/src/setup/setup.ts index 95d3a36cd0..2c5eb2ffd1 100644 --- a/dev/scripts/src/setup/setup.ts +++ b/dev/scripts/src/setup/setup.ts @@ -14,7 +14,6 @@ import path from "node:path"; import { BN } from "@polkadot/util"; -import { isAddress } from "@polkadot/util-crypto"; import { defaultConfig, getSecret } from "@prosopo/cli"; import { LogLevel, ProsopoEnvError, getLogger } from "@prosopo/common"; import { generateMnemonic, getPairAsync } from "@prosopo/contract"; @@ -28,6 +27,7 @@ import { import { get } from "@prosopo/util"; import fse from "fs-extra"; import { updateDemoHTMLFiles, updateEnvFiles } from "../util/index.js"; +import { registerSiteKey } from "./dapp.js"; import { setupProvider } from "./provider.js"; const logger = getLogger(LogLevel.enum.info, "setup"); @@ -147,6 +147,8 @@ export async function setup(force: boolean) { env.logger.info(`Registering dapp... ${defaultDapp.pair.address}`); + await registerSiteKey(env, defaultDapp.pair.address); + if (!hasProviderAccount) { await updateEnvFile({ PROVIDER_MNEMONIC: `"${mnemonic}"`, diff --git a/dev/ts-brand/package.json b/dev/ts-brand/package.json index e1170c64ec..189fc5ae74 100644 --- a/dev/ts-brand/package.json +++ b/dev/ts-brand/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/ts-brand", - "version": "2.1.3", + "version": "2.1.4", "description": "Brand your TypeScript types", "main": "./dist/index.js", "type": "module", @@ -24,7 +24,7 @@ "author": "Prosopo Limited", "license": "Apache-2.0", "dependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "dotenv": "16.4.5" }, "devDependencies": { diff --git a/dev/vite-plugin-watch-workspace/package.json b/dev/vite-plugin-watch-workspace/package.json index 85859d47da..45dd8532dd 100644 --- a/dev/vite-plugin-watch-workspace/package.json +++ b/dev/vite-plugin-watch-workspace/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/vite-plugin-watch-workspace", - "version": "2.1.3", + "version": "2.1.4", "description": "Vite plugin for watching and rebuilding external files", "main": "./dist/index.js", "type": "module", @@ -23,7 +23,7 @@ "author": "Prosopo Limited", "license": "Apache-2.0", "dependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "debug": "4.3.7", "esbuild": "0.23.1", "fast-glob": "3.3.2" diff --git a/docker/images/provider/package.json b/docker/images/provider/package.json index 33ec86ef62..72e5fae1bd 100644 --- a/docker/images/provider/package.json +++ b/docker/images/provider/package.json @@ -5,5 +5,5 @@ "node": ">=20", "npm": ">=9" }, - "version": "2.1.3" + "version": "2.1.4" } diff --git a/package-lock.json b/package-lock.json index d319240d7a..ed5bd212a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@prosopo/captcha", - "version": "2.1.3", + "version": "2.1.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@prosopo/captcha", - "version": "2.1.3", + "version": "2.1.4", "hasInstallScript": true, "license": "Apache-2.0", "workspaces": [ @@ -40,8 +40,9 @@ }, "demos/client-bundle-example": { "name": "@prosopo/client-bundle-example", - "version": "2.1.3", + "version": "2.1.4", "devDependencies": { + "@prosopo/dotenv": "2.1.4", "@types/node": "22.5.5", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", @@ -60,21 +61,21 @@ }, "demos/client-example": { "name": "@prosopo/client-example", - "version": "2.1.3", + "version": "2.1.4", "dependencies": { "@emotion/react": "11.13.3", "@emotion/styled": "11.13.0", "@mui/material": "5.9.1", "@polkadot/extension-dapp": "0.46.9", "@polkadot/extension-inject": "0.46.9", - "@prosopo/common": "2.1.3", - "@prosopo/locale-browser": "2.1.3", - "@prosopo/procaptcha-frictionless": "2.1.3", - "@prosopo/procaptcha-pow": "2.1.3", - "@prosopo/procaptcha-react": "2.1.3", - "@prosopo/server": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/vite-plugin-watch-workspace": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/locale-browser": "2.1.4", + "@prosopo/procaptcha-frictionless": "2.1.4", + "@prosopo/procaptcha-pow": "2.1.4", + "@prosopo/procaptcha-react": "2.1.4", + "@prosopo/server": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/vite-plugin-watch-workspace": "2.1.4", "@types/react-dom": "18.3.0", "@vitejs/plugin-react": "4.3.1", "react": "18.3.1", @@ -82,8 +83,8 @@ "react-router-dom": "6.26.2" }, "devDependencies": { - "@prosopo/config": "2.1.3", - "@prosopo/dotenv": "2.1.3", + "@prosopo/config": "2.1.4", + "@prosopo/dotenv": "2.1.4", "@types/node": "22.5.5", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", @@ -102,18 +103,18 @@ }, "demos/client-example-server": { "name": "@prosopo/client-example-server", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@noble/hashes": "1.5.0", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/contract": "2.1.3", - "@prosopo/dotenv": "2.1.3", - "@prosopo/server": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/contract": "2.1.4", + "@prosopo/dotenv": "2.1.4", + "@prosopo/server": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "@typegoose/auto-increment": "4.6.0", "cors": "2.8.5", "express": "4.21.0", @@ -123,7 +124,7 @@ "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@types/jsonwebtoken": "9.0.6", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", @@ -142,16 +143,16 @@ }, "demos/cypress-shared": { "name": "@prosopo/cypress-shared", - "version": "2.1.3", + "version": "2.1.4", "dependencies": { - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3" + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4" }, "devDependencies": { "@cypress/xpath": "2.0.3", - "@prosopo/common": "2.1.3", - "@prosopo/datasets": "2.1.3", - "@prosopo/types-database": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/datasets": "2.1.4", + "@prosopo/types-database": "2.1.4", "@types/node": "22.5.5", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", @@ -173,12 +174,12 @@ }, "demos/provider-mock": { "name": "@prosopo/provider-mock", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { - "@prosopo/common": "2.1.3", - "@prosopo/provider": "2.1.3", - "@prosopo/types": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/provider": "2.1.4", + "@prosopo/types": "2.1.4", "cors": "2.8.5", "express": "4.21.0" }, @@ -200,7 +201,7 @@ }, "dev/config": { "name": "@prosopo/config", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@babel/core": "7.25.2", @@ -208,9 +209,9 @@ "@babel/plugin-transform-react-jsx": "7.25.2", "@babel/plugin-transform-runtime": "7.25.4", "@babel/preset-env": "7.25.4", - "@prosopo/common": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "@rollup/plugin-alias": "5.1.0", "@rollup/plugin-babel": "6.0.4", "@rollup/plugin-commonjs": "26.0.1", @@ -266,16 +267,16 @@ }, "dev/flux": { "name": "@prosopo/flux", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@noble/curves": "1.6.0", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/config": "2.1.3", - "@prosopo/dotenv": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/config": "2.1.4", + "@prosopo/dotenv": "2.1.4", + "@prosopo/util": "2.1.4", "openpgp": "5.11.2", "qs": "6.13.0", "socket.io-client": "4.7.5", @@ -304,7 +305,7 @@ }, "dev/gh-actions": { "name": "@prosopo/gh-actions", - "version": "2.1.3", + "version": "2.1.4", "license": "ISC", "dependencies": { "@octokit/graphql": "8.1.1", @@ -328,10 +329,10 @@ }, "dev/lint": { "name": "@prosopo/lint", - "version": "2.1.3", + "version": "2.1.4", "license": "ISC", "dependencies": { - "@prosopo/util": "2.1.3", + "@prosopo/util": "2.1.4", "fast-glob": "3.3.2", "zod": "3.23.8" }, @@ -354,7 +355,7 @@ }, "dev/prosoponator-bot": { "name": "@prosopo/prosoponator-bot", - "version": "2.1.3", + "version": "2.1.4", "license": "ISC", "dependencies": { "@actions/core": "1.10.1", @@ -378,7 +379,7 @@ }, "dev/scripts": { "name": "@prosopo/scripts", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@iarna/toml": "2.2.5", @@ -387,18 +388,18 @@ "@polkadot/keyring": "12.6.2", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/cli": "2.1.3", - "@prosopo/common": "2.1.3", - "@prosopo/config": "2.1.3", - "@prosopo/contract": "2.1.3", - "@prosopo/datasets": "2.1.3", - "@prosopo/dotenv": "2.1.3", - "@prosopo/env": "2.1.3", - "@prosopo/provider": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/types-database": "2.1.3", - "@prosopo/types-env": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/cli": "2.1.4", + "@prosopo/common": "2.1.4", + "@prosopo/config": "2.1.4", + "@prosopo/contract": "2.1.4", + "@prosopo/datasets": "2.1.4", + "@prosopo/dotenv": "2.1.4", + "@prosopo/env": "2.1.4", + "@prosopo/provider": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/types-database": "2.1.4", + "@prosopo/types-env": "2.1.4", + "@prosopo/util": "2.1.4", "dotenv": "16.4.5", "fast-glob": "3.3.2", "fs-extra": "11.2.0", @@ -451,10 +452,10 @@ }, "dev/ts-brand": { "name": "@prosopo/ts-brand", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "dotenv": "16.4.5" }, "devDependencies": { @@ -475,10 +476,10 @@ }, "dev/vite-plugin-watch-workspace": { "name": "@prosopo/vite-plugin-watch-workspace", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "debug": "4.3.7", "esbuild": "0.23.1", "fast-glob": "3.3.2" @@ -19287,7 +19288,7 @@ }, "packages/account": { "name": "@prosopo/account", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@polkadot/extension-base": "0.46.9", @@ -19296,14 +19297,14 @@ "@polkadot/keyring": "12.6.2", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/detector": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/detector": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "react": "18.3.1" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", @@ -19321,13 +19322,13 @@ }, "packages/api": { "name": "@prosopo/api", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { - "@prosopo/types": "2.1.3" + "@prosopo/types": "2.1.4" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", @@ -19345,21 +19346,21 @@ }, "packages/cli": { "name": "@prosopo/cli", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@polkadot/keyring": "12.6.2", "@polkadot/types-codec": "10.13.1", "@polkadot/util-crypto": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/config": "2.1.3", - "@prosopo/contract": "2.1.3", - "@prosopo/dotenv": "2.1.3", - "@prosopo/env": "2.1.3", - "@prosopo/locale": "2.1.3", - "@prosopo/provider": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/config": "2.1.4", + "@prosopo/contract": "2.1.4", + "@prosopo/dotenv": "2.1.4", + "@prosopo/env": "2.1.4", + "@prosopo/locale": "2.1.4", + "@prosopo/provider": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "cors": "2.8.5", "cron-parser": "4.9.0", "dotenv": "16.4.5", @@ -19368,7 +19369,7 @@ "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@types/cors": "2.8.17", "@types/yargs": "17.0.33", "@vitest/coverage-v8": "2.1.1", @@ -19389,16 +19390,16 @@ }, "packages/common": { "name": "@prosopo/common", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@polkadot/util-crypto": "12.6.2", - "@prosopo/locale": "2.1.3", + "@prosopo/locale": "2.1.4", "consola": "3.2.3", "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "dotenv": "16.4.5", @@ -19417,7 +19418,7 @@ }, "packages/contract": { "name": "@prosopo/contract", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@polkadot/api": "10.13.1", @@ -19427,15 +19428,15 @@ "@polkadot/types-codec": "10.13.1", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/tx": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/tx": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "rxjs": "7.8.1" }, "devDependencies": { "@polkadot/api-augment": "10.13.1", - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", @@ -19480,13 +19481,13 @@ }, "packages/database": { "name": "@prosopo/database", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { - "@prosopo/common": "2.1.3", - "@prosopo/config": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/types-database": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/config": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/types-database": "2.1.4", "mongodb": "6.9.0", "mongodb-memory-server": "10.0.0", "mongoose": "8.6.2" @@ -19509,16 +19510,16 @@ }, "packages/datasets": { "name": "@prosopo/datasets", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@polkadot/util": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3" + "@prosopo/common": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "dotenv": "16.4.5", @@ -19537,15 +19538,15 @@ }, "packages/datasets-fs": { "name": "@prosopo/datasets-fs", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@noble/hashes": "1.5.0", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "bcrypt": "5.1.1", "cli-progress": "3.12.0", "sharp": "0.33.5", @@ -19553,7 +19554,7 @@ "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@types/bcrypt": "5.0.2", "@types/cli-progress": "3.11.6", "@vitest/coverage-v8": "2.1.1", @@ -19574,7 +19575,7 @@ }, "packages/detector": { "name": "@prosopo/detector", - "version": "2.1.3", + "version": "2.1.4", "devDependencies": { "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", @@ -19593,11 +19594,11 @@ }, "packages/dotenv": { "name": "@prosopo/dotenv", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { - "@prosopo/common": "2.1.3", - "@prosopo/config": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/config": "2.1.4", "dotenv": "16.4.5" }, "devDependencies": { @@ -19618,21 +19619,21 @@ }, "packages/env": { "name": "@prosopo/env", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@polkadot/keyring": "12.6.2", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/database": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/types-database": "2.1.3", - "@prosopo/types-env": "2.1.3", - "@prosopo/util": "2.1.3" + "@prosopo/common": "2.1.4", + "@prosopo/database": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/types-database": "2.1.4", + "@prosopo/types-env": "2.1.4", + "@prosopo/util": "2.1.4" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", @@ -19650,17 +19651,17 @@ }, "packages/file-server": { "name": "@prosopo/file-server", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { - "@prosopo/util": "2.1.3", + "@prosopo/util": "2.1.4", "dotenv": "16.4.5", "express": "4.21.0", "node-fetch": "3.3.2", "sharp": "0.33.5" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@types/express": "4.17.21", "@types/node": "22.5.5", "@vitest/coverage-v8": "2.1.1", @@ -19697,12 +19698,12 @@ }, "packages/load-balancer": { "name": "@prosopo/load-balancer", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { - "@prosopo/common": "2.1.3", - "@prosopo/config": "2.1.3", - "@prosopo/types": "2.1.3" + "@prosopo/common": "2.1.4", + "@prosopo/config": "2.1.4", + "@prosopo/types": "2.1.4" }, "devDependencies": { "@vitest/coverage-v8": "2.1.1", @@ -19722,7 +19723,7 @@ }, "packages/locale": { "name": "@prosopo/locale", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "i18next": "21.9.2", @@ -19731,7 +19732,7 @@ "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "dotenv": "16.4.5", "npm-run-all": "4.1.5", @@ -19749,16 +19750,16 @@ }, "packages/locale-browser": { "name": "@prosopo/locale-browser", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { - "@prosopo/locale": "2.1.3", + "@prosopo/locale": "2.1.4", "i18next-browser-languagedetector": "7.2.1", "react-i18next": "11.18.6", "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "dotenv": "16.4.5", "npm-run-all": "4.1.5", @@ -19776,25 +19777,25 @@ }, "packages/procaptcha": { "name": "@prosopo/procaptcha", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@polkadot/api": "10.13.1", "@polkadot/api-contract": "10.13.1", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/account": "2.1.3", - "@prosopo/api": "2.1.3", - "@prosopo/common": "2.1.3", - "@prosopo/datasets": "2.1.3", - "@prosopo/load-balancer": "2.1.3", - "@prosopo/procaptcha-common": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/account": "2.1.4", + "@prosopo/api": "2.1.4", + "@prosopo/common": "2.1.4", + "@prosopo/datasets": "2.1.4", + "@prosopo/load-balancer": "2.1.4", + "@prosopo/procaptcha-common": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "jsdom": "25.0.0" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "dotenv": "16.4.5", @@ -19813,21 +19814,21 @@ }, "packages/procaptcha-bundle": { "name": "@prosopo/procaptcha-bundle", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { - "@prosopo/dotenv": "2.1.3", - "@prosopo/locale": "2.1.3", - "@prosopo/procaptcha-frictionless": "2.1.3", - "@prosopo/procaptcha-pow": "2.1.3", - "@prosopo/procaptcha-react": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/dotenv": "2.1.4", + "@prosopo/locale": "2.1.4", + "@prosopo/procaptcha-frictionless": "2.1.4", + "@prosopo/procaptcha-pow": "2.1.4", + "@prosopo/procaptcha-react": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "react": "18.3.1", "react-dom": "18.3.1" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@types/jsdom": "21.1.7", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", @@ -19846,16 +19847,16 @@ }, "packages/procaptcha-common": { "name": "@prosopo/procaptcha-common", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { - "@prosopo/common": "2.1.3", - "@prosopo/load-balancer": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3" + "@prosopo/common": "2.1.4", + "@prosopo/load-balancer": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", @@ -19873,19 +19874,19 @@ }, "packages/procaptcha-frictionless": { "name": "@prosopo/procaptcha-frictionless", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { - "@prosopo/detector": "2.1.3", - "@prosopo/locale-browser": "2.1.3", - "@prosopo/procaptcha-pow": "2.1.3", - "@prosopo/procaptcha-react": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/web-components": "2.1.3", + "@prosopo/detector": "2.1.4", + "@prosopo/locale-browser": "2.1.4", + "@prosopo/procaptcha-pow": "2.1.4", + "@prosopo/procaptcha-react": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/web-components": "2.1.4", "react": "18.3.1" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", @@ -19903,23 +19904,23 @@ }, "packages/procaptcha-pow": { "name": "@prosopo/procaptcha-pow", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@polkadot/util": "12.6.2", - "@prosopo/account": "2.1.3", - "@prosopo/api": "2.1.3", - "@prosopo/common": "2.1.3", - "@prosopo/locale-browser": "2.1.3", - "@prosopo/procaptcha": "2.1.3", - "@prosopo/procaptcha-common": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", - "@prosopo/web-components": "2.1.3", + "@prosopo/account": "2.1.4", + "@prosopo/api": "2.1.4", + "@prosopo/common": "2.1.4", + "@prosopo/locale-browser": "2.1.4", + "@prosopo/procaptcha": "2.1.4", + "@prosopo/procaptcha-common": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", + "@prosopo/web-components": "2.1.4", "react": "18.3.1" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", @@ -19937,21 +19938,21 @@ }, "packages/procaptcha-react": { "name": "@prosopo/procaptcha-react", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { - "@prosopo/common": "2.1.3", - "@prosopo/locale-browser": "2.1.3", - "@prosopo/procaptcha": "2.1.3", - "@prosopo/procaptcha-common": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", - "@prosopo/web-components": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/locale-browser": "2.1.4", + "@prosopo/procaptcha": "2.1.4", + "@prosopo/procaptcha-common": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", + "@prosopo/web-components": "2.1.4", "csstype": "3.1.3", "react": "18.3.1" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", @@ -19996,23 +19997,23 @@ }, "packages/provider": { "name": "@prosopo/provider", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@noble/hashes": "1.5.0", "@polkadot/keyring": "12.6.2", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/config": "2.1.3", - "@prosopo/contract": "2.1.3", - "@prosopo/database": "2.1.3", - "@prosopo/datasets": "2.1.3", - "@prosopo/env": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/types-database": "2.1.3", - "@prosopo/types-env": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/config": "2.1.4", + "@prosopo/contract": "2.1.4", + "@prosopo/database": "2.1.4", + "@prosopo/datasets": "2.1.4", + "@prosopo/env": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/types-database": "2.1.4", + "@prosopo/types-env": "2.1.4", + "@prosopo/util": "2.1.4", "cron": "3.1.7", "express": "4.21.0", "node-fetch": "3.3.2", @@ -20055,18 +20056,18 @@ }, "packages/server": { "name": "@prosopo/server", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@polkadot/keyring": "12.6.2", "@polkadot/util": "12.6.2", - "@prosopo/api": "2.1.3", - "@prosopo/common": "2.1.3", - "@prosopo/contract": "2.1.3", - "@prosopo/types": "2.1.3" + "@prosopo/api": "2.1.4", + "@prosopo/common": "2.1.4", + "@prosopo/contract": "2.1.4", + "@prosopo/types": "2.1.4" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", @@ -20084,7 +20085,7 @@ }, "packages/tx": { "name": "@prosopo/tx", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@polkadot/api": "10.13.1", @@ -20094,11 +20095,11 @@ "@polkadot/types": "10.13.1", "@polkadot/types-codec": "10.13.1", "@polkadot/util": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/types": "2.1.3" + "@prosopo/common": "2.1.4", + "@prosopo/types": "2.1.4" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", @@ -20143,7 +20144,7 @@ }, "packages/types": { "name": "@prosopo/types", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@polkadot/api": "10.13.1", @@ -20153,13 +20154,13 @@ "@polkadot/types": "10.13.1", "@polkadot/types-codec": "10.13.1", "@polkadot/util": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/locale": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/locale": "2.1.4", "scale-ts": "1.6.0", "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@types/node": "22.5.5", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", @@ -20178,17 +20179,17 @@ }, "packages/types-database": { "name": "@prosopo/types-database", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { - "@prosopo/common": "2.1.3", - "@prosopo/types": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/types": "2.1.4", "mongodb": "6.9.0", "mongoose": "8.6.2", "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", @@ -20206,16 +20207,16 @@ }, "packages/types-env": { "name": "@prosopo/types-env", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@polkadot/keyring": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/types-database": "2.1.3" + "@prosopo/common": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/types-database": "2.1.4" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", @@ -20260,11 +20261,11 @@ }, "packages/util": { "name": "@prosopo/util", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@noble/hashes": "1.5.0", - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "dotenv": "16.4.5", "lodash": "4.17.21", "seedrandom": "3.0.5" @@ -20289,7 +20290,7 @@ }, "packages/web-components": { "name": "@prosopo/web-components", - "version": "2.1.3", + "version": "2.1.4", "license": "Apache-2.0", "dependencies": { "@emotion/react": "11.13.3", @@ -20297,7 +20298,7 @@ "react": "18.3.1" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", diff --git a/package.json b/package.json index fd99de3829..ae73ff0248 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/captcha", - "version": "2.1.3", + "version": "2.1.4", "author": "Prosopo", "type": "module", "repository": { diff --git a/packages/account/package.json b/packages/account/package.json index fc87c2e3be..2d39fa65bf 100644 --- a/packages/account/package.json +++ b/packages/account/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/account", - "version": "2.1.3", + "version": "2.1.4", "description": "Services and Utils for Prosopo account gen and management", "main": "dist/index.js", "type": "module", @@ -37,14 +37,14 @@ "@polkadot/keyring": "12.6.2", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/detector": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/detector": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "react": "18.3.1" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", diff --git a/packages/api/package.json b/packages/api/package.json index ce3d8ecae7..fc7ea2a367 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/api", - "version": "2.1.3", + "version": "2.1.4", "description": "Wrapper for the provider API", "main": "dist/index.js", "type": "module", @@ -31,10 +31,10 @@ }, "homepage": "https://github.com/prosopo/captcha#readme", "dependencies": { - "@prosopo/types": "2.1.3" + "@prosopo/types": "2.1.4" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", diff --git a/packages/api/src/api/HttpClientBase.ts b/packages/api/src/api/HttpClientBase.ts index c6f66ff6e3..57031dc52c 100644 --- a/packages/api/src/api/HttpClientBase.ts +++ b/packages/api/src/api/HttpClientBase.ts @@ -23,7 +23,12 @@ export class HttpClientBase { protected async fetch(input: RequestInfo, init?: RequestInit): Promise { try { const response = await fetch(this.baseURL + input, init); - if (!response.ok) { + if ( + !response.ok && + // Only throw an error if the response is not JSON and not a 400 error + response.status !== 400 && + !response.headers.get("content-type")?.includes("application/json") + ) { throw new HttpError(response.status, response.statusText, response.url); } return this.responseHandler(response); @@ -48,7 +53,12 @@ export class HttpClientBase { headers, ...init, }); - if (!response.ok) { + if ( + !response.ok && + // Only throw an error if the response is not JSON and not a 400 error + response.status !== 400 && + !response.headers.get("content-type")?.includes("application/json") + ) { throw new HttpError(response.status, response.statusText, response.url); } return this.responseHandler(response); diff --git a/packages/api/src/api/ProviderApi.ts b/packages/api/src/api/ProviderApi.ts index 874336ffe0..1e9e00abcb 100644 --- a/packages/api/src/api/ProviderApi.ts +++ b/packages/api/src/api/ProviderApi.ts @@ -16,7 +16,6 @@ import { ApiPaths, type CaptchaResponseBody, type CaptchaSolution, - CaptchaSolutionBody, type CaptchaSolutionBodyType, type CaptchaSolutionResponse, type GetPowCaptchaChallengeRequestBodyType, @@ -69,7 +68,7 @@ export default class ProviderApi userAccount: string, timestamp: string, providerRequestHashSignature: string, - userRequestHashSignature: string, + userTimestampSignature: string, ): Promise { const body: CaptchaSolutionBodyType = { [ApiParams.user]: userAccount, @@ -79,7 +78,7 @@ export default class ProviderApi [ApiParams.timestamp]: timestamp, [ApiParams.signature]: { [ApiParams.user]: { - [ApiParams.requestHash]: userRequestHashSignature, + [ApiParams.timestamp]: userTimestampSignature, }, [ApiParams.provider]: { [ApiParams.requestHash]: providerRequestHashSignature, @@ -105,20 +104,6 @@ export default class ProviderApi return this.post(ApiPaths.VerifyImageCaptchaSolutionDapp, payload); } - public verifyUser( - token: ProcaptchaToken, - dappUserSignature: string, - maxVerifiedTime?: number, - ): Promise { - const payload: VerifySolutionBodyTypeInput = { - [ApiParams.token]: token, - [ApiParams.dappSignature]: dappUserSignature, - ...(maxVerifiedTime && { [ApiParams.maxVerifiedTime]: maxVerifiedTime }), - }; - - return this.post(ApiPaths.VerifyImageCaptchaSolutionUser, payload); - } - public getPowCaptchaChallenge( user: string, dapp: string, diff --git a/packages/cli/package.json b/packages/cli/package.json index 856c0ff1d3..c1d3ee3f6a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/cli", - "version": "2.1.3", + "version": "2.1.4", "description": "CLI for Prosopo Provider", "main": "dist/index.js", "type": "module", @@ -29,15 +29,15 @@ "@polkadot/keyring": "12.6.2", "@polkadot/types-codec": "10.13.1", "@polkadot/util-crypto": "12.6.2", - "@prosopo/config": "2.1.3", - "@prosopo/common": "2.1.3", - "@prosopo/contract": "2.1.3", - "@prosopo/dotenv": "2.1.3", - "@prosopo/env": "2.1.3", - "@prosopo/locale": "2.1.3", - "@prosopo/provider": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/config": "2.1.4", + "@prosopo/common": "2.1.4", + "@prosopo/contract": "2.1.4", + "@prosopo/dotenv": "2.1.4", + "@prosopo/env": "2.1.4", + "@prosopo/locale": "2.1.4", + "@prosopo/provider": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "cors": "2.8.5", "cron-parser": "4.9.0", "dotenv": "16.4.5", @@ -46,7 +46,7 @@ "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@types/cors": "2.8.17", "@types/yargs": "17.0.33", "@vitest/coverage-v8": "2.1.1", diff --git a/packages/cli/src/RateLimiter.ts b/packages/cli/src/RateLimiter.ts index 8b2ca1b526..9fac9433a3 100644 --- a/packages/cli/src/RateLimiter.ts +++ b/packages/cli/src/RateLimiter.ts @@ -39,10 +39,6 @@ export const getRateLimitConfig = () => { windowMs: process.env.PROSOPO_VERIFY_IMAGE_CAPTCHA_SOLUTION_DAPP_WINDOW, limit: process.env.PROSOPO_VERIFY_IMAGE_CAPTCHA_SOLUTION_DAPP_LIMIT, }, - [ApiPaths.VerifyImageCaptchaSolutionUser]: { - windowMs: process.env.PROSOPO_VERIFY_IMAGE_CAPTCHA_SOLUTION_USER_WINDOW, - limit: process.env.PROSOPO_VERIFY_IMAGE_CAPTCHA_SOLUTION_USER_LIMIT, - }, [ApiPaths.GetProviderStatus]: { windowMs: process.env.PROSOPO_GET_PROVIDER_STATUS_WINDOW, limit: process.env.PROSOPO_GET_PROVIDER_STATUS_LIMIT, diff --git a/packages/cli/src/start.ts b/packages/cli/src/start.ts index e860ff1ea8..28b3dd475b 100644 --- a/packages/cli/src/start.ts +++ b/packages/cli/src/start.ts @@ -24,6 +24,7 @@ import { prosopoVerifyRouter, storeCaptchasExternally, } from "@prosopo/provider"; +import { authMiddleware } from "@prosopo/provider"; import type { CombinedApiPaths } from "@prosopo/types"; import cors from "cors"; import express from "express"; @@ -51,6 +52,7 @@ function startApi( apiApp.use(prosopoVerifyRouter(env)); if (admin) { + apiApp.use(authMiddleware(env)); apiApp.use(prosopoAdminRouter(env)); } diff --git a/packages/common/package.json b/packages/common/package.json index be1d3a0c75..405aecb69e 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/common", - "version": "2.1.3", + "version": "2.1.4", "description": "Prosopo common library", "main": "./dist/index.js", "type": "module", @@ -25,12 +25,12 @@ "license": "Apache-2.0", "dependencies": { "@polkadot/util-crypto": "12.6.2", - "@prosopo/locale": "2.1.3", + "@prosopo/locale": "2.1.4", "consola": "3.2.3", "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "dotenv": "16.4.5", diff --git a/packages/common/src/error.ts b/packages/common/src/error.ts index 362bdd2d58..05606b9704 100644 --- a/packages/common/src/error.ts +++ b/packages/common/src/error.ts @@ -1,4 +1,3 @@ -import { type TranslationKey, i18n as i18next } from "@prosopo/locale"; // Copyright 2021-2024 Prosopo (UK) Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,6 +11,8 @@ import { type TranslationKey, i18n as i18next } from "@prosopo/locale"; // WITHOUT 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 { type TranslationKey, i18n as i18next } from "@prosopo/locale"; import { type LogLevel, type Logger, getLoggerDefault } from "./index.js"; type BaseErrorOptions = { @@ -169,7 +170,13 @@ export class ProsopoApiError extends ProsopoBaseError { const optionsAll = { ...options, name: errorName, - context: { ...options?.context, code }, + context: { + ...options?.context, + code, + ...(error instanceof ProsopoBaseError && error.translationKey + ? { translationKey: error.translationKey } + : {}), + }, }; super(error, optionsAll); this.code = code; diff --git a/packages/common/src/locales/en.json b/packages/common/src/locales/en.json deleted file mode 100644 index 10af8c87a0..0000000000 --- a/packages/common/src/locales/en.json +++ /dev/null @@ -1,407 +0,0 @@ -{ - "ACCOUNT": { - "NO_POLKADOT_EXTENSION": "Polkadot extension not found" - }, - "WIDGET": { - "SELECT_ALL": "Select all containing the following", - "IF_NONE_CLICK_NEXT": "If there are none, click Next", - "NEXT": "Next", - "SUBMIT": "Submit", - "CANCEL": "Cancel", - "I_AM_HUMAN": "I am human", - "NO_ACCOUNTS_FOUND": "No accounts found", - "ACCOUNT_NOT_FOUND": "Account not found", - "NO_EXTENSION_FOUND": "No extension found" - }, - "GENERAL": { - "JSON_LOAD_FAILED": "Failed to load JSON file", - "MNEMONIC_UNDEFINED": "Mnemonic Undefined. Please set the mnemonic in environment variables", - "NO_MNEMONIC_OR_SEED": "No mnemonic or seed provided", - "CANT_FIND_KEYRINGPAIR": "Can't find the keyringpair for {{address}}", - "ENVIRONMENT_NOT_READY": "Environment not ready", - "INVALID_SIGNATURE": "Invalid signature", - "NOT_IMPLEMENTED": "Not implemented", - "SITE_KEY_MISSING": "SITE KEY missing", - "ACCOUNT_NOT_FOUND": "Account not found" - }, - "CONTRACT": { - "INVALID_METHOD": "Invalid contract method", - "TX_ERROR": "Error making tx", - "INVALID_ADDRESS": "Failed to encode invalid address", - "INVALID_STORAGE_NAME": "Failed to find given storage name", - "CAPTCHA_SOLUTION_COMMITMENT_DOES_NOT_EXIST": "Captcha solution commitment does not exist", - "CONTRACT_UNDEFINED": "Contract undefined", - "SIGNER_UNDEFINED": "Signer undefined", - "CANNOT_FIND_KEYPAIR": "Cannot find keypair", - "INTERRUPTED_EVENT": "Event interrupted", - "TOO_MANY_CALLS": "Too many calls", - "UNKNOWN_ERROR": "Unknown error", - "CHAIN_DECIMALS_UNDEFINED": "Chain decimals are not defined", - "INVALID_DATA_FORMAT": "Invalid data format", - "TX_QUEUE_ERROR": "Error in Transaction Queue", - "DISPATCH_ERROR": "Error dispatching transaction" - }, - "CONFIG": { - "UNKNOWN_ENVIRONMENT": "Unknown environment requested", - "INVALID_CAPTCHA_NUMBER": "Please configure captchas configurations correctly", - "INVALID_LOG_LEVEL": "Invalid log level", - "INVALID_PACKAGE_DIR": "Invalid package directory" - }, - "DATASET": { - "DATASET_PARSE_ERROR": "Error parsing dataset", - "SOLUTION_PARSE_ERROR": "Error parsing dataset", - "HASH_ERROR": "Error hashing dataset", - "DATASET_ID_UNDEFINED": "Dataset id undefined", - "NOT_ENOUGH_LABELS": "Not enough labels", - "NOT_ENOUGH_IMAGES": "Not enough images", - "CAPTCHAS_COUNT_LESS_THAN_CONFIGURED": "Number of captchas in dataset is less than configured number of captchas", - "SOLUTIONS_COUNT_LESS_THAN_CONFIGURED": "Number of solutions in dataset is less than configured number of solutions", - "DUPLICATE_IMAGE": "duplicate image detected", - "MERKLE_ERROR": "Error creating merkle tree" - }, - "DATABASE": { - "DATABASE_IMPORT_FAILED": "Failed to import database engine", - "DATABASE_UNDEFINED": "Database client is not connected", - "DATABASE_HOST_UNDEFINED": "Database host address is not defined", - "DATASET_LOAD_FAILED": "Data set load failed", - "DATASET_GET_FAILED": "Failed to get dataset", - "CAPTCHA_GET_FAILED": "Failed to get captcha", - "CAPTCHA_UPDATE_FAILED": "Failed to update captcha", - "IMAGE_GET_FAILED": "Failed to get image", - "PENDING_RECORD_NOT_FOUND": "No pending record found", - "INVALID_HASH": "Invalid hash", - "SOLUTION_GET_FAILED": "Failed to get solution", - "DATASET_WITH_SOLUTIONS_GET_FAILED": "No datasets found with required number of solutions", - "SOLUTION_APPROVE_FAILED": "Failed to approve solution", - "SOLUTION_FLAG_FAILED": "Failed to flag solution as processed", - "TABLES_UNDEFINED": "Tables undefined", - "CONNECTION_UNDEFINED": "Connection undefined", - "COMMITMENT_FLAG_FAILED": "Failed to flag commitment as processed" - }, - "CAPTCHA": { - "PARSE_ERROR": "Error parsing captcha", - "INVALID_CAPTCHA_ID": "Invalid captcha id", - "INVALID_ITEM_FORMAT": "Only image and text item types allowed", - "INVALID_SOLUTION_TYPE": "Invalid solution type", - "INVALID_ITEM_HASH": "Invalid item hash", - "DIFFERENT_DATASET_IDS": "Dataset ids do not match", - "INVALID_TIMESTAMP": "Invalid timestamp", - "ID_MISMATCH": "captcha id mismatch", - "MISSING_ITEM_HASH": "missing item hash", - "INVALID_CAPTCHA_CHALLENGE": "Invalid captcha challenge", - "DAPP_USER_SOLUTION_NOT_FOUND": "Dapp user solution not found", - "NO_CAPTCHA": "No captcha found", - "INVALID_TOKEN": "Invalid token", - "INVALID_SOLUTION": "Invalid solution" - }, - "API": { - "CAPTCHA_FAILED": "You answered one or more captchas incorrectly. Please try again", - "CAPTCHA_PASSED": "You correctly answered the captchas", - "BAD_REQUEST": "BadRequest", - "USER_VERIFIED": "User verified", - "USER_NOT_VERIFIED": "User not verified", - "USER_NOT_VERIFIED_TIME_EXPIRED": "User not verified. Captcha solution has expired.", - "USER_NOT_VERIFIED_NO_SOLUTION": "User not verified. No captcha solution found.", - "USER_ALREADY_VERIFIED": "This solution has already been verified. User should complete a new captcha.", - "UNKNOWN": "Unknown API error" - }, - "CLI": { - "PARAMETER_ERROR": "Invalid parameter" - }, - "DEVELOPER": { - "PROSOPO_SITE_KEY_MISSING": "PROSOPO_SITE_KEY is not set in .env file.", - "PROVIDER_NO_CAPTCHA": "No captchas returned from provider", - "MISSING_PROVIDER_PAIR": "Missing provider pair", - "MISSING_ENV_VARIABLE": "Missing environment variable", - "GENERAL": "General Dev Error, see context", - "MISSING_SECRET_KEY": "Missing secret key", - "KEY_ERROR": "Key error", - "METHOD_NOT_IMPLEMENTED": "Method not implemented" - }, - "FS": { - "FILE_NOT_FOUND": "File not found", - "FILE_ALREADY_EXISTS": "File already exists", - "INVALID_DIR_FORMAT": "Invalid directory format" - }, - "bird": "bird", - "bus": "bus", - "car": "car", - "cat": "cat", - "deer": "deer", - "dog": "dog", - "horse": "horse", - "plane": "plane", - "train": "train", - "animals": "animals", - "antelope": "antelope", - "backpack": "backpack", - "badger": "badger", - "baseball-bat": "baseball-bat", - "baseball-glove": "baseball-glove", - "basketball-hoop": "basketball-hoop", - "bat": "bat", - "bathtub": "bathtub", - "bear": "bear", - "bee": "bee", - "beer-mug": "beer-mug", - "beetle": "beetle", - "billiards": "billiards", - "binoculars": "binoculars", - "birdbath": "birdbath", - "bison": "bison", - "blimp": "blimp", - "boar": "boar", - "bonsai-tree": "bonsai-tree", - "boom-box": "boom-box", - "bowling-ball": "bowling-ball", - "bowling-pin": "bowling-pin", - "boxing-glove": "boxing-glove", - "breadmaker": "breadmaker", - "bulldozer": "bulldozer", - "butterfly": "butterfly", - "cactus": "cactus", - "cake": "cake", - "calculator": "calculator", - "camel": "camel", - "canoe": "canoe", - "car-tire": "car-tire", - "caterpillar": "caterpillar", - "cd": "cd", - "cereal-box": "cereal-box", - "chandelier": "chandelier", - "chess-board": "chess-board", - "chimpanzee": "chimpanzee", - "chopsticks": "chopsticks", - "cockroach": "cockroach", - "coffin": "coffin", - "coin": "coin", - "comet": "comet", - "computer-keyboard": "computer-keyboard", - "computer-monitor": "computer-monitor", - "computer-mouse": "computer-mouse", - "conch": "conch", - "cormorant": "cormorant", - "covered-wagon": "covered-wagon", - "cow": "cow", - "cowboy-hat": "cowboy-hat", - "coyote": "coyote", - "crab": "crab", - "crow": "crow", - "desk-globe": "desk-globe", - "diamond-ring": "diamond-ring", - "dice": "dice", - "dolphin": "dolphin", - "donkey": "donkey", - "doorknob": "doorknob", - "dragonfly": "dragonfly", - "drinking-straw": "drinking-straw", - "duck": "duck", - "dumb-bell": "dumb-bell", - "eagle": "eagle", - "eiffel-tower": "eiffel-tower", - "electric-guitar": "electric-guitar", - "elephant": "elephant", - "elk": "elk", - "eyeglasses": "eyeglasses", - "fern": "fern", - "fire-extinguisher": "fire-extinguisher", - "fire-hydrant": "fire-hydrant", - "fire-truck": "fire-truck", - "fireworks": "fireworks", - "flamingo": "flamingo", - "fly": "fly", - "fox": "fox", - "french-horn": "french-horn", - "fried-egg": "fried-egg", - "frisbee": "frisbee", - "frog": "frog", - "frying-pan": "frying-pan", - "galaxy": "galaxy", - "gas-pump": "gas-pump", - "giraffe": "giraffe", - "goat": "goat", - "golden-gate-bridge": "golden-gate-bridge", - "goldfish": "goldfish", - "golf-ball": "golf-ball", - "goose": "goose", - "gorilla": "gorilla", - "grapes": "grapes", - "grasshopper": "grasshopper", - "greyhound": "greyhound", - "guitar-pick": "guitar-pick", - "hamburger": "hamburger", - "hammock": "hammock", - "hamster": "hamster", - "hare": "hare", - "harmonica": "harmonica", - "harp": "harp", - "harpsichord": "harpsichord", - "head-phones": "head-phones", - "hedgehog": "hedgehog", - "helicopter": "helicopter", - "hibiscus": "hibiscus", - "hippopotamus": "hippopotamus", - "homer-simpson": "homer-simpson", - "hornbill": "hornbill", - "horseshoe-crab": "horseshoe-crab", - "hot-air-balloon": "hot-air-balloon", - "hot-dog": "hot-dog", - "hot-tub": "hot-tub", - "hourglass": "hourglass", - "house-fly": "house-fly", - "hummingbird": "hummingbird", - "hyena": "hyena", - "ice-cream-cone": "ice-cream-cone", - "iguana": "iguana", - "ipod": "ipod", - "jellyfish": "jellyfish", - "joy-stick": "joy-stick", - "kangaroo": "kangaroo", - "kayak": "kayak", - "killer-whale": "killer-whale", - "koala": "koala", - "ladder": "ladder", - "ladybugs": "ladybugs", - "laptop": "laptop", - "leopard": "leopard", - "license-plate": "license-plate", - "light-house": "light-house", - "lightbulb": "lightbulb", - "lightning": "lightning", - "lion": "lion", - "lizard": "lizard", - "llama": "llama", - "lobster": "lobster", - "mailbox": "mailbox", - "mandolin": "mandolin", - "mars": "mars", - "mattress": "mattress", - "megaphone": "megaphone", - "microscope": "microscope", - "microwave": "microwave", - "minaret": "minaret", - "minotaur": "minotaur", - "mosquito": "mosquito", - "moth": "moth", - "motorbikes": "motorbikes", - "mountain-bike": "mountain-bike", - "mouse": "mouse", - "mushroom": "mushroom", - "mussels": "mussels", - "necktie": "necktie", - "octopus": "octopus", - "okapi": "okapi", - "orangutan": "orangutan", - "ostrich": "ostrich", - "otter": "otter", - "owl": "owl", - "ox": "ox", - "oyster": "oyster", - "palm-pilot": "palm-pilot", - "palm-tree": "palm-tree", - "panda": "panda", - "paper-shredder": "paper-shredder", - "paperclip": "paperclip", - "parrot": "parrot", - "pci-card": "pci-card", - "pelecaniformes": "pelecaniformes", - "penguin": "penguin", - "photocopier": "photocopier", - "piano": "piano", - "picnic-table": "picnic-table", - "pig": "pig", - "pigeon": "pigeon", - "playing-card": "playing-card", - "porcupine": "porcupine", - "possum": "possum", - "pram": "pram", - "pyramid": "pyramid", - "raccoon": "raccoon", - "radio-telescope": "radio-telescope", - "rainbow": "rainbow", - "rat": "rat", - "refrigerator": "refrigerator", - "reindeer": "reindeer", - "rhinoceros": "rhinoceros", - "rotary-phone": "rotary-phone", - "roulette-wheel": "roulette-wheel", - "saddle": "saddle", - "sandpiper": "sandpiper", - "saturn": "saturn", - "school-bus": "school-bus", - "screwdriver": "screwdriver", - "seahorse": "seahorse", - "seal": "seal", - "segway": "segway", - "self-propelled-lawn-mower": "self-propelled-lawn-mower", - "shark": "shark", - "sheep": "sheep", - "sheet-music": "sheet-music", - "skateboard": "skateboard", - "skunk": "skunk", - "skyscraper": "skyscraper", - "smokestack": "smokestack", - "snail": "snail", - "snake": "snake", - "sneaker": "sneaker", - "snowmobile": "snowmobile", - "soccer-ball": "soccer-ball", - "socks": "socks", - "soda-can": "soda-can", - "spaghetti": "spaghetti", - "sparrow": "sparrow", - "speed-boat": "speed-boat", - "spoon": "spoon", - "squid": "squid", - "squirrel": "squirrel", - "stained-glass": "stained-glass", - "starfish": "starfish", - "steering-wheel": "steering-wheel", - "stirrups": "stirrups", - "sunflower": "sunflower", - "superman": "superman", - "sushi": "sushi", - "swan": "swan", - "t-shirt": "t-shirt", - "teapot": "teapot", - "teddy-bear": "teddy-bear", - "telephone-box": "telephone-box", - "tennis-ball": "tennis-ball", - "tennis-court": "tennis-court", - "tennis-racket": "tennis-racket", - "tiger": "tiger", - "toad": "toad", - "toaster": "toaster", - "tomato": "tomato", - "tombstone": "tombstone", - "top-hat": "top-hat", - "touring-bike": "touring-bike", - "tower-pisa": "tower-pisa", - "traffic-light": "traffic-light", - "treadmill": "treadmill", - "triceratops": "triceratops", - "tricycle": "tricycle", - "tripod": "tripod", - "tuning-fork": "tuning-fork", - "turkey": "turkey", - "turtle": "turtle", - "tweezer": "tweezer", - "umbrella": "umbrella", - "unicorn": "unicorn", - "video-projector": "video-projector", - "washing-machine": "washing-machine", - "watch": "watch", - "waterfall": "waterfall", - "watermelon": "watermelon", - "welding-mask": "welding-mask", - "whale": "whale", - "wheelbarrow": "wheelbarrow", - "windmill": "windmill", - "wine-bottle": "wine-bottle", - "wolf": "wolf", - "wombat": "wombat", - "woodpecker": "woodpecker", - "xylophone": "xylophone", - "yo-yo": "yo-yo", - "zebra": "zebra" -} diff --git a/packages/common/src/locales/es.json b/packages/common/src/locales/es.json deleted file mode 100644 index a9c3a42c6b..0000000000 --- a/packages/common/src/locales/es.json +++ /dev/null @@ -1,398 +0,0 @@ -{ - "ACCOUNT": { - "NO_POLKADOT_EXTENSION": "Extensión Polkadot no encontrada" - }, - "WIDGET": { - "SELECT_ALL": "Seleccionar todo lo que contiene lo siguiente", - "IF_NONE_CLICK_NEXT": "Si no hay ninguno, haz clic en Siguiente", - "NEXT": "Siguiente", - "SUBMIT": "Enviar", - "CANCEL": "Cancelar", - "I_AM_HUMAN": "Soy humano", - "NO_ACCOUNTS_FOUND": "No se encontraron cuentas", - "ACCOUNT_NOT_FOUND": "Cuenta no encontrada", - "NO_EXTENSION_FOUND": "Extensión no encontrada" - }, - "GENERAL": { - "JSON_LOAD_FAILED": "Error al cargar el archivo JSON", - "MNEMONIC_UNDEFINED": "Mnemónico indefinido. Por favor, establece el mnemónico en las variables de entorno", - "NO_MNEMONIC_OR_SEED": "No se proporcionó mnemónico ni semilla", - "CANT_FIND_KEYRINGPAIR": "No se puede encontrar el keyringpair para {{address}}", - "ENVIRONMENT_NOT_READY": "Entorno no listo", - "INVALID_SIGNATURE": "Firma inválida", - "NOT_IMPLEMENTED": "No implementado", - "SITE_KEY_MISSING": "Falta la CLAVE DEL SITIO", - "ACCOUNT_NOT_FOUND": "Cuenta no encontrada" - }, - "CONTRACT": { - "INVALID_METHOD": "Método de contrato inválido", - "TX_ERROR": "Error al hacer la tx", - "INVALID_ADDRESS": "Error al codificar dirección inválida", - "INVALID_STORAGE_NAME": "No se encontró el nombre de almacenamiento proporcionado", - "CAPTCHA_SOLUTION_COMMITMENT_DOES_NOT_EXIST": "El compromiso de la solución del captcha no existe", - "CONTRACT_UNDEFINED": "Contrato indefinido", - "SIGNER_UNDEFINED": "Firmante indefinido", - "CANNOT_FIND_KEYPAIR": "No se puede encontrar el par de claves", - "INTERRUPTED_EVENT": "Evento interrumpido", - "TOO_MANY_CALLS": "Demasiadas llamadas", - "UNKNOWN_ERROR": "Error desconocido", - "CHAIN_DECIMALS_UNDEFINED": "Decimales de la cadena no definidos", - "INVALID_DATA_FORMAT": "Formato de datos inválido", - "TX_QUEUE_ERROR": "Error en la cola de transacciones", - "DISPATCH_ERROR": "Error al despachar la transacción" - }, - "CONFIG": { - "UNKNOWN_ENVIRONMENT": "Entorno solicitado desconocido", - "INVALID_CAPTCHA_NUMBER": "Por favor configura las captchas correctamente", - "INVALID_LOG_LEVEL": "Nivel de registro inválido", - "INVALID_PACKAGE_DIR": "Directorio del paquete inválido" - }, - "DATASET": { - "DATASET_PARSE_ERROR": "Error al analizar el conjunto de datos", - "SOLUTION_PARSE_ERROR": "Error al analizar el conjunto de soluciones", - "HASH_ERROR": "Error al hashear el conjunto de datos", - "DATASET_ID_UNDEFINED": "ID de conjunto de datos indefinido", - "NOT_ENOUGH_LABELS": "No hay suficientes etiquetas", - "NOT_ENOUGH_IMAGES": "No hay suficientes imágenes", - "CAPTCHAS_COUNT_LESS_THAN_CONFIGURED": "La cantidad de captchas en el conjunto de datos es menor a la cantidad configurada", - "SOLUTIONS_COUNT_LESS_THAN_CONFIGURED": "La cantidad de soluciones en el conjunto de datos es menor a la cantidad configurada", - "DUPLICATE_IMAGE": "Imagen duplicada detectada", - "MERKLE_ERROR": "Error al crear el árbol de Merkle" - }, - "DATABASE": { - "DATABASE_IMPORT_FAILED": "Error al importar el motor de la base de datos", - "DATABASE_UNDEFINED": "El cliente de la base de datos no está conectado", - "DATABASE_HOST_UNDEFINED": "La dirección del host de la base de datos no está definida", - "DATASET_LOAD_FAILED": "Error al cargar el conjunto de datos", - "DATASET_GET_FAILED": "Error al obtener el conjunto de datos", - "CAPTCHA_GET_FAILED": "Error al obtener el captcha", - "CAPTCHA_UPDATE_FAILED": "Error al actualizar el captcha", - "IMAGE_GET_FAILED": "Error al obtener la imagen", - "PENDING_RECORD_NOT_FOUND": "No se encontró ningún registro pendiente", - "INVALID_HASH": "Hash inválido", - "SOLUTION_GET_FAILED": "Error al obtener la solución", - "DATASET_WITH_SOLUTIONS_GET_FAILED": "No se encontraron conjuntos de datos con la cantidad requerida de soluciones", - "SOLUTION_APPROVE_FAILED": "Error al aprobar la solución", - "SOLUTION_FLAG_FAILED": "Error al marcar la solución como procesada", - "TABLES_UNDEFINED": "Tablas indefinidas", - "CONNECTION_UNDEFINED": "Conexión indefinida", - "COMMITMENT_FLAG_FAILED": "Error al marcar el compromiso como procesado" - }, - "CAPTCHA": { - "PARSE_ERROR": "Error al analizar el captcha", - "INVALID_CAPTCHA_ID": "ID de captcha inválido", - "INVALID_ITEM_FORMAT": "Solo se permiten tipos de elementos de imagen y texto", - "INVALID_SOLUTION_TYPE": "Tipo de solución inválido", - "INVALID_ITEM_HASH": "Hash de elemento inválido", - "DIFFERENT_DATASET_IDS": "Los IDs del conjunto de datos no coinciden", - "INVALID_TIMESTAMP": "Marca de tiempo inválida", - "ID_MISMATCH": "Desajuste de ID de captcha", - "MISSING_ITEM_HASH": "Falta el hash del elemento", - "INVALID_CAPTCHA_CHALLENGE": "Desafío de captcha inválido", - "DAPP_USER_SOLUTION_NOT_FOUND": "No se encontró la solución del usuario de la Dapp", - "NO_CAPTCHA": "No se encontró captcha", - "INVALID_TOKEN": "Token inválido", - "INVALID_SOLUTION": "Solución inválida" - }, - "API": { - "CAPTCHA_FAILED": "Respondiste incorrectamente a uno o más captchas. Por favor, inténtalo de nuevo", - "CAPTCHA_PASSED": "Respondiste correctamente a los captchas", - "BAD_REQUEST": "Solicitud incorrecta", - "USER_VERIFIED": "Usuario verificado", - "USER_NOT_VERIFIED": "Usuario no verificado", - "USER_NOT_VERIFIED_TIME_EXPIRED": "Usuario no verificado. La solución del captcha ha expirado.", - "USER_NOT_VERIFIED_NO_SOLUTION": "Usuario no verificado. No se encontró solución de captcha.", - "USER_ALREADY_VERIFIED": "Esta solución ya ha sido verificada. El usuario debe completar un nuevo captcha.", - "UNKNOWN": "Error desconocido en el API" - }, - "CLI": { - "PARAMETER_ERROR": "Parámetro inválido" - }, - "DEVELOPER": { - "PROSOPO_SITE_KEY_MISSING": "PROSOPO_SITE_KEY no está configurado en el archivo .env.", - "PROVIDER_NO_CAPTCHA": "No se devolvieron captchas del proveedor", - "MISSING_PROVIDER_PAIR": "Falta el par de proveedor", - "MISSING_ENV_VARIABLE": "Falta una variable de entorno", - "GENERAL": "Error general de desarrollo, ver contexto", - "MISSING_SECRET_KEY": "Falta la clave secreta", - "KEY_ERROR": "Error de clave", - "METHOD_NOT_IMPLEMENTED": "Método no implementado" - }, - "FS": { - "FILE_NOT_FOUND": "Archivo no encontrado", - "FILE_ALREADY_EXISTS": "El archivo ya existe", - "INVALID_DIR_FORMAT": "Formato de directorio inválido" - }, - "animals": "animales", - "antelope": "antílope", - "backpack": "mochila", - "badger": "tejón", - "baseball-bat": "bate-de-béisbol", - "baseball-glove": "guante-de-béisbol", - "basketball-hoop": "aro-de-baloncesto", - "bat": "murciélago", - "bathtub": "bañera", - "bear": "oso", - "bee": "abeja", - "beer-mug": "jarra-de-cerveza", - "beetle": "escarabajo", - "billiards": "billar", - "binoculars": "binoculares", - "birdbath": "baño-de-aves", - "bison": "bisonte", - "blimp": "dirigible", - "boar": "jabalí", - "bonsai-tree": "árbol-bonsái", - "boom-box": "radiocasete", - "bowling-ball": "bola-de-bolos", - "bowling-pin": "pino-de-bolos", - "boxing-glove": "guante-de-boxeo", - "breadmaker": "panificadora", - "bulldozer": "bulldozer", - "butterfly": "mariposa", - "cactus": "cactus", - "cake": "pastel", - "calculator": "calculadora", - "camel": "camello", - "canoe": "canoa", - "car-tire": "neumático", - "caterpillar": "oruga", - "cd": "cd", - "cereal-box": "caja-de-cereal", - "chandelier": "candelabro", - "chess-board": "tablero-de-ajedrez", - "chimpanzee": "chimpancé", - "chopsticks": "palillos", - "cockroach": "cucaracha", - "coffin": "ataúd", - "coin": "moneda", - "comet": "cometa", - "computer-keyboard": "teclado-de-ordenador", - "computer-monitor": "monitor-de-ordenador", - "computer-mouse": "ratón-de-ordenador", - "conch": "caracola", - "cormorant": "cormorán", - "covered-wagon": "carro-cubierto", - "cow": "vaca", - "cowboy-hat": "sombrero-vaquero", - "coyote": "coyote", - "crab": "cangrejo", - "crow": "cuervo", - "desk-globe": "globo-terráqueo", - "diamond-ring": "anillo-de-diamantes", - "dice": "dados", - "dolphin": "delfín", - "donkey": "burro", - "doorknob": "manilla-de-puerta", - "dragonfly": "libélula", - "drinking-straw": "pajilla", - "duck": "pato", - "dumb-bell": "mancuerna", - "eagle": "águila", - "eiffel-tower": "torre-eiffel", - "electric-guitar": "guitarra-eléctrica", - "elephant": "elefante", - "elk": "alce", - "eyeglasses": "gafas", - "fern": "helecho", - "fire-extinguisher": "extintor", - "fire-hydrant": "hidrante", - "fire-truck": "camión-de-bomberos", - "fireworks": "fuegos-artificiales", - "flamingo": "flamenco", - "fly": "mosca", - "fox": "zorro", - "french-horn": "trompa", - "fried-egg": "huevo-frito", - "frisbee": "frisbee", - "frog": "rana", - "frying-pan": "sartén", - "galaxy": "galaxia", - "gas-pump": "bomba-de-gasolina", - "giraffe": "jirafa", - "goat": "cabra", - "golden-gate-bridge": "puente-golden-gate", - "goldfish": "pez-dorado", - "golf-ball": "pelota-de-golf", - "goose": "ganso", - "gorilla": "gorila", - "grapes": "uvas", - "grasshopper": "saltamontes", - "greyhound": "galgo", - "guitar-pick": "púa-de-guitarra", - "hamburger": "hamburguesa", - "hammock": "hamaca", - "hamster": "hámster", - "hare": "liebre", - "harmonica": "armónica", - "harp": "arpa", - "harpsichord": "clavicémbalo", - "head-phones": "auriculares", - "hedgehog": "erizo", - "helicopter": "helicóptero", - "hibiscus": "hibisco", - "hippopotamus": "hipopótamo", - "homer-simpson": "homer-simpson", - "hornbill": "cálao", - "horseshoe-crab": "cangrejo-herradura", - "hot-air-balloon": "globo-aerostático", - "hot-dog": "perro-caliente", - "hot-tub": "jacuzzi", - "hourglass": "reloj-de-arena", - "house-fly": "mosca-doméstica", - "hummingbird": "colibrí", - "hyena": "hiena", - "ice-cream-cone": "cono-de-helado", - "iguana": "iguana", - "ipod": "ipod", - "jellyfish": "medusa", - "joy-stick": "joystick", - "kangaroo": "canguro", - "kayak": "kayak", - "killer-whale": "orca", - "koala": "koala", - "ladder": "escalera", - "ladybugs": "mariquitas", - "laptop": "portátil", - "leopard": "leopardo", - "license-plate": "placa-de-matrícula", - "light-house": "faro", - "lightbulb": "bombilla", - "lightning": "relámpago", - "lion": "león", - "lizard": "lagarto", - "llama": "llama", - "lobster": "langosta", - "mailbox": "buzón", - "mandolin": "mandolina", - "mars": "marte", - "mattress": "colchón", - "megaphone": "megáfono", - "microscope": "microscopio", - "microwave": "microondas", - "minaret": "minarete", - "minotaur": "minotauro", - "mosquito": "mosquito", - "moth": "polilla", - "motorbikes": "motos", - "mountain-bike": "bicicleta-de-montaña", - "mouse": "ratón", - "mushroom": "seta", - "mussels": "mejillones", - "necktie": "corbata", - "octopus": "pulpo", - "okapi": "okapi", - "orangutan": "orangután", - "ostrich": "avestruz", - "otter": "nutria", - "owl": "búho", - "ox": "buey", - "oyster": "ostra", - "palm-pilot": "palm-pilot", - "palm-tree": "palmera", - "panda": "panda", - "paper-shredder": "trituradora-de-papel", - "paperclip": "clip", - "parrot": "loro", - "pci-card": "tarjeta-pci", - "pelecaniformes": "pelecaniformes", - "penguin": "pingüino", - "photocopier": "fotocopiadora", - "piano": "piano", - "picnic-table": "mesa-de-picnic", - "pig": "cerdo", - "pigeon": "paloma", - "playing-card": "carta", - "porcupine": "puercoespín", - "possum": "zarigüeya", - "pram": "carrito-de-bebé", - "pyramid": "pirámide", - "raccoon": "mapache", - "radio-telescope": "radiotelescopio", - "rainbow": "arcoíris", - "rat": "rata", - "refrigerator": "refrigerador", - "reindeer": "reno", - "rhinoceros": "rinoceronte", - "rotary-phone": "teléfono-rotatorio", - "roulette-wheel": "ruleta", - "saddle": "silla-de-montar", - "sandpiper": "correlimos", - "saturn": "saturno", - "school-bus": "autobús-escolar", - "screwdriver": "destornillador", - "seahorse": "caballito-de-mar", - "seal": "foca", - "segway": "segway", - "self-propelled-lawn-mower": "cortacésped-autopropulsado", - "shark": "tiburón", - "sheep": "oveja", - "sheet-music": "partitura", - "skateboard": "patineta", - "skunk": "mofeta", - "skyscraper": "rascacielos", - "smokestack": "chimenea-industrial", - "snail": "caracol", - "snake": "serpiente", - "sneaker": "zapatilla-deportiva", - "snowmobile": "moto-de-nieve", - "soccer-ball": "balón-de-fútbol", - "socks": "calcetines", - "soda-can": "lata-de-refresco", - "spaghetti": "espagueti", - "sparrow": "gorrión", - "speed-boat": "lancha-rápida", - "spoon": "cuchara", - "squid": "calamar", - "squirrel": "ardilla", - "stained-glass": "vidrieras", - "starfish": "estrella-de-mar", - "steering-wheel": "volante", - "stirrups": "estribos", - "sunflower": "girasol", - "superman": "superman", - "sushi": "sushi", - "swan": "cisne", - "t-shirt": "camiseta", - "teapot": "tetera", - "teddy-bear": "oso-de-peluche", - "telephone-box": "cabina-telefónica", - "tennis-ball": "pelota-de-tenis", - "tennis-court": "cancha-de-tenis", - "tennis-racket": "raqueta-de-tenis", - "tiger": "tigre", - "toad": "sapo", - "toaster": "tostadora", - "tomato": "tomate", - "tombstone": "lápida", - "top-hat": "sombrero-de-copa", - "touring-bike": "bicicleta-de-turismo", - "tower-pisa": "torre-de-pisa", - "traffic-light": "semáforo", - "treadmill": "cinta-de-correr", - "triceratops": "triceratops", - "tricycle": "triciclo", - "tripod": "trípode", - "tuning-fork": "diapasón", - "turkey": "pavo", - "turtle": "tortuga", - "tweezer": "pinzas", - "umbrella": "paraguas", - "unicorn": "unicornio", - "video-projector": "proyector-de-video", - "washing-machine": "lavadora", - "watch": "reloj", - "waterfall": "cascada", - "watermelon": "sandía", - "welding-mask": "máscara-de-soldar", - "whale": "ballena", - "wheelbarrow": "carretilla", - "windmill": "molino-de-viento", - "wine-bottle": "botella-de-vino", - "wolf": "lobo", - "wombat": "wombat", - "woodpecker": "pájaro-carpintero", - "xylophone": "xilófono", - "yo-yo": "yo-yo", - "zebra": "cebra" -} diff --git a/packages/common/src/locales/pt-BR.json b/packages/common/src/locales/pt-BR.json deleted file mode 100644 index cdf3f9035d..0000000000 --- a/packages/common/src/locales/pt-BR.json +++ /dev/null @@ -1,398 +0,0 @@ -{ - "ACCOUNT": { - "NO_POLKADOT_EXTENSION": "Extensão Polkadot não encontrada" - }, - "WIDGET": { - "SELECT_ALL": "Selecionar todos que contêm o seguinte", - "IF_NONE_CLICK_NEXT": "Se não houver nenhum, clique em Próximo", - "NEXT": "Próximo", - "SUBMIT": "Enviar", - "CANCEL": "Cancelar", - "I_AM_HUMAN": "Eu sou humano", - "NO_ACCOUNTS_FOUND": "Nenhuma conta encontrada", - "ACCOUNT_NOT_FOUND": "Conta não encontrada", - "NO_EXTENSION_FOUND": "Nenhuma extensão encontrada" - }, - "GENERAL": { - "JSON_LOAD_FAILED": "Falha ao carregar o arquivo JSON", - "MNEMONIC_UNDEFINED": "Mnemônico indefinido. Por favor, defina o mnemônico nas variáveis de ambiente", - "NO_MNEMONIC_OR_SEED": "Nenhum mnemônico ou seed fornecido", - "CANT_FIND_KEYRINGPAIR": "Não é possível encontrar o keyringpair para {{address}}", - "ENVIRONMENT_NOT_READY": "Ambiente não pronto", - "INVALID_SIGNATURE": "Assinatura inválida", - "NOT_IMPLEMENTED": "Não implementado", - "SITE_KEY_MISSING": "CHAVE DO SITE ausente", - "ACCOUNT_NOT_FOUND": "Conta não encontrada" - }, - "CONTRACT": { - "INVALID_METHOD": "Método de contrato inválido", - "TX_ERROR": "Erro ao fazer tx", - "INVALID_ADDRESS": "Falha ao codificar endereço inválido", - "INVALID_STORAGE_NAME": "Falha ao encontrar o nome de armazenamento fornecido", - "CAPTCHA_SOLUTION_COMMITMENT_DOES_NOT_EXIST": "O compromisso da solução do captcha não existe", - "CONTRACT_UNDEFINED": "Contrato indefinido", - "SIGNER_UNDEFINED": "Assinante indefinido", - "CANNOT_FIND_KEYPAIR": "Não é possível encontrar o par de chaves", - "INTERRUPTED_EVENT": "Evento interrompido", - "TOO_MANY_CALLS": "Muitas chamadas", - "UNKNOWN_ERROR": "Erro desconhecido", - "CHAIN_DECIMALS_UNDEFINED": "Decimais da cadeia não definidos", - "INVALID_DATA_FORMAT": "Formato de dados inválido", - "TX_QUEUE_ERROR": "Erro na fila de transações", - "DISPATCH_ERROR": "Erro ao despachar a transação" - }, - "CONFIG": { - "UNKNOWN_ENVIRONMENT": "Ambiente solicitado desconhecido", - "INVALID_CAPTCHA_NUMBER": "Por favor, configure corretamente os captchas", - "INVALID_LOG_LEVEL": "Nível de log inválido", - "INVALID_PACKAGE_DIR": "Diretório do pacote inválido" - }, - "DATASET": { - "DATASET_PARSE_ERROR": "Erro ao analisar o conjunto de dados", - "SOLUTION_PARSE_ERROR": "Erro ao analisar o conjunto de soluções", - "HASH_ERROR": "Erro ao gerar hash do conjunto de dados", - "DATASET_ID_UNDEFINED": "ID de conjunto de dados indefinido", - "NOT_ENOUGH_LABELS": "Não há rótulos suficientes", - "NOT_ENOUGH_IMAGES": "Não há imagens suficientes", - "CAPTCHAS_COUNT_LESS_THAN_CONFIGURED": "O número de captchas no conjunto de dados é menor que o número configurado", - "SOLUTIONS_COUNT_LESS_THAN_CONFIGURED": "O número de soluções no conjunto de dados é menor que o número configurado", - "DUPLICATE_IMAGE": "Imagem duplicada detectada", - "MERKLE_ERROR": "Erro ao criar a árvore de Merkle" - }, - "DATABASE": { - "DATABASE_IMPORT_FAILED": "Falha ao importar o mecanismo do banco de dados", - "DATABASE_UNDEFINED": "Cliente do banco de dados não está conectado", - "DATABASE_HOST_UNDEFINED": "Endereço do host do banco de dados não está definido", - "DATASET_LOAD_FAILED": "Falha ao carregar o conjunto de dados", - "DATASET_GET_FAILED": "Falha ao obter o conjunto de dados", - "CAPTCHA_GET_FAILED": "Falha ao obter o captcha", - "CAPTCHA_UPDATE_FAILED": "Falha ao atualizar o captcha", - "IMAGE_GET_FAILED": "Falha ao obter a imagem", - "PENDING_RECORD_NOT_FOUND": "Nenhum registro pendente encontrado", - "INVALID_HASH": "Hash inválido", - "SOLUTION_GET_FAILED": "Falha ao obter a solução", - "DATASET_WITH_SOLUTIONS_GET_FAILED": "Nenhum conjunto de dados encontrado com o número necessário de soluções", - "SOLUTION_APPROVE_FAILED": "Falha ao aprovar a solução", - "SOLUTION_FLAG_FAILED": "Falha ao marcar a solução como processada", - "TABLES_UNDEFINED": "Tabelas indefinidas", - "CONNECTION_UNDEFINED": "Conexão indefinida", - "COMMITMENT_FLAG_FAILED": "Falha ao marcar o compromisso como processado" - }, - "CAPTCHA": { - "PARSE_ERROR": "Erro ao analisar o captcha", - "INVALID_CAPTCHA_ID": "ID de captcha inválido", - "INVALID_ITEM_FORMAT": "Somente itens do tipo imagem e texto são permitidos", - "INVALID_SOLUTION_TYPE": "Tipo de solução inválido", - "INVALID_ITEM_HASH": "Hash do item inválido", - "DIFFERENT_DATASET_IDS": "Os IDs do conjunto de dados não correspondem", - "INVALID_TIMESTAMP": "Carimbo de data/hora inválido", - "ID_MISMATCH": "Divergência no ID do captcha", - "MISSING_ITEM_HASH": "Hash do item ausente", - "INVALID_CAPTCHA_CHALLENGE": "Desafio de captcha inválido", - "DAPP_USER_SOLUTION_NOT_FOUND": "Solução do usuário da Dapp não encontrada", - "NO_CAPTCHA": "Nenhum captcha encontrado", - "INVALID_TOKEN": "Token inválido", - "INVALID_SOLUTION": "Solução inválida" - }, - "API": { - "CAPTCHA_FAILED": "Você respondeu incorretamente a um ou mais captchas. Por favor, tente novamente", - "CAPTCHA_PASSED": "Você respondeu corretamente aos captchas", - "BAD_REQUEST": "Solicitação Inválida", - "USER_VERIFIED": "Usuário verificado", - "USER_NOT_VERIFIED": "Usuário não verificado", - "USER_NOT_VERIFIED_TIME_EXPIRED": "Usuário não verificado. A solução do captcha expirou.", - "USER_NOT_VERIFIED_NO_SOLUTION": "Usuário não verificado. Nenhuma solução de captcha encontrada.", - "USER_ALREADY_VERIFIED": "Esta solução já foi verificada. O usuário deve completar um novo captcha.", - "UNKNOWN": "Erro desconhecido na API" - }, - "CLI": { - "PARAMETER_ERROR": "Parâmetro inválido" - }, - "DEVELOPER": { - "PROSOPO_SITE_KEY_MISSING": "PROSOPO_SITE_KEY não está definido no arquivo .env.", - "PROVIDER_NO_CAPTCHA": "Nenhum captcha retornado do provedor", - "MISSING_PROVIDER_PAIR": "Par de provedor ausente", - "MISSING_ENV_VARIABLE": "Variável de ambiente ausente", - "GENERAL": "Erro geral de desenvolvimento, veja o contexto", - "MISSING_SECRET_KEY": "Chave secreta ausente", - "KEY_ERROR": "Erro de chave", - "METHOD_NOT_IMPLEMENTED": "Método não implementado" - }, - "FS": { - "FILE_NOT_FOUND": "Arquivo não encontrado", - "FILE_ALREADY_EXISTS": "Arquivo já existe", - "INVALID_DIR_FORMAT": "Formato de diretório inválido" - }, - "animals": "animais", - "antelope": "antílope", - "backpack": "mochila", - "badger": "texugo", - "baseball-bat": "taco-de-baseball", - "baseball-glove": "luva-de-baseball", - "basketball-hoop": "cesta-de-basquete", - "bat": "morcego", - "bathtub": "banheira", - "bear": "urso", - "bee": "abelha", - "beer-mug": "caneca-de-cerveja", - "beetle": "besouro", - "billiards": "sinuca", - "binoculars": "binóculos", - "birdbath": "bebedouro-para-pássaros", - "bison": "bisonte-ou-bisão", - "blimp": "dirigível", - "boar": "javali", - "bonsai-tree": "árvore-bonsai", - "boom-box": "aparelho-de-som-portátil", - "bowling-ball": "bola-de-boliche", - "bowling-pin": "pino-de-boliche", - "boxing-glove": "luva-de-boxe", - "breadmaker": "máquina-de-pão", - "bulldozer": "trator", - "butterfly": "borboleta", - "cactus": "cacto", - "cake": "bolo", - "calculator": "calculadora", - "camel": "camelo", - "canoe": "canoa", - "car-tire": "pneu", - "caterpillar": "lagarta", - "cd": "cd", - "cereal-box": "caixa-de-cereal", - "chandelier": "lustre", - "chess-board": "tabuleiro-de-xadrez", - "chimpanzee": "chimpanzé", - "chopsticks": "hashis", - "cockroach": "barata", - "coffin": "caixão", - "coin": "moeda", - "comet": "cometa", - "computer-keyboard": "teclado-de-computador", - "computer-monitor": "monitor-de-computador", - "computer-mouse": "mouse-de-computador", - "conch": "concha", - "cormorant": "corvo-marinho", - "covered-wagon": "carroça-coberta", - "cow": "vaca", - "cowboy-hat": "chapéu-de-cowboy", - "coyote": "coiote", - "crab": "caranguejo", - "crow": "corvo", - "desk-globe": "globo-terrestre", - "diamond-ring": "anel-de-diamante", - "dice": "dados", - "dolphin": "golfinho", - "donkey": "burro", - "doorknob": "maçaneta", - "dragonfly": "libélula", - "drinking-straw": "canudo", - "duck": "pato", - "dumb-bell": "halter", - "eagle": "águia", - "eiffel-tower": "torre-eiffel", - "electric-guitar": "guitarra-elétrica", - "elephant": "elefante", - "elk": "alce", - "eyeglasses": "óculos", - "fern": "samambaia", - "fire-extinguisher": "extintor-de-incêndio", - "fire-hydrant": "hidrante", - "fire-truck": "caminhão-de-bombeiros", - "fireworks": "fogos-de-artifício", - "flamingo": "flamingo", - "fly": "mosca", - "fox": "raposa", - "french-horn": "trompa", - "fried-egg": "ovo-frito", - "frisbee": "frisbee", - "frog": "sapo", - "frying-pan": "frigideira", - "galaxy": "galáxia", - "gas-pump": "bomba-de-gasolina", - "giraffe": "girafa", - "goat": "cabra", - "golden-gate-bridge": "ponte-golden-gate", - "goldfish": "peixinho-dourado", - "golf-ball": "bola-de-golfe", - "goose": "ganso", - "gorilla": "gorila", - "grapes": "uvas", - "grasshopper": "gafanhoto", - "greyhound": "galgo", - "guitar-pick": "palheta", - "hamburger": "hambúrguer", - "hammock": "rede", - "hamster": "hamster", - "hare": "lebre", - "harmonica": "gaita", - "harp": "harpa", - "harpsichord": "cravo", - "head-phones": "fones-de-ouvido", - "hedgehog": "ouriço", - "helicopter": "helicóptero", - "hibiscus": "hibisco", - "hippopotamus": "hipopótamo", - "homer-simpson": "homer-simpson", - "hornbill": "calau", - "horseshoe-crab": "caranguejo-ferradura", - "hot-air-balloon": "balão-de-ar-quente", - "hot-dog": "cachorro-quente", - "hot-tub": "banheira-de-hidromassagem", - "hourglass": "ampulheta", - "house-fly": "mosca-doméstica", - "hummingbird": "beija-flor", - "hyena": "hiena", - "ice-cream-cone": "casquinha-de-sorvete", - "iguana": "iguana", - "ipod": "ipod", - "jellyfish": "água-viva", - "joy-stick": "joystick", - "kangaroo": "canguru", - "kayak": "caiaque", - "killer-whale": "orca", - "koala": "coala", - "ladder": "escada", - "ladybugs": "joaninhas", - "laptop": "laptop", - "leopard": "leopardo", - "license-plate": "placa-de-carro", - "light-house": "farol", - "lightbulb": "lâmpada", - "lightning": "raio", - "lion": "leão", - "lizard": "lagarto", - "llama": "lhama", - "lobster": "lagosta", - "mailbox": "caixa-de-correio", - "mandolin": "bandolim", - "mars": "marte", - "mattress": "colchão", - "megaphone": "megafone", - "microscope": "microscópio", - "microwave": "micro-ondas", - "minaret": "minarete", - "minotaur": "minotauro", - "mosquito": "mosquito", - "moth": "mariposa", - "motorbikes": "motos", - "mountain-bike": "mountain-bik", - "mouse": "camundongo", - "mushroom": "cogumelo", - "mussels": "mexilhões", - "necktie": "gravata", - "octopus": "polvo", - "okapi": "ocapi", - "orangutan": "orangotango", - "ostrich": "avestruz", - "otter": "lontra", - "owl": "coruja", - "ox": "boi", - "oyster": "ostra", - "palm-pilot": "palm-pilot", - "palm-tree": "palmeira", - "panda": "panda", - "paper-shredder": "triturador-de-papel", - "paperclip": "clipe", - "parrot": "papagaio", - "pci-card": "placa-pci", - "pelecaniformes": "pelecaniformes", - "penguin": "pinguim", - "photocopier": "fotocopiadora", - "piano": "piano", - "picnic-table": "mesa-de-piquenique", - "pig": "porco", - "pigeon": "pombo", - "playing-card": "carta-de-baralho", - "porcupine": "porco-espinho", - "possum": "gambá", - "pram": "carrinho-de-bebê", - "pyramid": "pirâmide", - "raccoon": "guaxinim", - "radio-telescope": "radiotelescópio", - "rainbow": "arco-íris", - "rat": "rato", - "refrigerator": "geladeira", - "reindeer": "rena", - "rhinoceros": "rinoceronte", - "rotary-phone": "telefone-rotativo", - "roulette-wheel": "roleta", - "saddle": "sela", - "sandpiper": "maçarico", - "saturn": "saturno", - "school-bus": "ônibus-escolar", - "screwdriver": "chave-de-fenda", - "seahorse": "cavalo-marinho", - "seal": "foca", - "segway": "segway", - "self-propelled-lawn-mower": "cortador-de-grama-autopropelido", - "shark": "tubarão", - "sheep": "ovelha", - "sheet-music": "partitura", - "skateboard": "skate", - "skunk": "gambá", - "skyscraper": "arranha-céu", - "smokestack": "chaminé", - "snail": "caracol", - "snake": "cobra", - "sneaker": "tênis", - "snowmobile": "moto-de-neve", - "soccer-ball": "bola-de-futebol", - "socks": "meias", - "soda-can": "lata-de-refrigerante", - "spaghetti": "espaguete", - "sparrow": "pardal", - "speed-boat": "lancha", - "spoon": "colher", - "squid": "lula", - "squirrel": "esquilo", - "stained-glass": "vitrais", - "starfish": "estrela-do-mar", - "steering-wheel": "volante", - "stirrups": "estribos", - "sunflower": "girassol", - "superman": "superman", - "sushi": "sushi", - "swan": "cisne", - "t-shirt": "camiseta", - "teapot": "bule", - "teddy-bear": "urso-de-pelúcia", - "telephone-box": "cabine-telefônica", - "tennis-ball": "bola-de-tênis", - "tennis-court": "quadra-de-tênis", - "tennis-racket": "raquete-de-tênis", - "tiger": "tigre", - "toad": "sapo", - "toaster": "torradeira", - "tomato": "tomate", - "tombstone": "lápide", - "top-hat": "cartola", - "touring-bike": "bicicleta-de-turismo", - "tower-pisa": "torre-de-pisa", - "traffic-light": "semáforo", - "treadmill": "esteira", - "triceratops": "tricerátopo", - "tricycle": "triciclo", - "tripod": "tripé", - "tuning-fork": "diapasão", - "turkey": "peru", - "turtle": "tartaruga", - "tweezer": "pinça", - "umbrella": "guarda-chuva", - "unicorn": "unicórnio", - "video-projector": "projetor-de-vídeo", - "washing-machine": "máquina-de-lavar", - "watch": "relógio", - "waterfall": "cachoeira", - "watermelon": "melancia", - "welding-mask": "máscara-de-solda", - "whale": "baleia", - "wheelbarrow": "carrinho-de-mão", - "windmill": "moinho-de-vento", - "wine-bottle": "garrafa-de-vinho", - "wolf": "lobo", - "wombat": "vombate", - "woodpecker": "pica-pau", - "xylophone": "xilofone", - "yo-yo": "ioiô", - "zebra": "zebra" -} diff --git a/packages/common/src/locales/pt.json b/packages/common/src/locales/pt.json deleted file mode 100644 index 32e44439fb..0000000000 --- a/packages/common/src/locales/pt.json +++ /dev/null @@ -1,398 +0,0 @@ -{ - "ACCOUNT": { - "NO_POLKADOT_EXTENSION": "Extensão Polkadot não encontrada" - }, - "WIDGET": { - "SELECT_ALL": "Selecionar todos que contêm o seguinte", - "IF_NONE_CLICK_NEXT": "Se não houver nenhum, clique em Próximo", - "NEXT": "Próximo", - "SUBMIT": "Enviar", - "CANCEL": "Cancelar", - "I_AM_HUMAN": "Eu sou humano", - "NO_ACCOUNTS_FOUND": "Nenhuma conta encontrada", - "ACCOUNT_NOT_FOUND": "Conta não encontrada", - "NO_EXTENSION_FOUND": "Nenhuma extensão encontrada" - }, - "GENERAL": { - "JSON_LOAD_FAILED": "Falha ao carregar o arquivo JSON", - "MNEMONIC_UNDEFINED": "Mnemônico indefinido. Por favor, defina o mnemônico nas variáveis de ambiente", - "NO_MNEMONIC_OR_SEED": "Nenhum mnemônico ou seed fornecido", - "CANT_FIND_KEYRINGPAIR": "Não é possível encontrar o keyringpair para {{address}}", - "ENVIRONMENT_NOT_READY": "Ambiente não pronto", - "INVALID_SIGNATURE": "Assinatura inválida", - "NOT_IMPLEMENTED": "Não implementado", - "SITE_KEY_MISSING": "CHAVE DO SITE ausente", - "ACCOUNT_NOT_FOUND": "Conta não encontrada" - }, - "CONTRACT": { - "INVALID_METHOD": "Método de contrato inválido", - "TX_ERROR": "Erro ao fazer tx", - "INVALID_ADDRESS": "Falha ao codificar endereço inválido", - "INVALID_STORAGE_NAME": "Falha ao encontrar o nome de armazenamento fornecido", - "CAPTCHA_SOLUTION_COMMITMENT_DOES_NOT_EXIST": "O compromisso da solução do captcha não existe", - "CONTRACT_UNDEFINED": "Contrato indefinido", - "SIGNER_UNDEFINED": "Assinante indefinido", - "CANNOT_FIND_KEYPAIR": "Não é possível encontrar o par de chaves", - "INTERRUPTED_EVENT": "Evento interrompido", - "TOO_MANY_CALLS": "Muitas chamadas", - "UNKNOWN_ERROR": "Erro desconhecido", - "CHAIN_DECIMALS_UNDEFINED": "Decimais da cadeia não definidos", - "INVALID_DATA_FORMAT": "Formato de dados inválido", - "TX_QUEUE_ERROR": "Erro na fila de transações", - "DISPATCH_ERROR": "Erro ao despachar a transação" - }, - "CONFIG": { - "UNKNOWN_ENVIRONMENT": "Ambiente solicitado desconhecido", - "INVALID_CAPTCHA_NUMBER": "Por favor, configure corretamente os captchas", - "INVALID_LOG_LEVEL": "Nível de log inválido", - "INVALID_PACKAGE_DIR": "Diretório do pacote inválido" - }, - "DATASET": { - "DATASET_PARSE_ERROR": "Erro ao analisar o conjunto de dados", - "SOLUTION_PARSE_ERROR": "Erro ao analisar o conjunto de soluções", - "HASH_ERROR": "Erro ao gerar hash do conjunto de dados", - "DATASET_ID_UNDEFINED": "ID de conjunto de dados indefinido", - "NOT_ENOUGH_LABELS": "Não há rótulos suficientes", - "NOT_ENOUGH_IMAGES": "Não há imagens suficientes", - "CAPTCHAS_COUNT_LESS_THAN_CONFIGURED": "O número de captchas no conjunto de dados é menor que o número configurado", - "SOLUTIONS_COUNT_LESS_THAN_CONFIGURED": "O número de soluções no conjunto de dados é menor que o número configurado", - "DUPLICATE_IMAGE": "Imagem duplicada detectada", - "MERKLE_ERROR": "Erro ao criar a árvore de Merkle" - }, - "DATABASE": { - "DATABASE_IMPORT_FAILED": "Falha ao importar o mecanismo do banco de dados", - "DATABASE_UNDEFINED": "Cliente do banco de dados não está conectado", - "DATABASE_HOST_UNDEFINED": "Endereço do host do banco de dados não está definido", - "DATASET_LOAD_FAILED": "Falha ao carregar o conjunto de dados", - "DATASET_GET_FAILED": "Falha ao obter o conjunto de dados", - "CAPTCHA_GET_FAILED": "Falha ao obter o captcha", - "CAPTCHA_UPDATE_FAILED": "Falha ao atualizar o captcha", - "IMAGE_GET_FAILED": "Falha ao obter a imagem", - "PENDING_RECORD_NOT_FOUND": "Nenhum registro pendente encontrado", - "INVALID_HASH": "Hash inválido", - "SOLUTION_GET_FAILED": "Falha ao obter a solução", - "DATASET_WITH_SOLUTIONS_GET_FAILED": "Nenhum conjunto de dados encontrado com o número necessário de soluções", - "SOLUTION_APPROVE_FAILED": "Falha ao aprovar a solução", - "SOLUTION_FLAG_FAILED": "Falha ao marcar a solução como processada", - "TABLES_UNDEFINED": "Tabelas indefinidas", - "CONNECTION_UNDEFINED": "Conexão indefinida", - "COMMITMENT_FLAG_FAILED": "Falha ao marcar o compromisso como processado" - }, - "CAPTCHA": { - "PARSE_ERROR": "Erro ao analisar o captcha", - "INVALID_CAPTCHA_ID": "ID de captcha inválido", - "INVALID_ITEM_FORMAT": "Somente tipos de itens de imagem e texto são permitidos", - "INVALID_SOLUTION_TYPE": "Tipo de solução inválido", - "INVALID_ITEM_HASH": "Hash do item inválido", - "DIFFERENT_DATASET_IDS": "Os IDs do conjunto de dados não correspondem", - "INVALID_TIMESTAMP": "Carimbo de data/hora inválido", - "ID_MISMATCH": "Desajuste de ID do captcha", - "MISSING_ITEM_HASH": "Hash do item ausente", - "INVALID_CAPTCHA_CHALLENGE": "Desafio de captcha inválido", - "DAPP_USER_SOLUTION_NOT_FOUND": "Solução do usuário da Dapp não encontrada", - "NO_CAPTCHA": "Nenhum captcha encontrado", - "INVALID_TOKEN": "Token inválido", - "INVALID_SOLUTION": "Solução inválida" - }, - "API": { - "CAPTCHA_FAILED": "Você respondeu incorretamente a um ou mais captchas. Por favor, tente novamente", - "CAPTCHA_PASSED": "Você respondeu corretamente aos captchas", - "BAD_REQUEST": "Solicitação Inválida", - "USER_VERIFIED": "Usuário verificado", - "USER_NOT_VERIFIED": "Usuário não verificado", - "USER_NOT_VERIFIED_TIME_EXPIRED": "Usuário não verificado. A solução do captcha expirou.", - "USER_NOT_VERIFIED_NO_SOLUTION": "Usuário não verificado. Nenhuma solução de captcha encontrada.", - "USER_ALREADY_VERIFIED": "Esta solução já foi verificada. O usuário deve completar um novo captcha.", - "UNKNOWN": "Erro desconhecido na API" - }, - "CLI": { - "PARAMETER_ERROR": "Parâmetro inválido" - }, - "DEVELOPER": { - "PROSOPO_SITE_KEY_MISSING": "PROSOPO_SITE_KEY não está definido no arquivo .env.", - "PROVIDER_NO_CAPTCHA": "Nenhum captcha retornado do provedor", - "MISSING_PROVIDER_PAIR": "Par de provedor ausente", - "MISSING_ENV_VARIABLE": "Variável de ambiente ausente", - "GENERAL": "Erro geral de desenvolvimento, veja o contexto", - "MISSING_SECRET_KEY": "Chave secreta ausente", - "KEY_ERROR": "Erro de chave", - "METHOD_NOT_IMPLEMENTED": "Método não implementado" - }, - "FS": { - "FILE_NOT_FOUND": "Arquivo não encontrado", - "FILE_ALREADY_EXISTS": "Arquivo já existe", - "INVALID_DIR_FORMAT": "Formato de diretório inválido" - }, - "animals": "animais", - "antelope": "antílope", - "backpack": "mochila", - "badger": "texugo", - "baseball-bat": "taco-de-baseball", - "baseball-glove": "luva-de-baseball", - "basketball-hoop": "cesta-de-basquete", - "bat": "morcego", - "bathtub": "banheira", - "bear": "urso", - "bee": "abelha", - "beer-mug": "caneca-de-cerveja", - "beetle": "besouro", - "billiards": "sinuca", - "binoculars": "binóculos", - "birdbath": "bebedouro-para-pássaros", - "bison": "bisonte", - "blimp": "dirigível", - "boar": "javali", - "bonsai-tree": "árvore-bonsai", - "boom-box": "rádio-gravador", - "bowling-ball": "bola-de-boliche", - "bowling-pin": "pino-de-boliche", - "boxing-glove": "luva-de-boxe", - "breadmaker": "máquina-de-pão", - "bulldozer": "trator", - "butterfly": "borboleta", - "cactus": "cacto", - "cake": "bolo", - "calculator": "calculadora", - "camel": "camelo", - "canoe": "canoa", - "car-tire": "pneu", - "caterpillar": "lagarta", - "cd": "cd", - "cereal-box": "caixa-de-cereal", - "chandelier": "lustre", - "chess-board": "tabuleiro-de-xadrez", - "chimpanzee": "chimpanzé", - "chopsticks": "hashis", - "cockroach": "barata", - "coffin": "caixão", - "coin": "moeda", - "comet": "cometa", - "computer-keyboard": "teclado-de-computador", - "computer-monitor": "monitor-de-computador", - "computer-mouse": "mouse-de-computador", - "conch": "concha", - "cormorant": "corvo-marinho", - "covered-wagon": "carroça-coberta", - "cow": "vaca", - "cowboy-hat": "chapéu-de-cowboy", - "coyote": "coiote", - "crab": "caranguejo", - "crow": "corvo", - "desk-globe": "globo-terrestre", - "diamond-ring": "anel-de-diamante", - "dice": "dados", - "dolphin": "golfinho", - "donkey": "burro", - "doorknob": "maçaneta", - "dragonfly": "libélula", - "drinking-straw": "canudo", - "duck": "pato", - "dumb-bell": "halter", - "eagle": "águia", - "eiffel-tower": "torre-eiffel", - "electric-guitar": "guitarra-elétrica", - "elephant": "elefante", - "elk": "alce", - "eyeglasses": "óculos", - "fern": "samambaia", - "fire-extinguisher": "extintor-de-incêndio", - "fire-hydrant": "hidrante", - "fire-truck": "caminhão-de-bombeiros", - "fireworks": "fogos-de-artifício", - "flamingo": "flamingo", - "fly": "mosca", - "fox": "raposa", - "french-horn": "trompa", - "fried-egg": "ovo-frito", - "frisbee": "frisbee", - "frog": "sapo", - "frying-pan": "frigideira", - "galaxy": "galáxia", - "gas-pump": "bomba-de-gasolina", - "giraffe": "girafa", - "goat": "cabra", - "golden-gate-bridge": "ponte-golden-gate", - "goldfish": "peixinho-dourado", - "golf-ball": "bola-de-golfe", - "goose": "ganso", - "gorilla": "gorila", - "grapes": "uvas", - "grasshopper": "gafanhoto", - "greyhound": "galgo", - "guitar-pick": "palheta", - "hamburger": "hambúrguer", - "hammock": "rede", - "hamster": "hamster", - "hare": "lebre", - "harmonica": "gaita", - "harp": "harpa", - "harpsichord": "cravo", - "head-phones": "fones-de-ouvido", - "hedgehog": "ouriço", - "helicopter": "helicóptero", - "hibiscus": "hibisco", - "hippopotamus": "hipopótamo", - "homer-simpson": "homer-simpson", - "hornbill": "calau", - "horseshoe-crab": "caranguejo-ferradura", - "hot-air-balloon": "balão-de-ar-quente", - "hot-dog": "cachorro-quente", - "hot-tub": "banheira-de-hidromassagem", - "hourglass": "ampulheta", - "house-fly": "mosca-doméstica", - "hummingbird": "beija-flor", - "hyena": "hiena", - "ice-cream-cone": "casquinha-de-sorvete", - "iguana": "iguana", - "ipod": "ipod", - "jellyfish": "água-viva", - "joy-stick": "joystick", - "kangaroo": "canguru", - "kayak": "caiaque", - "killer-whale": "orca", - "koala": "coala", - "ladder": "escada", - "ladybugs": "joaninhas", - "laptop": "laptop", - "leopard": "leopardo", - "license-plate": "placa-de-carro", - "light-house": "farol", - "lightbulb": "lâmpada", - "lightning": "raio", - "lion": "leão", - "lizard": "lagarto", - "llama": "lhama", - "lobster": "lagosta", - "mailbox": "caixa-de-correio", - "mandolin": "bandolim", - "mars": "marte", - "mattress": "colchão", - "megaphone": "megafone", - "microscope": "microscópio", - "microwave": "micro-ondas", - "minaret": "minarete", - "minotaur": "minotauro", - "mosquito": "mosquito", - "moth": "mariposa", - "motorbikes": "motos", - "mountain-bike": "bicicleta-de-montanha", - "mouse": "camundongo", - "mushroom": "cogumelo", - "mussels": "mexilhões", - "necktie": "gravata", - "octopus": "polvo", - "okapi": "ocapi", - "orangutan": "orangotango", - "ostrich": "avestruz", - "otter": "lontra", - "owl": "coruja", - "ox": "boi", - "oyster": "ostra", - "palm-pilot": "palm-pilot", - "palm-tree": "palmeira", - "panda": "panda", - "paper-shredder": "triturador-de-papel", - "paperclip": "clipe", - "parrot": "papagaio", - "pci-card": "cartão-pci", - "pelecaniformes": "pelecaniformes", - "penguin": "pinguim", - "photocopier": "fotocopiadora", - "piano": "piano", - "picnic-table": "mesa-de-picnic", - "pig": "porco", - "pigeon": "pombo", - "playing-card": "carta-de-baralho", - "porcupine": "porco-espinho", - "possum": "gambá", - "pram": "carrinho-de-bebê", - "pyramid": "pirâmide", - "raccoon": "guaxinim", - "radio-telescope": "radiotelescópio", - "rainbow": "arco-íris", - "rat": "rato", - "refrigerator": "geladeira", - "reindeer": "rena", - "rhinoceros": "rinoceronte", - "rotary-phone": "telefone-rotativo", - "roulette-wheel": "roleta", - "saddle": "sela", - "sandpiper": "maçarico", - "saturn": "saturno", - "school-bus": "ônibus-escolar", - "screwdriver": "chave-de-fenda", - "seahorse": "cavalo-marinho", - "seal": "foca", - "segway": "segway", - "self-propelled-lawn-mower": "cortador-de-grama-autopropelido", - "shark": "tubarão", - "sheep": "ovelha", - "sheet-music": "partitura", - "skateboard": "skate", - "skunk": "gambá", - "skyscraper": "arranha-céu", - "smokestack": "chaminé", - "snail": "caracol", - "snake": "cobra", - "sneaker": "tênis", - "snowmobile": "moto-de-neve", - "soccer-ball": "bola-de-futebol", - "socks": "meias", - "soda-can": "lata-de-refrigerante", - "spaghetti": "espaguete", - "sparrow": "pardal", - "speed-boat": "lancha", - "spoon": "colher", - "squid": "lula", - "squirrel": "esquilo", - "stained-glass": "vitrais", - "starfish": "estrela-do-mar", - "steering-wheel": "volante", - "stirrups": "estribos", - "sunflower": "girassol", - "superman": "superman", - "sushi": "sushi", - "swan": "cisne", - "t-shirt": "camiseta", - "teapot": "bule", - "teddy-bear": "urso-de-pelúcia", - "telephone-box": "cabine-telefônica", - "tennis-ball": "bola-de-tênis", - "tennis-court": "quadra-de-tênis", - "tennis-racket": "raquete-de-tênis", - "tiger": "tigre", - "toad": "sapo", - "toaster": "torradeira", - "tomato": "tomate", - "tombstone": "lápide", - "top-hat": "cartola", - "touring-bike": "bicicleta-de-turismo", - "tower-pisa": "torre-de-pisa", - "traffic-light": "semáforo", - "treadmill": "esteira", - "triceratops": "tricerátopo", - "tricycle": "triciclo", - "tripod": "tripé", - "tuning-fork": "diapasão", - "turkey": "peru", - "turtle": "tartaruga", - "tweezer": "pinça", - "umbrella": "guarda-chuva", - "unicorn": "unicórnio", - "video-projector": "projetor-de-vídeo", - "washing-machine": "máquina-de-lavar", - "watch": "relógio", - "waterfall": "cachoeira", - "watermelon": "melancia", - "welding-mask": "máscara-de-solda", - "whale": "baleia", - "wheelbarrow": "carrinho-de-mão", - "windmill": "moinho-de-vento", - "wine-bottle": "garrafa-de-vinho", - "wolf": "lobo", - "wombat": "vombate", - "woodpecker": "pica-pau", - "xylophone": "xilofone", - "yo-yo": "ioiô", - "zebra": "zebra" -} diff --git a/packages/contract/package.json b/packages/contract/package.json index dbde667bd7..574d9bd748 100644 --- a/packages/contract/package.json +++ b/packages/contract/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/contract", - "version": "2.1.3", + "version": "2.1.4", "author": "PROSOPO LIMITED ", "license": "Apache-2.0", "scripts": { @@ -43,15 +43,15 @@ "@polkadot/types-codec": "10.13.1", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/tx": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/tx": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "rxjs": "7.8.1" }, "devDependencies": { "@polkadot/api-augment": "10.13.1", - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", diff --git a/packages/database/package.json b/packages/database/package.json index e4dc00bf57..9cb5b5b915 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/database", - "version": "2.1.3", + "version": "2.1.4", "description": "Prosopo database plugins for provider", "main": "dist/index.js", "type": "module", @@ -31,10 +31,10 @@ }, "homepage": "https://github.com/prosopo/captcha#readme", "dependencies": { - "@prosopo/common": "2.1.3", - "@prosopo/config": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/types-database": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/config": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/types-database": "2.1.4", "mongodb": "6.9.0", "mongodb-memory-server": "10.0.0", "mongoose": "8.6.2" diff --git a/packages/datasets-fs/package.json b/packages/datasets-fs/package.json index 0aeacf184c..9e2d633046 100644 --- a/packages/datasets-fs/package.json +++ b/packages/datasets-fs/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/datasets-fs", - "version": "2.1.3", + "version": "2.1.4", "author": "PROSOPO LIMITED ", "license": "Apache-2.0", "private": false, @@ -28,9 +28,9 @@ "@noble/hashes": "1.5.0", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "bcrypt": "5.1.1", "cli-progress": "3.12.0", "sharp": "0.33.5", @@ -38,7 +38,7 @@ "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@types/bcrypt": "5.0.2", "@types/cli-progress": "3.11.6", "@vitest/coverage-v8": "2.1.1", diff --git a/packages/datasets/package.json b/packages/datasets/package.json index 243d1b5cf8..1cde38e88e 100644 --- a/packages/datasets/package.json +++ b/packages/datasets/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/datasets", - "version": "2.1.3", + "version": "2.1.4", "author": "PROSOPO LIMITED ", "license": "Apache-2.0", "private": false, @@ -38,12 +38,12 @@ }, "dependencies": { "@polkadot/util": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3" + "@prosopo/common": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "dotenv": "16.4.5", diff --git a/packages/detector/package.json b/packages/detector/package.json index bbe293c51e..ce0ebb2333 100644 --- a/packages/detector/package.json +++ b/packages/detector/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/detector", - "version": "2.1.3", + "version": "2.1.4", "main": "src/index.js", "engines": { "node": ">=20", diff --git a/packages/dotenv/package.json b/packages/dotenv/package.json index f1dacae7ea..75def81031 100644 --- a/packages/dotenv/package.json +++ b/packages/dotenv/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/dotenv", - "version": "2.1.3", + "version": "2.1.4", "author": "PROSOPO LIMITED ", "license": "Apache-2.0", "private": false, @@ -25,8 +25,8 @@ }, "types": "./dist/index.d.ts", "dependencies": { - "@prosopo/common": "2.1.3", - "@prosopo/config": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/config": "2.1.4", "dotenv": "16.4.5" }, "devDependencies": { diff --git a/packages/env/package.json b/packages/env/package.json index e7b3ffd02b..817ca24171 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/env", - "version": "2.1.3", + "version": "2.1.4", "description": "Path env prosopo environment", "main": "dist/index.js", "type": "module", @@ -24,18 +24,18 @@ "@polkadot/keyring": "12.6.2", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/database": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/types-database": "2.1.3", - "@prosopo/types-env": "2.1.3", - "@prosopo/util": "2.1.3" + "@prosopo/common": "2.1.4", + "@prosopo/database": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/types-database": "2.1.4", + "@prosopo/types-env": "2.1.4", + "@prosopo/util": "2.1.4" }, "overrides": { "@polkadot/keyring": "12.6.2" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", diff --git a/packages/file-server/package.json b/packages/file-server/package.json index 2325245e51..b0781587fe 100644 --- a/packages/file-server/package.json +++ b/packages/file-server/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/file-server", - "version": "2.1.3", + "version": "2.1.4", "description": "Simple static file server", "main": "dist/index.js", "type": "module", @@ -22,14 +22,14 @@ "start": "node ./dist/index.js" }, "dependencies": { - "@prosopo/util": "2.1.3", + "@prosopo/util": "2.1.4", "dotenv": "16.4.5", "express": "4.21.0", "node-fetch": "3.3.2", "sharp": "0.33.5" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@types/express": "4.17.21", "@types/node": "22.5.5", "@vitest/coverage-v8": "2.1.1", diff --git a/packages/load-balancer/package.json b/packages/load-balancer/package.json index 46e4ffe63f..2a088b7537 100644 --- a/packages/load-balancer/package.json +++ b/packages/load-balancer/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/load-balancer", - "version": "2.1.3", + "version": "2.1.4", "description": "Provider load balancer", "main": "dist/index.js", "type": "module", @@ -31,9 +31,9 @@ }, "homepage": "https://github.com/prosopo/captcha#readme", "dependencies": { - "@prosopo/common": "2.1.3", - "@prosopo/config": "2.1.3", - "@prosopo/types": "2.1.3" + "@prosopo/common": "2.1.4", + "@prosopo/config": "2.1.4", + "@prosopo/types": "2.1.4" }, "devDependencies": { "@vitest/coverage-v8": "2.1.1", diff --git a/packages/locale-browser/package.json b/packages/locale-browser/package.json index 866986a578..a55dc41bdb 100644 --- a/packages/locale-browser/package.json +++ b/packages/locale-browser/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/locale-browser", - "version": "2.1.3", + "version": "2.1.4", "description": "Prosopo browser locale", "main": "./dist/index.js", "type": "module", @@ -24,13 +24,13 @@ "author": "Prosopo Limited", "license": "Apache-2.0", "dependencies": { - "@prosopo/locale": "2.1.3", + "@prosopo/locale": "2.1.4", "i18next-browser-languagedetector": "7.2.1", "react-i18next": "11.18.6", "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "dotenv": "16.4.5", "npm-run-all": "4.1.5", diff --git a/packages/locale/package.json b/packages/locale/package.json index 75867d6308..e7533facab 100644 --- a/packages/locale/package.json +++ b/packages/locale/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/locale", - "version": "2.1.3", + "version": "2.1.4", "description": "Prosopo locale library", "main": "./dist/index.js", "type": "module", @@ -30,7 +30,7 @@ "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "dotenv": "16.4.5", "npm-run-all": "4.1.5", diff --git a/packages/locale/src/locales/de.json b/packages/locale/src/locales/de.json index fa40dd02f3..9e9c811472 100644 --- a/packages/locale/src/locales/de.json +++ b/packages/locale/src/locales/de.json @@ -104,7 +104,8 @@ "USER_NOT_VERIFIED_NO_SOLUTION": "Benutzer nicht verifiziert. Keine Captcha-Lösung gefunden.", "USER_ALREADY_VERIFIED": "Diese Lösung wurde bereits verifiziert. Der Benutzer sollte ein neues Captcha ausfüllen.", "UNKNOWN": "Unbekannter API-Fehler", - "SITE_KEY_NOT_REGISTERED": "Site-Key nicht registriert" + "SITE_KEY_NOT_REGISTERED": "Site-Key nicht registriert", + "INVALID_SITE_KEY": "Ungültiger Site-Schlüssel" }, "CLI": { "PARAMETER_ERROR": "Ungültiger Parameter" diff --git a/packages/locale/src/locales/en.json b/packages/locale/src/locales/en.json index ce4072c537..a005e92bd2 100644 --- a/packages/locale/src/locales/en.json +++ b/packages/locale/src/locales/en.json @@ -104,7 +104,8 @@ "USER_NOT_VERIFIED_NO_SOLUTION": "User not verified. No captcha solution found.", "USER_ALREADY_VERIFIED": "This solution has already been verified. User should complete a new captcha.", "UNKNOWN": "Unknown API error", - "SITE_KEY_NOT_REGISTERED": "Site key not registered" + "SITE_KEY_NOT_REGISTERED": "Site key not registered", + "INVALID_SITE_KEY": "Invalid site key" }, "CLI": { "PARAMETER_ERROR": "Invalid parameter" diff --git a/packages/locale/src/locales/es.json b/packages/locale/src/locales/es.json index 2538efcd5c..195d3c7a90 100644 --- a/packages/locale/src/locales/es.json +++ b/packages/locale/src/locales/es.json @@ -104,7 +104,8 @@ "USER_NOT_VERIFIED_NO_SOLUTION": "Usuario no verificado. No se encontró solución de captcha.", "USER_ALREADY_VERIFIED": "Esta solución ya ha sido verificada. El usuario debe completar un nuevo captcha.", "UNKNOWN": "Error desconocido en el API", - "SITE_KEY_NOT_REGISTERED": "Clave del sitio no registrada" + "SITE_KEY_NOT_REGISTERED": "Clave del sitio no registrada", + "INVALID_SITE_KEY": "Clave de sitio no válida" }, "CLI": { "PARAMETER_ERROR": "Parámetro inválido" diff --git a/packages/locale/src/locales/fr.json b/packages/locale/src/locales/fr.json index a1110ea365..e0592e7db5 100644 --- a/packages/locale/src/locales/fr.json +++ b/packages/locale/src/locales/fr.json @@ -104,7 +104,8 @@ "USER_NOT_VERIFIED_NO_SOLUTION": "Utilisateur non vérifié. Aucune solution captcha trouvée.", "USER_ALREADY_VERIFIED": "Cette solution a déjà été vérifiée. L'utilisateur doit compléter un nouveau captcha.", "UNKNOWN": "Erreur API inconnue", - "SITE_KEY_NOT_REGISTERED": "Clé du site non enregistrée" + "SITE_KEY_NOT_REGISTERED": "Clé du site non enregistrée", + "INVALID_SITE_KEY": "Clé de site non valide" }, "CLI": { "PARAMETER_ERROR": "Paramètre invalide" diff --git a/packages/locale/src/locales/it.json b/packages/locale/src/locales/it.json index 261cdba876..c94bcf9efe 100644 --- a/packages/locale/src/locales/it.json +++ b/packages/locale/src/locales/it.json @@ -104,7 +104,8 @@ "USER_NOT_VERIFIED_NO_SOLUTION": "Utente non verificato. Nessuna soluzione captcha trovata.", "USER_ALREADY_VERIFIED": "Questa soluzione è già stata verificata. L'utente deve completare un nuovo captcha.", "UNKNOWN": "Errore API sconosciuto", - "SITE_KEY_NOT_REGISTERED": "Chiave del sito non registrata" + "SITE_KEY_NOT_REGISTERED": "Chiave del sito non registrata", + "INVALID_SITE_KEY": "Chiave del sito non valida" }, "CLI": { "PARAMETER_ERROR": "Parametro non valido" diff --git a/packages/locale/src/locales/pt-BR.json b/packages/locale/src/locales/pt-BR.json index 90148c693e..792edc6e37 100644 --- a/packages/locale/src/locales/pt-BR.json +++ b/packages/locale/src/locales/pt-BR.json @@ -104,7 +104,8 @@ "USER_NOT_VERIFIED_NO_SOLUTION": "Usuário não verificado. Nenhuma solução de captcha encontrada.", "USER_ALREADY_VERIFIED": "Esta solução já foi verificada. O usuário deve completar um novo captcha.", "UNKNOWN": "Erro desconhecido na API", - "SITE_KEY_NOT_REGISTERED": "Chave do site não registrada" + "SITE_KEY_NOT_REGISTERED": "Chave do site não registrada", + "INVALID_SITE_KEY": "Chave de site inválida" }, "CLI": { "PARAMETER_ERROR": "Parâmetro inválido" diff --git a/packages/locale/src/locales/pt.json b/packages/locale/src/locales/pt.json index bd81064a20..8305cb6261 100644 --- a/packages/locale/src/locales/pt.json +++ b/packages/locale/src/locales/pt.json @@ -104,7 +104,8 @@ "USER_NOT_VERIFIED_NO_SOLUTION": "Usuário não verificado. Nenhuma solução de captcha encontrada.", "USER_ALREADY_VERIFIED": "Esta solução já foi verificada. O usuário deve completar um novo captcha.", "UNKNOWN": "Erro desconhecido na API", - "SITE_KEY_NOT_REGISTERED": "Chave do site não registrada" + "SITE_KEY_NOT_REGISTERED": "Chave do site não registrada", + "INVALID_SITE_KEY": "Chave de site inválida" }, "CLI": { "PARAMETER_ERROR": "Parâmetro inválido" diff --git a/packages/procaptcha-bundle/package.json b/packages/procaptcha-bundle/package.json index fc1268f84f..4467856769 100644 --- a/packages/procaptcha-bundle/package.json +++ b/packages/procaptcha-bundle/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/procaptcha-bundle", - "version": "2.1.3", + "version": "2.1.4", "author": "PROSOPO LIMITED ", "license": "Apache-2.0", "main": "./dist/index.js", @@ -29,13 +29,13 @@ }, "browserslist": ["> 0.5%, last 2 versions, not dead"], "dependencies": { - "@prosopo/dotenv": "2.1.3", - "@prosopo/locale": "2.1.3", - "@prosopo/procaptcha-frictionless": "2.1.3", - "@prosopo/procaptcha-pow": "2.1.3", - "@prosopo/procaptcha-react": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/dotenv": "2.1.4", + "@prosopo/locale": "2.1.4", + "@prosopo/procaptcha-frictionless": "2.1.4", + "@prosopo/procaptcha-pow": "2.1.4", + "@prosopo/procaptcha-react": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "react": "18.3.1", "react-dom": "18.3.1" }, @@ -52,7 +52,7 @@ "registry": "https://registry.npmjs.org" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@types/jsdom": "21.1.7", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", diff --git a/packages/procaptcha-bundle/src/index.tsx b/packages/procaptcha-bundle/src/index.tsx index 0c95beb4c7..35ed493e48 100644 --- a/packages/procaptcha-bundle/src/index.tsx +++ b/packages/procaptcha-bundle/src/index.tsx @@ -12,31 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { ProcaptchaFrictionless } from "@prosopo/procaptcha-frictionless"; -import { ProcaptchaPow } from "@prosopo/procaptcha-pow"; -import { Procaptcha } from "@prosopo/procaptcha-react"; -import type { - ProcaptchaClientConfigOutput, - ProcaptchaRenderOptions, -} from "@prosopo/types"; +import type { ProcaptchaRenderOptions } from "@prosopo/types"; import { at } from "@prosopo/util"; -import { createRoot } from "react-dom/client"; +import type { Root } from "react-dom/client"; import { getCaptchaType } from "./util/captchaType.js"; import { extractParams, getConfig, getProcaptchaScript, } from "./util/config.js"; -import { - getDefaultCallbacks, - getWindowCallback, - setUserCallbacks, -} from "./util/defaultCallbacks.js"; -import { setLanguage } from "./util/language.js"; -import { setTheme } from "./util/theme.js"; -import { setValidChallengeLength } from "./util/timeout.js"; +import { getWindowCallback } from "./util/defaultCallbacks.js"; +import { renderLogic } from "./util/renderLogic.js"; const BUNDLE_NAME = "procaptcha.bundle.js"; +let procaptchaRoots: Root[] = []; // Implicit render for targeting all elements with class 'procaptcha' const implicitRender = () => { @@ -54,40 +43,12 @@ const implicitRender = () => { } const captchaType = getCaptchaType(elements); - renderLogic(elements, getConfig(siteKey), { captchaType, siteKey }); - } -}; + const root = renderLogic(elements, getConfig(siteKey), { + captchaType, + siteKey, + }); -const renderLogic = ( - elements: Element[], - config: ProcaptchaClientConfigOutput, - renderOptions?: ProcaptchaRenderOptions, -) => { - for (const element of elements) { - const callbacks = getDefaultCallbacks(element); - - setUserCallbacks(renderOptions, callbacks, element); - setTheme(renderOptions, element, config); - setValidChallengeLength(renderOptions, element, config); - setLanguage(renderOptions, element, config); - - switch (renderOptions?.captchaType) { - case "pow": - createRoot(element).render( - , - ); - break; - case "frictionless": - createRoot(element).render( - , - ); - break; - default: - createRoot(element).render( - , - ); - break; - } + procaptchaRoots.push(...root); } }; @@ -98,7 +59,9 @@ export const render = ( ) => { const siteKey = renderOptions.siteKey; - renderLogic([element], getConfig(siteKey), renderOptions); + const roots = renderLogic([element], getConfig(siteKey), renderOptions); + + procaptchaRoots.push(...roots); }; export default function ready(fn: () => void) { @@ -114,26 +77,49 @@ export default function ready(fn: () => void) { // extend the global Window interface to include the procaptcha object declare global { interface Window { - procaptcha: { ready: typeof ready; render: typeof render }; + procaptcha: { + ready: typeof ready; + render: typeof render; + reset: typeof reset; + }; } } -// set the procaptcha attribute on the window -window.procaptcha = { ready, render }; +const start = () => { + // onLoadUrlCallback defines the name of the callback function to be called when the script is loaded + // onRenderExplicit takes values of either explicit or implicit + const { onloadUrlCallback, renderExplicit } = extractParams(BUNDLE_NAME); -// onLoadUrlCallback defines the name of the callback function to be called when the script is loaded -// onRenderExplicit takes values of either explicit or implicit -const { onloadUrlCallback, renderExplicit } = extractParams(BUNDLE_NAME); + // Render the Procaptcha component implicitly if renderExplicit is not set to explicit + if (renderExplicit !== "explicit") { + ready(implicitRender); + } -// Render the Procaptcha component implicitly if renderExplicit is not set to explicit -if (renderExplicit !== "explicit") { - ready(implicitRender); -} + if (onloadUrlCallback) { + const onloadCallback = getWindowCallback(onloadUrlCallback); + let readyCalled = false; + // Add event listener to the script tag to call the callback function when the script is loaded + getProcaptchaScript(BUNDLE_NAME)?.addEventListener("load", () => { + ready(onloadCallback); + readyCalled = true; + }); + // or if the document has already loaded, call the callback function + if (document.readyState === "complete" && !readyCalled) { + ready(onloadCallback); + } + } +}; -if (onloadUrlCallback) { - const onloadCallback = getWindowCallback(onloadUrlCallback); - // Add event listener to the script tag to call the callback function when the script is loaded - getProcaptchaScript(BUNDLE_NAME)?.addEventListener("load", () => { - ready(onloadCallback); - }); -} +export const reset = () => { + for (const root of procaptchaRoots) { + root.unmount(); + } + procaptchaRoots = []; + + start(); +}; + +// set the procaptcha attribute on the window +window.procaptcha = { ready, render, reset }; + +start(); diff --git a/packages/procaptcha-bundle/src/tests/captchaType.test.ts b/packages/procaptcha-bundle/src/tests/util/captchaType.test.ts similarity index 97% rename from packages/procaptcha-bundle/src/tests/captchaType.test.ts rename to packages/procaptcha-bundle/src/tests/util/captchaType.test.ts index 5459d750ff..3b966bd801 100644 --- a/packages/procaptcha-bundle/src/tests/captchaType.test.ts +++ b/packages/procaptcha-bundle/src/tests/util/captchaType.test.ts @@ -15,7 +15,7 @@ import { FeaturesEnum } from "@prosopo/types"; import { JSDOM } from "jsdom"; import { beforeEach, describe, expect, it } from "vitest"; -import { getCaptchaType } from "../util/captchaType.js"; +import { getCaptchaType } from "../../util/captchaType.js"; describe("getCaptchaType", () => { beforeEach(() => { diff --git a/packages/procaptcha-bundle/src/tests/config.test.ts b/packages/procaptcha-bundle/src/tests/util/config.test.ts similarity index 97% rename from packages/procaptcha-bundle/src/tests/config.test.ts rename to packages/procaptcha-bundle/src/tests/util/config.test.ts index 19f843309b..05712fada7 100644 --- a/packages/procaptcha-bundle/src/tests/config.test.ts +++ b/packages/procaptcha-bundle/src/tests/util/config.test.ts @@ -15,7 +15,7 @@ import { JSDOM } from "jsdom"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; -import { extractParams, getProcaptchaScript } from "../util/config.js"; +import { extractParams, getProcaptchaScript } from "../../util/config.js"; describe("Config utility functions", () => { let dom: JSDOM; diff --git a/packages/procaptcha-bundle/src/tests/util/renderLogic.test.tsx b/packages/procaptcha-bundle/src/tests/util/renderLogic.test.tsx new file mode 100644 index 0000000000..d609ddd6aa --- /dev/null +++ b/packages/procaptcha-bundle/src/tests/util/renderLogic.test.tsx @@ -0,0 +1,201 @@ +// Copyright 2021-2024 Prosopo (UK) 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. + +import { ProcaptchaPow } from "@prosopo/procaptcha-pow"; +import { ProcaptchaConfigSchema, type ProcaptchaProps } from "@prosopo/types"; +import { type DOMWindow, JSDOM } from "jsdom"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { renderLogic } from "../../util/renderLogic.js"; + +interface TestContext { + document?: Document; + window?: DOMWindow; +} + +vi.mock("@prosopo/detector", async () => { + return { + isBot: async () => { + return { isBot: true }; + }, + }; +}); + +vi.mock("@polkadot/extension-dapp", async () => { + return { + web3Enable: async () => { + return []; + }, + }; +}); + +vi.mock( + "@prosopo/procaptcha-pow", + async () => { + return { + ProcaptchaPow: vi.fn((props: ProcaptchaProps) => { + return ( + // @ts-ignore +
+ ProcaptchaPow +
+ ); + }), + }; + }, + //{ spy: true }, +); + +vi.mock("@prosopo/procaptcha-frictionless", async () => { + return { + ProcaptchaFrictionless: vi.fn((props: ProcaptchaProps) => { + return ( + // @ts-ignore +
+ ProcaptchaFrictionless +
+ ); + }), + }; +}); + +describe("Config utility functions", () => { + let dom: JSDOM; + let self: DOMWindow; + + beforeEach((context) => { + dom = new JSDOM("", { + url: "https://example.com", + }); + context.document = dom.window.document; + context.window = dom.window; + // @ts-ignore + global.window = dom.window; + // @ts-ignore + global.document = dom.window.document; + vi.clearAllMocks(); + }); + + afterEach((context) => { + context.document = undefined; + context.window = undefined; + vi.clearAllMocks(); + }); + + it("renders the correct captcha type", async (ctx) => { + // @ts-ignore + const script = ctx.document.createElement("script"); + script.src = "https://example.com/procaptcha.js"; + document.body.appendChild(script); + + expect(ctx.window).toBeDefined(); + + if (!ctx.window) { + expect(true).toBe(false); + return; + } + + const config = ProcaptchaConfigSchema.parse({ + account: { address: "1234" }, + }); + + renderLogic([script], config, { + siteKey: "1234", + captchaType: "pow", + }); + + await vi.waitFor(() => { + expect(ProcaptchaPow).toHaveBeenCalledTimes(1); + }); + }); + + it("user callbacks should be set when defined on the window and pulled in via data attributes", async (ctx) => { + // @ts-ignore + const script = ctx.document.createElement("script"); + script.src = "https://example.com/procaptcha.js"; + // @ts-ignore + script["data-callback"] = "onHuman"; + // @ts-ignore + script["data-chalexpired-callback"] = "onChallengeExpired"; + // @ts-ignore + script["data-expired-callback"] = "onExpired"; + // @ts-ignore + script["data-error-callback"] = "onError"; + // @ts-ignore + script["data-close-callback"] = "onClose"; + // @ts-ignore + script["data-open-callback"] = "onOpen"; + // @ts-ignore + script["data-failed-callback"] = "onFailed"; + // @ts-ignore + script["data-reset-callback"] = "onReset"; + document.body.appendChild(script); + + const onHuman = () => console.log("onHuman"); + const onChallengeExpired = () => console.log("onChallengeExpired"); + const onExpired = () => console.log("onExpired"); + const onError = () => console.log("onError"); + const onClose = () => console.log("onClose"); + const onOpen = () => console.log("onOpen"); + const onFailed = () => console.log("onFailed"); + const onReset = () => console.log("onReset"); + + expect(ctx.window).toBeDefined(); + + if (!ctx.window) { + expect(true).toBe(false); + return; + } + + ctx.window.onHuman = onHuman; + ctx.window.onChallengeExpired = onChallengeExpired; + ctx.window.onExpired = onExpired; + ctx.window.onError = onError; + ctx.window.onClose = onClose; + ctx.window.onOpen = onOpen; + ctx.window.onFailed = onFailed; + ctx.window.onReset = onReset; + + const callbacks = { + onHuman, + onChallengeExpired, + onExpired, + onError, + onClose, + onOpen, + onFailed, + onReset, + }; + + const config = ProcaptchaConfigSchema.parse({ + account: { address: "1234" }, + }); + + renderLogic([script], config, { + siteKey: "1234", + captchaType: "pow", + }); + + // override , in renderLogic.tsx and check that the callbacks + // are set correctly + await vi.waitFor(() => { + expect(ProcaptchaPow).toHaveBeenCalledTimes(1); + // hack to get mock calls + // @ts-ignore + const call = ProcaptchaPow.mock.calls[0]; + const props = { config, callbacks }; + // TODO update when expect(ProcaptchaPow).toHaveBeenCalledWith is working with deep equality + expect(JSON.stringify(call[0])).to.equal(JSON.stringify(props)); + }); + }); +}); diff --git a/packages/procaptcha-bundle/src/util/defaultCallbacks.ts b/packages/procaptcha-bundle/src/util/defaultCallbacks.ts index 3e6187616e..5df2dd3dc5 100644 --- a/packages/procaptcha-bundle/src/util/defaultCallbacks.ts +++ b/packages/procaptcha-bundle/src/util/defaultCallbacks.ts @@ -49,6 +49,12 @@ export const getDefaultCallbacks = (element: Element) => ({ onOpen: () => { console.log("Challenge opened"); }, + onFailed: () => { + console.log("Challenge failed"); + }, + onReset: () => { + console.log("Captcha widget reset"); + }, }); export function setUserCallbacks( @@ -60,6 +66,8 @@ export function setUserCallbacks( onError: (error: Error) => void; onClose: () => void; onOpen: () => void; + onFailed: () => void; + onReset: () => void; }, element: Element, ) { @@ -172,6 +180,32 @@ export function setUserCallbacks( callbacks.onOpen = getWindowCallback(onOpenCallbackName); } } + + if (renderOptions?.["failed-callback"]) { + if (typeof renderOptions["failed-callback"] === "function") { + callbacks.onFailed = renderOptions["failed-callback"]; + } else { + const onFailedCallbackName = + typeof renderOptions?.["failed-callback"] === "string" + ? renderOptions?.["failed-callback"] + : element.getAttribute("data-failed-callback"); + if (onFailedCallbackName) + callbacks.onFailed = getWindowCallback(onFailedCallbackName); + } + } + // reset callback + if (renderOptions?.["reset-callback"]) { + if (typeof renderOptions["reset-callback"] === "function") { + callbacks.onReset = renderOptions["reset-callback"]; + } else { + const onResetCallbackName = + typeof renderOptions?.["reset-callback"] === "string" + ? renderOptions?.["reset-callback"] + : element.getAttribute("data-reset-callback"); + if (onResetCallbackName) + callbacks.onReset = getWindowCallback(onResetCallbackName); + } + } } const handleOnHuman = (element: Element, token: ProcaptchaToken) => { diff --git a/packages/procaptcha-bundle/src/util/renderLogic.tsx b/packages/procaptcha-bundle/src/util/renderLogic.tsx new file mode 100644 index 0000000000..276ad4284a --- /dev/null +++ b/packages/procaptcha-bundle/src/util/renderLogic.tsx @@ -0,0 +1,66 @@ +// Copyright 2021-2024 Prosopo (UK) 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. +import { ProcaptchaFrictionless } from "@prosopo/procaptcha-frictionless"; +import { ProcaptchaPow } from "@prosopo/procaptcha-pow"; +import { Procaptcha } from "@prosopo/procaptcha-react"; +import type { + ProcaptchaClientConfigOutput, + ProcaptchaRenderOptions, +} from "@prosopo/types"; +import { type Root, createRoot } from "react-dom/client"; +import { getDefaultCallbacks, setUserCallbacks } from "./defaultCallbacks.js"; +import { setLanguage } from "./language.js"; +import { setTheme } from "./theme.js"; +import { setValidChallengeLength } from "./timeout.js"; + +const identifierPrefix = "procaptcha-"; + +export const renderLogic = ( + elements: Element[], + config: ProcaptchaClientConfigOutput, + renderOptions?: ProcaptchaRenderOptions, +) => { + const roots: Root[] = []; + for (const element of elements) { + const callbacks = getDefaultCallbacks(element); + + setUserCallbacks(renderOptions, callbacks, element); + setTheme(renderOptions, element, config); + setValidChallengeLength(renderOptions, element, config); + setLanguage(renderOptions, element, config); + + let root: Root | null = null; + switch (renderOptions?.captchaType) { + case "pow": + console.log("rendering pow"); + root = createRoot(element, { identifierPrefix }); + root.render(); + break; + case "frictionless": + console.log("rendering frictionless"); + root = createRoot(element, { identifierPrefix }); + root.render( + , + ); + break; + default: + console.log("rendering image"); + root = createRoot(element, { identifierPrefix }); + root.render(); + break; + } + roots.push(root); + } + return roots; +}; diff --git a/packages/procaptcha-common/package.json b/packages/procaptcha-common/package.json index d29d3d01fc..8d155350f1 100644 --- a/packages/procaptcha-common/package.json +++ b/packages/procaptcha-common/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/procaptcha-common", - "version": "2.1.3", + "version": "2.1.4", "author": "PROSOPO LIMITED ", "license": "Apache-2.0", "main": "./dist/index.js", @@ -26,13 +26,13 @@ }, "browserslist": ["> 0.5%, last 2 versions, not dead"], "dependencies": { - "@prosopo/common": "2.1.3", - "@prosopo/load-balancer": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3" + "@prosopo/common": "2.1.4", + "@prosopo/load-balancer": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", diff --git a/packages/procaptcha-common/src/events.ts b/packages/procaptcha-common/src/events.ts index 7ff01ca22f..9748ffdfb4 100644 --- a/packages/procaptcha-common/src/events.ts +++ b/packages/procaptcha-common/src/events.ts @@ -58,6 +58,9 @@ export const getDefaultEvents = ( onClose: () => { console.info("captcha closed"); }, + onReset: () => { + console.info("captcha reset"); + }, }, callbacks, ); diff --git a/packages/procaptcha-frictionless/package.json b/packages/procaptcha-frictionless/package.json index fd6c9718ae..be540e94c0 100644 --- a/packages/procaptcha-frictionless/package.json +++ b/packages/procaptcha-frictionless/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/procaptcha-frictionless", - "version": "2.1.3", + "version": "2.1.4", "author": "PROSOPO LIMITED ", "license": "Apache-2.0", "main": "./dist/index.js", @@ -26,16 +26,16 @@ }, "browserslist": ["> 0.5%, last 2 versions, not dead"], "dependencies": { - "@prosopo/detector": "2.1.3", - "@prosopo/locale-browser": "2.1.3", - "@prosopo/procaptcha-pow": "2.1.3", - "@prosopo/procaptcha-react": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/web-components": "2.1.3", + "@prosopo/detector": "2.1.4", + "@prosopo/locale-browser": "2.1.4", + "@prosopo/procaptcha-pow": "2.1.4", + "@prosopo/procaptcha-react": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/web-components": "2.1.4", "react": "18.3.1" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", diff --git a/packages/procaptcha-pow/package.json b/packages/procaptcha-pow/package.json index 232291c605..9e285bfef6 100644 --- a/packages/procaptcha-pow/package.json +++ b/packages/procaptcha-pow/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/procaptcha-pow", - "version": "2.1.3", + "version": "2.1.4", "author": "PROSOPO LIMITED ", "license": "Apache-2.0", "main": "./dist/index.js", @@ -27,15 +27,15 @@ "browserslist": ["> 0.5%, last 2 versions, not dead"], "dependencies": { "@polkadot/util": "12.6.2", - "@prosopo/account": "2.1.3", - "@prosopo/api": "2.1.3", - "@prosopo/common": "2.1.3", - "@prosopo/locale-browser": "2.1.3", - "@prosopo/procaptcha": "2.1.3", - "@prosopo/procaptcha-common": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", - "@prosopo/web-components": "2.1.3", + "@prosopo/account": "2.1.4", + "@prosopo/api": "2.1.4", + "@prosopo/common": "2.1.4", + "@prosopo/locale-browser": "2.1.4", + "@prosopo/procaptcha": "2.1.4", + "@prosopo/procaptcha-common": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", + "@prosopo/web-components": "2.1.4", "react": "18.3.1" }, "overrides": { @@ -46,7 +46,7 @@ } }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", diff --git a/packages/procaptcha-pow/src/services/Manager.ts b/packages/procaptcha-pow/src/services/Manager.ts index 4bf4e9acb3..691c2ed067 100644 --- a/packages/procaptcha-pow/src/services/Manager.ts +++ b/packages/procaptcha-pow/src/services/Manager.ts @@ -113,6 +113,7 @@ export const Manager = ( clearTimeout(); clearSuccessfulChallengeTimeout(); updateState(defaultState()); + events.onReset(); }; const setValidChallengeTimeout = () => { @@ -192,8 +193,8 @@ export const Manager = ( if (challenge.error) { updateState({ - error: challenge.error, loading: false, + error: challenge.error.message, }); } else { const solution = solvePoW(challenge.challenge, challenge.difficulty); diff --git a/packages/procaptcha-react/package.json b/packages/procaptcha-react/package.json index dcd2df2ac0..c4d3c19caf 100644 --- a/packages/procaptcha-react/package.json +++ b/packages/procaptcha-react/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/procaptcha-react", - "version": "2.1.3", + "version": "2.1.4", "author": "PROSOPO LIMITED ", "license": "Apache-2.0", "main": "./dist/index.js", @@ -26,13 +26,13 @@ }, "browserslist": ["> 0.5%, last 2 versions, not dead"], "dependencies": { - "@prosopo/common": "2.1.3", - "@prosopo/locale-browser": "2.1.3", - "@prosopo/procaptcha": "2.1.3", - "@prosopo/procaptcha-common": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", - "@prosopo/web-components": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/locale-browser": "2.1.4", + "@prosopo/procaptcha": "2.1.4", + "@prosopo/procaptcha-common": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", + "@prosopo/web-components": "2.1.4", "csstype": "3.1.3", "react": "18.3.1" }, @@ -49,7 +49,7 @@ } }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", diff --git a/packages/procaptcha-react/src/components/ProcaptchaWidget.tsx b/packages/procaptcha-react/src/components/ProcaptchaWidget.tsx index ea19f3120c..04ae6d59b7 100644 --- a/packages/procaptcha-react/src/components/ProcaptchaWidget.tsx +++ b/packages/procaptcha-react/src/components/ProcaptchaWidget.tsx @@ -118,30 +118,24 @@ const ProcaptchaWidget = (props: ProcaptchaProps) => { >
- -
-
-
+ {state.loading ? ( -
+ ) : ( + + )}
diff --git a/packages/procaptcha/package.json b/packages/procaptcha/package.json index 307c4d4b36..b72bfe000b 100644 --- a/packages/procaptcha/package.json +++ b/packages/procaptcha/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/procaptcha", - "version": "2.1.3", + "version": "2.1.4", "author": "PROSOPO LIMITED ", "license": "Apache-2.0", "main": "./dist/index.js", @@ -33,14 +33,14 @@ "@polkadot/api-contract": "10.13.1", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/account": "2.1.3", - "@prosopo/api": "2.1.3", - "@prosopo/common": "2.1.3", - "@prosopo/datasets": "2.1.3", - "@prosopo/load-balancer": "2.1.3", - "@prosopo/procaptcha-common": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/account": "2.1.4", + "@prosopo/api": "2.1.4", + "@prosopo/common": "2.1.4", + "@prosopo/datasets": "2.1.4", + "@prosopo/load-balancer": "2.1.4", + "@prosopo/procaptcha-common": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/util": "2.1.4", "jsdom": "25.0.0" }, "overrides": { @@ -56,7 +56,7 @@ } }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "dotenv": "16.4.5", diff --git a/packages/procaptcha/src/modules/Manager.ts b/packages/procaptcha/src/modules/Manager.ts index 4d3a3dfd26..28697aa4a1 100644 --- a/packages/procaptcha/src/modules/Manager.ts +++ b/packages/procaptcha/src/modules/Manager.ts @@ -21,7 +21,6 @@ import { ProsopoEnvError, ProsopoError, } from "@prosopo/common"; -import { loadBalancer } from "@prosopo/load-balancer"; import { buildUpdateState, getDefaultEvents, @@ -150,7 +149,7 @@ export function Manager( if (challenge.error) { updateState({ loading: false, - error: challenge.error, + error: challenge.error.message, }); } else { if (challenge.captchas.length <= 0) { @@ -245,16 +244,16 @@ export function Manager( }); } - const userRequestHashSignature = await signer.signRaw({ + const userTimestampSignature = await signer.signRaw({ address: account.account.address, - data: stringToHex(challenge.requestHash), + data: stringToHex(challenge[ApiParams.timestamp]), type: "bytes", }); // send the commitment to the provider const submission: TCaptchaSubmitResult = await captchaApi.submitCaptchaSolution( - userRequestHashSignature.signature, + userTimestampSignature.signature, challenge.requestHash, captchaSolution, challenge.timestamp, @@ -264,11 +263,6 @@ export function Manager( // mark as is human if solution has been approved const isHuman = submission[0].verified; - if (!isHuman) { - // user failed the captcha for some reason according to the provider - events.onFailed(); - } - // update the state with the result of the submission updateState({ submission, @@ -295,12 +289,14 @@ export function Manager( challenge.signature.provider.requestHash, }, [ApiParams.user]: { - [ApiParams.requestHash]: userRequestHashSignature.signature, + [ApiParams.timestamp]: userTimestampSignature.signature, }, }, }), ); setValidChallengeTimeout(); + } else { + events.onFailed(); } }, start, @@ -400,6 +396,7 @@ export function Manager( // clear timeout just in case a timer is still active (shouldn't be) clearTimeout(); updateState(defaultState()); + events.onReset(); }; /** diff --git a/packages/procaptcha/src/modules/ProsopoCaptchaApi.ts b/packages/procaptcha/src/modules/ProsopoCaptchaApi.ts index 91aa778659..9149acf334 100644 --- a/packages/procaptcha/src/modules/ProsopoCaptchaApi.ts +++ b/packages/procaptcha/src/modules/ProsopoCaptchaApi.ts @@ -85,7 +85,7 @@ export class ProsopoCaptchaApi implements ProcaptchaApiInterface { } public async submitCaptchaSolution( - userRequestHashSignature: string, + userTimestampSignature: string, requestHash: string, solutions: CaptchaSolution[], timestamp: string, @@ -118,7 +118,7 @@ export class ProsopoCaptchaApi implements ProcaptchaApiInterface { this.userAccount, timestamp, providerRequestHashSignature, - userRequestHashSignature, + userTimestampSignature, ); } catch (error) { throw new ProsopoDatasetError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE", { diff --git a/packages/provider/package.json b/packages/provider/package.json index 2ef09ac922..9d6c1d05c4 100644 --- a/packages/provider/package.json +++ b/packages/provider/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/provider", - "version": "2.1.3", + "version": "2.1.4", "author": "PROSOPO LIMITED ", "license": "Apache-2.0", "main": "./dist/index.js", @@ -27,16 +27,16 @@ "@polkadot/keyring": "12.6.2", "@polkadot/util": "12.6.2", "@polkadot/util-crypto": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/config": "2.1.3", - "@prosopo/contract": "2.1.3", - "@prosopo/database": "2.1.3", - "@prosopo/datasets": "2.1.3", - "@prosopo/env": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/types-database": "2.1.3", - "@prosopo/types-env": "2.1.3", - "@prosopo/util": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/config": "2.1.4", + "@prosopo/contract": "2.1.4", + "@prosopo/database": "2.1.4", + "@prosopo/datasets": "2.1.4", + "@prosopo/env": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/types-database": "2.1.4", + "@prosopo/types-env": "2.1.4", + "@prosopo/util": "2.1.4", "cron": "3.1.7", "express": "4.21.0", "node-fetch": "3.3.2", diff --git a/packages/provider/src/api/admin.ts b/packages/provider/src/api/admin.ts index 87864cc3e0..c95e905296 100644 --- a/packages/provider/src/api/admin.ts +++ b/packages/provider/src/api/admin.ts @@ -20,15 +20,11 @@ import { import type { ProviderEnvironment } from "@prosopo/types-env"; import { Router } from "express"; import { Tasks } from "../index.js"; -import { authMiddleware } from "./authMiddleware.js"; export function prosopoAdminRouter(env: ProviderEnvironment): Router { const router = Router(); const tasks = new Tasks(env); - // Use the authMiddleware for all routes in this router - router.use(authMiddleware(env)); - router.post(AdminApiPaths.UpdateDataset, async (req, res, next) => { try { const result = await tasks.datasetManager.providerSetDataset(req.body); diff --git a/packages/provider/src/api/authMiddleware.ts b/packages/provider/src/api/authMiddleware.ts index 8adc413976..87a63b17c8 100644 --- a/packages/provider/src/api/authMiddleware.ts +++ b/packages/provider/src/api/authMiddleware.ts @@ -14,26 +14,35 @@ import type { KeyringPair } from "@polkadot/keyring/types"; import { hexToU8a, isHex } from "@polkadot/util"; import { ProsopoApiError, ProsopoEnvError } from "@prosopo/common"; +import { ApiPrefix } from "@prosopo/types"; import type { ProviderEnvironment } from "@prosopo/types-env"; import type { NextFunction, Request, Response } from "express"; -import type { Tasks } from "../index.js"; export const authMiddleware = (env: ProviderEnvironment) => { return async (req: Request, res: Response, next: NextFunction) => { try { + // Stops this middleware from running on non-api routes like /json /favicon.ico etc + if (req.url.indexOf(ApiPrefix) === -1) { + next(); + return; + } + const { signature, timestamp } = extractHeaders(req); if (!env.pair) { - throw new ProsopoEnvError("CONTRACT.CANNOT_FIND_KEYPAIR"); + res.status(401).json({ + error: "Unauthorized", + message: new ProsopoEnvError("CONTRACT.CANNOT_FIND_KEYPAIR"), + }); + return; } - - verifyEnvironmentKeyPair(env); verifySignature(signature, timestamp, env.pair); next(); } catch (err) { console.error("Auth Middleware Error:", err); res.status(401).json({ error: "Unauthorized", message: err }); + return; } }; }; @@ -42,9 +51,15 @@ const extractHeaders = (req: Request) => { const signature = req.headers.signature as string; const timestamp = req.headers.timestamp as string; - if (!signature || !timestamp) { - throw new ProsopoApiError("CONTRACT.INVALID_DATA_FORMAT", { - context: { error: "Missing signature or block number", code: 400 }, + if (!timestamp) { + throw new ProsopoApiError("GENERAL.INVALID_TIMESTAMP", { + context: { error: "Missing timestamp", code: 400 }, + }); + } + + if (!signature) { + throw new ProsopoApiError("GENERAL.INVALID_SIGNATURE", { + context: { error: "Missing signature", code: 400 }, }); } @@ -70,12 +85,6 @@ const extractHeaders = (req: Request) => { return { signature, timestamp }; }; -const verifyEnvironmentKeyPair = (env: ProviderEnvironment) => { - if (!env.pair) { - throw new ProsopoEnvError("CONTRACT.CANNOT_FIND_KEYPAIR"); - } -}; - export const verifySignature = ( signature: string, timestamp: string, diff --git a/packages/provider/src/api/captcha.ts b/packages/provider/src/api/captcha.ts index 717ff7cfdb..783c71c14d 100644 --- a/packages/provider/src/api/captcha.ts +++ b/packages/provider/src/api/captcha.ts @@ -19,15 +19,18 @@ import { ApiPaths, type Captcha, CaptchaRequestBody, + type CaptchaRequestBodyTypeOutput, type CaptchaResponseBody, CaptchaSolutionBody, type CaptchaSolutionBodyType, type CaptchaSolutionResponse, type DappUserSolutionResult, GetPowCaptchaChallengeRequestBody, + type GetPowCaptchaChallengeRequestBodyTypeOutput, type GetPowCaptchaResponse, type PowCaptchaSolutionResponse, SubmitPowCaptchaSolutionBody, + type SubmitPowCaptchaSolutionBodyTypeOutput, type TGetImageCaptchaChallengePathAndParams, } from "@prosopo/types"; import type { ProviderEnvironment } from "@prosopo/types-env"; @@ -56,18 +59,50 @@ export function prosopoRouter(env: ProviderEnvironment): Router { */ const GetImageCaptchaChallengePath: TGetImageCaptchaChallengePathAndParams = `${ApiPaths.GetImageCaptchaChallenge}/:${ApiParams.datasetId}/:${ApiParams.user}/:${ApiParams.dapp}`; router.get(GetImageCaptchaChallengePath, async (req, res, next) => { + let parsed: CaptchaRequestBodyTypeOutput; + try { + parsed = CaptchaRequestBody.parse(req.params); + } catch (err) { + return next( + new ProsopoApiError("CAPTCHA.PARSE_ERROR", { + context: { code: 400, error: err }, + }), + ); + } + + const { datasetId, user, dapp } = parsed; + try { - const { datasetId, user, dapp } = CaptchaRequestBody.parse(req.params); - validateAddress(user, false, 42); validateAddress(dapp, false, 42); + } catch (err) { + return next( + new ProsopoApiError("API.INVALID_SITE_KEY", { + context: { code: 400, error: err, siteKey: dapp }, + }), + ); + } + + try { + validateAddress(dapp, false, 42); + } catch (err) { + return next( + new ProsopoApiError("API.INVALID_SITE_KEY", { + context: { code: 400, error: err, siteKey: dapp }, + }), + ); + } + + try { + validateAddress(user, false, 42); const clientRecord = await tasks.db.getClientRecord(dapp); if (!clientRecord) { - return res.json({ - error: req.i18n.t("API.SITE_KEY_NOT_REGISTERED"), - code: 200, - }); + return next( + new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", { + context: { code: 400, siteKey: dapp }, + }), + ); } const taskData = @@ -95,10 +130,14 @@ export function prosopoRouter(env: ProviderEnvironment): Router { }; return res.json(captchaResponse); } catch (err) { - tasks.logger.error(err); + tasks.logger.error({ err, params: req.params }); return next( new ProsopoApiError("API.BAD_REQUEST", { - context: { error: err, code: 400 }, + context: { + error: err, + code: 500, + params: req.params, + }, }), ); } @@ -119,29 +158,44 @@ export function prosopoRouter(env: ProviderEnvironment): Router { } catch (err) { return next( new ProsopoApiError("CAPTCHA.PARSE_ERROR", { - context: { code: 400, error: err }, + context: { code: 400, error: err, body: req.body }, + }), + ); + } + + const { user, dapp } = parsed; + + try { + validateAddress(dapp, false, 42); + } catch (err) { + return next( + new ProsopoApiError("API.INVALID_SITE_KEY", { + context: { code: 400, error: err, siteKey: dapp }, }), ); } try { + validateAddress(user, false, 42); + const clientRecord = await tasks.db.getClientRecord(parsed.dapp); if (!clientRecord) { - return res.json({ - error: req.i18n.t("API.SITE_KEY_NOT_REGISTERED"), - code: 200, - }); + return next( + new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", { + context: { code: 400, siteKey: dapp }, + }), + ); } // TODO allow the dapp to override the length of time that the request hash is valid for const result: DappUserSolutionResult = await tasks.imgCaptchaManager.dappUserSolution( - parsed[ApiParams.user], - parsed[ApiParams.dapp], + user, + dapp, parsed[ApiParams.requestHash], parsed[ApiParams.captchas], - parsed[ApiParams.signature].user.requestHash, + parsed[ApiParams.signature].user.timestamp, Number.parseInt(parsed[ApiParams.timestamp]), parsed[ApiParams.signature].provider.requestHash, req.ip || NO_IP_ADDRESS, @@ -156,10 +210,10 @@ export function prosopoRouter(env: ProviderEnvironment): Router { }; return res.json(returnValue); } catch (err) { - tasks.logger.error(err); + tasks.logger.error({ err, body: req.body }); return next( - new ProsopoApiError("API.UNKNOWN", { - context: { code: 400, error: err }, + new ProsopoApiError("API.BAD_REQUEST", { + context: { code: 500, siteKey: req.body.dapp }, }), ); } @@ -172,19 +226,40 @@ export function prosopoRouter(env: ProviderEnvironment): Router { * @param {string} dappAccount - Dapp address */ router.post(ApiPaths.GetPowCaptchaChallenge, async (req, res, next) => { + let parsed: GetPowCaptchaChallengeRequestBodyTypeOutput; + try { - const { user, dapp } = GetPowCaptchaChallengeRequestBody.parse(req.body); + parsed = GetPowCaptchaChallengeRequestBody.parse(req.body); + } catch (err) { + return next( + new ProsopoApiError("CAPTCHA.PARSE_ERROR", { + context: { code: 400, error: err }, + }), + ); + } - validateAddress(user, false, 42); + const { user, dapp } = parsed; + try { validateAddress(dapp, false, 42); + } catch (err) { + return next( + new ProsopoApiError("API.INVALID_SITE_KEY", { + context: { code: 400, error: err, siteKey: dapp }, + }), + ); + } + + try { + validateAddress(user, false, 42); const clientRecord = await tasks.db.getClientRecord(dapp); if (!clientRecord) { - return res.json({ - error: req.i18n.t("API.SITE_KEY_NOT_REGISTERED"), - code: 200, - }); + return next( + new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", { + context: { code: 400, siteKey: dapp }, + }), + ); } // TODO do something with domains @@ -194,7 +269,7 @@ export function prosopoRouter(env: ProviderEnvironment): Router { if (!origin) { return next( new ProsopoApiError("API.BAD_REQUEST", { - context: { error: "origin header not found", code: 400 }, + context: { error: "Origin header not found", code: 400 }, }), ); } @@ -232,10 +307,13 @@ export function prosopoRouter(env: ProviderEnvironment): Router { return res.json(getPowCaptchaResponse); } catch (err) { - tasks.logger.error(err); + tasks.logger.error({ err, body: req.body }); return next( new ProsopoApiError("API.BAD_REQUEST", { - context: { code: 400, error: err }, + context: { + code: 500, + siteKey: req.body.dapp, + }, }), ); } @@ -251,17 +329,49 @@ export function prosopoRouter(env: ProviderEnvironment): Router { * @param {number} verifiedTimeout - the valid length of captcha solution in ms */ router.post(ApiPaths.SubmitPowCaptchaSolution, async (req, res, next) => { + let parsed: SubmitPowCaptchaSolutionBodyTypeOutput; + try { - const { challenge, difficulty, signature, nonce, verifiedTimeout, dapp } = - SubmitPowCaptchaSolutionBody.parse(req.body); + parsed = SubmitPowCaptchaSolutionBody.parse(req.body); + } catch (err) { + return next( + new ProsopoApiError("CAPTCHA.PARSE_ERROR", { + context: { code: 400, error: err, body: req.body }, + }), + ); + } + + const { + challenge, + difficulty, + signature, + nonce, + verifiedTimeout, + dapp, + user, + } = parsed; + + try { + validateAddress(dapp, false, 42); + } catch (err) { + return next( + new ProsopoApiError("API.INVALID_SITE_KEY", { + context: { code: 400, error: err, siteKey: dapp }, + }), + ); + } + + try { + validateAddress(user, false, 42); const clientRecord = await tasks.db.getClientRecord(dapp); if (!clientRecord) { - return res.json({ - error: req.i18n.t("API.SITE_KEY_NOT_REGISTERED"), - code: 200, - }); + return next( + new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", { + context: { code: 400, siteKey: dapp }, + }), + ); } const verified = await tasks.powCaptchaManager.verifyPowCaptchaSolution( @@ -277,10 +387,13 @@ export function prosopoRouter(env: ProviderEnvironment): Router { const response: PowCaptchaSolutionResponse = { status: "ok", verified }; return res.json(response); } catch (err) { - tasks.logger.error(err); + tasks.logger.error({ err, body: req.body }); return next( new ProsopoApiError("API.BAD_REQUEST", { - context: { code: 400, error: err }, + context: { + code: 500, + siteKey: req.body.dapp, + }, }), ); } @@ -293,10 +406,10 @@ export function prosopoRouter(env: ProviderEnvironment): Router { try { return res.json({ version, ...{ message: "Provider online" } }); } catch (err) { - tasks.logger.error(err); + tasks.logger.error({ err, params: req.params }); return next( new ProsopoApiError("API.BAD_REQUEST", { - context: { code: 400, error: err }, + context: { code: 500 }, }), ); } diff --git a/packages/provider/src/api/errorHandler.ts b/packages/provider/src/api/errorHandler.ts index eaedd52a24..af79861a58 100644 --- a/packages/provider/src/api/errorHandler.ts +++ b/packages/provider/src/api/errorHandler.ts @@ -1,4 +1,3 @@ -import { type ProsopoApiError, ProsopoBaseError } from "@prosopo/common"; // Copyright 2021-2024 Prosopo (UK) Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,8 +12,11 @@ import { type ProsopoApiError, ProsopoBaseError } from "@prosopo/common"; // See the License for the specific language governing permissions and // limitations under the License. // We need the unused params to make express recognise this function as an error handler +import { type ProsopoApiError, ProsopoBaseError } from "@prosopo/common"; +import { i18n as i18next } from "@prosopo/locale"; +import type { ApiJsonError } from "@prosopo/types"; import type { NextFunction, Request, Response } from "express"; -import type { ZodError } from "zod"; +import { ZodError } from "zod"; export const handleErrors = ( err: ProsopoApiError | SyntaxError | ZodError, @@ -23,15 +25,38 @@ export const handleErrors = ( next: NextFunction, ) => { const code = "code" in err ? err.code : 400; + let message = err.message; + let jsonError: ApiJsonError = { code, message }; + + jsonError.message = message; + response.statusMessage = err.message; // unwrap the errors to get the actual error message - while (err instanceof ProsopoBaseError && err.context && err.context.error) { - err = err.context.error; + while (err instanceof ProsopoBaseError && err.context) { + // base error will not have a translation key + jsonError.code = + err.context.translationKey || err.translationKey || jsonError.code; + jsonError.message = err.message; + if (err.context.error) { + err = err.context.error; + } else { + break; + } } - const message = err.message; - response - .writeHead(code, JSON.stringify(message), { - "content-type": "application/json", - }) - .end(); + if (err instanceof ZodError) { + message = i18next.t("CAPTCHA.PARSE_ERROR"); + response.statusMessage = message; + if (typeof err.message === "object") { + jsonError = err.message; + } else { + jsonError.message = JSON.parse(err.message); + } + } + + jsonError.code = jsonError.code || code; + + response.set("content-type", "application/json"); + response.status(code); + response.send({ error: jsonError }); + response.end(); }; diff --git a/packages/provider/src/api/verify.ts b/packages/provider/src/api/verify.ts index 0a2606c513..984085fc15 100644 --- a/packages/provider/src/api/verify.ts +++ b/packages/provider/src/api/verify.ts @@ -12,24 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. +import { validateAddress } from "@polkadot/util-crypto/address"; import { ProsopoApiError } from "@prosopo/common"; import { ApiParams, ApiPaths, - CaptchaStatus, type ImageVerificationResponse, ServerPowCaptchaVerifyRequestBody, + type ServerPowCaptchaVerifyRequestBodyOutput, type VerificationResponse, VerifySolutionBody, + type VerifySolutionBodyTypeOutput, + decodeProcaptchaOutput, } from "@prosopo/types"; -import { decodeProcaptchaOutput } from "@prosopo/types"; import type { ProviderEnvironment } from "@prosopo/types-env"; -import express, { - type NextFunction, - type Request, - type Response, - type Router, -} from "express"; +import express, { type Router } from "express"; import { Tasks } from "../tasks/tasks.js"; import { verifySignature } from "./authMiddleware.js"; import { handleErrors } from "./errorHandler.js"; @@ -44,106 +41,6 @@ export function prosopoVerifyRouter(env: ProviderEnvironment): Router { const router = express.Router(); const tasks = new Tasks(env); - /** - * Verifies a solution and returns the verification response. - * @param {Response} res - Express response object. - * @param {Request} req - Express request object. - * @param {NextFunction} next - Express next function. - * @param {boolean} isDapp - Indicates whether the verification is for a dapp (true) or user (false). - */ - async function verifyImageSolution( - res: Response, - req: Request, - next: NextFunction, - isDapp: boolean, - ) { - const parsed = VerifySolutionBody.parse(req.body); - try { - const { dappSignature, token } = parsed; - const { user, dapp, timestamp, commitmentId } = - decodeProcaptchaOutput(token); - - // Verify using the appropriate pair based on isDapp flag - const keyPair = isDapp - ? env.keyring.addFromAddress(dapp) - : env.keyring.addFromAddress(user); - - // Will throw an error if the signature is invalid - verifySignature(dappSignature, timestamp.toString(), keyPair); - - const solution = await (commitmentId - ? tasks.imgCaptchaManager.getDappUserCommitmentById(commitmentId) - : tasks.imgCaptchaManager.getDappUserCommitmentByAccount(user, dapp)); - - // No solution exists - if (!solution) { - tasks.logger.debug("Not verified - no solution found"); - const noSolutionResponse: VerificationResponse = { - [ApiParams.status]: req.t("API.USER_NOT_VERIFIED_NO_SOLUTION"), - [ApiParams.verified]: false, - }; - return res.json(noSolutionResponse); - } - - if (isDapp) { - if (solution.serverChecked) { - const alreadyCheckedResponse: VerificationResponse = { - [ApiParams.status]: req.t("API.USER_ALREADY_VERIFIED"), - [ApiParams.verified]: false, - }; - return res.json(alreadyCheckedResponse); - } - // Mark solution as checked - await tasks.imgCaptchaManager.db.markDappUserCommitmentsChecked([ - solution.id, - ]); - } - - // A solution exists but is disapproved - if (solution.result.status === CaptchaStatus.disapproved) { - const disapprovedResponse: VerificationResponse = { - [ApiParams.status]: req.t("API.USER_NOT_VERIFIED"), - [ApiParams.verified]: false, - }; - return res.json(disapprovedResponse); - } - - const maxVerifiedTime = parsed.maxVerifiedTime || 60 * 1000; // Default to 1 minute - - // Check if solution was completed recently - if (maxVerifiedTime) { - const currentTime = Date.now(); - const timeSinceCompletion = currentTime - solution.requestedAtTimestamp; - - // A solution exists but has timed out - if (timeSinceCompletion > parsed.maxVerifiedTime) { - const expiredResponse: VerificationResponse = { - [ApiParams.status]: req.t("API.USER_NOT_VERIFIED_TIME_EXPIRED"), - [ApiParams.verified]: false, - }; - tasks.logger.debug("Not verified - time run out"); - return res.json(expiredResponse); - } - } - - const isApproved = solution.result.status === CaptchaStatus.approved; - const response: ImageVerificationResponse = { - [ApiParams.status]: req.t( - isApproved ? "API.USER_VERIFIED" : "API.USER_NOT_VERIFIED", - ), - [ApiParams.verified]: isApproved, - [ApiParams.commitmentId]: solution.id.toString(), - }; - return res.json(response); - } catch (err) { - return next( - new ProsopoApiError("API.BAD_REQUEST", { - context: { code: 400, error: err }, - }), - ); - } - } - /** * Verifies a dapp's solution as being approved or not * @@ -157,36 +54,67 @@ export function prosopoVerifyRouter(env: ProviderEnvironment): Router { router.post( ApiPaths.VerifyImageCaptchaSolutionDapp, async (req, res, next) => { + // We can be helpful and provide a more detailed error message when there are missing fields + let parsed: VerifySolutionBodyTypeOutput; try { - await verifyImageSolution(res, req, next, true); + parsed = VerifySolutionBody.parse(req.body); } catch (err) { return next( new ProsopoApiError("CAPTCHA.PARSE_ERROR", { - context: { code: 400, error: err }, + context: { code: 400, error: err, body: req.body }, }), ); } - }, - ); - /** - * Verifies a user's solution as being approved or not - * - * @param {string} user - Dapp User AccountId - * @param {string} dapp - Dapp Contract AccountId - * @param {string} dappUserSignature - The signature for dapp user - * @param {string} commitmentId - The captcha solution to look up - * @param {number} maxVerifiedTime - The maximum time in milliseconds since the blockNumber - */ - router.post( - ApiPaths.VerifyImageCaptchaSolutionUser, - async (req, res, next) => { + // We don't want to expose any other errors to the client except for specific situations + const { dappSignature, token } = parsed; try { - await verifyImageSolution(res, req, next, false); + // This can error if the token is invalid + const { user, dapp, timestamp, commitmentId } = + decodeProcaptchaOutput(token); + + // Do this before checking the db + validateAddress(dapp, false, 42); + validateAddress(user, false, 42); + + // Reject any unregistered site keys + const clientRecord = await tasks.db.getClientRecord(dapp); + if (!clientRecord) { + return next( + new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", { + context: { code: 400, siteKey: dapp }, + }), + ); + } + + // Verify using the appropriate pair based on isDapp flag + const keyPair = env.keyring.addFromAddress(dapp); + + // Will throw an error if the signature is invalid + + verifySignature(dappSignature, timestamp.toString(), keyPair); + + const response = + await tasks.imgCaptchaManager.verifyImageCaptchaSolution( + user, + dapp, + commitmentId, + parsed.maxVerifiedTime, + ); + + const verificationResponse: ImageVerificationResponse = { + [ApiParams.status]: req.t(response.status), + [ApiParams.verified]: response[ApiParams.verified], + ...(response.commitmentId && { + [ApiParams.commitmentId]: response.commitmentId, + }), + }; + res.json(verificationResponse); } catch (err) { + tasks.logger.error({ err, body: req.body }); return next( - new ProsopoApiError("CAPTCHA.PARSE_ERROR", { - context: { code: 400, error: err }, + new ProsopoApiError("API.BAD_REQUEST", { + context: { code: 500 }, }), ); } @@ -201,10 +129,39 @@ export function prosopoVerifyRouter(env: ProviderEnvironment): Router { * @param {number} verifiedTimeout - The maximum time in milliseconds to be valid */ router.post(ApiPaths.VerifyPowCaptchaSolution, async (req, res, next) => { + let parsed: ServerPowCaptchaVerifyRequestBodyOutput; + // We can be helpful and provide a more detailed error message when there are missing fields + try { + parsed = ServerPowCaptchaVerifyRequestBody.parse(req.body); + } catch (err) { + return next( + new ProsopoApiError("CAPTCHA.PARSE_ERROR", { + context: { code: 400, error: err, body: req.body }, + }), + ); + } + + // We don't want to expose any other errors to the client try { - const { token, dappSignature, verifiedTimeout } = - ServerPowCaptchaVerifyRequestBody.parse(req.body); - const { dapp, timestamp, challenge } = decodeProcaptchaOutput(token); + const { token, dappSignature, verifiedTimeout } = parsed; + + // This can error if the token is invalid + const { dapp, user, timestamp, challenge } = + decodeProcaptchaOutput(token); + + // Do this before checking the db + validateAddress(dapp, false, 42); + validateAddress(user, false, 42); + + // Reject any unregistered site keys + const clientRecord = await tasks.db.getClientRecord(dapp); + if (!clientRecord) { + return next( + new ProsopoApiError("API.SITE_KEY_NOT_REGISTERED", { + context: { code: 400, siteKey: dapp }, + }), + ); + } if (!challenge) { const unverifiedResponse: VerificationResponse = { @@ -234,10 +191,10 @@ export function prosopoVerifyRouter(env: ProviderEnvironment): Router { return res.json(verificationResponse); } catch (err) { - tasks.logger.error(err); + tasks.logger.error({ err, body: req.body }); return next( new ProsopoApiError("API.BAD_REQUEST", { - context: { code: 400, error: err }, + context: { code: 500 }, }), ); } diff --git a/packages/provider/src/index.ts b/packages/provider/src/index.ts index be7601ea32..70c2afa041 100644 --- a/packages/provider/src/index.ts +++ b/packages/provider/src/index.ts @@ -17,5 +17,6 @@ export * from "./api/captcha.js"; export * from "./api/verify.js"; export * from "./api/admin.js"; export * from "./api/errorHandler.js"; +export * from "./api/authMiddleware.js"; export * from "./schedulers/captchaScheduler.js"; export * from "./schedulers/getClientList.js"; diff --git a/packages/provider/src/tasks/imgCaptcha/imgCaptchaTasks.ts b/packages/provider/src/tasks/imgCaptcha/imgCaptchaTasks.ts index dbfe0b0bd9..a689f23aae 100644 --- a/packages/provider/src/tasks/imgCaptcha/imgCaptchaTasks.ts +++ b/packages/provider/src/tasks/imgCaptcha/imgCaptchaTasks.ts @@ -29,6 +29,7 @@ import { DEFAULT_IMAGE_CAPTCHA_TIMEOUT, type DappUserSolutionResult, type Hash, + type ImageVerificationResponse, type PendingCaptchaRequest, type RequestHeaders, } from "@prosopo/types"; @@ -175,7 +176,7 @@ export class ImgCaptchaManager { dappAccount: string, requestHash: string, captchas: CaptchaSolution[], - userRequestHashSignature: string, // the signature to indicate ownership of account + userTimestampSignature: string, // the signature to indicate ownership of account timestamp: number, providerRequestHashSignature: string, ipAddress: string, @@ -183,19 +184,19 @@ export class ImgCaptchaManager { ): Promise { // check that the signature is valid (i.e. the user has signed the request hash with their private key, proving they own their account) const verification = signatureVerify( - stringToHex(requestHash), - userRequestHashSignature, + stringToHex(timestamp.toString()), + userTimestampSignature, userAccount, ); if (!verification.isValid) { // the signature is not valid, so the user is not the owner of the account. May have given a false account address with good reputation in an attempt to impersonate - this.logger.info("Invalid user requestHash signature"); + this.logger.info("Invalid user timestamp signature"); throw new ProsopoEnvError("GENERAL.INVALID_SIGNATURE", { context: { failedFuncName: this.dappUserSolution.name, userAccount }, }); } - // check that the timestamp signature is valid and signed by the provider + // check that the requestHash signature is valid and signed by the provider const providerRequestHashSignatureVerify = signatureVerify( stringToHex(requestHash.toString()), providerRequestHashSignature, @@ -253,7 +254,7 @@ export class ImgCaptchaManager { providerAccount: this.pair.address, datasetId, result: { status: CaptchaStatus.pending }, - userSignature: userRequestHashSignature, + userSignature: userTimestampSignature, userSubmitted: true, serverChecked: false, requestedAtTimestamp: timestamp, @@ -403,4 +404,56 @@ export class ImgCaptchaManager { } return undefined; } + + async verifyImageCaptchaSolution( + user: string, + dapp: string, + commitmentId: string | undefined, + maxVerifiedTime?: number, + ): Promise { + const solution = await (commitmentId + ? this.getDappUserCommitmentById(commitmentId) + : this.getDappUserCommitmentByAccount(user, dapp)); + + // No solution exists + if (!solution) { + this.logger.debug("Not verified - no solution found"); + return { status: "API.USER_NOT_VERIFIED_NO_SOLUTION", verified: false }; + } + + if (solution.serverChecked) { + return { status: "API.USER_ALREADY_VERIFIED", verified: false }; + } + // Mark solution as checked + await this.db.markDappUserCommitmentsChecked([solution.id]); + + // A solution exists but is disapproved + if (solution.result.status === CaptchaStatus.disapproved) { + return { status: "API.USER_NOT_VERIFIED", verified: false }; + } + + maxVerifiedTime = maxVerifiedTime || 60 * 1000; // Default to 1 minute + + // Check if solution was completed recently + if (maxVerifiedTime) { + const currentTime = Date.now(); + const timeSinceCompletion = currentTime - solution.requestedAtTimestamp; + + // A solution exists but has timed out + if (timeSinceCompletion > maxVerifiedTime) { + this.logger.debug("Not verified - timed out"); + return { + status: "API.USER_NOT_VERIFIED_TIME_EXPIRED", + verified: false, + }; + } + } + + const isApproved = solution.result.status === CaptchaStatus.approved; + return { + status: isApproved ? "API.USER_VERIFIED" : "API.USER_NOT_VERIFIED", + verified: isApproved, + commitmentId: solution.id.toString(), + }; + } } diff --git a/packages/provider/src/tests/integration/imgCaptcha.integration.test.ts b/packages/provider/src/tests/integration/imgCaptcha.integration.test.ts index e44fe53c1f..490ed30e22 100644 --- a/packages/provider/src/tests/integration/imgCaptcha.integration.test.ts +++ b/packages/provider/src/tests/integration/imgCaptcha.integration.test.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. import { stringToU8a, u8aToHex } from "@polkadot/util"; -import { getPairAsync } from "@prosopo/contract"; +import { generateMnemonic, getPairAsync } from "@prosopo/contract"; import { datasetWithSolutionHashes } from "@prosopo/datasets"; import { ApiParams, @@ -32,29 +32,6 @@ const solutions = datasetWithSolutionHashes; const baseUrl = "http://localhost:9229"; const dappAccount = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"; const userAccount = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"; -const unRegisteredDappAccount = - "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL"; - -const getSolvedCaptchas = ( - captchas: Captcha[], - solutions: typeof datasetWithSolutionHashes.captchas, -) => - captchas.map((captcha) => { - const solvedCaptcha = solutions.find( - (solvedCaptcha) => - solvedCaptcha.captchaContentId === captcha.captchaContentId, - ); - if (!solvedCaptcha || !solvedCaptcha.solution) { - throw new Error("Solution not found for captcha"); - } - - return { - captchaContentId: captcha.captchaContentId, - captchaId: captcha.captchaId, - salt: solvedCaptcha.salt, - solution: solvedCaptcha.solution, - }; - }); describe("Image Captcha Integration Tests", () => { describe("GetImageCaptchaChallenge", () => { @@ -81,7 +58,8 @@ describe("Image Captcha Integration Tests", () => { it("should not supply an image captcha challenge to a Dapp User if the site key is not registered", async () => { const origin = "http://localhost"; - const getImageCaptchaURL: TGetImageCaptchaChallengeURL = `${baseUrl}${ApiPaths.GetImageCaptchaChallenge}/${solutions.datasetId}/${userAccount}/${unRegisteredDappAccount}`; + const [_mnemonic, unregisteredAccount] = await generateMnemonic(); + const getImageCaptchaURL: TGetImageCaptchaChallengeURL = `${baseUrl}${ApiPaths.GetImageCaptchaChallenge}/${solutions.datasetId}/${userAccount}/${unregisteredAccount}`; const response = await fetch(getImageCaptchaURL, { method: "GET", @@ -91,10 +69,29 @@ describe("Image Captcha Integration Tests", () => { }, }); - expect(response.status).toBe(200); + expect(response.status).toBe(400); const data = (await response.json()) as CaptchaResponseBody; expect(data).toHaveProperty("error"); - expect(data.error).toBe("Site key not registered"); + expect(data.error?.message).toBe("Site key not registered"); + }); + + it("should not supply an image captcha challenge to a Dapp User if an invalid site key is provided", async () => { + const invalidSiteKey = "junk"; + const origin = "http://localhost"; + const getImageCaptchaURL: TGetImageCaptchaChallengeURL = `${baseUrl}${ApiPaths.GetImageCaptchaChallenge}/${solutions.datasetId}/${userAccount}/${invalidSiteKey}`; + + const response = await fetch(getImageCaptchaURL, { + method: "GET", + headers: { + "Content-Type": "application/json", + Origin: origin, + }, + }); + + expect(response.status).toBe(400); + const data = (await response.json()) as CaptchaResponseBody; + expect(data).toHaveProperty("error"); + expect(data.error?.message).toBe("Invalid site key"); }); it("should fail if datasetID is incorrect", async () => { @@ -107,7 +104,7 @@ describe("Image Captcha Integration Tests", () => { }, }); - expect(response.status).toBe(400); + expect(response.status).toBe(500); }); }); describe("SubmitImageCaptchaSolution", () => { @@ -167,8 +164,8 @@ describe("Image Captcha Integration Tests", () => { [ApiParams.requestHash]: data.requestHash, [ApiParams.signature]: { [ApiParams.user]: { - [ApiParams.requestHash]: u8aToHex( - pair.sign(stringToU8a(data.requestHash)), + [ApiParams.timestamp]: u8aToHex( + pair.sign(stringToU8a(data.timestamp)), ), }, [ApiParams.provider]: data[ApiParams.signature][ApiParams.provider], diff --git a/packages/provider/src/tests/integration/powCaptcha.integration.test.ts b/packages/provider/src/tests/integration/powCaptcha.integration.test.ts index 5204bc7843..4819806da5 100644 --- a/packages/provider/src/tests/integration/powCaptcha.integration.test.ts +++ b/packages/provider/src/tests/integration/powCaptcha.integration.test.ts @@ -14,7 +14,7 @@ import { sha256 } from "@noble/hashes/sha256"; import { u8aToHex } from "@polkadot/util/u8a"; -import { getPairAsync } from "@prosopo/contract"; +import { generateMnemonic, getPairAsync } from "@prosopo/contract"; import { ApiParams, ApiPaths, @@ -273,6 +273,7 @@ describe("PoW Integration Tests", () => { }); it("should return an error for an unregistered site key", async () => { + const [_mnemonic, unregisteredAccount] = await generateMnemonic(); const userPair = await getPairAsync( dummyUserAccount.seed, undefined, @@ -291,52 +292,44 @@ describe("PoW Integration Tests", () => { "Content-Type": "application/json", Origin: origin, }, - body: JSON.stringify({ user: userAccount, dapp: dappAccount }), + body: JSON.stringify({ + user: userAccount, + dapp: unregisteredAccount, + }), }, ); - const challengeBody = (await captchaRes.json()) as GetPowCaptchaResponse; + const data = (await captchaRes.json()) as GetPowCaptchaResponse; - const challenge = challengeBody.challenge; - const difficulty = challengeBody.difficulty; - const signature = challengeBody.signature; - const nonce = failPoW(challenge, difficulty); - const verifiedTimeout = 120000; + expect(data).toHaveProperty("error"); + expect(data.error?.message).toBe("Site key not registered"); + }); + }); - const dapp = "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y"; - const body: SubmitPowCaptchaSolutionBodyType = { - challenge, - difficulty, - [ApiParams.signature]: { - [ApiParams.provider]: signature[ApiParams.provider], - [ApiParams.user]: { - [ApiParams.timestamp]: u8aToHex( - userPair.sign(challengeBody[ApiParams.timestamp].toString()), - ), - }, - }, - nonce, - verifiedTimeout, - user: userPair.address, - dapp, - }; - const response = await fetch( - `${baseUrl}${ApiPaths.SubmitPowCaptchaSolution}`, - { - method: "POST", - headers: { - Connection: "close", - "Content-Type": "application/json", - }, - body: JSON.stringify(body), - }, - ); + it("should return an error for an invalid site key", async () => { + const userPair = await getPairAsync( + dummyUserAccount.seed, + undefined, + "sr25519", + 42, + ); + const userAccount = userPair.address; + const origin = "http://localhost"; + const invalidSiteKey = "junk"; + + const captchaRes = await fetch(`${baseUrl}${getPowCaptchaChallengePath}`, { + method: "POST", + headers: { + Connection: "close", + "Content-Type": "application/json", + Origin: origin, + }, + body: JSON.stringify({ user: userAccount, dapp: invalidSiteKey }), + }); - expect(response.status).toBe(200); + const challengeBody = (await captchaRes.json()) as GetPowCaptchaResponse; - const data = (await response.json()) as PowCaptchaSolutionResponse; - expect(data).toHaveProperty("error"); - expect(data.error).toBe("Site key not registered"); - }); + expect(challengeBody).toHaveProperty("error"); + expect(challengeBody.error?.message).toBe("Invalid site key"); }); }); diff --git a/packages/provider/src/tests/integration/registerSitekey.ts b/packages/provider/src/tests/integration/registerSitekey.ts index df3bc810ba..b39fe0316d 100644 --- a/packages/provider/src/tests/integration/registerSitekey.ts +++ b/packages/provider/src/tests/integration/registerSitekey.ts @@ -15,21 +15,25 @@ import { ProviderDatabase } from "@prosopo/database"; import type { ClientRecord } from "@prosopo/types-database"; export const registerSiteKey = async (siteKey: string): Promise => { - const username = process.env.PROSOPO_DATABASE_USERNAME || "root"; - const pw = process.env.PROSOPO_DATABASE_PASSWORD || "root"; - const host = process.env.PROSOPO_DATABASE_HOST || "localhost"; - const port = process.env.PROSOPO_DATABASE_PORT || 27017; - const db = new ProviderDatabase( - `mongodb://${username}:${pw}@${host}:${port}`, - process.env.PROSOPO_DATABASE_NAME || "prosopo", - process.env.PROSOPO_DATABASE_AUTH_SOURCE || "admin", - ); - await db.connect(); - console.log("Registering site key", siteKey); - await db.updateClientRecords([ - { - account: siteKey, - } as ClientRecord, - ]); - await db.connection?.close(); + try { + const username = process.env.PROSOPO_DATABASE_USERNAME || "root"; + const pw = process.env.PROSOPO_DATABASE_PASSWORD || "root"; + const host = process.env.PROSOPO_DATABASE_HOST || "localhost"; + const port = process.env.PROSOPO_DATABASE_PORT || 27017; + const db = new ProviderDatabase( + `mongodb://${username}:${pw}@${host}:${port}`, + process.env.PROSOPO_DATABASE_NAME || "prosopo", + process.env.PROSOPO_DATABASE_AUTH_SOURCE || "admin", + ); + await db.connect(); + console.log("Registering site key", siteKey); + await db.updateClientRecords([ + { + account: siteKey, + } as ClientRecord, + ]); + await db.connection?.close(); + } catch (err) { + throw new Error(`Failed to register site key: ${err}`); + } }; diff --git a/packages/provider/src/tests/unit/api/authMiddleware.unit.test.ts b/packages/provider/src/tests/unit/api/authMiddleware.unit.test.ts index b554cad614..031027fe8f 100644 --- a/packages/provider/src/tests/unit/api/authMiddleware.unit.test.ts +++ b/packages/provider/src/tests/unit/api/authMiddleware.unit.test.ts @@ -37,6 +37,7 @@ const mockEnv = { describe("authMiddleware", () => { it("should call next() if signature is valid", async () => { const mockReq = { + url: "/v1/prosopo/provider/captcha/image", headers: { signature: "0x1234", timestamp: new Date().getTime(), @@ -63,6 +64,7 @@ describe("authMiddleware", () => { it("should return 401 if signature is invalid", async () => { const mockReq = { + url: "/v1/prosopo/provider/captcha/image", headers: { signature: "0x1234", timestamp: new Date().getTime(), @@ -93,6 +95,7 @@ describe("authMiddleware", () => { it("should return 401 if key pair is missing", async () => { const mockReq = { + url: "/v1/prosopo/provider/captcha/image", headers: { signature: "0x1234", timestamp: new Date().getTime(), @@ -120,4 +123,27 @@ describe("authMiddleware", () => { message: expect.any(ProsopoEnvError), }); }); + + it("should call next() immediately if url does not contain /v1/prosopo", async () => { + const mockReq = { + url: "/favicon.ico", + headers: { + signature: "0x1234", + timestamp: new Date().getTime(), + }, + } as unknown as Request; + + const mockRes = { + status: vi.fn().mockReturnThis(), + json: vi.fn(), + } as unknown as Response; + + const mockNext = vi.fn() as unknown as NextFunction; + + const middleware = authMiddleware(mockEnv); + await middleware(mockReq, mockRes, mockNext); + + expect(mockNext).toHaveBeenCalled(); + expect(mockRes.status).not.toHaveBeenCalled(); + }); }); diff --git a/packages/provider/src/tests/unit/api/errorHandler.unit.test.ts b/packages/provider/src/tests/unit/api/errorHandler.unit.test.ts index 0c62cf69c0..fe23c2c749 100644 --- a/packages/provider/src/tests/unit/api/errorHandler.unit.test.ts +++ b/packages/provider/src/tests/unit/api/errorHandler.unit.test.ts @@ -26,6 +26,9 @@ describe("handleErrors", () => { const mockRequest = {} as Request; const mockResponse = { writeHead: vi.fn().mockReturnThis(), + set: vi.fn().mockReturnThis(), + status: vi.fn().mockReturnThis(), + send: vi.fn(), end: vi.fn(), } as unknown as Response; const mockNext = vi.fn() as unknown as NextFunction; @@ -34,13 +37,17 @@ describe("handleErrors", () => { handleErrors(error, mockRequest, mockResponse, mockNext); - expect(mockResponse.writeHead).toHaveBeenCalledWith( - 500, - JSON.stringify("Invalid data format"), - { - "content-type": "application/json", - }, + expect(mockResponse.set).toHaveBeenCalledWith( + "content-type", + "application/json", ); + expect(mockResponse.send).toHaveBeenCalledWith({ + error: { + code: "CONTRACT.INVALID_DATA_FORMAT", + message: "Invalid data format", + }, + }); + expect(mockResponse.status).toHaveBeenCalledWith(500); expect(mockResponse.end).toHaveBeenCalled(); }); @@ -48,26 +55,30 @@ describe("handleErrors", () => { const mockRequest = {} as Request; const mockResponse = { writeHead: vi.fn().mockReturnThis(), + set: vi.fn().mockReturnThis(), + status: vi.fn().mockReturnThis(), + send: vi.fn(), end: vi.fn(), } as unknown as Response; const mockNext = vi.fn() as unknown as NextFunction; const [len, max] = [100, 50]; - const error = new SyntaxError( - `Input length: ${len}, exceeds maximum allowed length: ${max}`, - ); + const message = `Input length: ${len}, exceeds maximum allowed length: ${max}`; + const error = new SyntaxError(message); handleErrors(error, mockRequest, mockResponse, mockNext); - expect(mockResponse.writeHead).toHaveBeenCalledWith( - 400, - JSON.stringify( - `Input length: ${len}, exceeds maximum allowed length: ${max}`, - ), - { - "content-type": "application/json", - }, + expect(mockResponse.set).toHaveBeenCalledWith( + "content-type", + "application/json", ); + expect(mockResponse.status).toHaveBeenCalledWith(400); + expect(mockResponse.send).toHaveBeenCalledWith({ + error: { + message, + code: 400, + }, + }); expect(mockResponse.end).toHaveBeenCalled(); }); @@ -75,16 +86,29 @@ describe("handleErrors", () => { const mockRequest = {} as Request; const mockResponse = { writeHead: vi.fn().mockReturnThis(), + set: vi.fn().mockReturnThis(), + status: vi.fn().mockReturnThis(), + send: vi.fn(), end: vi.fn(), } as unknown as Response; const mockNext = vi.fn() as unknown as NextFunction; - const error = new ZodError([]); + const zodError = { + code: "custom" as const, + message: "Invalid input", + path: ["some", "variable"], + }; + const error = new ZodError([zodError]); handleErrors(error, mockRequest, mockResponse, mockNext); - expect(mockResponse.writeHead).toHaveBeenCalledWith(400, `\"[]\"`, { - "content-type": "application/json", + expect(mockResponse.set).toHaveBeenCalledWith( + "content-type", + "application/json", + ); + expect(mockResponse.status).toHaveBeenCalledWith(400); + expect(mockResponse.send).toHaveBeenCalledWith({ + error: { code: 400, message: [zodError] }, }); expect(mockResponse.end).toHaveBeenCalled(); }); @@ -93,6 +117,9 @@ describe("handleErrors", () => { const mockRequest = {} as Request; const mockResponse = { writeHead: vi.fn().mockReturnThis(), + set: vi.fn().mockReturnThis(), + status: vi.fn().mockReturnThis(), + send: vi.fn(), end: vi.fn(), } as unknown as Response; const mockNext = vi.fn() as unknown as NextFunction; @@ -102,13 +129,17 @@ describe("handleErrors", () => { handleErrors(apiError, mockRequest, mockResponse, mockNext); - expect(mockResponse.writeHead).toHaveBeenCalledWith( - 500, - JSON.stringify("Environment not ready"), - { - "content-type": "application/json", - }, + expect(mockResponse.set).toHaveBeenCalledWith( + "content-type", + "application/json", ); + expect(mockResponse.status).toHaveBeenCalledWith(500); + expect(mockResponse.send).toHaveBeenCalledWith({ + error: { + code: "GENERAL.ENVIRONMENT_NOT_READY", + message: "Environment not ready", + }, + }); expect(mockResponse.end).toHaveBeenCalled(); }); }); diff --git a/packages/server/package.json b/packages/server/package.json index 7a53beed13..08926502ad 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/server", - "version": "2.1.3", + "version": "2.1.4", "description": "NodeJS package for server side communication with the prosopo captcha client", "main": "./dist/index.js", "type": "module", @@ -34,13 +34,13 @@ "dependencies": { "@polkadot/keyring": "12.6.2", "@polkadot/util": "12.6.2", - "@prosopo/api": "2.1.3", - "@prosopo/common": "2.1.3", - "@prosopo/contract": "2.1.3", - "@prosopo/types": "2.1.3" + "@prosopo/api": "2.1.4", + "@prosopo/common": "2.1.4", + "@prosopo/contract": "2.1.4", + "@prosopo/types": "2.1.4" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", diff --git a/packages/server/src/config.ts b/packages/server/src/config.ts index 9e5cc61b83..2c34b38b2a 100644 --- a/packages/server/src/config.ts +++ b/packages/server/src/config.ts @@ -15,7 +15,7 @@ import { ProsopoServerConfigSchema } from "@prosopo/types"; export const getServerConfig = () => ProsopoServerConfigSchema.parse({ - defaultEnvironment: process.env.PROSOPO_DEFAULT_ENVIRONMENT, // enviromental variables + defaultEnvironment: process.env.PROSOPO_DEFAULT_ENVIRONMENT, // environmental variables serverUrl: getServerUrl(), dappName: process.env.PROSOPO_DAPP_NAME || "client-example-server", account: { diff --git a/packages/tx/package.json b/packages/tx/package.json index dcad048840..aeff84c20f 100644 --- a/packages/tx/package.json +++ b/packages/tx/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/tx", - "version": "2.1.3", + "version": "2.1.4", "author": "PROSOPO LIMITED ", "license": "Apache-2.0", "scripts": { @@ -37,11 +37,11 @@ "@polkadot/types": "10.13.1", "@polkadot/types-codec": "10.13.1", "@polkadot/util": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/types": "2.1.3" + "@prosopo/common": "2.1.4", + "@prosopo/types": "2.1.4" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", diff --git a/packages/types-database/package.json b/packages/types-database/package.json index a14a2dd8d3..3136bf4254 100644 --- a/packages/types-database/package.json +++ b/packages/types-database/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/types-database", - "version": "2.1.3", + "version": "2.1.4", "description": "Types for prosopo database", "main": "dist/index.js", "type": "module", @@ -31,14 +31,14 @@ }, "homepage": "https://github.com/prosopo/captcha#readme", "dependencies": { - "@prosopo/common": "2.1.3", - "@prosopo/types": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/types": "2.1.4", "mongodb": "6.9.0", "mongoose": "8.6.2", "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", diff --git a/packages/types-env/package.json b/packages/types-env/package.json index a1e2ee8a4d..3e25344a76 100644 --- a/packages/types-env/package.json +++ b/packages/types-env/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/types-env", - "version": "2.1.3", + "version": "2.1.4", "description": "Types for prosopo environment", "main": "dist/index.js", "type": "module", @@ -32,12 +32,12 @@ "homepage": "https://github.com/prosopo/captcha#readme", "dependencies": { "@polkadot/keyring": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/types": "2.1.3", - "@prosopo/types-database": "2.1.3" + "@prosopo/common": "2.1.4", + "@prosopo/types": "2.1.4", + "@prosopo/types-database": "2.1.4" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5", diff --git a/packages/types/package.json b/packages/types/package.json index b0b81cac2f..37234ba519 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/types", - "version": "2.1.3", + "version": "2.1.4", "description": "Types for prosopo TypeScript packages", "main": "dist/index.js", "type": "module", @@ -38,13 +38,13 @@ "@polkadot/types": "10.13.1", "@polkadot/types-codec": "10.13.1", "@polkadot/util": "12.6.2", - "@prosopo/common": "2.1.3", - "@prosopo/locale": "2.1.3", + "@prosopo/common": "2.1.4", + "@prosopo/locale": "2.1.4", "scale-ts": "1.6.0", "zod": "3.23.8" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@types/node": "22.5.5", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", diff --git a/packages/types/src/api/api.ts b/packages/types/src/api/api.ts index b8afe8b1f6..5e117ba425 100644 --- a/packages/types/src/api/api.ts +++ b/packages/types/src/api/api.ts @@ -45,14 +45,6 @@ export interface ProviderApiInterface { commitmentId?: string, maxVerifiedTime?: number, ): Promise; - verifyUser( - dapp: string, - userAccount: string, - blockNumber: number, - dappUserSignature: string, - commitmentId?: string, - maxVerifiedTime?: number, - ): Promise; getPowCaptchaChallenge( userAccount: string, dappAccount: string, diff --git a/packages/types/src/procaptcha-bundle/index.ts b/packages/types/src/procaptcha-bundle/index.ts index 90dc527ee0..cb1e557c29 100644 --- a/packages/types/src/procaptcha-bundle/index.ts +++ b/packages/types/src/procaptcha-bundle/index.ts @@ -34,5 +34,7 @@ export interface ProcaptchaRenderOptions { "open-callback"?: string | (() => void); "close-callback"?: string | (() => void); "error-callback"?: string | (() => void); + "failed-callback"?: string | (() => void); + "reset-callback"?: string | (() => void); language?: typeof LanguageSchema; } diff --git a/packages/types/src/procaptcha/api.ts b/packages/types/src/procaptcha/api.ts index 571ee5338d..e371d4dd43 100644 --- a/packages/types/src/procaptcha/api.ts +++ b/packages/types/src/procaptcha/api.ts @@ -24,7 +24,7 @@ export interface ProcaptchaApiInterface { dappAccount: string; web2: boolean; submitCaptchaSolution( - userRequestHashSignature: string, + userTimestampSignature: string, requestHash: string, solutions: CaptchaSolution[], timestamp: string, diff --git a/packages/types/src/procaptcha/manager.ts b/packages/types/src/procaptcha/manager.ts index b1d9110de0..ba417d6447 100644 --- a/packages/types/src/procaptcha/manager.ts +++ b/packages/types/src/procaptcha/manager.ts @@ -51,6 +51,7 @@ export interface ProcaptchaEvents { onFailed: () => void; onOpen: () => void; onClose: () => void; + onReset: () => void; } /** diff --git a/packages/types/src/procaptcha/token.ts b/packages/types/src/procaptcha/token.ts index cf1251169f..651540ee04 100644 --- a/packages/types/src/procaptcha/token.ts +++ b/packages/types/src/procaptcha/token.ts @@ -28,6 +28,12 @@ export const ChallengeSignatureSchema = object({ export type ChallengeSignature = zInfer; +export const TimestampSignatureSchema = object({ + [ApiParams.timestamp]: string(), +}); + +export type TimestampSignature = zInfer; + export const SignatureTypesSchema = object({ [ApiParams.challenge]: string().optional(), [ApiParams.requestHash]: string().optional(), diff --git a/packages/types/src/provider/api.ts b/packages/types/src/provider/api.ts index e98d090a00..9800c6681a 100644 --- a/packages/types/src/provider/api.ts +++ b/packages/types/src/provider/api.ts @@ -43,8 +43,11 @@ import { ProcaptchaTokenSpec, type RequestHashSignature, RequestHashSignatureSchema, + TimestampSignatureSchema, } from "../procaptcha/index.js"; +export const ApiPrefix = "/v1/prosopo" as const; + export enum ApiPaths { GetImageCaptchaChallenge = "/v1/prosopo/provider/captcha/image", GetPowCaptchaChallenge = "/v1/prosopo/provider/captcha/pow", @@ -52,7 +55,6 @@ export enum ApiPaths { SubmitPowCaptchaSolution = "/v1/prosopo/provider/pow/solution", VerifyPowCaptchaSolution = "/v1/prosopo/provider/pow/verify", VerifyImageCaptchaSolutionDapp = "/v1/prosopo/provider/image/dapp/verify", - VerifyImageCaptchaSolutionUser = "/v1/prosopo/provider/image/user/verify", GetProviderStatus = "/v1/prosopo/provider/status", GetProviderDetails = "/v1/prosopo/provider/details", SubmitUserEvents = "/v1/prosopo/provider/events", @@ -86,7 +88,6 @@ export const ProviderDefaultRateLimits = { [ApiPaths.SubmitPowCaptchaSolution]: { windowMs: 60000, limit: 60 }, [ApiPaths.VerifyPowCaptchaSolution]: { windowMs: 60000, limit: 60 }, [ApiPaths.VerifyImageCaptchaSolutionDapp]: { windowMs: 60000, limit: 60 }, - [ApiPaths.VerifyImageCaptchaSolutionUser]: { windowMs: 60000, limit: 60 }, [ApiPaths.GetProviderStatus]: { windowMs: 60000, limit: 60 }, [ApiPaths.GetProviderDetails]: { windowMs: 60000, limit: 60 }, [ApiPaths.SubmitUserEvents]: { windowMs: 60000, limit: 60 }, @@ -172,6 +173,7 @@ export const CaptchaRequestBody = object({ }); export type CaptchaRequestBodyType = zInfer; +export type CaptchaRequestBodyTypeOutput = output; export interface CaptchaResponseBody extends ApiResponse { [ApiParams.captchas]: Captcha[]; @@ -189,7 +191,7 @@ export const CaptchaSolutionBody = object({ [ApiParams.requestHash]: string(), [ApiParams.timestamp]: string(), [ApiParams.signature]: object({ - [ApiParams.user]: RequestHashSignatureSchema, + [ApiParams.user]: TimestampSignatureSchema, [ApiParams.provider]: RequestHashSignatureSchema, }), }); @@ -222,9 +224,14 @@ export interface ProviderRegistered { status: "Registered" | "Unregistered"; } +export type ApiJsonError = { + message: string; + code: number; +}; + export interface ApiResponse { [ApiParams.status]: string; - [ApiParams.error]?: string; + [ApiParams.error]?: ApiJsonError; } export interface VerificationResponse extends ApiResponse { @@ -246,7 +253,7 @@ export interface GetPowCaptchaResponse extends ApiResponse { export interface PowCaptchaSolutionResponse extends ApiResponse { [ApiParams.verified]: boolean; - [ApiParams.error]?: string; + [ApiParams.error]?: ApiJsonError; } /** @@ -263,6 +270,10 @@ export const ServerPowCaptchaVerifyRequestBody = object({ .default(DEFAULT_POW_CAPTCHA_VERIFIED_TIMEOUT), }); +export type ServerPowCaptchaVerifyRequestBodyOutput = output< + typeof ServerPowCaptchaVerifyRequestBody +>; + export const GetPowCaptchaChallengeRequestBody = object({ [ApiParams.user]: string(), [ApiParams.dapp]: string(), @@ -272,6 +283,10 @@ export type GetPowCaptchaChallengeRequestBodyType = zInfer< typeof GetPowCaptchaChallengeRequestBody >; +export type GetPowCaptchaChallengeRequestBodyTypeOutput = output< + typeof GetPowCaptchaChallengeRequestBody +>; + export type ServerPowCaptchaVerifyRequestBodyType = zInfer< typeof ServerPowCaptchaVerifyRequestBody >; @@ -299,6 +314,10 @@ export type SubmitPowCaptchaSolutionBodyType = zInfer< typeof SubmitPowCaptchaSolutionBody >; +export type SubmitPowCaptchaSolutionBodyTypeOutput = output< + typeof SubmitPowCaptchaSolutionBody +>; + export const VerifyPowCaptchaSolutionBody = object({ [ApiParams.siteKey]: string(), }); diff --git a/packages/util/package.json b/packages/util/package.json index 69159bde07..b7745aa671 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/util", - "version": "2.1.3", + "version": "2.1.4", "author": "PROSOPO LIMITED ", "license": "Apache-2.0", "private": false, @@ -32,7 +32,7 @@ "types": "./dist/index.d.ts", "dependencies": { "@noble/hashes": "1.5.0", - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "dotenv": "16.4.5", "lodash": "4.17.21", "seedrandom": "3.0.5" diff --git a/packages/web-components/package.json b/packages/web-components/package.json index 01246e330d..4244d4b53a 100644 --- a/packages/web-components/package.json +++ b/packages/web-components/package.json @@ -1,6 +1,6 @@ { "name": "@prosopo/web-components", - "version": "2.1.3", + "version": "2.1.4", "description": "Non business logic utilities for web applications", "main": "dist/index.js", "type": "module", @@ -36,7 +36,7 @@ "react": "18.3.1" }, "devDependencies": { - "@prosopo/config": "2.1.3", + "@prosopo/config": "2.1.4", "@vitest/coverage-v8": "2.1.1", "concurrently": "9.0.1", "npm-run-all": "4.1.5",