Compare commits

...

226 Commits

Author SHA1 Message Date
Aravinth Manivannan
b624c7d326 fix: s/docker-compose/docker compose/ 2025-10-02 14:33:54 +05:30
Aravinth Manivannan
60a6ad92d9 fix: port eslint config 2025-10-02 14:19:22 +05:30
Aravinth Manivannan
3c0ed48aac fix: gh CI: update node 2025-10-02 14:04:15 +05:30
Aravinth Manivannan
a05f8ec6f0 fix: remove tarpaulin annotations 2025-10-02 14:00:16 +05:30
Aravinth Manivannan
5c29c2e71e fix: restore gh actions 2025-10-02 13:51:12 +05:30
Aravinth Manivannan
721d880a7a Merge pull request 'chore(deps): update rust crate thiserror to v2.0.17' (#107) from renovate/thiserror-2.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/107
2025-10-02 13:45:14 +05:30
Aravinth Manivannan
4ad2fe36cc Merge pull request 'chore(deps): update jest monorepo to v30 (major)' (#105) from renovate/major-jest-monorepo into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/105
2025-10-02 13:45:06 +05:30
Renovate Bot
140889ff6d chore(deps): update jest monorepo to v30 2025-10-02 08:12:29 +00:00
Renovate Bot
324d13f0d4 chore(deps): update rust crate thiserror to v2.0.17 2025-10-02 08:12:10 +00:00
Aravinth Manivannan
840f14868a Merge pull request 'chore(deps): update dependency webpack-dev-server to v5' (#47) from renovate/webpack-dev-server-5.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/47
2025-10-02 13:33:36 +05:30
Aravinth Manivannan
5c476090d1 Merge pull request 'chore(deps): update dependency sinon to v21' (#104) from renovate/sinon-21.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/104
2025-10-02 13:33:09 +05:30
Aravinth Manivannan
f1661d0f7f Merge pull request 'chore(deps): update dependency jsdom to v27' (#103) from renovate/jsdom-27.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/103
2025-10-02 13:31:54 +05:30
Aravinth Manivannan
6eda1e8a34 Merge pull request 'chore(deps): update dependency css-minimizer-webpack-plugin to v7' (#41) from renovate/css-minimizer-webpack-plugin-7.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/41
2025-10-02 13:31:37 +05:30
Aravinth Manivannan
848c3f80bf Merge pull request 'chore(deps): update rust crate uuid to v1.18.1' (#80) from renovate/uuid-1.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/80
2025-10-02 13:31:01 +05:30
Renovate Bot
a66f27c084 chore(deps): update dependency webpack-dev-server to v5 2025-10-02 07:58:22 +00:00
Renovate Bot
c886989c25 chore(deps): update dependency sinon to v21 2025-10-02 07:58:15 +00:00
Renovate Bot
982243aff0 chore(deps): update dependency jsdom to v27 2025-10-02 07:58:10 +00:00
Renovate Bot
31f96081a2 chore(deps): update dependency css-minimizer-webpack-plugin to v7 2025-10-02 07:58:00 +00:00
Renovate Bot
8507d130b0 chore(deps): update rust crate uuid to v1.18.1 2025-10-02 07:57:39 +00:00
Aravinth Manivannan
355853b5ea Merge pull request 'chore(deps): update postgres docker tag to v18' (#106) from renovate/postgres-18.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/106
2025-10-02 13:20:35 +05:30
Aravinth Manivannan
ca6193241b Merge pull request 'chore(deps): update dependency @redocly/cli to v2' (#102) from renovate/redocly-cli-2.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/102
2025-10-02 13:17:32 +05:30
Aravinth Manivannan
1a73c2d10b Merge pull request 'chore(deps): update rust crate num_cpus to v1.17.0' (#101) from renovate/num_cpus-1.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/101
2025-10-02 13:16:23 +05:30
Aravinth Manivannan
c0695a8b40 Merge pull request 'fix(deps): update rust crate reqwest to 0.12.0' (#34) from renovate/reqwest-0.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/34
2025-10-02 13:16:02 +05:30
Aravinth Manivannan
4c71f3ae5e Merge pull request 'chore(deps): update dependency ts-jest to v29.4.4' (#99) from renovate/ts-jest-29.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/99
2025-10-02 13:15:49 +05:30
Aravinth Manivannan
fb0b9ff71c Merge pull request 'chore(deps): update rust crate actix-rt to v2.11.0' (#100) from renovate/actix-rt-2.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/100
2025-10-02 13:15:25 +05:30
Aravinth Manivannan
29d4cfff7d Merge pull request 'chore(deps): update rust crate url to v2.5.7' (#97) from renovate/url-2.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/97
2025-10-02 13:13:25 +05:30
Aravinth Manivannan
85466e360c Merge pull request 'chore(deps): update rust crate serde_json to v1.0.145' (#96) from renovate/serde_json-1.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/96
2025-10-02 13:12:49 +05:30
Aravinth Manivannan
df242c8fda Merge pull request 'chore(deps): update rust crate serde to v1.0.228' (#95) from renovate/serde-monorepo into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/95
2025-10-02 13:12:40 +05:30
Aravinth Manivannan
628166ea15 Merge pull request 'chore(deps): update rust crate openssl to v0.10.73' (#94) from renovate/openssl-0.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/94
2025-10-02 13:12:10 +05:30
Aravinth Manivannan
45c7b39c32 Merge pull request 'chore(deps): update rust crate log to v0.4.28' (#93) from renovate/log-0.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/93
2025-10-02 13:10:32 +05:30
Renovate Bot
1b379f47ac chore(deps): update postgres docker tag to v18 2025-10-02 07:26:22 +00:00
Renovate Bot
d54404a493 chore(deps): update dependency @redocly/cli to v2 2025-10-02 07:25:42 +00:00
Renovate Bot
464b25d957 fix(deps): update rust crate reqwest to 0.12.0 2025-10-02 07:25:27 +00:00
Renovate Bot
9aa5789591 chore(deps): update rust crate num_cpus to v1.17.0 2025-10-02 07:25:12 +00:00
Renovate Bot
e5dd0711ca chore(deps): update rust crate actix-rt to v2.11.0 2025-10-02 07:25:03 +00:00
Renovate Bot
c472cf678b chore(deps): update dependency ts-jest to v29.4.4 2025-10-02 07:24:57 +00:00
Renovate Bot
adfb887657 chore(deps): update rust crate url to v2.5.7 2025-10-02 07:24:50 +00:00
Renovate Bot
a4de37c7d2 chore(deps): update rust crate serde_json to v1.0.145 2025-10-02 07:24:45 +00:00
Renovate Bot
ef1afa1fa9 chore(deps): update rust crate serde to v1.0.228 2025-10-02 07:24:43 +00:00
Renovate Bot
839444834e chore(deps): update rust crate openssl to v0.10.73 2025-10-02 07:24:41 +00:00
Renovate Bot
9c20f2d222 chore(deps): update rust crate log to v0.4.28 2025-10-02 07:24:38 +00:00
Aravinth Manivannan
8c8dd9c1d3 Merge pull request 'chore(deps): update rust crate lettre to v0.11.18' (#92) from renovate/lettre-0.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/92
2025-10-02 12:53:46 +05:30
Aravinth Manivannan
624e95f3ec Merge pull request 'chore(deps): update rust crate derive_more to v0.99.20' (#91) from renovate/derive_more-0.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/91
2025-10-02 12:53:25 +05:30
Aravinth Manivannan
5b7e4ba1b6 Merge pull request 'chore(deps): update rust crate config to v0.15.18' (#90) from renovate/config-0.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/90
2025-10-02 12:53:21 +05:30
Aravinth Manivannan
bb18229263 Merge pull request 'chore(deps): update rust crate async-trait to v0.1.89' (#89) from renovate/async-trait-0.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/89
2025-10-02 12:50:31 +05:30
Aravinth Manivannan
1ce1ac3b48 Merge pull request 'chore(deps): update dependency ts-loader to v9.5.4' (#88) from renovate/ts-loader-9.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/88
2025-10-02 12:49:51 +05:30
Aravinth Manivannan
a0c9790203 Merge pull request 'chore(deps): update dependency mini-css-extract-plugin to v2.9.4' (#87) from renovate/mini-css-extract-plugin-2.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/87
2025-10-02 12:49:47 +05:30
Aravinth Manivannan
e19bcc88ee Merge pull request 'chore(deps): update dependency @redocly/cli to v1.34.5' (#86) from renovate/redocly-cli-1.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/86
2025-10-02 12:49:43 +05:30
Aravinth Manivannan
1f7326cacc fix: dont run on renovate branches 2025-10-02 12:48:33 +05:30
Aravinth Manivannan
d390bb10a8 Merge pull request 'fix(deps): update rust crate thiserror to v2' (#56) from renovate/thiserror-2.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/56
2025-10-02 12:44:13 +05:30
Aravinth Manivannan
1f7bcf018f Merge pull request 'chore(deps): update typescript-eslint monorepo to v8 (major)' (#53) from renovate/major-typescript-eslint-monorepo into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/53
2025-10-02 12:43:57 +05:30
Aravinth Manivannan
ed5885f7bf Merge pull request 'chore(deps): update node.js to v22' (#51) from renovate/node-22.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/51
2025-10-02 12:43:52 +05:30
Aravinth Manivannan
dcc9c4c540 Merge pull request 'chore(deps): update dependency webpack-cli to v6' (#46) from renovate/webpack-cli-6.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/46
2025-10-02 12:43:40 +05:30
Aravinth Manivannan
a9f97ec423 Merge pull request 'chore(deps): update dependency sass-loader to v16' (#44) from renovate/sass-loader-16.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/44
2025-10-02 12:43:35 +05:30
Aravinth Manivannan
5f556c3c21 Merge pull request 'chore(deps): update dependency css-loader to v7' (#40) from renovate/css-loader-7.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/40
2025-10-02 12:43:00 +05:30
Aravinth Manivannan
9a8c9c46f2 Merge pull request 'fix(deps): update rust crate rust-embed to v8' (#55) from renovate/rust-embed-8.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/55
2025-10-02 12:42:36 +05:30
Renovate Bot
9b62a7cd13 chore(deps): update rust crate lettre to v0.11.18 2025-10-02 07:12:11 +00:00
Renovate Bot
e8b3debfde chore(deps): update rust crate derive_more to v0.99.20 2025-10-02 07:12:07 +00:00
Renovate Bot
f54cfb42ff chore(deps): update rust crate config to v0.15.18 2025-10-02 07:12:04 +00:00
Renovate Bot
8f9db61b5f chore(deps): update rust crate async-trait to v0.1.89 2025-10-02 07:11:59 +00:00
Renovate Bot
adb71c19c0 chore(deps): update dependency ts-loader to v9.5.4 2025-10-02 07:11:48 +00:00
Renovate Bot
2d26c58dc8 chore(deps): update dependency mini-css-extract-plugin to v2.9.4 2025-10-02 07:11:41 +00:00
Renovate Bot
c91c265ab2 chore(deps): update dependency @redocly/cli to v1.34.5 2025-10-02 07:11:31 +00:00
Aravinth Manivannan
7cdbf18fd1 Merge pull request 'fix(deps): update rust crate validator to 0.20' (#37) from renovate/validator-0.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/37
2025-10-02 12:35:37 +05:30
Aravinth Manivannan
ba601720f4 Merge pull request 'fix(deps): update rust crate sqlx to 0.8' (#36) from renovate/sqlx-0.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/36
2025-10-02 12:35:03 +05:30
Aravinth Manivannan
8fe6f8ce13 Merge pull request 'chore(deps): update rust crate tokio to v1.47.1' (#79) from renovate/tokio-1.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/79
2025-10-02 12:34:43 +05:30
Aravinth Manivannan
aa3451a1de Merge pull request 'chore(deps): update rust crate actix-web to v4.11.0' (#77) from renovate/actix-web-4.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/77
2025-10-02 12:34:38 +05:30
Aravinth Manivannan
13158c4660 Merge pull request 'chore(deps): update postgres docker tag to v13.22' (#74) from renovate/postgres-13.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/74
2025-10-02 12:34:35 +05:30
Aravinth Manivannan
81016e6c66 Merge pull request 'fix(deps): update rust crate pretty_env_logger to 0.5' (#33) from renovate/pretty_env_logger-0.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/33
2025-10-02 12:34:19 +05:30
Aravinth Manivannan
a319325f5a Merge pull request 'chore(deps): update dependency webpack to v5.102.0' (#73) from renovate/webpack-5.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/73
2025-10-02 12:34:01 +05:30
Renovate Bot
af78594b75 fix(deps): update rust crate thiserror to v2 2025-10-02 06:58:09 +00:00
Renovate Bot
e87681086d fix(deps): update rust crate rust-embed to v8 2025-10-02 06:58:08 +00:00
Renovate Bot
bf82127d58 chore(deps): update typescript-eslint monorepo to v8 2025-10-02 06:57:39 +00:00
Renovate Bot
a63ac30afd chore(deps): update node.js to v22 2025-10-02 06:56:59 +00:00
Renovate Bot
210e08dbec chore(deps): update dependency webpack-cli to v6 2025-10-02 06:56:42 +00:00
Renovate Bot
dba69111bf chore(deps): update dependency sass-loader to v16 2025-10-02 06:56:36 +00:00
Renovate Bot
dc746388c6 chore(deps): update dependency css-loader to v7 2025-10-02 06:56:24 +00:00
Renovate Bot
53fd774a3f fix(deps): update rust crate validator to 0.20 2025-10-02 06:56:19 +00:00
Renovate Bot
3f8a4cacd2 fix(deps): update rust crate sqlx to 0.8 2025-10-02 06:56:15 +00:00
Renovate Bot
acd6eebcfe fix(deps): update rust crate pretty_env_logger to 0.5 2025-10-02 06:55:58 +00:00
Renovate Bot
adef372720 chore(deps): update rust crate tokio to v1.47.1 2025-10-02 06:55:48 +00:00
Renovate Bot
4b025a94c8 chore(deps): update rust crate actix-web to v4.11.0 2025-10-02 06:55:42 +00:00
Renovate Bot
d38fd374f4 chore(deps): update postgres docker tag to v13.22 2025-10-02 06:55:19 +00:00
Renovate Bot
6ee914e797 chore(deps): update dependency webpack to v5.102.0 2025-10-02 06:55:06 +00:00
Aravinth Manivannan
37799184bf feat: cache builds 2025-10-02 12:08:28 +05:30
Aravinth Manivannan
e5690ff4ea fix: disable actions 2025-10-02 11:49:24 +05:30
Aravinth Manivannan
9662d03450 feat: update base img to trixie 2025-10-02 11:24:35 +05:30
Aravinth Manivannan
151aba467a Merge pull request 'chore(deps): update dependency typescript to v5.9.3' (#72) from renovate/typescript-5.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/72
2025-10-01 17:33:02 +05:30
Aravinth Manivannan
d010eb000d Merge pull request 'chore(deps): update rust crate awc to v3.8.0' (#75) from renovate/awc-3.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/75
2025-10-01 17:24:54 +05:30
Aravinth Manivannan
69fc6c0d90 Merge pull request 'chore(deps): update dependency eslint to v9' (#42) from renovate/major-eslint-monorepo into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/42
2025-10-01 17:24:33 +05:30
Aravinth Manivannan
fa6493ec43 Merge pull request 'chore(deps): update dependency sass to v1.93.2' (#71) from renovate/sass-1.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/71
2025-10-01 16:51:03 +05:30
Renovate Bot
e849ae43e7 chore(deps): update dependency eslint to v9 2025-10-01 11:04:26 +00:00
Renovate Bot
9fa1c45492 chore(deps): update rust crate awc to v3.8.0 2025-10-01 11:03:08 +00:00
Renovate Bot
76ba9ded00 chore(deps): update dependency typescript to v5.9.3 2025-10-01 11:01:25 +00:00
Renovate Bot
403a47856e chore(deps): update dependency sass to v1.93.2 2025-10-01 11:01:21 +00:00
Aravinth Manivannan
d352bc691d Merge pull request 'chore(deps): update dependency node to v20.19.0' (#70) from renovate/node-20.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/70
2025-04-20 20:08:46 +05:30
Aravinth Manivannan
127c61b455 Merge pull request 'chore(deps): update dependency @redocly/cli to v1.34.2' (#69) from renovate/redocly-cli-1.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/69
2025-04-20 20:08:37 +05:30
Aravinth Manivannan
16da3d258f Merge pull request 'fix(deps): update rust crate serde_json to v1.0.140' (#68) from renovate/serde_json-1.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/68
2025-04-20 20:01:26 +05:30
Aravinth Manivannan
4c5401941c Merge pull request 'fix(deps): update rust crate serde to v1.0.219' (#67) from renovate/serde-monorepo into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/67
2025-04-20 20:01:14 +05:30
Aravinth Manivannan
80f9748aa7 Merge pull request 'fix(deps): update rust crate openssl to v0.10.72' (#66) from renovate/openssl-0.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/66
2025-04-20 20:01:07 +05:30
Aravinth Manivannan
44efdd3541 Merge pull request 'fix(deps): update rust crate log to v0.4.27' (#65) from renovate/log-0.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/65
2025-04-20 19:53:02 +05:30
Aravinth Manivannan
1dcad0d507 Merge pull request 'fix(deps): update rust crate derive_more to v0.99.19' (#64) from renovate/derive_more-0.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/64
2025-04-20 19:43:32 +05:30
Aravinth Manivannan
7f6a703556 Merge pull request 'fix(deps): update rust crate config to v0.15.11' (#63) from renovate/config-0.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/63
2025-04-20 19:43:29 +05:30
Aravinth Manivannan
f8da5ca3bf Merge pull request 'fix(deps): update rust crate actix-service to v2.0.3' (#61) from renovate/actix-service-2.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/61
2025-04-20 19:43:25 +05:30
Aravinth Manivannan
7c643e013e Merge pull request 'fix(deps): update rust crate async-trait to v0.1.88' (#62) from renovate/async-trait-0.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/62
2025-04-20 19:37:20 +05:30
Aravinth Manivannan
be4c7779fa Merge pull request 'chore(deps): update dependency ts-jest to v29.3.2' (#58) from renovate/ts-jest-29.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/58
2025-04-20 19:37:10 +05:30
Aravinth Manivannan
a9e11a8c56 Merge pull request 'fix(deps): update rust crate actix-cors to v0.7.1' (#60) from renovate/actix-cors-0.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/60
2025-04-20 19:29:55 +05:30
Aravinth Manivannan
567a8c6b60 Merge pull request 'chore(deps): update dependency ts-loader to v9.5.2' (#59) from renovate/ts-loader-9.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/59
2025-04-20 19:29:51 +05:30
Aravinth Manivannan
5da73093c4 Merge pull request 'chore(deps): update dependency @types/node to v20.17.30' (#57) from renovate/node-20.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/57
2025-04-20 19:29:43 +05:30
Renovate Bot
0d37495a61 chore(deps): update dependency node to v20.19.0 2025-04-20 13:40:37 +00:00
Renovate Bot
c7a30fe62e chore(deps): update dependency @redocly/cli to v1.34.2 2025-04-20 13:40:30 +00:00
Renovate Bot
26f23f6700 fix(deps): update rust crate serde_json to v1.0.140 2025-04-20 13:40:14 +00:00
Renovate Bot
b851bdb2b0 fix(deps): update rust crate serde to v1.0.219 2025-04-20 13:40:04 +00:00
Renovate Bot
83c90c78e2 fix(deps): update rust crate openssl to v0.10.72 2025-04-20 13:39:53 +00:00
Renovate Bot
9768998926 fix(deps): update rust crate log to v0.4.27 2025-04-20 13:39:49 +00:00
Renovate Bot
78186f13e6 fix(deps): update rust crate derive_more to v0.99.19 2025-04-20 13:39:45 +00:00
Renovate Bot
5500483e1b fix(deps): update rust crate config to v0.15.11 2025-04-20 13:39:41 +00:00
Renovate Bot
0a82a62adc fix(deps): update rust crate async-trait to v0.1.88 2025-04-20 13:39:37 +00:00
Renovate Bot
fac6c92812 fix(deps): update rust crate actix-service to v2.0.3 2025-04-20 13:39:29 +00:00
Renovate Bot
3fea35b481 fix(deps): update rust crate actix-cors to v0.7.1 2025-04-20 13:39:24 +00:00
Renovate Bot
7d564973b7 chore(deps): update dependency ts-loader to v9.5.2 2025-04-20 13:39:03 +00:00
Renovate Bot
718d8d4c37 chore(deps): update dependency ts-jest to v29.3.2 2025-04-20 13:38:56 +00:00
Renovate Bot
b8143fb634 chore(deps): update dependency @types/node to v20.17.30 2025-04-20 13:38:50 +00:00
Aravinth Manivannan
9ca6992e16 Merge pull request 'chore(deps): update dependency @types/sinon to v17' (#39) from renovate/sinon-17.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/39
2025-04-03 11:52:39 +05:30
Aravinth Manivannan
cfd88cd20b Merge pull request 'chore(deps): update actions/setup-node action to v4' (#38) from renovate/actions-setup-node-4.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/38
2025-04-03 11:30:34 +05:30
Aravinth Manivannan
4f28931875 Merge pull request 'fix(deps): update rust crate lettre to 0.11.0' (#32) from renovate/lettre-0.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/32
2025-04-03 11:30:05 +05:30
Aravinth Manivannan
3f3fd3ce83 Merge pull request 'fix(deps): update rust crate derive_builder to 0.20' (#31) from renovate/derive_builder-0.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/31
2025-04-03 11:30:00 +05:30
Aravinth Manivannan
26c83e1af1 Merge pull request 'chore(deps): update dependency ts-jest to v29.3.1' (#22) from renovate/ts-jest-29.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/22
2025-04-03 11:29:47 +05:30
Aravinth Manivannan
8ea48c6f2a Merge pull request 'chore(deps): update dependency mini-css-extract-plugin to v2.9.2' (#20) from renovate/mini-css-extract-plugin-2.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/20
2025-04-03 10:50:11 +05:30
Aravinth Manivannan
6f9489403a Merge pull request 'chore(deps): update dependency css-loader to v6.11.0' (#18) from renovate/css-loader-6.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/18
2025-04-03 10:50:06 +05:30
Aravinth Manivannan
1effdc5a18 Merge pull request 'chore(deps): update dependency webpack-dev-server to v4.15.2' (#8) from renovate/webpack-dev-server-4.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/8
2025-04-03 10:50:00 +05:30
Aravinth Manivannan
8bfa53cc4f Merge pull request 'chore(deps): update dependency ts-node to v10.9.2' (#7) from renovate/ts-node-10.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/7
2025-04-03 10:49:55 +05:30
Renovate Bot
4046d389ac chore(deps): update dependency @types/sinon to v17 2025-04-03 05:01:44 +00:00
Renovate Bot
09d2ab4e75 chore(deps): update actions/setup-node action to v4 2025-04-03 05:01:40 +00:00
Renovate Bot
20dbcb5b8f fix(deps): update rust crate lettre to 0.11.0 2025-04-03 05:01:12 +00:00
Renovate Bot
2a71dfe459 fix(deps): update rust crate derive_builder to 0.20 2025-04-03 05:01:07 +00:00
Renovate Bot
047a0d1e2c chore(deps): update dependency ts-jest to v29.3.1 2025-04-03 05:00:33 +00:00
Renovate Bot
7584d2a574 chore(deps): update dependency mini-css-extract-plugin to v2.9.2 2025-04-03 05:00:24 +00:00
Renovate Bot
beb3d8a8e2 chore(deps): update dependency css-loader to v6.11.0 2025-04-03 05:00:17 +00:00
Renovate Bot
d59ce15043 chore(deps): update dependency webpack-dev-server to v4.15.2 2025-04-03 05:00:04 +00:00
Renovate Bot
d350f8d2a9 chore(deps): update dependency ts-node to v10.9.2 2025-04-03 04:59:58 +00:00
Aravinth Manivannan
f02a79637f Merge pull request 'fix(deps): update rust crate config to 0.15' (#30) from renovate/config-0.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/30
2025-01-02 17:46:05 +05:30
Renovate Bot
ce0eb26422 fix(deps): update rust crate config to 0.15 2024-12-30 14:19:22 +00:00
Aravinth Manivannan
1ee5f01444 Merge pull request 'fix(deps): update rust crate actix-cors to 0.7.0' (#28) from renovate/actix-cors-0.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/28
2024-12-30 19:24:53 +05:30
Aravinth Manivannan
0ab3599c63 Merge pull request 'chore(deps): update typescript-eslint monorepo to v6.21.0' (#27) from renovate/typescript-eslint-monorepo into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/27
2024-12-30 19:23:34 +05:30
Aravinth Manivannan
4278335fdd Merge pull request 'chore(deps): update rust crate mcaptcha_pow_sha256 to 0.5' (#26) from renovate/mcaptcha_pow_sha256-0.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/26
2024-12-30 19:23:29 +05:30
Aravinth Manivannan
ef5e2fee27 Merge pull request 'chore(deps): update postgres docker tag to v13.18' (#25) from renovate/postgres-13.x into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/25
2024-12-30 19:23:22 +05:30
Aravinth Manivannan
795d29bcb0 Merge pull request 'chore(deps): update dependency webpack to v5.97.1' (#24) from renovate/webpack-5.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/24
2024-12-30 19:05:39 +05:30
Aravinth Manivannan
b5766622fb Merge pull request 'chore(deps): update dependency typescript to v5.7.2' (#23) from renovate/typescript-5.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/23
2024-12-30 19:05:20 +05:30
Aravinth Manivannan
ab679e71d2 Merge pull request 'chore(deps): update dependency sass to v1.83.0' (#21) from renovate/sass-1.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/21
2024-12-30 18:58:11 +05:30
Aravinth Manivannan
ac9aecdb95 Merge pull request 'chore(deps): update dependency eslint to v8.57.1' (#19) from renovate/eslint-monorepo into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/19
2024-12-30 18:50:55 +05:30
Aravinth Manivannan
22bd14056c Merge pull request 'chore(deps): update dependency @types/node to v20.17.10' (#17) from renovate/node-20.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/17
2024-12-30 18:47:13 +05:30
Aravinth Manivannan
8810931d10 Merge pull request 'chore(deps): update dependency @redocly/cli to v1.26.1' (#16) from renovate/redocly-cli-1.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/16
2024-12-30 18:46:39 +05:30
Aravinth Manivannan
e11d71a5fa Merge pull request 'fix(deps): update rust crate serde_json to v1.0.134' (#15) from renovate/serde_json-1.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/15
2024-12-30 18:46:35 +05:30
Aravinth Manivannan
d9509cc4ce Merge pull request 'fix(deps): update rust crate serde to v1.0.217' (#14) from renovate/serde-monorepo into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/14
2024-12-30 18:26:00 +05:30
Aravinth Manivannan
0c2915f334 Merge pull request 'fix(deps): update dependency @mcaptcha/vanilla-glue to v0.1.0-rc2' (#9) from renovate/mcaptcha-vanilla-glue-0.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/9
2024-12-30 18:25:56 +05:30
Aravinth Manivannan
71fc1b4d46 Merge pull request 'chore(deps): update dependency ts-loader to v9.5.1' (#6) from renovate/ts-loader-9.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/6
2024-12-30 18:25:19 +05:30
Aravinth Manivannan
36dc200193 Merge pull request 'chore(deps): update dependency sinon to v17.0.1' (#5) from renovate/sinon-17.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/5
2024-12-30 18:10:05 +05:30
Aravinth Manivannan
589b476e43 Merge pull request 'chore(deps): update dependency sass-loader to v13.3.3' (#4) from renovate/sass-loader-13.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/4
2024-12-30 18:09:48 +05:30
Aravinth Manivannan
43da103f34 Merge pull request 'chore(deps): update dependency @types/jsdom to v21.1.7' (#3) from renovate/jsdom-21.x-lockfile into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/3
2024-12-30 18:09:42 +05:30
Aravinth Manivannan
c0899265c2 Merge pull request 'chore(deps): update dependency @types/jest to v29.5.14' (#2) from renovate/jest-monorepo into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/2
2024-12-30 18:09:24 +05:30
Renovate Bot
c827208144 fix(deps): update rust crate actix-cors to 0.7.0 2024-12-30 12:27:51 +00:00
Renovate Bot
58e2dcb9d4 chore(deps): update typescript-eslint monorepo to v6.21.0 2024-12-30 12:27:36 +00:00
Renovate Bot
a0c89d0e59 chore(deps): update rust crate mcaptcha_pow_sha256 to 0.5 2024-12-30 12:27:10 +00:00
Renovate Bot
86a12c5c6a chore(deps): update postgres docker tag to v13.18 2024-12-30 12:26:55 +00:00
Renovate Bot
30fa1bb165 chore(deps): update dependency webpack to v5.97.1 2024-12-30 12:26:42 +00:00
Renovate Bot
4606da212d chore(deps): update dependency typescript to v5.7.2 2024-12-30 12:26:24 +00:00
Renovate Bot
8d6de7ed5a chore(deps): update dependency sass to v1.83.0 2024-12-30 12:25:52 +00:00
Renovate Bot
2ae9c51da3 chore(deps): update dependency eslint to v8.57.1 2024-12-30 12:25:14 +00:00
Renovate Bot
2b6f039dd4 chore(deps): update dependency @types/node to v20.17.10 2024-12-30 12:24:43 +00:00
Renovate Bot
eb65259bc1 chore(deps): update dependency @redocly/cli to v1.26.1 2024-12-30 12:24:26 +00:00
Renovate Bot
3fdf215459 fix(deps): update rust crate serde_json to v1.0.134 2024-12-30 12:24:13 +00:00
Renovate Bot
ef8e07c44b fix(deps): update rust crate serde to v1.0.217 2024-12-30 12:24:09 +00:00
Renovate Bot
bacdbdc9e8 fix(deps): update dependency @mcaptcha/vanilla-glue to v0.1.0-rc2 2024-12-30 12:23:59 +00:00
Renovate Bot
d9cf53d399 chore(deps): update dependency ts-loader to v9.5.1 2024-12-30 12:23:17 +00:00
Renovate Bot
d619092598 chore(deps): update dependency sinon to v17.0.1 2024-12-30 12:23:02 +00:00
Renovate Bot
54ed53e03d chore(deps): update dependency sass-loader to v13.3.3 2024-12-30 12:22:48 +00:00
Renovate Bot
6bd8cfb711 chore(deps): update dependency @types/jsdom to v21.1.7 2024-12-30 12:22:30 +00:00
Renovate Bot
ef652541ee chore(deps): update dependency @types/jest to v29.5.14 2024-12-30 12:22:18 +00:00
Aravinth Manivannan
474437c360 Merge pull request 'fix: update deps to fix compilation with rustc' (#13) from update-deps into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/13
2024-12-30 17:37:23 +05:30
Aravinth Manivannan
1956e97503 fix: update deps to fix compilation with rustc 2024-12-30 16:58:39 +05:30
Aravinth Manivannan
a5d4dad508 Merge pull request 'chore: Configure Renovate' (#1) from renovate/configure into woodpecker-pipeline
Reviewed-on: https://git.batsense.net/mCaptcha/mCaptcha/pulls/1
2024-12-24 22:55:27 +05:30
Renovate Bot
afb281c93a Add renovate.json 2024-12-24 15:06:12 +00:00
Aravinth Manivannan
182635bad1 feat: woodpecker pipeline init 2024-12-24 20:09:21 +05:30
Aravinth Manivannan
9922c23322 Merge pull request #162 from lorenzleutgeb/patch-1
fix: SPDX identifier for license in package.json
2024-04-17 23:47:51 +05:30
Lorenz Leutgeb
2a92e8f672 fix: SPDX identifier for license in package.json 2024-04-17 19:04:09 +02:00
Aravinth Manivannan
581e6f9440 Merge pull request #157 from mCaptcha/fix-154
fix: exit loop when paginated DB query in easy PoW auto-enhance loop returns empty array
2024-03-24 08:53:08 +05:30
Aravinth Manivannan
3a7e71b499 fix: exit loop when paginated DB query returns empty array
fixes: https://github.com/mCaptcha/mCaptcha/issues/154
2024-03-24 08:40:52 +05:30
Aravinth Manivannan
91955501e2 feat: enable easy PoW configuration auto-enhance by default 2024-03-24 08:37:26 +05:30
Aravinth Manivannan
cb72b0adfa Merge pull request #152 from mCaptcha/fix-151
fix: difficulty factor for "broke my site" should be greater than peak sustainable traffic"
2024-03-23 13:55:28 +05:30
Aravinth Manivannan
c1fe45d409 fix: difficulty factor for "broke my site" should be greater than "peak sustainable traffic"
fixes: #151
2024-03-23 13:31:49 +05:30
Aravinth Manivannan
59e339f287 Merge pull request #150 from mCaptcha/fix-144
feat: add curl to the final image to aid in healthchecks
2024-03-15 18:01:12 +05:30
Aravinth Manivannan
ddcde9cf18 Merge pull request #146 from 15aura35/master
Update .env.docker-compose to use port 7000 instead of 7001
2024-03-15 18:00:17 +05:30
Aravinth Manivannan
65c92ee96e feat: add curl to the final image to aid in healthchecks
closes: https://github.com/mCaptcha/mCaptcha/issues/144
2024-03-14 20:52:37 +05:30
Aravinth Manivannan
40766ff44f Merge pull request #147 from mitallast/master
Fix: ensuring worker is ready
2024-03-12 19:41:49 +05:30
mitallast
ddc3008009 await worker is ready 2024-03-05 13:42:10 +03:00
mitallast
cba056aba6 fix wasm bigint progress handler 2024-03-05 13:13:47 +03:00
15aura35
16c975d2ec Update .env.docker-compose 2024-03-01 21:53:32 +00:00
Aravinth Manivannan
f67fdf917e Merge pull request #142 from mCaptcha/release-ci
feat: publish tagged docker images and bins
2024-02-23 15:51:56 +05:30
Aravinth Manivannan
e1746223c8 feat: publish tagged docker images and bins 2024-02-23 15:40:45 +05:30
Aravinth Manivannan
ae08c09702 fix: tmp disable bin publication 2024-02-23 15:40:27 +05:30
Aravinth Manivannan
1c9e242d7e Merge pull request #140 from mCaptcha/fix-134
fix: typecast BigInt to number in progress computation
2024-02-22 19:21:03 +05:30
Aravinth Manivannan
3cb0ca38ec fix: typecast BigInt to number in progress computation
closes: https://github.com/mCaptcha/mCaptcha/issues/134
2024-02-22 18:40:38 +05:30
Aravinth Manivannan
3cd38511fa Merge pull request #135 from SebastianGode/widget-dark
Added automatic dark mode to the widget
2024-02-19 20:13:58 +05:30
Sebastian Gode
d765bd7491 Added dark mode to widget 2024-02-13 13:30:14 +00:00
Sebastian Gode
8e33e75659 Added dark mode to widget 2024-02-13 12:59:20 +00:00
Aravinth Manivannan
c00857dd28 Merge pull request #133 from mCaptcha/aria-labels
feat: add aria labels to widget progress bar and checkbox
2024-02-04 01:09:09 +05:30
Aravinth Manivannan
9cf0eb596a feat: add aria labels to widget progress bar and checkbox 2024-02-03 19:33:45 +05:30
Aravinth Manivannan
d010a1cbd4 Merge pull request #131 from mCaptcha/fix-upload-config-file
fix: publish config file in tarball
2024-01-08 00:21:52 +05:30
Aravinth Manivannan
453be36201 fix: publish config file in tarball 2024-01-08 00:14:14 +05:30
Aravinth Manivannan
d4967626ee Merge pull request #130 from mCaptcha/document-configuration-parameters
feat: list all env vars and load in docker-compose
2024-01-07 23:52:49 +05:30
Aravinth Manivannan
2ee0a0ae5f feat: list all env vars and load in docker-compose 2024-01-07 23:35:31 +05:30
Aravinth Manivannan
5722a5327c Merge pull request #128 from mCaptcha/feat-auto-captcha
Use time (in seconds) instead of difficulty factor to describe PoW
2024-01-05 01:25:19 +05:30
Aravinth Manivannan
239e0bfd47 feat: easy captcha update job runner 2024-01-05 01:03:38 +05:30
Aravinth Manivannan
790fd8f393 feat: create runner method for updating easy captchas 2024-01-05 01:03:13 +05:30
Aravinth Manivannan
c70a30e640 feat: fetch username of owner and description in easy captcha method 2024-01-05 01:02:27 +05:30
Aravinth Manivannan
3b8051159d feat: use time for easy captcha when option is configured by admin 2024-01-04 23:29:20 +05:30
Aravinth Manivannan
91c235b3f4 feat: add database method to get all easy captcha configurations with pagination 2024-01-04 23:28:50 +05:30
Aravinth Manivannan
9bcf6af3ab feat: add options to use time for easy captcha configuration 2024-01-04 23:28:04 +05:30
Aravinth Manivannan
e0d6188853 fix: terminate demo user job cleanly 2024-01-04 23:24:36 +05:30
Aravinth Manivannan
1b2096d955 Merge pull request #127 from mCaptcha/feat-auto-captcha
feat: new dashboard page to show percentile scores on PoW performance analysis records
2024-01-04 18:47:44 +05:30
Aravinth Manivannan
13c3066b86 fix: unused import 2024-01-04 17:22:10 +05:30
Aravinth Manivannan
da934f5ba7 feat: new dashboard page to show percentile scores on PoW performance analysis records 2024-01-04 01:58:19 +05:30
Aravinth Manivannan
26ad05d284 Merge pull request #125 from mCaptcha/fix-embedded-cache-health
fix: health endpoint crashing with embedded cache usage
2023-12-09 01:32:21 +05:30
Aravinth Manivannan
b6326603d1 fix: health endpoint crashing with embedded cache usage 2023-12-09 01:15:25 +05:30
Aravinth Manivannan
8bed3cb352 Merge pull request #121 from mCaptcha/feat-percentile
compute percentile on analytics records
2023-11-04 20:32:28 +00:00
63 changed files with 24660 additions and 9663 deletions

43
.env.docker-compose Normal file
View File

@@ -0,0 +1,43 @@
MCAPTCHA_debug=false
MCAPTCHA_commercial=false
MCAPTCHA_source_code=https://github.com/mCaptcha/mCaptcha
MCAPTCHA_allow_registration=false
MCAPTCHA_allow_demo=false
# database
DATABASE_URL=postgres://postgres:password@mcaptcha_postgres:5432/postgres
MCAPTCHA_database_POOL=4
# redis
MCAPTCHA_redis_URL=redis://mcaptcha_redis
MCAPTCHA_redis_POOL=4
# server
PORT=7000
MCAPTCHA_server_DOMAIN=localhost
MCAPTCHA__server_COOKIE_SECRET=pleasereplacethiswithrandomstring # PLEASE SET RANDOM STRING. MIN LENGTH=32
MCAPTCHA__server_IP= 0.0.0.0
# captcha
MCAPTCHA_captcha_SALT=pleasereplacethiswithrandomstring # PLEASE SET RANDOM STRING. MIN LENGTH=32
MCAPTCHA_captcha_GC=30
MCAPTCHA_captcha_RUNNERS=4
MCAPTCHA_captcha_QUEUE_LENGTH=2000
MCAPTCHA_captcha_ENABLE_STATS=true
MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_avg_traffic_difficulty=50000 # almost instant solution
MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_peak_sustainable_traffic_difficulty=3000000 # greater than 3.5s
MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_broke_my_site_traffic_difficulty=5000000 # roughly 1.5s
MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_duration=30 # cooldown period in seconds
MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_avg_traffic_time=1 # almost instant solution
MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_peak_sustainable_traffic_time=3
MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_broke_my_site_traffic_time=5
# SMTP
#MCAPTCHA_smtp_FROM=
#MCAPTCHA_smtp_REPLY=
#MCAPTCHA_smtp_URL=
#MCAPTCHA_smtp_USERNAME=
#MCAPTCHA_smtp_PASSWORD=
#MCAPTCHA_smtp_PORT=

View File

@@ -84,7 +84,7 @@ jobs:
- uses: actions/setup-node@v2
with:
node-version: "20.0.0"
node-version: "22"
- uses: actions-rust-lang/setup-rust-toolchain@v1

125
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,125 @@
name: Publish release
on:
release:
type: [published]
jobs:
build_and_test:
strategy:
fail-fast: false
name: x86_64-unknown-linux-gnu
runs-on: ubuntu-latest
services:
postgres:
image: postgres
env:
POSTGRES_PASSWORD: password
POSTGRES_USER: postgres
POSTGRES_DB: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
mcaptcha-redis:
image: mcaptcha/cache
ports:
- 6379:6379
mcaptcha-smtp:
image: maildev/maildev
env:
MAILDEV_WEB_PORT: "1080"
MAILDEV_INCOMING_USER: "admin"
MAILDEV_INCOMING_PASS: "password"
ports:
- 1080:1080
- 10025:1025
maria:
image: mariadb:10
env:
MARIADB_USER: "maria"
MARIADB_PASSWORD: "password"
MARIADB_ROOT_PASSWORD: "password"
MARIADB_DATABASE: "maria"
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=10
ports:
- 3306:3306
steps:
- uses: actions/checkout@v4
- name: configure GPG key
run: echo -n "$RELEASE_BOT_GPG_SIGNING_KEY" | gpg --batch --import --pinentry-mode loopback
env:
RELEASE_BOT_GPG_SIGNING_KEY: ${{ secrets.RELEASE_BOT_GPG_SIGNING_KEY }}
- name: Set release version
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: load env
run: |
source .env_sample \
&& echo "POSTGRES_DATABASE_URL=$POSTGRES_DATABASE_URL" >> $GITHUB_ENV \
&& echo "MARIA_DATABASE_URL=$MARIA_DATABASE_URL" >> $GITHUB_ENV
- uses: actions/setup-node@v2
with:
node-version: "20.0.0"
- uses: actions-rust-lang/setup-rust-toolchain@v1
- name: install nightwatch dep
run: sudo apt-get install xvfb
- name: Run migrations
run: make migrate
env:
POSTGRES_DATABASE_URL: "${{ env.POSTGRES_DATABASE_URL }}"
MARIA_DATABASE_URL: "${{ env.MARIA_DATABASE_URL }}"
- name: build
run: make
env:
POSTGRES_DATABASE_URL: "${{ env.POSTGRES_DATABASE_URL }}"
MARIA_DATABASE_URL: "${{ env.MARIA_DATABASE_URL }}"
- name: lint frontend
run: yarn lint
- name: run tests
run: make test
env:
POSTGRES_DATABASE_URL: "${{ env.POSTGRES_DATABASE_URL }}"
MARIA_DATABASE_URL: "${{ env.MARIA_DATABASE_URL }}"
- name: run integration tests
run: make test.integration
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: mcaptcha
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: build docker images
run: docker build -t mcaptcha/mcaptcha:${RELEASE_VERSION} .
- name: publish docker images
run: docker push mcaptcha/mcaptcha:${RELEASE_VERSION}
- name: publish bins
run: ./scripts/publish.sh publish $RELEASE_VERSION latest $DUMBSERVE_PASSWORD
env:
DUMBSERVE_PASSWORD: ${{ secrets.DUMBSERVE_PASSWORD }}
GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }}

View File

@@ -1,32 +1,32 @@
name: Create binary for release
# Only on tags that start with a "v"
on:
push:
tags:
- "v*"
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-pc-windows-gnu
archive: zip
- target: x86_64-unknown-linux-musl
archive: tar.gz tar.xz
- target: x86_64-apple-darwin
archive: zip
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Compile and release
uses: rust-build/rust-build.action@v1.3.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
RUSTTARGET: ${{ matrix.target }}
ARCHIVE_TYPES: ${{ matrix.archive }}
#name: Create binary for release
#
## Only on tags that start with a "v"
#on:
# push:
# tags:
# - "v*"
#
#jobs:
# build:
# runs-on: ubuntu-latest
# strategy:
# fail-fast: false
# matrix:
# include:
# - target: x86_64-pc-windows-gnu
# archive: zip
# - target: x86_64-unknown-linux-musl
# archive: tar.gz tar.xz
# - target: x86_64-apple-darwin
# archive: zip
# steps:
# - name: Checkout
# uses: actions/checkout@v3
#
# - name: Compile and release
# uses: rust-build/rust-build.action@v1.3.2
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# with:
# RUSTTARGET: ${{ matrix.target }}
# ARCHIVE_TYPES: ${{ matrix.archive }}

2
.nvmrc
View File

@@ -1 +1 @@
20
22

29
.woodpecker.yaml Normal file
View File

@@ -0,0 +1,29 @@
steps:
frontend:
image: node:22
commands:
- make frontend
when:
- event: [pull_request, push]
- branch:
include: [woodpecker-pipeline]
exclude:
- renovate/\*
volumes:
- /var/lib/woodpercker-cache/mcaptcha/yarn:/root/.cache/yarn
backend:
image: rust:latest
when:
- event: [pull_request, push]
- branch:
include: [woodpecker-pipeline]
exclude:
- renovate/\*
commands:
- cargo --version
- make cache-bust
- cargo build --release
volumes:
- /var/lib/woodpercker-cache/mcaptcha/cargo:/root/.cargo
- /var/lib/woodpercker-cache/mcaptcha/target:/woodpecker/src/git.batsense.net/mCaptcha/mCaptcha/target

2962
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -28,23 +28,23 @@ actix = "0.13"
actix-identity = "0.4.0"
actix-http = "3.0.4"
actix-rt = "2"
actix-cors = "0.6.1"
actix-cors = "0.7.0"
actix-service = "2.0.0"
async-trait = "0.1.51"
mime_guess = "2.0.3"
rust-embed = "6.4.0"
rust-embed = "8.0.0"
libcachebust = "0.3.0"
futures = "0.3.15"
tokio = { version = "1.14", features = ["sync"]}
sqlx = { version = "0.7", features = [ "runtime-tokio-rustls", "postgres", "time", "mysql"] }
sqlx = { version = "0.8", features = [ "runtime-tokio-rustls", "postgres", "time", "mysql"] }
argon2-creds = { branch = "master", git = "https://github.com/realaravinth/argon2-creds"}
#argon2-creds = { version="*", path = "../../argon2-creds/" }
config = "0.13"
validator = { version = "0.15", features = ["derive"]}
config = "0.15"
validator = { version = "0.20", features = ["derive"]}
derive_builder = "0.11"
derive_builder = "0.20"
derive_more = "0.99"
serde = "1"
@@ -53,7 +53,7 @@ serde_json = "1"
url = "2.2"
urlencoding = "2.1.0"
pretty_env_logger = "0.4"
pretty_env_logger = "0.5"
log = "0.4"
lazy_static = "1.4"
@@ -69,7 +69,7 @@ mime = "0.3.16"
num_cpus = "1.13.1"
lettre = { version = "0.10.0-rc.3", features = [
lettre = { version = "0.11.0", features = [
"builder",
"tokio1",
"tokio1-native-tls",
@@ -78,7 +78,7 @@ lettre = { version = "0.10.0-rc.3", features = [
openssl = { version = "0.10.48", features = ["vendored"] }
uuid = { version = "1.4.0", features = ["v4", "serde"] }
reqwest = { version = "0.11.18", features = ["json", "gzip"] }
reqwest = { version = "0.12.0", features = ["json", "gzip"] }
[dependencies.db-core]
@@ -101,10 +101,10 @@ features = ["actix_identity_backend"]
[build-dependencies]
serde_json = "1"
sqlx = { version = "0.7", features = [ "runtime-tokio-rustls", "postgres", "time", "mysql" ] }
sqlx = { version = "0.8", features = [ "runtime-tokio-rustls", "postgres", "time", "mysql" ] }
[dev-dependencies]
mcaptcha_pow_sha256 = "0.4"
mcaptcha_pow_sha256 = "0.5"
awc = "3.0.0"

View File

@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
FROM node:20 as frontend
FROM node:22 as frontend
RUN set -ex; \
apt-get update; \
DEBIAN_FRONTEND=noninteractive \
@@ -29,8 +29,12 @@ RUN cargo --version
RUN make cache-bust
RUN cargo build --release
FROM debian:bookworm as mCaptcha
FROM debian:trixie as mCaptcha
LABEL org.opencontainers.image.source https://github.com/mCaptcha/mCaptcha
RUN set -ex; \
apt-get update; \
DEBIAN_FRONTEND=noninteractive \
apt-get install -y --no-install-recommends curl
RUN useradd -ms /bin/bash -u 1001 mcaptcha
WORKDIR /home/mcaptcha
COPY --from=rust /src/target/release/mcaptcha /usr/local/bin/

View File

@@ -34,8 +34,11 @@ enable_stats = true
[captcha.default_difficulty_strategy]
avg_traffic_difficulty = 50000 # almost instant solution
avg_traffic_time = 1 # almost instant solution
peak_sustainable_traffic_difficulty = 3000000 # roughly 1.5s
peak_sustainable_traffic_time = 3
broke_my_site_traffic_difficulty = 5000000 # greater than 3.5s
broke_my_site_traffic_time = 5
duration = 30 # cooldown period in seconds
[database]

View File

@@ -10,7 +10,7 @@ authors = ["realaravinth <realaravinth@batsense.net>"]
[dependencies]
async-trait = "0.1.51"
thiserror = "1.0.30"
thiserror = "2.0.0"
serde = { version = "1", features = ["derive"]}
url = { version = "2.2.2", features = ["serde"] }
libmcaptcha = "0.2.4"

View File

@@ -202,6 +202,13 @@ pub trait MCDatabase: std::marker::Send + std::marker::Sync + CloneSPDatabase {
captcha_key: &str,
) -> DBResult<TrafficPattern>;
/// Get all easy captcha configurations on instance
async fn get_all_easy_captchas(
&self,
limit: usize,
offset: usize,
) -> DBResult<Vec<EasyCaptcha>>;
/// Delete traffic configuration
async fn delete_traffic_pattern(
&self,
@@ -383,6 +390,19 @@ pub struct AddNotification<'a> {
pub message: &'a str,
}
#[derive(Default, PartialEq, Serialize, Deserialize, Clone, Debug)]
/// Represents Easy captcha configuration
pub struct EasyCaptcha {
/// traffic pattern of easy captcha
pub traffic_pattern: TrafficPattern,
/// captcha key/sitekey
pub key: String,
/// captcha description
pub description: String,
/// Owner of the captcha configuration
pub username: String,
}
#[derive(Default, PartialEq, Serialize, Deserialize, Clone, Debug)]
/// User's traffic pattern; used in generating a captcha configuration
pub struct TrafficPattern {

View File

@@ -223,6 +223,11 @@ pub async fn database_works<'a, T: MCDatabase>(
tp
);
// get all traffic patterns
let patterns = db.get_all_easy_captchas(10, 0).await.unwrap();
assert_eq!(patterns.get(0).as_ref().unwrap().key, c.key);
assert_eq!(&patterns.get(0).unwrap().traffic_pattern, tp);
// delete traffic pattern
db.delete_traffic_pattern(p.username, c.key).await.unwrap();
assert!(

View File

@@ -10,4 +10,4 @@ authors = ["realaravinth <realaravinth@batsense.net>"]
[dependencies]
actix-rt = "2"
sqlx = { version = "0.7", features = [ "runtime-tokio-rustls", "postgres", "time", "mysql" ] }
sqlx = { version = "0.8", features = [ "runtime-tokio-rustls", "postgres", "time", "mysql" ] }

View File

@@ -8,7 +8,6 @@ use std::env;
use sqlx::postgres::PgPoolOptions;
use sqlx::mysql::MySqlPoolOptions;
#[cfg(not(tarpaulin_include))]
#[actix_rt::main]
async fn main() {
//TODO featuregate sqlite and postgres

View File

@@ -0,0 +1,80 @@
{
"db_name": "MySQL",
"query": "SELECT \n mcaptcha_sitekey_user_provided_avg_traffic.avg_traffic, \n mcaptcha_sitekey_user_provided_avg_traffic.peak_sustainable_traffic, \n mcaptcha_sitekey_user_provided_avg_traffic.broke_my_site_traffic,\n mcaptcha_config.name,\n mcaptcha_users.name as username,\n mcaptcha_config.captcha_key\n FROM \n mcaptcha_sitekey_user_provided_avg_traffic \n INNER JOIN\n mcaptcha_config\n ON\n mcaptcha_config.config_id = mcaptcha_sitekey_user_provided_avg_traffic.config_id\n INNER JOIN\n mcaptcha_users\n ON\n mcaptcha_config.user_id = mcaptcha_users.ID\n ORDER BY mcaptcha_config.config_id\n LIMIT ? OFFSET ?",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "avg_traffic",
"type_info": {
"type": "Long",
"flags": "NOT_NULL | NO_DEFAULT_VALUE",
"char_set": 63,
"max_size": 11
}
},
{
"ordinal": 1,
"name": "peak_sustainable_traffic",
"type_info": {
"type": "Long",
"flags": "NOT_NULL | NO_DEFAULT_VALUE",
"char_set": 63,
"max_size": 11
}
},
{
"ordinal": 2,
"name": "broke_my_site_traffic",
"type_info": {
"type": "Long",
"flags": "",
"char_set": 63,
"max_size": 11
}
},
{
"ordinal": 3,
"name": "name",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 400
}
},
{
"ordinal": 4,
"name": "username",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | UNIQUE_KEY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 400
}
},
{
"ordinal": 5,
"name": "captcha_key",
"type_info": {
"type": "VarString",
"flags": "NOT_NULL | UNIQUE_KEY | NO_DEFAULT_VALUE",
"char_set": 224,
"max_size": 400
}
}
],
"parameters": {
"Right": 2
},
"nullable": [
false,
false,
true,
false,
false,
false
]
},
"hash": "d587844217f202c23d29c3cb4c819551bc204dd459c956c41024fa74aadbba64"
}

View File

@@ -12,11 +12,11 @@ authors = ["realaravinth <realaravinth@batsense.net>"]
async-trait = "0.1.51"
db-core = {path = "../db-core"}
futures = "0.3.15"
sqlx = { version = "0.7", features = [ "runtime-tokio-rustls", "mysql", "time"] }
sqlx = { version = "0.8", features = [ "runtime-tokio-rustls", "mysql", "time"] }
uuid = { version = "1.4.0", features = ["v4", "serde"] }
[dev-dependencies]
actix-rt = "2"
sqlx = { version = "0.7", features = [ "runtime-tokio-rustls", "mysql", "time" ] }
sqlx = { version = "0.8", features = [ "runtime-tokio-rustls", "mysql", "time" ] }
db-core = {path = "../db-core", features = ["test"]}
url = { version = "2.2.2", features = ["serde"] }

View File

@@ -43,7 +43,6 @@ pub mod dev {
pub use super::errors::*;
pub use super::Database;
pub use db_core::dev::*;
pub use prelude::*;
pub use sqlx::Error;
}
@@ -1274,6 +1273,66 @@ impl MCDatabase for Database {
Err(e) => Err(map_row_not_found_err(e, DBError::CaptchaNotFound)),
}
}
/// Get all easy captcha configurations on instance
async fn get_all_easy_captchas(
&self,
limit: usize,
offset: usize,
) -> DBResult<Vec<EasyCaptcha>> {
struct InnerEasyCaptcha {
captcha_key: String,
name: String,
username: String,
peak_sustainable_traffic: i32,
avg_traffic: i32,
broke_my_site_traffic: Option<i32>,
}
let mut inner_res = sqlx::query_as!(
InnerEasyCaptcha,
"SELECT
mcaptcha_sitekey_user_provided_avg_traffic.avg_traffic,
mcaptcha_sitekey_user_provided_avg_traffic.peak_sustainable_traffic,
mcaptcha_sitekey_user_provided_avg_traffic.broke_my_site_traffic,
mcaptcha_config.name,
mcaptcha_users.name as username,
mcaptcha_config.captcha_key
FROM
mcaptcha_sitekey_user_provided_avg_traffic
INNER JOIN
mcaptcha_config
ON
mcaptcha_config.config_id = mcaptcha_sitekey_user_provided_avg_traffic.config_id
INNER JOIN
mcaptcha_users
ON
mcaptcha_config.user_id = mcaptcha_users.ID
ORDER BY mcaptcha_config.config_id
LIMIT ? OFFSET ?",
limit as i64,
offset as i64
)
.fetch_all(&self.pool)
.await
.map_err(|e| map_row_not_found_err(e, DBError::TrafficPatternNotFound))?;
let mut res = Vec::with_capacity(inner_res.len());
inner_res.drain(0..).for_each(|v| {
res.push(EasyCaptcha {
key: v.captcha_key,
description: v.name,
username: v.username,
traffic_pattern: TrafficPattern {
broke_my_site_traffic: v
.broke_my_site_traffic
.as_ref()
.map(|v| *v as u32),
avg_traffic: v.avg_traffic as u32,
peak_sustainable_traffic: v.peak_sustainable_traffic as u32,
},
})
});
Ok(res)
}
}
#[derive(Clone)]

View File

@@ -0,0 +1,53 @@
{
"db_name": "PostgreSQL",
"query": "SELECT \n mcaptcha_sitekey_user_provided_avg_traffic.avg_traffic, \n mcaptcha_sitekey_user_provided_avg_traffic.peak_sustainable_traffic, \n mcaptcha_sitekey_user_provided_avg_traffic.broke_my_site_traffic,\n mcaptcha_config.name,\n mcaptcha_users.name as username,\n mcaptcha_config.key\n FROM \n mcaptcha_sitekey_user_provided_avg_traffic \n INNER JOIN\n mcaptcha_config\n ON\n mcaptcha_config.config_id = mcaptcha_sitekey_user_provided_avg_traffic.config_id\n INNER JOIN\n mcaptcha_users\n ON\n mcaptcha_config.user_id = mcaptcha_users.ID\n ORDER BY mcaptcha_config.config_id\n OFFSET $1 LIMIT $2; ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "avg_traffic",
"type_info": "Int4"
},
{
"ordinal": 1,
"name": "peak_sustainable_traffic",
"type_info": "Int4"
},
{
"ordinal": 2,
"name": "broke_my_site_traffic",
"type_info": "Int4"
},
{
"ordinal": 3,
"name": "name",
"type_info": "Varchar"
},
{
"ordinal": 4,
"name": "username",
"type_info": "Varchar"
},
{
"ordinal": 5,
"name": "key",
"type_info": "Varchar"
}
],
"parameters": {
"Left": [
"Int8",
"Int8"
]
},
"nullable": [
false,
false,
true,
false,
false,
false
]
},
"hash": "f01a9c09c8722bc195f477a8c3ce6466d415e7c74665fa882eff4a8566e70577"
}

View File

@@ -12,11 +12,11 @@ authors = ["realaravinth <realaravinth@batsense.net>"]
async-trait = "0.1.51"
db-core = {path = "../db-core"}
futures = "0.3.15"
sqlx = { version = "0.7", features = [ "runtime-tokio-rustls", "postgres", "time" ] }
sqlx = { version = "0.8", features = [ "runtime-tokio-rustls", "postgres", "time" ] }
uuid = { version = "1.4.0", features = ["v4", "serde"] }
[dev-dependencies]
actix-rt = "2"
sqlx = { version = "0.7", features = [ "runtime-tokio-rustls", "postgres", "time"] }
sqlx = { version = "0.8", features = [ "runtime-tokio-rustls", "postgres", "time"] }
db-core = {path = "../db-core", features = ["test"]}
url = { version = "2.2.2", features = ["serde"] }

View File

@@ -43,7 +43,6 @@ pub mod dev {
pub use super::errors::*;
pub use super::Database;
pub use db_core::dev::*;
pub use prelude::*;
pub use sqlx::Error;
}
@@ -670,13 +669,8 @@ impl MCDatabase for Database {
username: &str,
captcha_key: &str,
) -> DBResult<TrafficPattern> {
struct Traffic {
peak_sustainable_traffic: i32,
avg_traffic: i32,
broke_my_site_traffic: Option<i32>,
}
let res = sqlx::query_as!(
Traffic,
InnerTraffic,
"SELECT
avg_traffic,
peak_sustainable_traffic,
@@ -707,11 +701,67 @@ impl MCDatabase for Database {
.fetch_one(&self.pool)
.await
.map_err(|e| map_row_not_found_err(e, DBError::TrafficPatternNotFound))?;
Ok(TrafficPattern {
broke_my_site_traffic: res.broke_my_site_traffic.as_ref().map(|v| *v as u32),
avg_traffic: res.avg_traffic as u32,
peak_sustainable_traffic: res.peak_sustainable_traffic as u32,
})
Ok(res.into())
}
/// Get all easy captcha configurations on instance
async fn get_all_easy_captchas(
&self,
limit: usize,
offset: usize,
) -> DBResult<Vec<EasyCaptcha>> {
struct InnerEasyCaptcha {
key: String,
peak_sustainable_traffic: i32,
avg_traffic: i32,
broke_my_site_traffic: Option<i32>,
name: String,
username: String,
}
let mut inner_res = sqlx::query_as!(
InnerEasyCaptcha,
"SELECT
mcaptcha_sitekey_user_provided_avg_traffic.avg_traffic,
mcaptcha_sitekey_user_provided_avg_traffic.peak_sustainable_traffic,
mcaptcha_sitekey_user_provided_avg_traffic.broke_my_site_traffic,
mcaptcha_config.name,
mcaptcha_users.name as username,
mcaptcha_config.key
FROM
mcaptcha_sitekey_user_provided_avg_traffic
INNER JOIN
mcaptcha_config
ON
mcaptcha_config.config_id = mcaptcha_sitekey_user_provided_avg_traffic.config_id
INNER JOIN
mcaptcha_users
ON
mcaptcha_config.user_id = mcaptcha_users.ID
ORDER BY mcaptcha_config.config_id
OFFSET $1 LIMIT $2; ",
offset as i32,
limit as i32
)
.fetch_all(&self.pool)
.await
.map_err(|e| map_row_not_found_err(e, DBError::TrafficPatternNotFound))?;
let mut res = Vec::with_capacity(inner_res.len());
inner_res.drain(0..).for_each(|v| {
res.push(EasyCaptcha {
key: v.key,
description: v.name,
username: v.username,
traffic_pattern: TrafficPattern {
broke_my_site_traffic: v
.broke_my_site_traffic
.as_ref()
.map(|v| *v as u32),
avg_traffic: v.avg_traffic as u32,
peak_sustainable_traffic: v.peak_sustainable_traffic as u32,
},
})
});
Ok(res)
}
/// Delete traffic configuration
@@ -1346,3 +1396,19 @@ impl From<InternaleCaptchaConfig> for Captcha {
}
}
}
struct InnerTraffic {
peak_sustainable_traffic: i32,
avg_traffic: i32,
broke_my_site_traffic: Option<i32>,
}
impl From<InnerTraffic> for TrafficPattern {
fn from(v: InnerTraffic) -> Self {
TrafficPattern {
broke_my_site_traffic: v.broke_my_site_traffic.as_ref().map(|v| *v as u32),
avg_traffic: v.avg_traffic as u32,
peak_sustainable_traffic: v.peak_sustainable_traffic as u32,
}
}
}

View File

@@ -9,17 +9,14 @@ services:
image: mcaptcha/mcaptcha:latest
ports:
- 7000:7000
environment:
DATABASE_URL: postgres://postgres:password@mcaptcha_postgres:5432/postgres # set password at placeholder
MCAPTCHA_redis_URL: "redis://mcaptcha_redis/"
RUST_LOG: "debug"
PORT: 7000
env_file:
- .env.docker-compose
depends_on:
- mcaptcha_postgres
- mcaptcha_redis
mcaptcha_postgres:
image: postgres:13.2
image: postgres:18.0
volumes:
- mcaptcha-data:/var/lib/postgresql/
environment:

View File

@@ -56,23 +56,22 @@ you will be overriding the values set in the configuration files.
### Captcha
| Name | Value |
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | --- |
| `MCAPTCHA_captcha_SALT` | Salt has to be long and random |
| `MCAPTCHA_captcha_GC` | Garbage collection duration in seconds, requires tuning but 30 is a good starting point |
| `MCAPTCHA_captcha_RUNNERS` | [Performance] Number of runners to use for PoW validation. Defaults to number of CPUs available |
| `MCAPTCHA_captcha_QUEUE_LENGTH` | [Performance] PoW Validation queue length, controls how many pending validation jobs can be held in queue |
| `MCAPTCHA_captcha_ENABLE_STATS` | Record for CAPTCHA events like configuration fetch, solves and authentication of validation token. Useful for commercial deployments. | |
| `MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_avg_traffic_difficulty`% | Default difficulty factor to use in easy mode CAPTCHA configuration estimation for average traffic metric |
| `MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_peak_sustainable_traffic_difficulty`% | Default difficulty factor to use in easy mode CAPTCHA configuration estimation for peak traffic metric |
| `MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_broke_my_site_traffic_difficulty`% | Default difficulty factor to use in easy mode CAPTCHA configuration estimation for traffic that took the website down |
| `MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_duration`% | Default duration to use in CAPTCHA configuration in easy mode |
| Name | Value |
| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `MCAPTCHA_captcha_SALT` | Salt has to be long and random |
| `MCAPTCHA_captcha_GC` | Garbage collection duration in seconds, requires tuning but 30 is a good starting point |
| `MCAPTCHA_captcha_RUNNERS` | [Performance] Number of runners to use for PoW validation. Defaults to number of CPUs available |
| `MCAPTCHA_captcha_QUEUE_LENGTH` | [Performance] PoW Validation queue length, controls how many pending validation jobs can be held in queue |
| `MCAPTCHA_captcha_ENABLE_STATS` | Record for CAPTCHA events like configuration fetch, solves and authentication of validation token. Useful for commercial deployments. |
| `MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_avg_traffic_difficulty` | Default difficulty factor to use in easy mode CAPTCHA configuration estimation for average traffic metric |
| `MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_avg_traffic_time` | This difficulty factor is used in to use in easy mode CAPTCHA configuration estimation for average traffic metric |
| `MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_peak_sustainable_traffic_difficulty` | Default difficulty factor to use in easy mode CAPTCHA configuration estimation for peak traffic metric |
| `MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_peak_sustainable_traffic_time` | This difficulty factor is used in to use in easy mode CAPTCHA configuration estimation for peak traffic metric |
| `MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_broke_my_site_traffic_difficulty` | Default difficulty factor to use in easy mode CAPTCHA configuration estimation for traffic that took the website down |
| `MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_broke_my_site_traffic_time` | Default time (in seconds) to use to compute difficulty factor using stored PoW performance records. |
| `MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_duration` | Default duration to use in CAPTCHA configuration in easy mode |
\% See commits
[`54b14291ec140e`](https://github.com/mCaptcha/mCaptcha/commit/54b14291ec140ea4cbbf73462d3d6fc2d39f2d2c)
and
[`42544ec421e0`](https://github.com/mCaptcha/mCaptcha/commit/42544ec421e0c3ec4a8d132e6101ab4069bf0065)
for more info.
See commits [`54b14291ec140e`](https://github.com/mCaptcha/mCaptcha/commit/54b14291ec140ea4cbbf73462d3d6fc2d39f2d2c) and [`42544ec421e0`](https://github.com/mCaptcha/mCaptcha/commit/42544ec421e0c3ec4a8d132e6101ab4069bf0065) for more info.
### SMTP

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,6 @@
},
"homepage": "https://github.com/mCaptcha/mCaptcha#readme",
"devDependencies": {
"@redocly/cli": "^1.4.0"
"@redocly/cli": "^2.0.0"
}
}

File diff suppressed because it is too large Load Diff

46
eslint.config.js Normal file
View File

@@ -0,0 +1,46 @@
const {
defineConfig,
} = require("eslint/config");
const globals = require("globals");
const tsParser = require("@typescript-eslint/parser");
const typescriptEslint = require("@typescript-eslint/eslint-plugin");
const js = require("@eslint/js");
const {
FlatCompat,
} = require("@eslint/eslintrc");
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all
});
module.exports = defineConfig([{
languageOptions: {
globals: {
...globals.browser,
},
parser: tsParser,
ecmaVersion: 12,
sourceType: "module",
parserOptions: {},
},
extends: compat.extends("eslint:recommended", "plugin:@typescript-eslint/recommended"),
plugins: {
"@typescript-eslint": typescriptEslint,
},
rules: {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/ban-types": "off",
indent: ["error", 2],
"linebreak-style": ["error", "unix"],
quotes: ["error", "double"],
semi: ["error", "always"],
},
}]);

19900
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
"name": "vanilla",
"main": "index.js",
"version": "1.0.0",
"license": "AGPL-3.0",
"license": "AGPL-3.0-or-later",
"scripts": {
"build": "webpack --mode production",
"lint": "yarn run eslint templates",
@@ -10,35 +10,38 @@
"test": "jest"
},
"devDependencies": {
"@types/jest": "^29.5.6",
"@types/jsdom": "^21.1.4",
"@types/node": "^20.8.9",
"@types/sinon": "^10.0.20",
"@typescript-eslint/eslint-plugin": "^6.9.0",
"@typescript-eslint/parser": "^6.9.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.36.0",
"@types/jest": "^30.0.0",
"@types/jsdom": "^27.0.0",
"@types/node": "^22.0.0",
"@types/sinon": "^17.0.0",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"@wasm-tool/wasm-pack-plugin": "^1.7.0",
"css-loader": "^6.8.1",
"css-minimizer-webpack-plugin": "^5.0.1",
"eslint": "^8.52.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"css-loader": "^7.0.0",
"css-minimizer-webpack-plugin": "^7.0.0",
"eslint": "^9.0.0",
"globals": "^16.4.0",
"jest": "^30.0.0",
"jest-environment-jsdom": "^30.0.0",
"jest-fetch-mock": "^3.0.3",
"jsdom": "^22.1.0",
"jsdom": "^27.0.0",
"mini-css-extract-plugin": "^2.7.6",
"sass": "^1.69.5",
"sass-loader": "^13.3.2",
"sinon": "^17.0.0",
"sass-loader": "^16.0.0",
"sinon": "^21.0.0",
"ts-jest": "^29.1.1",
"ts-loader": "^9.5.0",
"ts-node": "^10.9.1",
"typescript": "^5.2.2",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
"webpack-cli": "^6.0.0",
"webpack-dev-server": "^5.0.0"
},
"dependencies": {
"@mcaptcha/pow_sha256-polyfill": "^0.1.0-rc2",
"@mcaptcha/vanilla-glue": "^0.1.0-rc1",
"@mcaptcha/pow-wasm": "^0.1.0-rc2"
"@mcaptcha/pow-wasm": "^0.1.0-rc2",
"@mcaptcha/vanilla-glue": "^0.1.0-rc1"
}
}

21
renovate.json Normal file
View File

@@ -0,0 +1,21 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":dependencyDashboard"
],
"labels": [
"renovate-bot"
],
"prHourlyLimit": 0,
"timezone": "Asia/kolkata",
"prCreation": "immediate",
"vulnerabilityAlerts": {
"enabled": true,
"labels": [
"renovate-bot",
"renovate-security",
"security"
]
}
}

View File

@@ -22,8 +22,8 @@ is_ci(){
docker-compose down -v --remove-orphans || true
docker-compose up -d
docker compose down -v --remove-orphans || true
docker compose up -d
cd $(mktemp -d)
pwd
find
@@ -40,4 +40,4 @@ else
fi
cd $PROJECT_ROOT
docker-compose down -v --remove-orphans || true
docker compose down -v --remove-orphans || true

View File

@@ -44,6 +44,7 @@ copy() {
mkdir $TARGET_DIR/docs
cp docs/DEPLOYMENT.md $TARGET_DIR/docs
cp docs/CONFIGURATION.md $TARGET_DIR/docs
cp config/default.toml $TARGET_DIR/config.toml
get_bin
}

View File

@@ -101,6 +101,79 @@ pub fn calculate(
Ok(levels)
}
async fn calculate_with_percentile(
data: &AppData,
tp: &TrafficPattern,
) -> ServiceResult<Option<Vec<Level>>> {
use crate::api::v1::stats::{percentile_bench_runner, PercentileReq};
let strategy = &data.settings.captcha.default_difficulty_strategy;
if strategy.avg_traffic_time.is_none()
&& strategy.peak_sustainable_traffic_time.is_none()
&& strategy.broke_my_site_traffic_time.is_none()
{
return Ok(None);
}
let mut req = PercentileReq {
time: strategy.avg_traffic_time.unwrap(),
percentile: 90.00,
};
let resp = percentile_bench_runner(data, &req).await?;
if resp.difficulty_factor.is_none() {
return Ok(None);
}
let avg_traffic_difficulty = resp.difficulty_factor.unwrap();
req.time = strategy.peak_sustainable_traffic_time.unwrap();
let resp = percentile_bench_runner(data, &req).await?;
if resp.difficulty_factor.is_none() {
return Ok(None);
}
let peak_sustainable_traffic_difficulty = resp.difficulty_factor.unwrap();
req.time = strategy.broke_my_site_traffic_time.unwrap();
let resp = percentile_bench_runner(data, &req).await?;
let broke_my_site_traffic_difficulty = if resp.difficulty_factor.is_none() {
resp.difficulty_factor.unwrap()
} else {
peak_sustainable_traffic_difficulty * 2
};
let mut levels = vec![
LevelBuilder::default()
.difficulty_factor(avg_traffic_difficulty)?
.visitor_threshold(tp.avg_traffic)
.build()?,
LevelBuilder::default()
.difficulty_factor(peak_sustainable_traffic_difficulty)?
.visitor_threshold(tp.peak_sustainable_traffic)
.build()?,
];
let mut highest_level = LevelBuilder::default();
highest_level.difficulty_factor(broke_my_site_traffic_difficulty)?;
match tp.broke_my_site_traffic {
Some(broke_my_site_traffic) => {
highest_level.visitor_threshold(broke_my_site_traffic)
}
None => match tp
.peak_sustainable_traffic
.checked_add(tp.peak_sustainable_traffic / 2)
{
Some(num) => highest_level.visitor_threshold(num),
// TODO check for overflow: database saves these values as i32, so this u32 is cast
// into i32. Should choose bigger number or casts properly
None => highest_level.visitor_threshold(u32::MAX),
},
};
levels.push(highest_level.build()?);
Ok(Some(levels))
}
#[my_codegen::post(
path = "crate::V1_API_ROUTES.captcha.easy.create",
wrap = "crate::api::v1::get_middleware()"
@@ -113,8 +186,12 @@ async fn create(
let username = id.identity().unwrap();
let payload = payload.into_inner();
let pattern = (&payload).into();
let levels =
calculate(&pattern, &data.settings.captcha.default_difficulty_strategy)?;
let levels = if let Some(levels) = calculate_with_percentile(&data, &pattern).await?
{
levels
} else {
calculate(&pattern, &data.settings.captcha.default_difficulty_strategy)?
};
let msg = CreateCaptcha {
levels,
duration: data.settings.captcha.default_difficulty_strategy.duration,
@@ -147,6 +224,15 @@ async fn update(
) -> ServiceResult<impl Responder> {
let username = id.identity().unwrap();
let payload = payload.into_inner();
update_runner(&data, payload, username).await?;
Ok(HttpResponse::Ok())
}
pub async fn update_runner(
data: &AppData,
payload: UpdateTrafficPattern,
username: String,
) -> ServiceResult<()> {
let pattern = (&payload.pattern).into();
let levels =
calculate(&pattern, &data.settings.captcha.default_difficulty_strategy)?;
@@ -167,7 +253,7 @@ async fn update(
.add_traffic_pattern(&username, &msg.key, &pattern)
.await?;
Ok(HttpResponse::Ok())
Ok(())
}
#[cfg(test)]

View File

@@ -62,6 +62,7 @@ impl Health {
#[my_codegen::get(path = "crate::V1_API_ROUTES.meta.health")]
async fn health(data: AppData) -> impl Responder {
let mut resp_builder = HealthBuilder::default();
resp_builder.redis(None);
resp_builder.db(data.db.ping().await);

View File

@@ -32,82 +32,89 @@ pub mod routes {
}
}
/// Get difficulty factor with max time limit for percentile of stats
#[my_codegen::post(path = "crate::V1_API_ROUTES.stats.percentile_benches")]
async fn percentile_benches(
data: AppData,
payload: web::Json<PercentileReq>,
) -> ServiceResult<impl Responder> {
let count = data.db.stats_get_num_logs_under_time(payload.time).await?;
pub async fn percentile_bench_runner(
data: &AppData,
req: &PercentileReq,
) -> ServiceResult<PercentileResp> {
let count = data.db.stats_get_num_logs_under_time(req.time).await?;
if count == 0 {
return Ok(HttpResponse::Ok().json(PercentileResp {
return Ok(PercentileResp {
difficulty_factor: None,
}));
});
}
if count < 2 {
return Ok(HttpResponse::Ok().json(PercentileResp {
return Ok(PercentileResp {
difficulty_factor: None,
}));
});
}
let location = ((count - 1) as f64 * (payload.percentile / 100.00)) + 1.00;
let location = ((count - 1) as f64 * (req.percentile / 100.00)) + 1.00;
let fraction = location - location.floor();
if fraction > 0.00 {
if let (Some(base), Some(ceiling)) = (
data.db
.stats_get_entry_at_location_for_time_limit_asc(
payload.time,
req.time,
location.floor() as u32,
)
.await?,
data.db
.stats_get_entry_at_location_for_time_limit_asc(
payload.time,
req.time,
location.floor() as u32 + 1,
)
.await?,
) {
let res = base as u32 + ((ceiling - base) as f64 * fraction).floor() as u32;
return Ok(HttpResponse::Ok().json(PercentileResp {
return Ok(PercentileResp {
difficulty_factor: Some(res),
}));
});
}
} else {
if let Some(base) = data
.db
.stats_get_entry_at_location_for_time_limit_asc(
payload.time,
req.time,
location.floor() as u32,
)
.await?
{
let res = base as u32;
return Ok(HttpResponse::Ok().json(PercentileResp {
return Ok(PercentileResp {
difficulty_factor: Some(res),
}));
});
}
};
Ok(HttpResponse::Ok().json(PercentileResp {
Ok(PercentileResp {
difficulty_factor: None,
}))
})
}
/// Get difficulty factor with max time limit for percentile of stats
#[my_codegen::post(path = "crate::V1_API_ROUTES.stats.percentile_benches")]
async fn percentile_benches(
data: AppData,
payload: web::Json<PercentileReq>,
) -> ServiceResult<impl Responder> {
Ok(HttpResponse::Ok().json(percentile_bench_runner(&data, &payload).await?))
}
#[derive(Clone, Debug, Deserialize, Builder, Serialize)]
/// Health check return datatype
pub struct PercentileReq {
time: u32,
percentile: f64,
pub time: u32,
pub percentile: f64,
}
#[derive(Clone, Debug, Deserialize, Builder, Serialize)]
/// Health check return datatype
pub struct PercentileResp {
difficulty_factor: Option<u32>,
pub difficulty_factor: Option<u32>,
}
pub fn services(cfg: &mut web::ServiceConfig) {

View File

@@ -188,7 +188,6 @@ impl Data {
.build()
.unwrap()
}
#[cfg(not(tarpaulin_include))]
/// create new instance of app data
pub async fn new(s: &Settings, survey_secrets: SecretsStore) -> Arc<Self> {
let creds = Self::get_creds();

View File

@@ -8,6 +8,7 @@ use std::time::Duration;
use actix::clock::sleep;
use actix::spawn;
use tokio::sync::oneshot::{channel, error::TryRecvError, Receiver, Sender};
use tokio::task::JoinHandle;
use crate::api::v1::account::delete::runners::delete_user;
@@ -23,20 +24,24 @@ pub const DEMO_USER: &str = "aaronsw";
pub const DEMO_PASSWORD: &str = "password";
pub struct DemoUser {
handle: JoinHandle<()>,
tx: Sender<()>,
}
impl DemoUser {
pub async fn spawn(data: AppData, duration: Duration) -> ServiceResult<Self> {
let handle = Self::run(data, duration).await?;
let d = Self { handle };
pub async fn spawn(
data: AppData,
duration: u32,
) -> ServiceResult<(Self, JoinHandle<()>)> {
let (tx, rx) = channel();
let handle = Self::run(data, duration, rx).await?;
let d = Self { tx };
Ok(d)
Ok((d, handle))
}
#[allow(dead_code)]
pub fn abort(&self) {
self.handle.abort();
pub fn abort(mut self) {
self.tx.send(());
}
/// register demo user runner
@@ -71,16 +76,38 @@ impl DemoUser {
pub async fn run(
data: AppData,
duration: Duration,
duration: u32,
mut rx: Receiver<()>,
) -> ServiceResult<JoinHandle<()>> {
Self::register_demo_user(&data).await?;
fn can_run(rx: &mut Receiver<()>) -> bool {
match rx.try_recv() {
Err(TryRecvError::Empty) => true,
_ => false,
}
}
let mut exit = false;
let fut = async move {
loop {
sleep(duration).await;
if exit {
break;
}
for _ in 0..duration {
if can_run(&mut rx) {
sleep(Duration::new(1, 0)).await;
continue;
} else {
exit = true;
break;
}
}
if let Err(e) = Self::delete_demo_user(&data).await {
log::error!("Error while deleting demo user: {:?}", e);
}
if let Err(e) = Self::register_demo_user(&data).await {
log::error!("Error while registering demo user: {:?}", e);
}
@@ -133,7 +160,7 @@ mod tests {
assert!(!username_exists(&payload, &data).await.unwrap().exists);
// test the runner
let user = DemoUser::spawn(data, duration).await.unwrap();
let user = DemoUser::spawn(data, DURATION as u32).await.unwrap();
let (_, signin_resp, token_key) =
add_levels_util(data_inner, DEMO_USER, DEMO_PASSWORD).await;
let cookies = get_cookie!(signin_resp);
@@ -162,6 +189,7 @@ mod tests {
assert_eq!(resp.status(), StatusCode::OK);
let res_levels: Vec<Level> = test::read_body_json(resp).await;
assert!(res_levels.is_empty());
user.abort();
user.0.abort();
user.1.await.unwrap();
}
}

136
src/easy.rs Normal file
View File

@@ -0,0 +1,136 @@
// Copyright (C) 2024// Copyright (C) 2024 Aravinth Manivannan <realaravinth@batsense.net>
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
use std::time::Duration;
//use std::sync::atomicBool
use actix::clock::sleep;
use actix::spawn;
use tokio::sync::oneshot::{channel, error::TryRecvError, Receiver, Sender};
use tokio::task::JoinHandle;
use crate::api::v1::mcaptcha::easy::{
update_runner, TrafficPatternRequest, UpdateTrafficPattern,
};
use crate::*;
use errors::*;
pub struct UpdateEasyCaptcha {
tx: Sender<()>,
}
impl UpdateEasyCaptcha {
pub async fn spawn(
data: AppData,
duration: u32,
) -> ServiceResult<(Self, JoinHandle<()>)> {
let (tx, rx) = channel();
let handle = Self::run(data, duration, rx).await?;
let d = Self { tx };
Ok((d, handle))
}
#[allow(dead_code)]
pub fn abort(mut self) {
self.tx.send(());
}
/// update configurations
async fn update_captcha_configurations(
data: &AppData,
rx: &mut Receiver<()>,
) -> ServiceResult<()> {
let limit = 10;
let mut offset = 0;
let mut page = 0;
loop {
offset = page * limit;
if !Self::can_run(rx) {
return Ok(());
}
let mut patterns = data.db.get_all_easy_captchas(limit, offset).await?;
if patterns.is_empty() {
break;
}
for pattern in patterns.drain(0..) {
if !Self::can_run(rx) {
return Ok(());
}
let publish_benchmarks =
data.db.analytics_captcha_is_published(&pattern.key).await?;
let req = UpdateTrafficPattern {
pattern: TrafficPatternRequest {
avg_traffic: pattern.traffic_pattern.avg_traffic,
peak_sustainable_traffic: pattern
.traffic_pattern
.peak_sustainable_traffic,
broke_my_site_traffic: pattern
.traffic_pattern
.broke_my_site_traffic,
description: pattern.description,
publish_benchmarks,
},
key: pattern.key,
};
if !Self::can_run(rx) {
return Ok(());
}
update_runner(&data, req, pattern.username).await?;
}
page += 1;
}
Ok(())
}
fn can_run(rx: &mut Receiver<()>) -> bool {
match rx.try_recv() {
Err(TryRecvError::Empty) => true,
_ => false,
}
}
pub async fn run(
data: AppData,
duration: u32,
mut rx: Receiver<()>,
) -> ServiceResult<JoinHandle<()>> {
let mut exit = false;
let fut = async move {
loop {
if exit {
break;
}
for _ in 0..duration {
if Self::can_run(&mut rx) {
sleep(Duration::new(1, 0)).await;
continue;
} else {
exit = true;
break;
}
}
if let Some(err) = Self::update_captcha_configurations(&data, &mut rx)
.await
.err()
{
log::error!(
"Tried to update easy captcha configurations in background {:?}",
err
);
}
}
};
let handle = spawn(fut);
Ok(handle)
}
}

View File

@@ -40,7 +40,6 @@ impl std::cmp::PartialEq for SmtpErrorWrapper {
}
#[derive(Debug, Display, PartialEq, Error)]
#[cfg(not(tarpaulin_include))]
pub enum ServiceError {
#[display(fmt = "internal server error")]
InternalServerError,
@@ -114,14 +113,11 @@ pub enum ServiceError {
}
#[derive(Serialize, Deserialize)]
#[cfg(not(tarpaulin_include))]
pub struct ErrorToResponse {
pub error: String,
}
#[cfg(not(tarpaulin_include))]
impl ResponseError for ServiceError {
#[cfg(not(tarpaulin_include))]
fn error_response(&self) -> HttpResponse {
HttpResponseBuilder::new(self.status_code())
.append_header((header::CONTENT_TYPE, "application/json; charset=UTF-8"))
@@ -133,7 +129,6 @@ impl ResponseError for ServiceError {
)
}
#[cfg(not(tarpaulin_include))]
fn status_code(&self) -> StatusCode {
match self {
ServiceError::ClosedForRegistration => StatusCode::FORBIDDEN,
@@ -177,7 +172,6 @@ impl ResponseError for ServiceError {
}
impl From<CredsError> for ServiceError {
#[cfg(not(tarpaulin_include))]
fn from(e: CredsError) -> ServiceError {
match e {
CredsError::UsernameCaseMappedError => ServiceError::UsernameCaseMappedError,
@@ -192,7 +186,6 @@ impl From<CredsError> for ServiceError {
}
impl From<DBError> for ServiceError {
#[cfg(not(tarpaulin_include))]
fn from(e: DBError) -> ServiceError {
println!("from conversin: {}", e);
match e {
@@ -208,57 +201,46 @@ impl From<DBError> for ServiceError {
}
impl From<ValidationErrors> for ServiceError {
#[cfg(not(tarpaulin_include))]
fn from(_: ValidationErrors) -> ServiceError {
ServiceError::NotAnEmail
}
}
impl From<ParseError> for ServiceError {
#[cfg(not(tarpaulin_include))]
fn from(_: ParseError) -> ServiceError {
ServiceError::NotAUrl
}
}
#[cfg(not(tarpaulin_include))]
impl From<CaptchaError> for ServiceError {
fn from(e: CaptchaError) -> ServiceError {
ServiceError::CaptchaError(e)
}
}
#[cfg(not(tarpaulin_include))]
impl From<SmtpError> for ServiceError {
#[cfg(not(tarpaulin_include))]
fn from(e: SmtpError) -> Self {
ServiceError::UnableToSendEmail(SmtpErrorWrapper(e))
}
}
#[cfg(not(tarpaulin_include))]
impl From<RecvError> for ServiceError {
#[cfg(not(tarpaulin_include))]
fn from(e: RecvError) -> Self {
log::error!("{:?}", e);
ServiceError::InternalServerError
}
}
#[cfg(not(tarpaulin_include))]
impl From<MailboxError> for ServiceError {
#[cfg(not(tarpaulin_include))]
fn from(e: MailboxError) -> Self {
log::error!("{:?}", e);
ServiceError::InternalServerError
}
}
#[cfg(not(tarpaulin_include))]
pub type ServiceResult<V> = std::result::Result<V, ServiceError>;
#[derive(Debug, Display, PartialEq, Error)]
#[cfg(not(tarpaulin_include))]
pub enum PageError {
#[display(fmt = "Something weng wrong: Internal server error")]
InternalServerError,
@@ -267,17 +249,13 @@ pub enum PageError {
ServiceError(ServiceError),
}
#[cfg(not(tarpaulin_include))]
impl From<ServiceError> for PageError {
#[cfg(not(tarpaulin_include))]
fn from(e: ServiceError) -> Self {
PageError::ServiceError(e)
}
}
#[cfg(not(tarpaulin_include))]
impl From<DBError> for PageError {
#[cfg(not(tarpaulin_include))]
fn from(e: DBError) -> Self {
let se: ServiceError = e.into();
se.into()
@@ -297,7 +275,6 @@ impl ResponseError for PageError {
}
}
#[cfg(not(tarpaulin_include))]
fn status_code(&self) -> StatusCode {
match self {
PageError::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR,
@@ -306,7 +283,6 @@ impl ResponseError for PageError {
}
}
#[cfg(not(tarpaulin_include))]
pub type PageResult<V> = std::result::Result<V, PageError>;
#[cfg(test)]

View File

@@ -14,6 +14,7 @@ use actix_web::{
};
use lazy_static::lazy_static;
use log::info;
use tokio::task::JoinHandle;
mod api;
mod data;
@@ -21,6 +22,7 @@ mod date;
mod db;
mod demo;
mod docs;
mod easy;
mod email;
mod errors;
#[macro_use]
@@ -90,7 +92,6 @@ pub const CACHE_AGE: u32 = 604800;
pub type ArcData = Arc<crate::data::Data>;
pub type AppData = actix_web::web::Data<ArcData>;
#[cfg(not(tarpaulin_include))]
#[actix_web::main]
async fn main() -> std::io::Result<()> {
use std::time::Duration;
@@ -110,11 +111,22 @@ async fn main() -> std::io::Result<()> {
let data = Data::new(&settings, secrets.clone()).await;
let data = actix_web::web::Data::new(data);
let mut demo_user: Option<DemoUser> = None;
let mut demo_user: Option<(DemoUser, JoinHandle<()>)> = None;
if settings.allow_demo && settings.allow_registration {
demo_user = Some(
DemoUser::spawn(data.clone(), Duration::from_secs(60 * 30))
demo_user = Some(DemoUser::spawn(data.clone(), 60 * 30).await.unwrap());
}
let mut update_easy_captcha: Option<(easy::UpdateEasyCaptcha, JoinHandle<()>)> =
None;
if settings
.captcha
.default_difficulty_strategy
.avg_traffic_time
.is_some()
{
update_easy_captcha = Some(
easy::UpdateEasyCaptcha::spawn(data.clone(), 60 * 30)
.await
.unwrap(),
);
@@ -156,7 +168,13 @@ async fn main() -> std::io::Result<()> {
}
if let Some(demo_user) = demo_user {
demo_user.abort();
demo_user.0.abort();
demo_user.1.await.unwrap();
}
if let Some(update_easy_captcha) = update_easy_captcha {
update_easy_captcha.0.abort();
update_easy_captcha.1.await.unwrap();
}
if let Some(survey_upload_handle) = survey_upload_handle {
@@ -166,7 +184,6 @@ async fn main() -> std::io::Result<()> {
Ok(())
}
#[cfg(not(tarpaulin_include))]
pub fn get_json_err() -> JsonConfig {
JsonConfig::default().error_handler(|err, _| {
//debug!("JSON deserialization error: {:?}", &err);
@@ -174,7 +191,6 @@ pub fn get_json_err() -> JsonConfig {
})
}
#[cfg(not(tarpaulin_include))]
pub fn get_identity_service(
settings: &Settings,
) -> IdentityService<CookieIdentityPolicy> {

View File

@@ -25,7 +25,6 @@ pub fn get_middleware() -> Authentication<routes::Routes> {
Authentication::with_identity(routes::ROUTES)
}
#[cfg(not(tarpaulin_include))]
#[cfg(test)]
mod tests {
use actix_web::http::StatusCode;

View File

@@ -10,6 +10,7 @@ use sailfish::TemplateOnce;
mod notifications;
mod settings;
pub mod sitekey;
mod utils;
use db_core::Captcha;
@@ -47,18 +48,21 @@ pub fn services(cfg: &mut actix_web::web::ServiceConfig) {
cfg.service(panel);
settings::services(cfg);
sitekey::services(cfg);
utils::services(cfg);
cfg.service(notifications::notifications);
}
pub mod routes {
use super::settings::routes::Settings;
use super::sitekey::routes::Sitekey;
use super::utils::routes::Utils;
pub struct Panel {
pub home: &'static str,
pub sitekey: Sitekey,
pub notifications: &'static str,
pub settings: Settings,
pub utils: Utils,
}
impl Panel {
@@ -68,10 +72,11 @@ pub mod routes {
sitekey: Sitekey::new(),
notifications: "/notifications",
settings: Settings::new(),
utils: Utils::new(),
}
}
pub const fn get_sitemap() -> [&'static str; 5] {
pub const fn get_sitemap() -> [&'static str; 6] {
const PANEL: Panel = Panel::new();
const S: [&str; 2] = Sitekey::get_sitemap();
@@ -81,6 +86,7 @@ pub mod routes {
S[0],
S[1],
Settings::get_sitemap()[0],
Utils::get_sitemap()[0],
]
}
}

280
src/pages/panel/utils.rs Normal file
View File

@@ -0,0 +1,280 @@
// Copyright (C) 2024 Aravinth Manivannan <realaravinth@batsense.net>
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
use actix_identity::Identity;
use actix_web::{web, HttpResponse, Responder};
use sailfish::TemplateOnce;
use crate::api::v1::stats::{percentile_bench_runner, PercentileReq, PercentileResp};
use crate::errors::PageResult;
use crate::pages::auth::sudo::SudoPage;
use crate::AppData;
pub mod routes {
pub struct Utils {
pub percentile: &'static str,
}
impl Utils {
pub const fn new() -> Self {
Utils {
percentile: "/utils/percentile",
}
}
pub const fn get_sitemap() -> [&'static str; 1] {
const S: Utils = Utils::new();
[S.percentile]
}
}
}
pub fn services(cfg: &mut actix_web::web::ServiceConfig) {
cfg.service(get_percentile);
cfg.service(post_percentile);
}
const PAGE: &str = "Difficulty factor statistics";
#[derive(TemplateOnce, Clone)]
#[template(path = "panel/utils/percentile/index.html")]
pub struct PercentilePage {
time: Option<u32>,
percentile: Option<f64>,
difficulty_factor: Option<u32>,
}
#[my_codegen::get(
path = "crate::PAGES.panel.utils.percentile",
wrap = "crate::pages::get_middleware()"
)]
async fn get_percentile(id: Identity) -> PageResult<impl Responder> {
let data = PercentilePage {
time: None,
percentile: None,
difficulty_factor: None,
};
let body = data.render_once().unwrap();
Ok(HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(body))
}
#[my_codegen::post(
path = "crate::PAGES.panel.utils.percentile",
wrap = "crate::pages::get_middleware()"
)]
async fn post_percentile(
data: AppData,
id: Identity,
payload: web::Form<PercentileReq>,
) -> PageResult<impl Responder> {
let resp = percentile_bench_runner(&data, &payload).await?;
let page = PercentilePage {
time: Some(payload.time),
percentile: Some(payload.percentile),
difficulty_factor: resp.difficulty_factor,
};
let body = page.render_once().unwrap();
Ok(HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(body))
}
#[cfg(test)]
mod tests {
use actix_web::{http::StatusCode, test, web::Bytes, App};
use super::*;
use crate::api::v1::services;
use crate::*;
#[actix_rt::test]
async fn page_stats_bench_work_pg() {
let data = crate::tests::pg::get_data().await;
page_stats_bench_work(data).await;
}
#[actix_rt::test]
async fn page_stats_bench_work_maria() {
let data = crate::tests::maria::get_data().await;
page_stats_bench_work(data).await;
}
async fn page_stats_bench_work(data: ArcData) {
use crate::tests::*;
const NAME: &str = "pagebenchstatsuesr";
const EMAIL: &str = "pagebenchstatsuesr@testadminuser.com";
const PASSWORD: &str = "longpassword2";
const DEVICE_USER_PROVIDED: &str = "foo";
const DEVICE_SOFTWARE_RECOGNISED: &str = "Foobar.v2";
const THREADS: i32 = 4;
let data = &data;
{
delete_user(&data, NAME).await;
}
register_and_signin(data, NAME, EMAIL, PASSWORD).await;
// create captcha
let (_, signin_resp, key) = add_levels_util(data, NAME, PASSWORD).await;
let app = get_app!(data).await;
let cookies = get_cookie!(signin_resp);
let page = 1;
let tmp_id = uuid::Uuid::new_v4();
let download_rotue = V1_API_ROUTES
.survey
.get_download_route(&tmp_id.to_string(), page);
let download_req = test::call_service(
&app,
test::TestRequest::get().uri(&download_rotue).to_request(),
)
.await;
assert_eq!(download_req.status(), StatusCode::NOT_FOUND);
data.db
.analytics_create_psuedo_id_if_not_exists(&key.key)
.await
.unwrap();
let psuedo_id = data
.db
.analytics_get_psuedo_id_from_capmaign_id(&key.key)
.await
.unwrap();
for i in 1..6 {
println!("[{i}] Saving analytics");
let analytics = db_core::CreatePerformanceAnalytics {
time: i,
difficulty_factor: i,
worker_type: "wasm".into(),
};
data.db.analysis_save(&key.key, &analytics).await.unwrap();
}
let msg = PercentileReq {
time: 1,
percentile: 99.00,
};
let resp = test::call_service(
&app,
post_request!(&msg, V1_API_ROUTES.stats.percentile_benches).to_request(),
)
.await;
assert_eq!(resp.status(), StatusCode::OK);
let resp: PercentileResp = test::read_body_json(resp).await;
assert!(resp.difficulty_factor.is_none());
let msg = PercentileReq {
time: 1,
percentile: 100.00,
};
let resp = test::call_service(
&app,
post_request!(&msg, V1_API_ROUTES.stats.percentile_benches).to_request(),
)
.await;
assert_eq!(resp.status(), StatusCode::OK);
let resp: PercentileResp = test::read_body_json(resp).await;
// start
let percentile_resp = test::call_service(
&app,
test::TestRequest::get()
.uri(&crate::PAGES.panel.utils.percentile)
.cookie(cookies.clone())
.to_request(),
)
.await;
assert_eq!(percentile_resp.status(), StatusCode::OK);
let body: Bytes = test::read_body(percentile_resp).await;
let body = String::from_utf8(body.to_vec()).unwrap();
assert!(body.contains("Maximum time taken"));
let percentile_resp = test::call_service(
&app,
test::TestRequest::get()
.uri(&crate::PAGES.panel.utils.percentile)
.cookie(cookies.clone())
.to_request(),
)
.await;
assert_eq!(percentile_resp.status(), StatusCode::OK);
let body: Bytes = test::read_body(percentile_resp).await;
let body = String::from_utf8(body.to_vec()).unwrap();
assert!(body.contains("Maximum time taken"));
// end
// start post
let msg = PercentileReq {
time: 1,
percentile: 99.00,
};
let percentile_resp = test::call_service(
&app,
test::TestRequest::post()
.uri(&crate::PAGES.panel.utils.percentile)
.set_form(&msg)
.cookie(cookies.clone())
.to_request(),
)
.await;
assert_eq!(percentile_resp.status(), StatusCode::OK);
let body: Bytes = test::read_body(percentile_resp).await;
let body = String::from_utf8(body.to_vec()).unwrap();
assert!(body.contains(
"Not enough inputs to compute statistics. Please try again later"
));
assert!(body.contains(&1.to_string()));
assert!(body.contains(&99.00.to_string()));
// end post
// start post
let msg = PercentileReq {
time: 2,
percentile: 100.00,
};
let percentile_resp = test::call_service(
&app,
test::TestRequest::post()
.uri(&crate::PAGES.panel.utils.percentile)
.set_form(&msg)
.cookie(cookies.clone())
.to_request(),
)
.await;
assert_eq!(percentile_resp.status(), StatusCode::OK);
let body: Bytes = test::read_body(percentile_resp).await;
let body = String::from_utf8(body.to_vec()).unwrap();
assert!(body.contains("Difficulty factor: 2"));
assert!(body.contains(&2.to_string()));
assert!(body.contains(&100.00.to_string()));
}
}

View File

@@ -37,8 +37,11 @@ pub struct Captcha {
#[derive(Debug, Clone, Deserialize, Eq, PartialEq)]
pub struct DefaultDifficultyStrategy {
pub avg_traffic_difficulty: u32,
pub broke_my_site_traffic_difficulty: u32,
pub avg_traffic_time: Option<u32>,
pub peak_sustainable_traffic_difficulty: u32,
pub peak_sustainable_traffic_time: Option<u32>,
pub broke_my_site_traffic_time: Option<u32>,
pub broke_my_site_traffic_difficulty: u32,
pub duration: u32,
}
@@ -53,7 +56,6 @@ pub struct Smtp {
}
impl Server {
#[cfg(not(tarpaulin_include))]
pub fn get_ip(&self) -> String {
format!("{}:{}", self.ip, self.port)
}
@@ -113,7 +115,7 @@ pub struct Settings {
pub smtp: Option<Smtp>,
}
const ENV_VAR_CONFIG: [(&str, &str); 29] = [
const ENV_VAR_CONFIG: [(&str, &str); 32] = [
/* top-level */
("debug", "MCAPTCHA_debug"),
("commercial", "MCAPTCHA_commercial"),
@@ -150,6 +152,9 @@ const ENV_VAR_CONFIG: [(&str, &str); 29] = [
( "captcha.default_difficulty_strategy.duration",
"MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_duration"
),
("captcha.default_difficulty_strategy.avg_traffic_time", "MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_avg_traffic_time"),
("captcha.default_difficulty_strategy.peak_sustainable_traffic_time", "MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_peak_sustainable_traffic_time"),
("captcha.default_difficulty_strategy.broke_my_site_traffic_time", "MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_broke_my_site_traffic_time"),
/* SMTP */
@@ -199,7 +204,6 @@ const DEPRECATED_ENV_VARS: [(&str, &str); 23] = [
("smtp.port", "MCAPTCHA_SMTP_PORT"),
];
#[cfg(not(tarpaulin_include))]
impl Settings {
pub fn new() -> Result<Self, ConfigError> {
let mut s = Config::builder();
@@ -251,6 +255,28 @@ impl Settings {
Ok(settings)
}
fn check_easy_captcha_config(&self) {
let s = &self.captcha.default_difficulty_strategy;
if s.avg_traffic_time.is_some() {
if s.broke_my_site_traffic_time.is_none()
|| s.peak_sustainable_traffic_time.is_none()
{
panic!("if captcha.default_difficulty_strategy.avg_traffic_time is set, then captcha.default_difficulty_strategy.broke_my_site_traffic_time and captcha.default_difficulty_strategy.peak_sustainable_traffic_time must also be set");
}
}
if s.peak_sustainable_traffic_time.is_some() {
if s.avg_traffic_time.is_none() || s.peak_sustainable_traffic_time.is_none()
{
panic!("if captcha.default_difficulty_strategy.peak_sustainable_traffic_time is set, then captcha.default_difficulty_strategy.broke_my_site_traffic_time and captcha.default_difficulty_strategy.avg_traffic_time must also be set");
}
}
if s.broke_my_site_traffic_time.is_some() {
if s.avg_traffic_time.is_none() || s.peak_sustainable_traffic_time.is_none()
{
panic!("if captcha.default_difficulty_strategy.broke_my_site_traffic_time is set, then captcha.default_difficulty_strategy.peak_sustainable_traffic_time and captcha.default_difficulty_strategy.avg_traffic_time must also be set");
}
}
}
fn env_override(mut s: ConfigBuilder<DefaultState>) -> ConfigBuilder<DefaultState> {
for (parameter, env_var_name) in DEPRECATED_ENV_VARS.iter() {
@@ -538,6 +564,30 @@ mod tests {
999,
captcha.default_difficulty_strategy.duration
);
helper!(
"MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_avg_traffic_time",
"10",
Some(10),
captcha.default_difficulty_strategy.avg_traffic_time
);
helper!(
"MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_peak_sustainable_traffic_time",
"20",
Some(20),
captcha
.default_difficulty_strategy
.peak_sustainable_traffic_time
);
helper!(
"MCAPTCHA_captcha_DEFAULT_DIFFICULTY_STRATEGY_broke_my_site_traffic_time",
"30",
Some(30),
captcha
.default_difficulty_strategy
.broke_my_site_traffic_time
);
/* SMTP */

View File

@@ -2,13 +2,13 @@
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
"use strict";
import createError from "./index";
import * as e from "./index";
import setup from "./setUpTests";
"use strict";
jest.useFakeTimers();

View File

@@ -5,15 +5,15 @@ SPDX-License-Identifier: AGPL-3.0-or-later
-->
<nav class="secondary-menu">
<input type="checkbox" class="nav-toggle" id="nav-toggle" >
<input type="checkbox" class="nav-toggle" id="nav-toggle" >
<div class="secondary-menu__heading">
<a class="novisit" href="/">
<img class="secondary-menu__logo" src="<.= crate::MCAPTCHA_TRANS_ICON.0 .>" alt="<.= crate::MCAPTCHA_TRANS_ICON.1 .>" />
<a class="novisit" href="/">
<img class="secondary-menu__logo" src="<.= crate::MCAPTCHA_TRANS_ICON.0 .>" alt="<.= crate::MCAPTCHA_TRANS_ICON.1 .>" />
</a>
<a href="/" class="secondary-menu__brand-name">
mCaptcha
</a>
<label class="nav__hamburger-menu"for="nav-toggle">
<label class="nav__hamburger-menu"for="nav-toggle">
<span></span>
<span></span>
<span></span>
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later
</div>
<ul class="secondary-menu__list">
<li class="secondary-menu__item">
<a class="secondary-menu__item-link" href="<.= crate::PAGES.home .>">
<a class="secondary-menu__item-link" href="<.= crate::PAGES.home .>">
<img class="secondary-menu__icon" src="<.= crate::HOME.0 .>" alt="<.= crate::HOME.1 .>" />
<div class="secondary-menu__item-name">
Overview
@@ -29,13 +29,21 @@ SPDX-License-Identifier: AGPL-3.0-or-later
</a>
</li>
<li class="secondary-menu__item">
<a class="secondary-menu__item-link" href="<.= crate::PAGES.panel.sitekey.list .>">
<a class="secondary-menu__item-link" href="<.= crate::PAGES.panel.sitekey.list .>">
<img class="secondary-menu__icon" src="<.= crate::KEY.0 .>" alt="<.= crate::KEY.1 .>" />
<div class="secondary-menu__item-name">
Site Keys
</div>
</a>
</li>
<li class="secondary-menu__item">
<a class="secondary-menu__item-link" href="<.= crate::PAGES.panel.utils.percentile .>">
<img class="secondary-menu__icon" src="<.= crate::BAR_CHART.0 .>" alt="<.= crate::BAR_CHART.1 .>" />
<div class="secondary-menu__item-name">
Statistics
</div>
</a>
</li>
<li class="secondary-menu__item">
<a class="secondary-menu__item-link" href="<.= crate::PAGES.panel.settings.home .>">
<img class="secondary-menu__icon" src="<.= crate::SETTINGS_ICON.0 .>" alt="<.= crate::SETTINGS_ICON.1 .>" />

View File

@@ -0,0 +1,67 @@
<!--
SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
SPDX-License-Identifier: AGPL-3.0-or-later
-->
<. include!("../../../components/headers/index.html"); .>
<. include!("../../navbar/index.html"); .>
<div class="tmp-layout">
<. include!("../../header/index.html"); .>
<main class="panel-main">
<. include!("../../help-banner/index.html"); .>
<!-- Main content container -->
<div class="inner-container">
<div class="sitekey-form" action="<.= crate::V1_API_ROUTES.captcha.create .>" method="post">
<h1 class="form__title">
<.= PAGE .>
</h1>
<form class="settings__form" id="utils_percentile-form"
action="<.= crate::PAGES.panel.utils.percentile .>" method="post">
<. if let Some(difficulty_factor) = difficulty_factor { .>
<legend class="sitekey__level-title">
<p>Difficulty factor: <.= difficulty_factor .></p>
</legend>
<. } else { .>
<. if time.is_some() && percentile.is_some() { .>
<legend class="sitekey__level-title">
<p>Not enough inputs to compute statistics. Please try again later</p>
</legend>
<. } .>
<. } .>
<label class="settings-form__label" for="time">
Maximum time taken to solve CAPTCHA (in seconds)
<input
class="settings-form__input"
type="number"
name="time"
required
id="time"
<. if let Some(time) = time { .>
value="<.= time .>"
<. } .>
/>
</label>
<label class="settings-form__label" for="percentile">
Percentile of requests coming under time limit
<input
class="settings-form__input"
type="number"
name="percentile"
required
id="percentile"
<. if let Some(percentile) = percentile { .>
value="<.= percentile .>"
<. } .>
/>
</label>
<button class="settings__submit-btn" type="submit">Search</button>
</form>
</div>
</div>
<!-- end of container -->
<. include!("../../../components/footers.html"); .>

View File

@@ -2,10 +2,10 @@
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
"use strict";
import {Router} from "./router";
"use strict";
const result = {
result: "",

View File

@@ -2,10 +2,10 @@
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
"use strict";
import genJsonPayload from "./genJsonPayload";
"use strict";
const payload = {
username: "Jhon",

View File

@@ -2,12 +2,11 @@
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
"use strict";
import getFormUrl from "./getFormUrl";
import {getLoginFormHtml} from "../setUpTests";
"use strict";
const formClassName = "form__box";
const formURL = "/api/v1/signin";

View File

@@ -2,6 +2,7 @@
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
"use strict";
import isBlankString from "./isBlankString";
import {mockAlert} from "../setUpTests";
@@ -9,7 +10,6 @@ import {mockAlert} from "../setUpTests";
import setup from "../components/error/setUpTests";
"use strict";
mockAlert();

View File

@@ -2,11 +2,10 @@
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
"use strict";
import isNumber from "./isNumber";
"use strict";
it("getFromUrl workds", () => {
expect(isNumber("test")).toBe(false);
expect(isNumber("1test213")).toBe(false);

View File

@@ -56,53 +56,41 @@ type messageTextReturn = {
error: () => void;
};
export const messageText = (): messageTextReturn => {
const beforeID = "widget__verification-text--before";
const duringID = "widget__verification-text--during";
const errorID = "widget__verification-text--error";
const afterID = "widget__verification-text--after";
export const BEFORE = "I'm not a robot";
export const DURING = "Processing...";
export const AFTER = "Verified!";
export const ERROR = "Something went wrong";
const before = new LazyElement(beforeID);
const after = new LazyElement(afterID);
const during = new LazyElement(duringID);
const error = new LazyElement(errorID);
export const messageText = (): messageTextReturn => {
const conatinerID = "widget__verification-text";
const container = new LazyElement(conatinerID);
/** runner fn to display HTMLElement **/
const showMsg = (e: HTMLElement) => (e.style.display = "block");
/** runner fn to hide HTMLElement **/
const hideMsg = (e: HTMLElement) => (e.style.display = "none");
const showMsg = (value: string) => {
container.get().innerText = value;
btn().ariaValueText = value;
};
return {
/** display "before" message **/
before: () => {
showMsg(before.get());
hideMsg(after.get());
hideMsg(during.get());
hideMsg(error.get());
showMsg(BEFORE);
},
/** display "after" message **/
after: () => {
hideMsg(before.get());
showMsg(after.get());
hideMsg(during.get());
hideMsg(error.get());
showMsg(AFTER);
},
/** display "during" message **/
during: () => {
hideMsg(before.get());
hideMsg(after.get());
showMsg(during.get());
hideMsg(error.get());
showMsg(DURING);
},
/** display "error" message **/
error: () => {
hideMsg(before.get());
hideMsg(after.get());
hideMsg(during.get());
showMsg(error.get());
showMsg(ERROR);
},
};
};

View File

@@ -19,14 +19,15 @@ SPDX-License-Identifier: MIT OR Apache-2.0
</div>
</noscript>
<label class="widget__verification-container" for="widget__verification-checkbox">
<input
<span id="widget__verification-text"
>I'm not a robot</span>
<input disabled
id="widget__verification-checkbox"
aria-valuenow="I'm not a robot"
aria-checked="false"
role="checkbox"
class="widget__verification-checkbox"
type="checkbox" />
<span id="widget__verification-text--before">I'm not a robot</span>
<span id="widget__verification-text--during">Processing...</span>
<span id="widget__verification-text--after">Verified!</span>
<span id="widget__verification-text--error">Something went wrong</span>
</label>
<div class="widget__mcaptcha-details">
<a href="<.= crate::PKG_HOMEPAGE .>"
@@ -54,6 +55,8 @@ SPDX-License-Identifier: MIT OR Apache-2.0
</div>
</div>
</form>
<div class="progress__bar"><div class="progress__fill"></div></div>
<div class="progress__bar"><div
aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"
role="progressbar" class="progress__fill"></div></div>
</main>
<.include!("./footer.html"); .>

View File

@@ -3,7 +3,7 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0
import { Work, ServiceWorkerMessage } from "./types";
import {Work, ServiceWorkerMessage} from "./types";
import fetchPoWConfig from "./fetchPoWConfig";
import sendWork from "./sendWork";
import sendToParent from "./sendToParent";
@@ -12,7 +12,17 @@ import * as CONST from "./const";
import "./main.scss";
let LOCK = false;
const worker = new Worker("/bench.js");
const workerPromise = new Promise<Worker>((res) => {
const worker = new Worker("/bench.js");
worker.onmessage = (event: MessageEvent) => {
const message: ServiceWorkerMessage = event.data;
if(message.type === "ready") {
console.log("worker ready");
res(worker);
}
};
});
/** add mcaptcha widget element to DOM */
export const registerVerificationEventHandler = (): void => {
@@ -20,11 +30,21 @@ export const registerVerificationEventHandler = (): void => {
document.querySelector(".widget__verification-container")
);
verificationContainer.style.display = "flex";
CONST.btn().addEventListener("click", (e) => solveCaptchaRunner(e));
workerPromise.then((worker: Worker) => {
const btn = CONST.btn();
btn.disabled = false;
btn.addEventListener("click", (e) => solveCaptchaRunner(worker, e));
});
};
export const solveCaptchaRunner = async (e: Event): Promise<void> => {
export const solveCaptchaRunner = async (worker: Worker, e: Event): Promise<void> => {
const PROGRESS_FILL = <HTMLElement>document.querySelector(".progress__fill");
const setWidth = (width: number) => {
PROGRESS_FILL.style.width = `${width}%`;
PROGRESS_FILL.ariaValueNow = <any>parseInt(<any>width);
};
let width = 0;
if (LOCK) {
@@ -36,8 +56,9 @@ export const solveCaptchaRunner = async (e: Event): Promise<void> => {
LOCK = true;
if (CONST.btn().checked == false) {
width = 0;
PROGRESS_FILL.style.width = `${width}%`;
setWidth(width);
CONST.messageText().before();
CONST.btn().ariaChecked = <any>false;
LOCK = false;
return;
}
@@ -57,7 +78,7 @@ export const solveCaptchaRunner = async (e: Event): Promise<void> => {
if (resp.type === "work") {
width = 80;
PROGRESS_FILL.style.width = `${width}%`;
setWidth(width);
console.log(
`Proof generated. Difficuly: ${config.difficulty_factor} Duration: ${resp.value.work.time}`
);
@@ -72,22 +93,23 @@ export const solveCaptchaRunner = async (e: Event): Promise<void> => {
};
width = 90;
PROGRESS_FILL.style.width = `${width}%`;
setWidth(width);
// 3. submit work
const token = await sendWork(proof);
// 4. send token
sendToParent(token);
// 5. mark checkbox checked
CONST.btn().checked = true;
CONST.btn().ariaChecked = <any>true;
width = 100;
PROGRESS_FILL.style.width = `${width}%`;
setWidth(width);
CONST.messageText().after();
LOCK = false;
}
if (resp.type === "progress") {
if (width < 80) {
width = (resp.nonce / max_recorded_nonce) * 100;
PROGRESS_FILL.style.width = `${width}%`;
width = resp.nonce / max_recorded_nonce * 100;
setWidth(width);
}
console.log(`received nonce ${resp.nonce}`);
}

View File

@@ -28,6 +28,8 @@ body {
display: flex;
height: 100%;
width: 100%;
background-color: #f6f6f6;
border: 2px solid #e5e5e5;
}
.widget__noscript-container {
@@ -56,7 +58,8 @@ body {
.widget__verification-container {
align-items: center;
display: none;
display: flex;
flex-direction: row-reverse;
line-height: 30px;
font-size: 1rem;
}
@@ -67,36 +70,6 @@ body {
margin: 0 10px;
}
#widget__verification-text--during {
display: none;
}
#widget__verification-text--after {
display: none;
color: green;
}
#widget__verification-text--error {
display: none;
color: red;
}
.widget__verification-checkbox:checked ~ #widget__verification-text--before {
display: none;
}
.widget__verification-checkbox:checked ~ #widget__verification-text--during {
display: none;
}
.widget__verification-checkbox:checked ~ #widget__verification-text--error {
display: none;
}
.widget__verification-checkbox:checked ~ #widget__verification-text--after {
display: block;
}
.widget__mcaptcha-details {
display: flex;
flex-direction: column;
@@ -127,6 +100,29 @@ body {
margin: 2px;
}
@media (prefers-color-scheme: dark) {
.widget__container {
background-color: #1c1c1c;
}
.widget__inner-container {
background-color: #1c1c1c;
border: 2px solid #656569;
}
.widget__verification-container {
color: rgb(232, 230, 227);
}
.widget__mcaptcha-brand-name {
color: #7d94f9;
}
.widget__mcaptcha-info-link {
color: #7d94f9;
}
}
/* progress bar courtesy of https://codepen.io/Bizzy-Coding/pen/poOymVJ?editors=1111 */
.progress__bar {
position: relative;
@@ -142,3 +138,9 @@ body {
height: 100%;
width: 0%;
}
@media (prefers-color-scheme: dark) {
.progress__bar {
background: unset;
}
}

View File

@@ -30,7 +30,7 @@ const prove = async (
config.string,
config.difficulty_factor,
STEPS,
progress
(nonce: bigint | number) => progress(Number(nonce))
);
const t1 = performance.now();
time = t1 - t0;

View File

@@ -9,6 +9,12 @@ import prove from "./prove";
import { PoWConfig, ServiceWorkerMessage, ServiceWorkerWork } from "./types";
log.log("worker registered");
const ready: ServiceWorkerMessage = {
type: "ready",
};
postMessage(ready);
onmessage = async (e) => {
console.debug("message received at worker");
const config: PoWConfig = e.data;

View File

@@ -5,7 +5,7 @@
import * as CONST from "../const";
import {getBaseHtml, sitekey, checkbox} from "./setupTests";
import { getBaseHtml, sitekey, checkbox } from "./setupTests";
import * as TESTElements from "./setupTests";
it("const works", () => {
@@ -17,29 +17,17 @@ it("const works", () => {
// display after
CONST.messageText().after();
expect(TESTElements.afterMsg.style.display).toBe("block");
expect(TESTElements.beforeMsg.style.display).toBe("none");
expect(TESTElements.duringMsg.style.display).toBe("none");
expect(TESTElements.errorMsg.style.display).toBe("none");
expect(TESTElements.Msg.innerText).toBe(CONST.AFTER);
// display before
CONST.messageText().before();
expect(TESTElements.afterMsg.style.display).toBe("none");
expect(TESTElements.beforeMsg.style.display).toBe("block");
expect(TESTElements.duringMsg.style.display).toBe("none");
expect(TESTElements.errorMsg.style.display).toBe("none");
expect(TESTElements.Msg.innerText).toBe(CONST.BEFORE);
// display during
CONST.messageText().during();
expect(TESTElements.afterMsg.style.display).toBe("none");
expect(TESTElements.beforeMsg.style.display).toBe("none");
expect(TESTElements.duringMsg.style.display).toBe("block");
expect(TESTElements.errorMsg.style.display).toBe("none");
expect(TESTElements.Msg.innerText).toBe(CONST.DURING);
// display error
CONST.messageText().error();
expect(TESTElements.afterMsg.style.display).toBe("none");
expect(TESTElements.beforeMsg.style.display).toBe("none");
expect(TESTElements.duringMsg.style.display).toBe("none");
expect(TESTElements.errorMsg.style.display).toBe("block");
expect(TESTElements.Msg.innerText).toBe(CONST.ERROR);
});

View File

@@ -11,25 +11,19 @@ export const checkbox = <HTMLInputElement>document.createElement("input");
checkbox.type = "checkbox";
checkbox.id = CONST.btnId;
const getMessages = (state: string) => {
const getMessages = () => {
const msg = <HTMLElement>document.createElement("span");
msg.id = `widget__verification-text--${state}`;
msg.id = "widget__verification-text";
msg.innerText = "I'm not a robot";
return msg;
};
export const beforeMsg = getMessages("before");
export const afterMsg = getMessages("after");
export const duringMsg = getMessages("during");
export const errorMsg = getMessages("error");
export const Msg = getMessages();
/** get base HTML with empty mCaptcha container */
export const getBaseHtml = (): HTMLFormElement => {
const form = <HTMLFormElement>document.createElement("form");
form.appendChild(checkbox);
form.appendChild(beforeMsg);
form.appendChild(duringMsg);
form.appendChild(afterMsg);
form.appendChild(errorMsg);
form.appendChild(Msg);
return form;
};

View File

@@ -40,5 +40,6 @@ export type Token = {
};
export type ServiceWorkerMessage =
| { type: "ready" }
| { type: "work"; value: ServiceWorkerWork }
| { type: "progress"; nonce: number };

View File

@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "block-buffer"
@@ -177,6 +177,12 @@ dependencies = [
"walkdir",
]
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "mime"
version = "0.3.17"
@@ -195,18 +201,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.69"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
@@ -228,33 +234,45 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.189"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.189"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
"syn 2.0.93",
]
[[package]]
name = "serde_json"
version = "1.0.107"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
]
[[package]]
@@ -287,9 +305,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.38"
version = "2.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058"
dependencies = [
"proc-macro2",
"quote",

6055
yarn.lock

File diff suppressed because it is too large Load Diff