Compare commits

..

950 Commits

Author SHA1 Message Date
Stepan Vladovskiy
5758fe1e2c feat: Dockerfile with big ASSSsssss
All checks were successful
deploy / deploy (push) Successful in 36s
2025-05-14 15:35:07 -03:00
Stepan Vladovskiy
9c27d7fcab restart branch
All checks were successful
deploy / deploy (push) Successful in 36s
2025-05-14 15:28:50 -03:00
Stepan Vladovskiy
282c700431 restart branch 2025-05-14 15:27:33 -03:00
acc471e500 log-error-more
All checks were successful
deploy / deploy (push) Successful in 7s
2024-12-11 22:40:26 +03:00
586b2cc3af deploy
All checks were successful
deploy / deploy (push) Successful in 1m44s
2024-06-06 09:46:38 +03:00
6a4b3553af premerge 2024-06-06 09:44:19 +03:00
48e1dbf8aa get-id-debug
All checks were successful
deploy / deploy (push) Successful in 1m29s
2024-06-06 07:51:45 +03:00
55121656a8 get-id
All checks were successful
deploy / deploy (push) Successful in 1m28s
2024-06-05 23:47:41 +03:00
e9f54a74f0 get-id
All checks were successful
deploy / deploy (push) Successful in 1m26s
2024-06-05 23:40:09 +03:00
bfa192d21e log-author-id
All checks were successful
deploy / deploy (push) Successful in 1m27s
2024-06-05 23:20:37 +03:00
800fc4f8d8 fixed-redis-intfix4
All checks were successful
deploy / deploy (push) Successful in 1m27s
2024-06-05 22:17:18 +03:00
86453d483e fixed-redis-intfix2-вуигп
All checks were successful
deploy / deploy (push) Successful in 1m27s
2024-06-05 22:15:03 +03:00
7d5d31692f fixed-redis-intfix2
All checks were successful
deploy / deploy (push) Successful in 1m27s
2024-06-05 22:10:43 +03:00
5745a9b3c4 fixed-redis-intfix
Some checks failed
deploy / deploy (push) Failing after 1m4s
2024-06-05 22:07:06 +03:00
641c5bc248 fixed-redis-debug
All checks were successful
deploy / deploy (push) Successful in 1m31s
2024-06-05 22:01:24 +03:00
085faf73e7 fixed-redis-cache-5
All checks were successful
deploy / deploy (push) Successful in 1m27s
2024-06-05 21:53:59 +03:00
3197154fec fixed-redis-cache-3
All checks were successful
deploy / deploy (push) Successful in 1m27s
2024-06-05 21:32:10 +03:00
b8f5e9ebb9 fixed-redis-cache-2
Some checks failed
deploy / deploy (push) Failing after 1m3s
2024-06-05 21:27:51 +03:00
76f976937a fixed-redis-cache
Some checks failed
deploy / deploy (push) Failing after 1m3s
2024-06-05 21:20:36 +03:00
693f252ae9 redis-key 2024-05-30 18:34:25 +03:00
12af7fc617 reimplement-following-data-proxy-4
All checks were successful
deploy / deploy (push) Successful in 1m29s
2024-05-21 03:38:56 +03:00
32eaff5c5d reimplement-following-data-proxy-3
Some checks failed
deploy / deploy (push) Failing after 29s
2024-05-21 03:37:14 +03:00
0b823ffc33 reimplement-following-data-proxy-2
Some checks failed
deploy / deploy (push) Failing after 1m3s
2024-05-21 03:31:06 +03:00
42017035fc reimplement-following-data-proxy
Some checks failed
deploy / deploy (push) Failing after 1m7s
2024-05-21 03:25:15 +03:00
c18209418f postmerge
All checks were successful
deploy / deploy (push) Successful in 1m34s
2024-05-21 03:10:43 +03:00
c8ba8c1453 yml-fix
Some checks failed
deploy / deploy (push) Failing after 5s
2024-05-21 03:03:59 +03:00
04a8e292c0 Merge branch 'main' into discours
Some checks failed
deploy / deploy (push) Failing after 6s
2024-05-21 03:00:07 +03:00
Lakhan Samani
9336d3e003 Merge pull request #455 from testwill/ioutil
chore: remove refs to deprecated io/ioutil
2024-05-20 18:23:40 +05:30
guoguangwu
c6f19a82a4 chore: remove refs to deprecated io/ioutil
Signed-off-by: guoguangwu <guoguangwug@gmail.com>
2024-05-18 15:02:45 +08:00
Lakhan Samani
28390ebbe4 fix: social login state 2024-05-08 09:56:06 +05:30
Lakhan Samani
5add964449 Merge pull request #453 from authorizerdev/feat-add-roblox-login
Add roblox login feature
2024-05-01 18:17:17 +05:30
Lakhan Samani
4eb8ca0d65 update roblox ui 2024-05-01 17:40:11 +05:30
Lakhan Samani
d924e36786 feat: fix roblox image 2024-05-01 17:04:33 +05:30
Lakhan Samani
d0e6392f1a fix roblox oauth 2024-05-01 16:36:21 +05:30
Lakhan Samani
b931652769 Add roblox login feature 2024-04-30 13:13:27 +05:30
fb65fcbad5 deploy
Some checks failed
deploy / deploy (push) Failing after 6s
2024-04-15 14:12:12 +03:00
79d2d52fc0 deploy
Some checks failed
deploy / deploy (push) Failing after 5s
2024-04-15 09:33:44 +03:00
2a766d587f branch 2024-04-15 09:32:16 +03:00
5c33b159dc Merge remote-tracking branch 'hub/main' into discours 2024-04-15 09:31:44 +03:00
Stepan Vladovskiy
ca0118df5e debug: no force in push to staging
All checks were successful
deploy / deploy (push) Successful in 29s
2024-04-11 17:16:39 -03:00
Stepan Vladovskiy
8eb9650142 feat: merge with mailgun
All checks were successful
deploy / deploy (push) Successful in 1m32s
2024-04-11 17:14:25 -03:00
Stepan Vladovskiy
cfaf6c49b4 feat: CI mailgun->v2 and dev->staging
All checks were successful
deploy / deploy (push) Successful in 50s
2024-04-11 17:10:55 -03:00
Stepan Vladovskiy
4990a77786 feat: CI mailgun->v2 and dev->staging
Some checks failed
deploy / deploy (push) Failing after 12s
2024-04-11 17:09:38 -03:00
Lakhan Samani
82f639757f Merge pull request #450 from authorizerdev/fix-user-verification
fix: user verification
2024-04-02 15:39:48 +05:30
Lakhan Samani
28b574c827 Update tests 2024-04-02 15:25:11 +05:30
Lakhan Samani
67f866a787 fix: user verification 2024-03-28 13:57:47 +05:30
Stepan Vladovskiy
2420297c20 feat: branch dev with CI to deploy dev in staging.discour.io
Some checks failed
deploy / deploy (push) Failing after 1m39s
2024-03-18 00:12:17 -03:00
5616186be7 cache-fix 2024-03-12 15:51:49 +03:00
4351f5cd32 type-fix-8 2024-03-02 14:51:37 +03:00
29026ed6fb type-fix-7 2024-03-02 14:44:34 +03:00
f244330401 type-fix-6 2024-03-02 14:40:40 +03:00
132f965c8b type-fix-5 2024-03-02 14:35:40 +03:00
8deba4849d type-fix-4 2024-03-02 14:22:27 +03:00
3a3407f85e type-fix-3 2024-03-02 14:14:54 +03:00
7a1df30325 type-fix 2024-03-02 14:06:20 +03:00
d9fd6c2b36 type-fix 2024-03-02 13:56:57 +03:00
11472d5b06 logs-fix-14 2024-03-02 13:48:10 +03:00
3502d8b3d1 logs-fix-14 2024-03-02 13:46:58 +03:00
7c949417c1 logs-fix-13 2024-03-02 13:41:23 +03:00
09e4daf66e logs-fix-12 2024-03-02 13:34:00 +03:00
32f903f40a logs-fix-11 2024-03-02 13:28:20 +03:00
348a9426cc logs-fix-10 2024-03-02 13:22:19 +03:00
9ddbfb5df7 logs-fix-9 2024-03-02 13:18:22 +03:00
9fa248ca41 logs-fix-9 2024-03-02 13:17:57 +03:00
0b0ab12bf0 logs-fix-8 2024-03-02 13:13:20 +03:00
be58bb8c6d logs-fix-7 2024-03-02 13:08:48 +03:00
cdad58bd02 logs-fix-6 2024-03-02 12:58:39 +03:00
934f433dd2 logs-fix-5 2024-03-02 12:55:46 +03:00
d6fec9121c logs-fix-4 2024-03-02 12:51:26 +03:00
f9e1316145 logs-fix-3 2024-03-02 12:47:06 +03:00
362d410e37 logs-fix-2 2024-03-02 12:35:12 +03:00
78930dc8f3 logs-fix 2024-03-02 11:56:24 +03:00
baeb5a68ea appdata-debug-4 2024-03-02 10:19:56 +03:00
040f7ead7e appdata-debug-3 2024-03-02 10:12:05 +03:00
3246d32a23 appdata-debug-2 2024-03-02 10:08:38 +03:00
ce5b09953e appdata-debug 2024-03-02 09:56:48 +03:00
31079f2628 redis-provider-fix
Some checks failed
deploy / deploy (push) Failing after 5s
2024-02-22 11:51:20 +03:00
8fca4cf4c0 appdata-patch
Some checks failed
deploy / deploy (push) Failing after 5s
2024-02-22 10:40:39 +03:00
94917e7735 login-patched
Some checks failed
deploy / deploy (push) Failing after 5s
2024-02-22 09:13:56 +03:00
0f9b95cafe core-api-redis
Some checks failed
deploy / deploy (push) Failing after 5s
2024-02-22 00:33:24 +03:00
871c65b9dd app-data-patch
Some checks failed
deploy / deploy (push) Failing after 5s
2024-02-22 00:29:46 +03:00
2487077adc Merge branch 'feature/discoursio-auth' into mailgun
All checks were successful
deploy / deploy (push) Successful in 5s
2024-02-17 14:23:28 +03:00
7c03b383f1 Merge branch 'mailgun' of https://dev.discours.io/discours.io/authorizer into mailgun 2024-02-17 14:23:23 +03:00
e0db7c1051 ci 2024-02-17 14:21:45 +03:00
19f5e7e174 forgot-resolver-patch
Some checks failed
deploy / deploy (push) Failing after 5s
2024-02-08 19:18:31 +03:00
02deafa879 magic-lnk-login-only
All checks were successful
deploy / deploy (push) Successful in 1m35s
2024-02-08 19:04:24 +03:00
42efbc232b check-not-basic
All checks were successful
deploy / deploy (push) Successful in 1m35s
2024-02-08 18:54:44 +03:00
7bc9fca4c4 magic
All checks were successful
deploy / deploy (push) Successful in 1m37s
2024-02-08 18:05:08 +03:00
b20f5986c1 is-registered-debug
All checks were successful
deploy / deploy (push) Successful in 1m33s
2024-02-06 12:25:40 +03:00
a66393e754 resolver-fix
All checks were successful
deploy / deploy (push) Successful in 1m32s
2024-02-06 12:20:01 +03:00
e963f325f8 is-registered-hotfix-4
All checks were successful
deploy / deploy (push) Successful in 1m31s
2024-02-06 12:12:34 +03:00
570436e6e8 schema-regen
All checks were successful
deploy / deploy (push) Successful in 1m25s
2024-02-06 12:03:42 +03:00
c4472340ef is-registered-hotfix-3
All checks were successful
deploy / deploy (push) Successful in 32s
2024-02-06 11:38:45 +03:00
7a6ae24aaa is-registered-hotfix-2
All checks were successful
deploy / deploy (push) Successful in 1m29s
2024-02-06 11:35:48 +03:00
db25ceb0b5 is-registered-hotfix
All checks were successful
deploy / deploy (push) Successful in 1m29s
2024-02-06 11:34:00 +03:00
40b8e020a3 fixfix
All checks were successful
deploy / deploy (push) Successful in 1m30s
2024-02-02 16:06:57 +03:00
4b249d643f is-registered-fix-2
Some checks failed
deploy / deploy (push) Failing after 1m2s
2024-02-01 16:37:49 +03:00
41f99d1199 Merge branch 'mailgun' of https://dev.discours.io/discours.io/authorizer into mailgun
All checks were successful
deploy / deploy (push) Successful in 1m30s
2024-02-01 16:22:28 +03:00
83bb18ae79 is-registered-fix 2024-02-01 16:21:53 +03:00
to
416640ac13 Update server/graph/schema.graphqls
All checks were successful
deploy / deploy (push) Successful in 1m28s
2024-02-01 10:35:11 +00:00
Stepan Vladovskii
853f8c44bb feat: add CI pipline to mailgun brunch with check is this branch is mailgun
All checks were successful
deploy / deploy (push) Successful in 1m33s
2024-01-28 19:33:27 -03:00
bdcf2c39f7 logs-fix-4 2024-01-22 15:02:22 +03:00
c53ada95eb logs-fix-3 2024-01-22 14:49:58 +03:00
07a26991e9 logs-fix-2 2024-01-22 14:32:27 +03:00
c8413665ae logs-fix 2024-01-22 14:29:32 +03:00
69a87896e9 Merge branch 'main' of https://github.com/authorizerdev/authorizer into mailgun 2024-01-22 13:48:09 +03:00
Lakhan Samani
27b51ad369 Merge pull request #436 from team-scaletech/feat/totp_for_signup
Feat/totp for signup
2024-01-19 21:26:35 +05:30
Lakhan Samani
747c82f1b9 Merge pull request #439 from team-scaletech/fix/role-deletion
role deletion
2024-01-19 21:25:36 +05:30
Lakhan Samani
b1bfaf6688 Merge pull request #443 from cosark/patch-1
Added one-click deployment option to readme table
2024-01-19 21:23:41 +05:30
034d80303f Merge remote-tracking branch 'hub/main' into mailgun 2024-01-18 18:50:16 +03:00
cosark
a328121aa3 Added one-click deployment option to readme table
Adding RepoCloud.io as a 1-click deployment options for Authorizer instances. Provides diversity and a user-friendly deployment option.
2024-01-12 02:15:04 -07:00
lemonScaletech
40a0a2fbcc feat:
* added chunks of 1000 for deletion of role
2024-01-10 12:56:30 +05:30
lemonScaletech
e52164665f Merge branch 'main' into fix/role-deletion 2024-01-10 12:30:47 +05:30
Anand Kumar Panigrahi
a63d00b0c8 Merge branch 'authorizerdev:main' into main 2024-01-10 12:23:03 +05:30
Lakhan Samani
0bce901749 remove debug logs 2024-01-08 14:28:23 +05:30
Lakhan Samani
e5fbaa26e1 fix: pkce flow for oauth login 2024-01-08 14:21:24 +05:30
bb62b4adfd templates-fix 2024-01-05 20:35:58 +03:00
d47b39bb1d link-fix 2024-01-05 19:43:45 +03:00
0aa1c2532b debug-link-2 2024-01-05 19:29:50 +03:00
0ab26c19b6 debug-link 2024-01-05 19:27:59 +03:00
b8aade7dc0 sdk-fix 2024-01-05 19:17:55 +03:00
6e0ab799b8 bytes-fix 2024-01-05 18:56:13 +03:00
2ccc2ae4eb mailgun-debug 2024-01-05 18:51:39 +03:00
95807ae319 monkey-fix 2024-01-05 18:45:13 +03:00
e62f356a79 debug-mailgun 2024-01-05 18:26:58 +03:00
0331d34afc mailgun-response-debug 2024-01-05 18:04:19 +03:00
5af71dfc94 template-names-fix 2024-01-05 16:44:37 +03:00
dd4b41674a is-registered-1 2024-01-05 14:58:16 +03:00
58a91814ae minor-fix 2024-01-05 14:17:56 +03:00
2fba6af769 t 2024-01-05 14:09:02 +03:00
ee454aeabe first-only 2024-01-05 14:00:39 +03:00
896e890421 less-bloat-2 2024-01-05 13:19:46 +03:00
c5aaad0662 less-bloat 2024-01-05 13:15:22 +03:00
68c761a181 fixed-imports 2024-01-05 13:12:13 +03:00
f30881de3d mailgun-rest 2024-01-05 13:06:43 +03:00
534b5624af sha256-fix 2024-01-05 01:41:52 +03:00
be88e231e0 imports-fix 2024-01-04 23:53:20 +03:00
1f3cb1aab9 universal-hashing-sha256 2024-01-04 22:15:22 +03:00
scaletech-milan
cb01dea902 Refactor:
- Remove redundant mail otp check
2024-01-02 12:18:51 +05:30
lemonScaletech
7bcd5a70c3 feat:
* PR suggested changes
2024-01-02 11:50:26 +05:30
Lakhan Samani
3bd3a52d3b Merge branch 'main' of https://github.com/authorizerdev/authorizer 2023-12-30 21:19:54 +05:30
Lakhan Samani
ade676f92c fix: remove access_token & id_token from query string 2023-12-30 21:19:44 +05:30
Lakhan Samani
ca71aba96d Merge pull request #440 from foestauf/feat/discord-provider
feat: Add Discord as Identity Provider
2023-12-30 11:16:14 +05:30
Lakhan Samani
ef2a590608 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2023-12-26 21:10:58 +05:30
Lakhan Samani
59ed4e273f Update react app 2023-12-26 21:10:10 +05:30
Robert McKee
751933d40e feat: Add Discord as Identity Provider 2023-12-23 17:05:02 -05:00
Lakhan Samani
5fac440205 Merge pull request #432 from authorizerdev/fix/forgot-password
feat: add forgot password for mobile login
2023-12-22 01:32:49 +05:30
Lakhan Samani
5ba30ccd12 remove todo 2023-12-22 01:32:12 +05:30
Lakhan Samani
02f7a62918 feat: add testing & ui for forgot password with mobile 2023-12-22 01:26:14 +05:30
scaletech-milan
1172c95a23 Merge branch 'authorizerdev:main' into main 2023-12-15 10:28:20 +05:30
Lakhan Samani
c9c2789097 Merge branch 'main' of https://github.com/authorizerdev/authorizer into fix/forgot-password 2023-12-14 22:45:36 +05:30
Lakhan Samani
ed3100c179 fix: allow logout using access token 2023-12-14 22:12:03 +05:30
Anand Kumar Panigrahi
b8c6ee0a6d Merge branch 'authorizerdev:main' into main 2023-12-11 15:12:23 +05:30
Lakhan Samani
0a5357c948 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2023-12-09 02:03:36 +05:30
Lakhan Samani
a002e2faf7 fix: remove forgot password for magic link login 2023-12-09 02:03:27 +05:30
lemonScaletech
d8b9ffe9ce Merge remote-tracking branch 'origin/fix/role-deletion' into fix/role-deletion 2023-12-08 18:22:57 +05:30
lemonScaletech
47f26103b0 test:
* added integration test for role deletion functionality
2023-12-08 18:22:24 +05:30
Anand Kumar Panigrahi
48ada9ab26 Merge branch 'authorizerdev:main' into fix/role-deletion 2023-12-08 17:31:01 +05:30
lemonScaletech
b8c2ab4cf8 refactoring:
* removed extra for loop
* commenting on functions
2023-12-08 10:38:09 +05:30
lemonScaletech
5cb94a7820 fix:
* added logic if role is deleted then also be deleted from user side if role is assigned to that user.
* default role should be subset of roles
2023-12-07 19:33:59 +05:30
scaletech-milan
7e9fac335b Feat:
- Add TOTP MFA for signup
- Test cases for totp signup and verify_email
2023-12-06 15:53:01 +05:30
scaletech-milan
febf4f9b15 Merge branch 'authorizerdev:main' into main 2023-12-06 15:35:25 +05:30
Lakhan Samani
b0f54b181d Merge pull request #435 from siimsams/bugfix-appdata-validation 2023-12-06 07:42:09 +05:30
Siim Sams
df5978fb5a fix unable to update app_data for user 2023-12-05 20:41:39 +02:00
Lakhan Samani
2a0e0da436 Merge branch 'main' of https://github.com/authorizerdev/authorizer into fix/forgot-password 2023-12-04 23:35:13 +05:30
Lakhan Samani
e4a8eb3542 fix: signup field 2023-12-04 13:44:39 +05:30
Lakhan Samani
06214f0b1d feat: add switch to enable totp 2023-12-04 13:37:58 +05:30
scaletech-milan
a203b853f2 Merge branch 'authorizerdev:main' into main 2023-12-04 11:48:25 +05:30
Lakhan Samani
109b38e588 Merge pull request #433 from authorizerdev/fix/phone_number_verification
fix: phone_number_verified_at set during signup
2023-12-04 11:30:24 +05:30
Lakhan Samani
e8b99f73c3 fix: phone number verification 2023-12-04 11:28:27 +05:30
Lakhan Samani
3fa892431e fix: phone_number_verified_at set during signup 2023-12-04 09:26:59 +05:30
Lakhan Samani
4b341c0a5d Merge branch 'main' of https://github.com/authorizerdev/authorizer into fix/forgot-password 2023-12-03 23:05:39 +05:30
Lakhan Samani
c95db8b07b feat: add forgot password for mobile login 2023-12-03 22:49:40 +05:30
Lakhan Samani
32fcba0f8d Fix/forgot password (#430)
* fix: forgot password shown with magic link login

* fix: forgot password shown with magic link login

* fix is basic auth enabled
2023-12-03 22:27:56 +05:30
Lakhan Samani
5b75521490 fix is basic auth enabled 2023-12-03 22:27:37 +05:30
Lakhan Samani
f70310f04f fix: forgot password shown with magic link login 2023-12-03 11:27:39 +05:30
Lakhan Samani
bea6eb8342 fix: forgot password shown with magic link login 2023-12-03 11:17:34 +05:30
Lakhan Samani
cac67b7915 feat: add totp UI & recovery code (#429) 2023-12-03 09:03:22 +05:30
Lakhan Samani
d7da81d308 fix comment for twitch login 2023-12-02 12:22:27 +05:30
scaletech-milan
e49e315967 Feat: Add oauth2 for twitch (#426)
* fix:
* removed fmt.Println

* Feat:
- Add OAuth for twitch

---------

Co-authored-by: lemonScaletech <anand.panigrahi@scaletech.xyz>
Co-authored-by: Anand Kumar Panigrahi <70533637+lemonScaletech@users.noreply.github.com>
2023-12-02 12:21:53 +05:30
scaletech-milan
fbb4975c02 Merge branch 'authorizerdev:main' into main 2023-12-01 14:11:20 +05:30
Lakhan Samani
7f6ddca3fc fix: totp login 2023-12-01 14:00:01 +05:30
Anand Kumar Panigrahi
e71da3def6 Merge branch 'authorizerdev:main' into main 2023-11-28 11:34:24 +05:30
Lakhan Samani
46d6f86ab0 Remove logs 2023-11-25 11:44:52 +05:30
Lakhan Samani
1890db8f03 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2023-11-25 11:34:15 +05:30
Lakhan Samani
0e96e0b6f0 fix: nil check 2023-11-25 11:34:01 +05:30
Aris Ripandi
bbddf484ed feat: add support for libsql / Turso database (#421) 2023-11-25 09:35:39 +05:30
lemonScaletech
7d4c641297 fix:
* removed fmt.Println
2023-11-24 13:52:17 +05:30
Lakhan Samani
bd343f0b27 fix: disable totp by default 2023-11-23 20:54:03 +05:30
Lakhan Samani
ad8bd64987 fix: remove width from logo for vertical logos 2023-11-23 15:42:17 +05:30
scaletech-milan
de5c18b60f Feat: add screen_hint param in /authorize api for explicit signup redirection (#420)
* Feat:
- Introduce screen_hint param in /authorize for explicit signup redirection

* Feat:
- Declare variable for base path and signup path
- Add social login on signup page

* Refactor:
- Update variable name for screen hint param
2023-11-21 13:08:32 +05:30
Lakhan Samani
fe4c693324 feat: add totp login API (#416)
* fix:
* removed hasReversedValue in playground

* feat:
* added totp methods in db's providers
* adding totp in login method

* feat:
* added toggle in dashboard
* fixing issue with env set

* feat:
* integrated totp

* feat:
* encrypted userid
* added totp_verified column in user table
* started test for totp

* feat:
* test cases totp

* test-cases:
* completed test cases
* tested for all dbs

* fixes:
* return variable to snake case
* import refactoring

* feat:
* created seperate folder for authenticator with totp subfolder
* refactored code
* created new table for authenticators
* added recovery code for totp

* feat:
* adding functions to different db providers

* feat:
* added authenticators method for all db

* feat:
* added logic for updating mfa in user_profile update

* fix:
* merge conflict

* fix:
* resolved mongodb, dynamodb and arangodb test case bug
* added new condition for checking first time totp user or not

* feat:
* changes in all respective db with authenticator

* fix:
* PR suggested changes

* fix(cassandra): list users

* Update verify otp

* fix totp login api

---------

Co-authored-by: lemonScaletech <anand.panigrahi@scaletech.xyz>
2023-11-16 18:30:54 +05:30
Lakhan Samani
d8cd965004 Merge pull request #415 from testwill/typo
fix: typo
2023-11-16 10:44:12 +05:30
guangwu
fdd41721a8 fix: typo 2023-11-16 11:25:57 +08:00
Lakhan Samani
281714b86d Merge pull request #414 from authorizerdev/fix/apple-login
Fix/apple login
2023-11-09 13:46:38 +05:30
Lakhan Samani
a05f5ce063 fix apple login nil pointer exception 2023-11-09 13:45:29 +05:30
Lakhan Samani
b7627a36a6 Add debug point 2023-11-09 10:49:17 +05:30
Lakhan Samani
85bbd1223e Fix validating session 2023-11-01 18:12:56 +05:30
Lakhan Samani
885a147463 Merge pull request #409 from authorizerdev/fix/use-login-signup-for-mobile
feat: unify email & mobile singup + login
2023-10-26 10:08:30 +05:30
Lakhan Samani
9a6f1a659a Allow empty email 2023-10-26 00:55:10 +05:30
Lakhan Samani
4bddbde280 Update comments 2023-10-22 02:36:10 +05:30
Lakhan Samani
3ed31b0557 feat: unify email & mobile singup + login 2023-10-22 02:33:36 +05:30
Lakhan Samani
4d1fcc3004 Merge pull request #408 from Olatunji-Longe/olatunji-longe/OIDC-config-fix
changed subject_types_supported to array of strings according to OIDC…
2023-10-22 01:01:07 +05:30
Olatunji Longe
99441964b5 Clean up ignored files 2023-10-20 20:19:46 -04:00
Olatunji Longe
be3d8cf69b changed subject_types_supported to array of strings according to OIDC specs 2023-10-20 20:10:10 -04:00
Lakhan Samani
734e54db69 Merge pull request #403 from authorizerdev/fix/upgrade-packages
fix: upgrade packages
2023-10-14 18:08:40 +05:30
Lakhan Samani
a3bda429d0 fix: upgrade packages
- fix app_data for cassandra & scylladb
2023-10-14 18:06:29 +05:30
Lakhan Samani
3fba4e4c28 fix: upgrade packages 2023-10-13 10:49:26 +05:30
Lakhan Samani
2a759de311 fix: upgrade packages 2023-10-13 10:48:51 +05:30
Lakhan Samani
e7c4ee5630 Merge pull request #402 from authorizerdev/fix/profile-access
fix: use session / access_token for profile related queries or mutation
2023-10-13 09:09:46 +05:30
Lakhan Samani
6a4568dcf3 fix: use session / access_token for profile related queries or mutation 2023-10-13 08:11:55 +05:30
Lakhan Samani
e941e4834a Merge pull request #401 from authorizerdev/fix/deactivate-acc
Fix calling deactivate acc
2023-10-12 11:49:11 +05:30
Lakhan Samani
f906fb74af Fix calling deactivate acc 2023-10-12 11:48:37 +05:30
Lakhan Samani
7ced811e6e Merge pull request #399 from authorizerdev/feat/deativate-account
Add api to deactivate user account
2023-10-11 00:22:15 +05:30
Lakhan Samani
c1e1ee13f2 Add webhook event to fe 2023-10-11 00:21:58 +05:30
Lakhan Samani
60de61a74e fix webhook for deactiavtion 2023-10-11 00:20:15 +05:30
Lakhan Samani
843bc022fe Add api to deactivate user account 2023-10-11 00:16:53 +05:30
Lakhan Samani
ad41bcf792 Merge pull request #397 from VishwasShashidhar/main
Support macOS arm64 release binaries
2023-09-30 23:54:33 +05:30
Vishwas Shashidhar
ea2596b9ae support darwin-arm64 builds 2023-09-30 23:23:10 +05:30
Lakhan Samani
c8ccb89a67 Merge pull request #391 from team-scaletech/fix/webhook_bug_389
Fix/webhook bug 389
2023-09-13 17:46:55 +05:30
lemonScaletech
9519b53d4e Merge branch 'main' into fix/webhook_bug_389 2023-09-11 11:57:19 +05:30
Anand Kumar Panigrahi
e7cfaf4fbe Merge branch 'authorizerdev:main' into main 2023-09-11 11:55:27 +05:30
Lakhan Samani
0428488dab Merge pull request #393 from Juneezee/refactor/redundant-nil-check
refactor(server/utils): remove redundant nil check
2023-09-11 11:42:58 +05:30
Eng Zer Jun
f3b672a4cf refactor(server/utils): remove redundant nil check
From the Go specification:

  "3. If the map is nil, the number of iterations is 0." [1]

Therefore, an additional nil check for before the loop is unnecessary.

[1]: https://go.dev/ref/spec#For_range

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2023-09-09 12:57:20 +08:00
Lakhan Samani
1d6f569f92 fix: default value for disable playground 2023-09-09 07:11:13 +05:30
Lakhan Samani
fbdc24f314 Merge pull request #390 from team-scaletech/feat/disable_playground
feat: disable playground for non admin
2023-09-08 21:57:57 +05:30
Anand Kumar Panigrahi
1275644abe Merge branch 'authorizerdev:main' into main 2023-09-06 11:30:06 +05:30
Lakhan Samani
ac6b08c093 Merge pull request #392 from imchairmanm/feat/add_koyeb_button
Add button to deploy to Koyeb
2023-09-03 21:11:46 +05:30
Justin Ellingwood
fe461b675b Add button to deploy to Koyeb 2023-09-01 13:30:44 +01:00
lemonScaletech
77e219d990 fix:
* #389 resolved
* typo fixed
2023-08-29 18:14:48 +05:30
lemonScaletech
3ea13d7e93 refactor:
* added blank line eof .env.sample
2023-08-29 12:19:16 +05:30
lemonScaletech
940a09d172 Merge remote-tracking branch 'origin/feat/disable_playground' into feat/disable_playground
# Conflicts:
#	app/yarn.lock
#	server/constants/env.go
#	server/env/env.go
#	server/env/persist_env.go
#	server/memorystore/providers/redis/store.go
2023-08-29 12:00:24 +05:30
lemonScaletech
3ac6875f87 feat:
* resolved conflicts
2023-08-29 11:55:27 +05:30
lemonScaletech
937506ff64 Merge branch 'main' into feat/disable_playground
# Conflicts:
#	app/yarn.lock
#	dashboard/yarn.lock
#	server/constants/env.go
#	server/env/env.go
#	server/env/persist_env.go
#	server/graph/model/models_gen.go
#	server/memorystore/providers/redis/store.go
2023-08-29 11:44:07 +05:30
lemonScaletech
9795ba9097 feat:
* added disable playground functionality
 * added toggle button for playground in dashboard
2023-08-29 11:36:18 +05:30
vipul patel
11dbe9d97a Merge pull request #1 from authorizerdev/main
Authorizer to foke authorizer
2023-08-29 09:15:53 +05:30
lemonScaletech
c9b8bbc3e1 feat:
* added disable playground functionality
 * added toggle button for playground in dashboard
2023-08-28 19:51:42 +05:30
Lakhan Samani
a124edfaee Add user to validate_session
Resolves #379
2023-08-19 20:45:20 +05:30
Lakhan Samani
5e6b033024 fix microsoft active directory config 2023-08-17 14:20:31 +05:30
Lakhan Samani
171d4e3fff remove unused code 2023-08-14 14:16:54 +05:30
Lakhan Samani
cf96a0087f Fix tests for verifying otp using mfa session 2023-08-14 14:15:52 +05:30
Lakhan Samani
09cfad9c27 Merge pull request #382 from authorizerdev/feat-add-field-for-app-data
Add app_data
2023-08-14 12:05:58 +05:30
Lakhan Samani
35e563ab3b Add app_data 2023-08-14 12:01:37 +05:30
Lakhan Samani
e625ed9633 allow common tenant for microsoft 2023-08-03 14:43:27 +05:30
Lakhan Samani
a042c202a0 fix microsoft active directory config 2023-08-03 13:29:07 +05:30
Lakhan Samani
7a76b783b1 Merge pull request #372 from catusax/main
feat: add mfa session to secure otp login
2023-08-03 12:34:39 +05:30
Lakhan Samani
e5400bc7bd fix microsoft active directory config 2023-08-03 12:33:20 +05:30
Lakhan Samani
a8503666e3 fix: add events for signup 2023-08-02 10:02:41 +05:30
Lakhan Samani
b028be3cbc Merge pull request #377 from authorizerdev/fix-webhook-test-endpoint
fix: test webhook endpoint mutation
2023-08-02 00:04:55 +05:30
Lakhan Samani
9a8d20b698 fix: test webhook endpoint mutation
Resolves #376
2023-08-02 00:04:07 +05:30
Lakhan Samani
fab3c2f87e Merge pull request #375 from authorizerdev/fix-db-refs
Fix db refs
2023-08-01 23:38:00 +05:30
Lakhan Samani
3fefcfcd9a fix: refs for dynamo db 2023-08-01 17:37:34 +05:30
Lakhan Samani
c2defdbaac fix: refs for cassandra db 2023-08-01 16:09:17 +05:30
Lakhan Samani
15a4be5431 fix: refs for db provider and few utils 2023-07-31 16:42:11 +05:30
Lakhan Samani
146561bacb fix: allow multi tenant for microsooft 2023-07-25 11:45:08 +05:30
catusax
0c334856bc Merge branch 'main' into main 2023-07-24 14:04:26 +08:00
catusax
ba0cf189de userid ass mfa session key 2023-07-24 12:00:30 +08:00
Lakhan Samani
9f52c08883 [app] bump authorizer-react 1.1.13 2023-07-24 11:56:56 +08:00
Lakhan Samani
80f3698f06 [app] bump authorizer-react 1.1.12 2023-07-24 11:56:56 +08:00
Lakhan Samani
2a2b7abc08 Add optional show_mobile_otp_screen 2023-07-24 11:56:56 +08:00
Lakhan Samani
27e3ed82e4 Update resend otp 2023-07-24 11:56:55 +08:00
Lakhan Samani
6077702626 fix: tests for otp refactor 2023-07-24 11:56:55 +08:00
Lakhan Samani
cf54fcef03 Fix tests 2023-07-24 11:56:55 +08:00
Lakhan Samani
2f849b8f0c Refactor code for otp 2023-07-24 11:56:55 +08:00
Lakhan Samani
85ca0f09bf [draft] Move sms verificaiton to otp models 2023-07-24 11:55:26 +08:00
Lakhan Samani
2fc438d810 [app] bump authorizer-react 1.1.13 2023-07-23 16:13:40 +05:30
Lakhan Samani
1796cace15 [app] bump authorizer-react 1.1.12 2023-07-23 11:30:43 +05:30
Lakhan Samani
43fdc826c4 Add optional show_mobile_otp_screen 2023-07-23 11:23:24 +05:30
catusax
e7652db89c add comments 2023-07-23 13:02:14 +08:00
Lakhan Samani
c80b0d7028 Merge pull request #368 from authorizerdev/fix-sms-verification-for-alldb
Move sms verificaiton to otp models
2023-07-23 10:04:18 +05:30
Lakhan Samani
55fc4b2608 Update resend otp 2023-07-23 10:03:37 +05:30
Lakhan Samani
fac333e195 fix: tests for otp refactor 2023-07-23 07:29:29 +05:30
catusax
5018462559 feat: add mfa session to secure otp login 2023-07-20 15:11:39 +08:00
Lakhan Samani
edb5412c17 Fix tests 2023-07-18 22:50:23 +05:30
Lakhan Samani
87a962504f Increase timeout for redis 2023-07-16 22:57:56 +05:30
Lakhan Samani
d04f79557a Refactor code for otp 2023-07-13 11:39:22 +05:30
Lakhan Samani
c20e9b810a Merge branch 'main' of https://github.com/authorizerdev/authorizer into fix-sms-verification-for-alldb 2023-07-12 22:16:07 +05:30
Lakhan Samani
8d145bd5fe Merge pull request #369 from authorizerdev/feat-add-validate-cookie-api
feat: add resolver to validate browser session
2023-07-12 22:13:47 +05:30
Lakhan Samani
6fa0ad1809 feat: add resolver to validate browser session 2023-07-12 22:12:17 +05:30
Lakhan Samani
abe809ca68 [draft] Move sms verificaiton to otp models 2023-07-12 11:24:13 +05:30
Lakhan Samani
07f71e883b Add comments for twillio 2023-07-11 14:49:16 +05:30
Lakhan Samani
6cef9064c3 Update provider template for sms verification 2023-07-11 14:48:37 +05:30
Lakhan Samani
9ae616b6b5 Merge pull request #365 from JokerQyou/patch-1
Fix wrong response_type parsed when missing response_mode
2023-06-30 18:10:31 +05:30
Joker_
356428ea02 Fix wrong response_type parsed when missing response_mode 2023-06-29 23:10:44 +08:00
Lakhan Samani
7f47177741 Merge pull request #359 from MussieT/feat/sms_confirmation
Feat/sms confirmation
2023-06-13 09:38:23 +05:30
Mussie Teshome
9fb00544cd removed unwanted comment 2023-06-11 20:44:09 +03:00
Mussie Teshome
2b022d1058 Fix typo 2023-06-11 16:23:31 +03:00
Mussie Teshome
1c84d9f4a8 Merge branch 'authorizerdev:main' into feat/sms_confirmation 2023-06-11 16:05:29 +03:00
Mussie Teshome
0838b60fae Added VerifyMobileTest to the resolver 2023-06-11 16:03:16 +03:00
Mussie Teshome
325134466d Testing verify_mobile resolver 2023-06-11 16:02:46 +03:00
Mussie Teshome
58d9978dd5 Updated to test verification 2023-06-11 16:01:49 +03:00
Mussie Teshome
801d64e2f5 Twilio configuration 2023-06-11 16:00:30 +03:00
Mussie Teshome
dd3cc9de3a Verify mobile resolver 2023-06-11 16:00:07 +03:00
Mussie Teshome
8dc7366182 Updated mobile signup to send sms when service enabled 2023-06-11 15:59:53 +03:00
Mussie Teshome
7749534087 generated 2023-06-11 15:59:18 +03:00
Mussie Teshome
510f16e7b0 New resolver - Verify Moblie 2023-06-11 15:59:03 +03:00
Mussie Teshome
d5e83ea14f Schema update for SMSVerificationRequest 2023-06-11 15:58:50 +03:00
Mussie Teshome
b4a90de1d4 Updated to support disable sms verification request 2023-06-11 15:58:04 +03:00
Mussie Teshome
c525ad92f2 SQL Related dbs CRUD implementation for SMS 2023-06-11 15:57:14 +03:00
Mussie Teshome
9028682e93 Added SMSVerificationRequests model to automigrate 2023-06-11 15:56:40 +03:00
Mussie Teshome
3d6bfe4480 mongo implementation for the sms crud 2023-06-11 15:56:02 +03:00
Mussie Teshome
043af08bf0 Mongo collection for SMSVerificationRequest model 2023-06-11 15:55:11 +03:00
Mussie Teshome
0af78479fc Different dbs fn skeleton which fn yet not written 2023-06-11 15:54:23 +03:00
Mussie Teshome
096f686495 Added delete sms request to the interface 2023-06-11 15:52:33 +03:00
Mussie Teshome
c574c6a679 configure twilio via environment variables 2023-06-11 15:52:07 +03:00
Mussie Teshome
6428b74e64 twilio - new package 2023-06-11 15:50:09 +03:00
Mussie Teshome
aa3892025d New resolvers for sms requests 2023-06-11 15:49:25 +03:00
Mussie Teshome
b2f3d6eb80 sms verification requests model 2023-06-08 11:53:06 +03:00
Mussie Teshome
348cbf8c38 Add sms verification to collection 2023-06-08 11:52:39 +03:00
Mussie Teshome
8ac33a085c commented out sms twilio sender 2023-06-01 15:29:22 +03:00
Lakhan Samani
6c9b359081 Merge pull request #355 from minilikmila/fix/facebook-login
Modify the Facebook login authentication callback to enable user email access through the response body.
2023-05-29 10:32:27 +05:30
Mila Shumete
0fde46d274 setting on facebook user email method --- change the parameter(key) passed to get the email from map 2023-05-28 17:10:29 +03:00
Lakhan Samani
1a5b446894 Merge pull request #353 from authorizerdev/add-get-user-by-email
[server] add ability to get user by email
2023-05-20 09:50:59 +05:30
Lakhan Samani
930c934fdb [server] add ability to get user by email 2023-05-20 09:49:18 +05:30
Lakhan Samani
4e7074d75b Merge pull request #351 from miqe/feat/add_sender_name
Feat/add sender name
2023-05-16 14:07:18 +05:30
Michael Sahlu
bdfa045a43 add SENDER_NAME env 2023-05-16 00:59:45 +03:00
Michael Sahlu
a258399bde add Sender Name input 2023-05-16 00:59:01 +03:00
Michael Sahlu
55a25436a8 add SENDER_NAME in env variable query 2023-05-16 00:58:26 +03:00
Michael Sahlu
9fa402f5c8 add SENDER_NAME environment and types 2023-05-16 00:57:33 +03:00
Michael Sahlu
1111729ad4 add sender name / from name 2023-05-16 00:51:28 +03:00
Michael Sahlu
e56c2f58e5 add sender name on schema and resolver 2023-05-16 00:46:22 +03:00
Michael Sahlu
8dbd2556eb retrive sender name from env 2023-05-16 00:40:14 +03:00
Michael Sahlu
17bb077f3e add EnvKeySenderName for SENDER_NAME env variable 2023-05-16 00:39:25 +03:00
Lakhan Samani
f291417378 Merge pull request #350 from authorizerdev/fix/redirect-uri-error
[server]fix: error redirection for email verification
2023-05-12 16:43:38 +05:30
Lakhan Samani
f831379d27 revert change for forgot password 2023-05-12 16:39:02 +05:30
Lakhan Samani
a50f6becbd [server]fix: error redirection for email verification 2023-05-02 18:39:10 +05:30
Lakhan Samani
a6f6e0b18a Merge pull request #349 from darshvaghela/main 2023-04-24 09:32:38 +05:30
darshvaghela
3868157e11 refactored code 2023-04-23 17:31:24 +05:30
darshvaghela
d693c05483 Features enhancement (Disable/Enable) 2023-04-22 15:21:47 +05:30
Lakhan Samani
6f1fbf886d Merge pull request #346 from MussieT/feat/return-user-info-on-invitation
Feat/return user info on invitation
2023-04-20 13:59:49 +05:30
Mussie Teshome
b86487fda4 assert message and response is not null 2023-04-20 10:43:06 +03:00
Mussie Teshome
28d4ddeb50 Return the new emails only 2023-04-19 15:38:30 +03:00
Mussie Teshome
b9ab1d3761 return err on err 2023-04-19 15:31:57 +03:00
Mussie Teshome
a5b643e127 removed unnecessary comment 2023-04-19 15:19:17 +03:00
Mussie Teshome
691664e629 Invite members resolver updated to return user info 2023-04-19 14:46:27 +03:00
Mussie Teshome
efb67a9538 New response type for invite members 2023-04-19 14:45:22 +03:00
Mussie Teshome
a0f2eeba3e golang package updates 2023-04-19 14:44:50 +03:00
Mussie Teshome
d1a0ccd790 package updates while running make clean && make 2023-04-19 14:44:17 +03:00
Lakhan Samani
729c23f578 [chore]: update authorizer-react 2023-04-15 14:14:47 +05:30
Lakhan Samani
a074f85391 [chore]: update contributing guide 2023-04-15 08:33:33 +05:30
Lakhan Samani
4e7ec6cb7b [chore]: add command to generate db template 2023-04-15 08:13:10 +05:30
Lakhan Samani
6d541cbfb9 fix: use normal mutex for cache 2023-04-10 15:33:59 +05:30
Lakhan Samani
1ebba7f2b7 Merge pull request #343 from authorizerdev/fix/session-storage
fix: session storage
2023-04-08 18:07:52 +05:30
Lakhan Samani
428a0be3db feat: add cache clear 2023-04-08 18:02:53 +05:30
Lakhan Samani
02c0ebb9c4 fix: session storage 2023-04-08 13:06:15 +05:30
Lakhan Samani
9a284c03ca fix: redis session 2023-04-03 10:26:27 +05:30
Lakhan Samani
c8fe05eabc Merge pull request #342 from authorizerdev/feat/default-oauth-configs
feat: add support for default response mode & type env
2023-04-01 17:42:02 +05:30
Lakhan Samani
48344ffd4c feat: add support for default response mode & type env
Resolves #341
2023-04-01 17:36:07 +05:30
Lakhan Samani
77f34e1149 Merge pull request #339 from authorizerdev/fix/webhooks
fix: allow multiple hooks for same event
2023-03-29 07:31:46 +05:30
Lakhan Samani
16136931a9 fix: add event description to webhook res 2023-03-29 07:31:07 +05:30
Lakhan Samani
c908ac94da fix: continue in case of error for register events 2023-03-29 07:29:44 +05:30
Lakhan Samani
6604b6bbdd fix: update dashboard ui for webhooks 2023-03-29 07:27:56 +05:30
Lakhan Samani
2c227b5518 chore: delete couchbase container after tests 2023-03-29 07:10:36 +05:30
Lakhan Samani
e822b6f31a fix: queries for webhooks + improve tests 2023-03-29 07:06:33 +05:30
Lakhan Samani
a38e9d4e6c fix: rename title -> event_description 2023-03-26 07:48:06 +05:30
Lakhan Samani
deaf1e2ff7 fix: allow multiple hooks for same event 2023-03-26 07:20:45 +05:30
Lakhan Samani
f324976801 Merge pull request #338 from authorizerdev/fix/accessibility
fix: accessibility
2023-03-25 10:48:49 +05:30
Lakhan Samani
fad90ce1a8 fix: accessibility
Resolves #337
2023-03-25 10:48:09 +05:30
Lakhan Samani
df406ba053 Merge pull request #332 from authorizerdev/fix/open-id
fix: add missing info for openid config
2023-03-07 08:44:26 +05:30
Lakhan Samani
4a7877a21b fix: remove duplicate files 2023-03-04 16:13:31 +05:30
Lakhan Samani
79089cc009 Merge pull request #330 from productdevbook/patch-1
feat: github sponsor
2023-03-04 16:12:52 +05:30
Lakhan Samani
149d0cac7a fix: add missing info for openid config
Resolves #304
2023-03-04 16:11:37 +05:30
Lakhan Samani
8863140e75 Create FUNDING.yaml 2023-03-03 08:11:38 +05:30
Mehmet
b8ffadd36c Create FUNDING.yml 2023-03-02 13:07:05 +03:00
Lakhan Samani
7dd20128af Merge pull request #329 from authorizerdev/fix/add-sub-user-info
[server][fix]: add sub to userinfo
2023-02-28 12:52:21 +05:30
Lakhan Samani
19f5ff61c0 [server][fix]: add sub to userinfo
Resolves: #327
2023-02-28 12:51:11 +05:30
Lakhan Samani
146707d062 Merge pull request #328 from authorizerdev/feat/add-microsoft-login
feat: add microsoft login
2023-02-26 06:05:42 +05:30
Lakhan Samani
0810c4a201 chore: update app authorizer-react 1.1.8 2023-02-26 06:05:15 +05:30
Lakhan Samani
3603af9f84 feat: add microsoft login 2023-02-26 05:23:02 +05:30
Lakhan Samani
1ac8ba4ce0 Merge pull request #324 from authorizerdev/fix/neon-db-support
[server]: fix support for neondb
2023-02-10 18:08:22 +05:30
Lakhan Samani
cdcdc444b2 [server]: fix support for neondb
Update gorm/postgres driver version 1.4.7
2023-02-10 10:39:53 +05:30
Lakhan Samani
330f35f2fc Merge pull request #322 from authorizerdev/fix/use-scopes-as-string
[server] use scope string instead of string array in tokens
2023-02-08 09:41:17 +05:30
Lakhan Samani
70242debe1 [server] fix scope response type + add extra claims to access token 2023-02-08 09:39:08 +05:30
Lakhan Samani
4018da6697 [server] use scope string instead of string array in tokens 2023-02-07 01:13:03 +05:30
Lakhan Samani
a73c6ee49e Merge pull request #319 from authorizerdev/fix/arangodb-connection 2023-02-06 21:32:32 +05:30
Lakhan Samani
c23fb1bb32 [server] update arangodb version for test 3.10.3 2023-02-06 20:39:22 +05:30
Lakhan Samani
270853a6a3 [server] add support for arangodb cert, username, password
Adding support for db username, password and ca cert will
enable users to connect arangodb cloud platform
2023-02-06 18:14:19 +05:30
Lakhan Samani
2d0346ff23 [server] remove unused error condition for couchbase 2023-02-05 11:03:26 +05:30
Lakhan Samani
4b26e1ce85 [server] fix bucket creation for couchbase
Run create bucket query only if bucket is not found.
Required while running couchbase cloud version
2023-02-05 11:01:20 +05:30
Lakhan Samani
8212e81023 [server] common util for couchbase syntax 2023-02-02 13:06:18 +05:30
Lakhan Samani
642581eefd [server] Add COUCHBASE_BUCKET_RAM_QUOTA
Resolves #317
2023-02-02 12:43:17 +05:30
Lakhan Samani
b7357dde21 [server] fix primary index creation for couchbase 2023-02-02 12:28:52 +05:30
Lakhan Samani
a1df2ce31f [server] use encryption_key for couchbase env as hash is reserved keyword 2023-01-31 11:18:20 +05:30
Lakhan Samani
748761926d [server] fix make command 2023-01-25 05:19:01 +05:30
Lakhan Samani
d632195ba5 Merge pull request #315 from authorizerdev/feat/couchbase
feat: add couchbase provider
2023-01-19 23:32:20 +05:30
Lakhan Samani
25970f80e1 [app] update authorizer-react 2023-01-19 23:30:54 +05:30
Lakhan Samani
a52b7c86e7 [server] add couchbase to all db test 2023-01-19 23:29:48 +05:30
Lakhan Samani
504d0f34d7 [server] add couchbase envs 2023-01-19 23:28:21 +05:30
Lakhan Samani
44879f1a8f feat: add couchbase provider 2023-01-18 01:38:00 +05:30
Lakhan Samani
b39f0b87fd Merge pull request #313 from authorizerdev/feat/add-get-user
Add get user api for admin
2023-01-05 20:17:48 +05:30
Lakhan Samani
b2423140e2 Add get user api for admin 2023-01-05 20:16:41 +05:30
Lakhan Samani
4f810d2f8b Merge pull request #307 from authorizerdev/feat/mobile-basic-auth
feat: add mobile based basic auth
2022-12-25 03:28:55 +05:30
Lakhan Samani
313b510ba1 feat: add signup + login using mobile 2022-12-25 03:22:42 +05:30
Lakhan Samani
105d9be685 Merge pull request #310 from Lentech-AS/main
Rnd warning fixes
2022-12-24 09:18:07 +05:30
Leander Gangsø
bc68b61879 fix: oauth2.NoContext is deprecated
using context.TODO instead
2022-12-23 21:12:11 +01:00
Leander Gangsø
2847300bf6 fix: update deprecated func since go 1.16
might want to update the docs if you accept this, as it states go > 1.15 is required
2022-12-23 21:12:11 +01:00
Leander Gangsø
d438480f37 fix: remove unused formatting directive 2022-12-23 21:12:11 +01:00
Lakhan Samani
da29f9d055 Merge pull request #308 from Lentech-AS/main
change toast position to top-right
2022-12-23 07:10:11 +05:30
Leander Gangsø
f29256a8f5 change toast position to top-right 2022-12-22 19:53:18 +01:00
Lakhan Samani
1eb8965f98 feat: add mobile based basic auth 2022-12-21 23:14:24 +05:30
Lakhan Samani
1c4e29fa7c fix: access_token renew + web_message redirect 2022-11-29 05:27:29 +05:30
Lakhan Samani
7a28795fa0 Merge pull request #302 from authorizerdev/fix/sql-user-deletion
fix(sql): user deletion
2022-11-25 22:46:57 +05:30
Lakhan Samani
f5db00beb0 fix(sql): user deletion
Resolves: https://github.com/authorizerdev/docs/issues/18
2022-11-25 22:45:23 +05:30
Lakhan Samani
d515a1f41d chore: update app 1.1.4 2022-11-25 22:40:14 +05:30
Lakhan Samani
c948c98e94 Merge pull request #298 from authorizerdev/fix/url-parsing
fix: remove extra slash from host
2022-11-24 19:13:50 +05:30
Lakhan Samani
e985e096bc fix: remove extra slash from host 2022-11-24 12:58:04 +05:30
Lakhan Samani
70bab70ead fix: validating id_token 2022-11-23 22:03:08 +05:30
Lakhan Samani
6ddaf88e3f Merge pull request #294 from authorizerdev/fix/sql-unique-phone-constraint
fix(server): unique constraint for phone_number on mssql
2022-11-17 23:10:55 +05:30
Lakhan Samani
f8bcd0fe51 fix(server): unique constraint for phone_number on mssql 2022-11-17 23:08:17 +05:30
Lakhan Samani
16c4b8ab76 fix(server): global logging 2022-11-17 10:35:38 +05:30
Lakhan Samani
0788c5ff5e fix(server): default loglevel via cli arg 2022-11-17 10:27:55 +05:30
Lakhan Samani
2d5d38de02 fix(server): gorm logging
- add support for LOG_LEVEL env var (Resolves #271)
2022-11-17 10:25:00 +05:30
Lakhan Samani
0dd06d9afd fix(server): basic auth check 2022-11-17 05:44:40 +05:30
Lakhan Samani
d2f472a9cf Merge pull request #293 from luclu7/main
fix: codeVerifier shouldn't be empty for basic auth
2022-11-17 05:44:01 +05:30
Luclu7
4678193300 fix: codeVerifier shouldn't be empty for basic auth 2022-11-16 23:31:39 +01:00
Lakhan Samani
67be8ae285 feat(server): allow using client_id & secret from basic auth header in token endpoint 2022-11-16 22:40:45 +05:30
Lakhan Samani
f9d2130c65 Merge pull request #270 from authorizerdev/fix/oauth-provider
fix(server): authorizer openid flow
2022-11-16 12:27:30 +05:30
Lakhan Samani
bb2a42a1db fix: update app package 2022-11-16 12:20:32 +05:30
Lakhan Samani
f857c993c8 Merge branch 'main' of https://github.com/authorizerdev/authorizer into fix/oauth-provider 2022-11-16 11:47:47 +05:30
Lakhan Samani
824f286b9b Merge pull request #290 from authorizerdev/feat/plain-html-editor
feat: add plain html editor for email templates
2022-11-16 11:31:42 +05:30
anik-ghosh-au7
ecefe12355 chore: format graphql schema 2022-11-15 22:39:04 +05:30
anik-ghosh-au7
5c8f9406f6 fix: email template resolver and for matting changes 2022-11-15 22:12:14 +05:30
Lakhan Samani
75a547cfe2 fix: other auth recipes for oidc idp + remove logs 2022-11-15 21:45:08 +05:30
Lakhan Samani
579899c397 fix(server): creepy @@ string split logic for auth_token 2022-11-13 01:22:21 +05:30
Lakhan Samani
9320f1cb07 fix(server): add flow comment 2022-11-13 00:40:28 +05:30
Lakhan Samani
c09558043e fix(server): spacing 2022-11-13 00:16:22 +05:30
Lakhan Samani
49556b1709 fix: openid flow 2022-11-12 23:54:37 +05:30
Lakhan Samani
4775641431 Merge branch 'main' of https://github.com/authorizerdev/authorizer into fix/oauth-provider 2022-11-10 22:51:13 +05:30
anik-ghosh-au7
ae84213e34 fix: allow design variable empty value for email templates 2022-11-09 22:54:05 +05:30
anik-ghosh-au7
87bf1c3045 fix: allow design variable empty value for email templates 2022-11-09 22:48:12 +05:30
Lakhan Samani
e525877467 fix: latest tag 2022-11-09 22:27:52 +05:30
anik-ghosh-au7
b467e7002d feat: add plain html email template editor 2022-11-09 22:17:41 +05:30
Lakhan Samani
78bdd10a15 Merge pull request #289 from authorizerdev/fix/build
Fix/build
2022-11-09 22:08:13 +05:30
Lakhan Samani
512fd4f1f7 chore: add multi arch setup 2022-11-09 17:36:53 +05:30
Lakhan Samani
67da4a49e4 chore: use zip for windows assets 2022-11-09 16:07:27 +05:30
Lakhan Samani
48deae1d11 chore: fix multi arch build 2022-11-09 15:02:37 +05:30
Lakhan Samani
1f2ded4219 chore: fix tags 2022-11-09 14:15:09 +05:30
Lakhan Samani
cb5af1e679 use non c binding for sqlite 2022-11-09 13:07:20 +05:30
Lakhan Samani
27160ecbd5 chore: fix arm dependencies 2022-11-09 09:46:27 +05:30
Lakhan Samani
c6c3af1114 chore: update dependencies for cross compile 2022-11-09 04:31:04 +05:30
Lakhan Samani
e54b7f18f0 chore: add dependencies for cross compile 2022-11-09 04:25:15 +05:30
Lakhan Samani
a18046748b chore: add cgo param to gox 2022-11-09 04:09:55 +05:30
Lakhan Samani
1bff6720fc chore: install gox 2022-11-09 03:59:20 +05:30
Lakhan Samani
024ffd85f3 chore: fix yaml indentation 2022-11-09 03:52:47 +05:30
Lakhan Samani
e171820614 chore: use checkout@v3 2022-11-09 03:51:08 +05:30
Lakhan Samani
19f9caf478 chore: test gox + buildx 2022-11-09 03:47:28 +05:30
Lakhan Samani
4afd544c41 feat(server): add allowed_roles in access_token + refresh_token 2022-11-07 07:11:23 +05:30
Lakhan Samani
307c6f7d15 fix: refresh token login method claim 2022-11-04 01:40:18 +05:30
Lakhan Samani
bbc6394cf3 Merge pull request #286 from Pjort/main
fixed validation of refresh token
2022-11-03 20:10:40 +05:30
Pjort Kat
63c8e2e55f fixed validation of refresh token 2022-11-03 11:51:59 +01:00
Lakhan Samani
b224892a39 fix: minor space 2022-11-02 08:48:56 +05:30
Lakhan Samani
13edf1965c Merge pull request #285 from authorizerdev/development
feat(server): add jwt claims as part of validation endpoint
2022-11-01 10:15:08 +05:30
Lakhan Samani
1f220a5205 add jwt claims as part of validation endpoint 2022-11-01 10:12:01 +05:30
Lakhan Samani
32fb954a1c Merge pull request #281 from authorizerdev/development
chore: 1.1.22-rc.0
2022-10-25 08:27:02 +05:30
Lakhan Samani
65eadb66fa chore: format 2022-10-25 08:25:23 +05:30
Lakhan Samani
9ce53eb8e8 Merge branch 'development' of https://github.com/authorizerdev/authorizer into development 2022-10-25 08:21:32 +05:30
Lakhan Samani
3b196f074b Merge pull request #280 from authorizerdev/feat/user-roles-multi-select
feat: add user roles multi select input
2022-10-25 08:19:08 +05:30
Lakhan Samani
f2fe584793 feat: add support for SMTP LocalName
Resolves #274
2022-10-25 08:18:29 +05:30
Lakhan Samani
287b952dad fix: forgot password redirect from app 2022-10-24 11:37:42 +05:30
Lakhan Samani
e690066652 fix(server):give higher preference to redirect_uri
While using forgot_password redirect URI was ignored if not present

Resolves #275
2022-10-24 11:15:36 +05:30
anik-ghosh-au7
0f67d74657 feat: add user roles multi select input 2022-10-23 22:59:17 +05:30
Lakhan Samani
274909b7c9 feat: add nonce variable to create auth token 2022-10-23 21:08:08 +05:30
Lakhan Samani
549385e5df Merge branch 'development' of https://github.com/authorizerdev/authorizer into fix/oauth-provider 2022-10-23 16:56:25 +05:30
Lakhan Samani
6e09307c22 Merge pull request #279 from authorizerdev/fix/sql-server-unique-index
fix(server): sql server not allow multiple null
2022-10-21 22:00:55 +05:30
Lakhan Samani
7fc69dcc55 fix(server): sql server not allow multiple null
multiple null values for unique constrained column
is not allowed on sqlserver

Resolves #278
2022-10-21 22:00:16 +05:30
Lakhan Samani
8449821d1b fix(server): dynamodb tests + provider config 2022-10-21 15:55:54 +05:30
Lakhan Samani
476bdf00fc fix(server): open_id config 2022-10-21 11:21:21 +05:30
Lakhan Samani
e41f123866 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-10-21 11:19:37 +05:30
Lakhan Samani
094782aeca fix(server): linting issues 2022-10-21 11:19:32 +05:30
Lakhan Samani
9630cbbc3e Merge pull request #277 from authorizerdev/development
chore(1.1.21.rc)
2022-10-21 11:17:14 +05:30
Lakhan Samani
1ac060136a fix: make env vars name more persistent 2022-10-20 16:27:00 +05:30
Lakhan Samani
c6019e650b fix: add manual code generation 2022-10-20 15:35:26 +05:30
Lakhan Samani
b2e0a3371f fix: revert nonce 2022-10-20 00:14:06 +05:30
Lakhan Samani
a68876a6f4 fix: comment 2022-10-19 23:55:47 +05:30
Lakhan Samani
2c867b0314 fix: issuer token endpoint 2022-10-19 23:41:08 +05:30
Lakhan Samani
74b858ac24 fix: binding 2022-10-19 23:39:48 +05:30
Lakhan Samani
fedc3173fe fix: get nonce from query request if possible 2022-10-19 23:36:33 +05:30
Lakhan Samani
de4381261e fix: add nonce to supported claims 2022-10-19 23:17:13 +05:30
Lakhan Samani
a916b8c32c fix: add nonce 2022-10-19 19:04:15 +05:30
Lakhan Samani
89f08b6d31 fix: redirect from app 2022-10-19 12:20:22 +05:30
Lakhan Samani
cc23784df8 fix: add code to login query params 2022-10-19 12:01:34 +05:30
Lakhan Samani
7ff3b3018a fix: add code to query params 2022-10-19 11:29:49 +05:30
Lakhan Samani
2b52932e98 fix: add code to other response methods 2022-10-19 09:03:00 +05:30
Lakhan Samani
c716638725 fix(server): revert the state & code_challenge validation 2022-10-18 23:24:19 +05:30
Lakhan Samani
252cd1fa2d fix: make code_challenge optional 2022-10-18 23:14:24 +05:30
Lakhan Samani
7c2693b086 fix: form post template 2022-10-18 23:03:55 +05:30
Lakhan Samani
eaa10ec5bc fix: error detection 2022-10-18 22:34:57 +05:30
Lakhan Samani
253128ca0c fix: query params for code response 2022-10-18 22:00:54 +05:30
Lakhan Samani
cddfe1e088 fix: response 2022-10-18 21:46:37 +05:30
Lakhan Samani
8e655bcb5b fix: authorize response 2022-10-18 21:29:09 +05:30
Lakhan Samani
9a411e673c fix: reponse 2022-10-18 21:08:53 +05:30
Lakhan Samani
346c8e5a47 fix: handle response 2022-10-16 22:16:37 +05:30
Lakhan Samani
3cd99fe5f6 fix: open id config 2022-10-16 21:03:37 +05:30
Lakhan Samani
2bd92d6028 feat: add form_post method 2022-10-16 20:46:54 +05:30
Lakhan Samani
844e867d96 Merge pull request #262 from manojown/feat/dynamo-db-support
Feat/dynamo db support
2022-10-14 09:42:08 +05:30
Lakhan Samani
ff805e3ef2 fix: add comments 2022-10-12 13:10:24 +05:30
Lakhan Samani
0115128ee7 fix(server): authorizer as oauth provider 2022-10-09 19:48:13 +05:30
manoj
820d294130 comments resolved for requireEnv, Provider and test env 2022-10-09 00:49:31 +05:30
manoj
e37472d498 update the local test url for dynamodb 2022-10-08 16:20:16 +05:30
manoj
589af96888 resolve conflict over the db models 2022-10-08 16:07:07 +05:30
manoj
dccc70e5c0 resolve conflict with main branch 2022-10-08 15:47:44 +05:30
Lakhan Samani
d8eceadd7f Merge pull request #267 from authorizerdev/fix/sqlserver-text-type
fix(server): text type for sql server 2019
2022-10-08 15:38:32 +05:30
Lakhan Samani
e6c4fdff26 fix(server): text type for sql server 2019
Resolves #266
2022-10-07 10:13:20 +05:30
manoj
896d8e046d remove the white space 2022-10-05 16:11:31 +05:30
manoj
a6d5d4af24 require env condition needed to be added for dynamodb 2022-10-05 15:52:17 +05:30
Manoj
cc4eaa2847 dynamod db index changes added to the schema 2022-10-05 15:32:32 +05:30
Manoj
dba8944565 provider: dynamo-db support added 2022-10-03 01:08:12 +05:30
Lakhan Samani
e760a5598e Merge pull request #260 from authorizerdev/chore/add-prettier-app
chore(app): add prettier
2022-10-02 22:40:16 +05:30
Lakhan Samani
f62a22619b chore(app): add prettier 2022-10-02 22:39:47 +05:30
Lakhan Samani
c32a7fa1e4 Merge pull request #259 from authorizerdev/chore/add-prettier-dashboard
chore(dashboard): add prettier
2022-10-02 22:38:06 +05:30
Lakhan Samani
399b97079d chore(dashboard): add prettier 2022-10-02 22:37:20 +05:30
Lakhan Samani
fe687cb0ca Merge pull request #258 from authorizerdev/fix/cookie-security-features
feat(dashboard): allow setting admin / app cookie security
2022-10-02 22:03:43 +05:30
Lakhan Samani
9cb011e921 feat(dashboard): allow setting admin / app cookie security
Fixes #233
2022-10-02 22:01:22 +05:30
Lakhan Samani
4e1bba2ba8 Merge pull request #246 from jerebtw/main
feat: add app & admin cookie secure variable to dashboard
2022-10-02 21:36:36 +05:30
ruessej
f1509f90f0 feat: Update generated and models_gen 2022-10-01 18:12:49 +02:00
Jerebtw
bd4d48c7c5 fix: schema.graphqls 2022-10-01 17:57:23 +02:00
Jerebtw
0e3242372b feat: add app & admin cookie secure variable to dashboard
Todo: Generate graphql (i don't work on my PC (Windows))
2022-10-01 17:48:05 +02:00
Lakhan Samani
89cea39c41 Merge pull request #257 from authorizerdev/chore/update-go-gin-1.18.1
chore: update go-gin server to 1.18.1
2022-10-01 17:42:00 +05:30
Lakhan Samani
570a0b9531 chore: update go-gin server to 1.18.1 2022-10-01 17:41:16 +05:30
Lakhan Samani
686b3a4666 Merge pull request #255 from authorizerdev/chore/update-go-1.19.1
chore: update golang to 1.19.1
2022-10-01 17:20:48 +05:30
Lakhan Samani
b266a14108 chore: update golang to 1.19.1 2022-10-01 15:17:11 +05:30
Lakhan Samani
e5972a0dee Merge pull request #254 from authorizerdev/feat/update-gqlgen-0.17.20
chore: update gqlgen to 0.17.20
2022-10-01 15:15:13 +05:30
Lakhan Samani
6f46f1e6ef chore: update gqlgen to 0.17.20 2022-09-30 15:37:59 +05:30
Lakhan Samani
cfbce17ab8 fix: set same site cookie to none for cross site 2022-09-28 18:42:42 +05:30
Lakhan Samani
aa6601e62c fix: same site cookie 2022-09-28 18:30:30 +05:30
Lakhan Samani
d8ea0c656f Merge pull request #247 from authorizerdev/fix/same-site-cookie
fix(server): use sameSite as lax by default for app cookie
2022-09-28 11:18:03 +05:30
Lakhan Samani
f5323e0eec fix(server): update comments for host & cookies 2022-09-28 10:36:56 +05:30
Lakhan Samani
b1bc7b5370 fix(server): set default app cookie to lax mode 2022-09-28 09:51:04 +05:30
Lakhan Samani
536fd87c3c fix: debug log 2022-09-27 06:45:38 +05:30
Lakhan Samani
f8c96a9fee Merge pull request #244 from authorizerdev/fix/remove-user-verification-request-when-deleted
fix: remove entries from otp + verification when user is deleted
2022-09-27 06:44:22 +05:30
Lakhan Samani
837fc781de fix: remove entries from otp + verification when user is deleted
Resolves #234
2022-09-27 00:27:36 +05:30
Lakhan Samani
640bb8c9ed chore: bump app/authorizer-react 1.1.2 2022-09-27 00:07:52 +05:30
Lakhan Samani
d9bba0bbe7 Merge pull request #243 from authorizerdev/fix/bool-env-vars-secure-cookie
fix: app & admin cookie secure variable type while persisting info
2022-09-27 00:03:31 +05:30
Lakhan Samani
f91ec1880f fix: app & admin cookie secure variable type while persisting info
Resolves #241
2022-09-27 00:01:38 +05:30
Lakhan Samani
19e2153379 Update README.md 2022-09-15 12:24:47 +05:30
Lakhan Samani
221009bf0a Merge pull request #229 from ruessej/main
feat: Add a option to disable httpOnly cookies
2022-09-15 11:22:27 +05:30
ruessej
6085c2d535 Fix incorrect type 2022-09-14 12:24:19 +02:00
Jerebtw
8e0c5e4380 Make the default value true 2022-09-14 11:56:48 +02:00
Lakhan Samani
21b70e4b26 Merge pull request #230 from authorizerdev/fix/github-oauth-scopes
fix: scope for github auth
2022-09-14 11:46:46 +05:30
Lakhan Samani
993693884d fix: scope for github auth 2022-09-14 11:45:38 +05:30
Lakhan Samani
ed849fa6f6 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-09-14 10:44:09 +05:30
Lakhan Samani
aec1f5df53 fix: github endpoint to get user emails 2022-09-14 10:44:01 +05:30
ruessej
195bd1bc6a Add a option to disable httpOnly cookies 2022-09-12 14:37:42 +02:00
Lakhan Samani
45b4c41bca Merge pull request #228 from Deep-Codes/main 2022-09-10 11:40:11 +05:30
Deepankar
63d486821e fix: lint 2022-09-10 11:39:01 +05:30
Deep-Codes
4b56afdc98 fix(type): __authorizer__ on window 2022-09-10 11:23:20 +05:30
Lakhan Samani
6455ff956a fix: remove varible log 2022-09-10 10:52:56 +05:30
Lakhan Samani
3898e43fff feat: add button to jwt config as json 2022-09-10 10:50:15 +05:30
Lakhan Samani
2c305e5bde Update README.md 2022-09-09 10:24:30 +05:30
Lakhan Samani
b8fd08e576 Update README.md 2022-09-09 09:29:27 +05:30
Lakhan Samani
6dafa45051 fix: invalid login message
Resolves #224
2022-09-03 21:48:33 +05:30
Lakhan Samani
ead3514113 chore: update railway template 2022-08-31 13:09:00 +05:30
Lakhan Samani
75a413e5f2 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-08-31 11:02:50 +05:30
Lakhan Samani
91bf0e2478 fix: use replace all 2022-08-31 11:02:46 +05:30
Lakhan Samani
7a1305cf96 Merge pull request #222 from Deep-Codes/main 2022-08-31 07:04:20 +05:30
Deep-Codes
ff5a6ec301 feat(server): add log to show PORT 2022-08-30 23:35:43 +05:30
Lakhan Samani
b7b97b4f8d Merge pull request #221 from Deep-Codes/main
fix(dashboard): users table overflow
2022-08-30 22:38:49 +05:30
Deep-Codes
d9bc989c74 fix(dashboard): users table overflow 2022-08-30 21:56:28 +05:30
Lakhan Samani
d1f80d4088 feat: add support for twitter login 2022-08-29 08:37:53 +05:30
Lakhan Samani
4b299f0da2 fix: log 2022-08-29 08:19:11 +05:30
Lakhan Samani
ed8006db4c Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-08-29 08:18:42 +05:30
Lakhan Samani
97f6c7d50a fix: authorize endpoint setting user session 2022-08-29 08:18:20 +05:30
Lakhan Samani
5e3f68a180 Merge pull request #216 from szczepad/feat/twitter-login
Feat/twitter login
2022-08-24 08:53:52 +05:30
szczepad
f73d1fc588 feat: Adds login via twitter 2022-08-22 09:25:10 +02:00
szczepad
aa232de426 fix: Uses whitespace as seperator for oauth scopes in state-string
This is necessary, as the previous delimiter (,) was being redacted
after a redirect. This resulted in the scopes not being correctly
parseable and the state not being fetched correctly after the
oauth-callback
2022-08-22 09:25:10 +02:00
Lakhan Samani
34ce754ef6 feat: bootstrap twitter login config 2022-08-22 09:03:29 +02:00
Lakhan Samani
5f385b2016 fix: remove unused file 2022-08-18 07:21:50 +05:30
Lakhan Samani
da7c17271e Merge pull request #215 from wabscale/main
fix: rootless container
2022-08-18 06:11:32 +05:30
John McCann Cunniff Jr
69fbd631ff fix: rootless container 2022-08-17 20:33:05 -04:00
Lakhan Samani
deb209e358 Update README.md 2022-08-15 22:28:43 +05:30
Lakhan Samani
ea6b4cbc8d Update README.md 2022-08-15 22:28:13 +05:30
Lakhan Samani
2f21a09b2e chore: bump app/authorizer-react 1.0.0 2022-08-15 21:06:57 +05:30
Lakhan Samani
4ab775f2c1 fix: apple & linkedin env config 2022-08-13 12:37:04 +05:30
Lakhan Samani
b6e8023104 Merge pull request #211 from authorizerdev/fix/email-template
fix email template
2022-08-13 11:58:07 +05:30
Lakhan Samani
4f1597e5d2 fix: update note on features 2022-08-13 11:57:03 +05:30
Lakhan Samani
4f81d1969e fix email template
- fix verification types
- add design to cassandra db provider for email_template
- fix default email verification types to include update_email
2022-08-13 11:34:24 +05:30
Lakhan Samani
ad3e615ac7 Merge pull request #210 from authorizerdev/fix/dashboard-ui
Fix/dashboard UI
2022-08-13 03:57:19 +05:30
anik-ghosh-au7
e9a2301d2b feat: [dashboard] add env options for multi factor auth 2022-08-11 17:50:45 +05:30
anik-ghosh-au7
48bbfa31af fix: template editor design 2022-08-11 17:08:23 +05:30
anik-ghosh-au7
d7f5f563cc fix: add design to email template 2022-08-11 16:45:59 +05:30
anik-ghosh-au7
6c29149fbe fix: email template editor 2022-08-11 15:08:50 +05:30
Lakhan Samani
bbd4d43317 fix: add padding to editor 2022-08-09 12:10:50 +05:30
Lakhan Samani
c4d2f62657 fix: clear form on close 2022-08-09 11:55:55 +05:30
Lakhan Samani
5d78bf178f fix: email template info 2022-08-09 11:41:51 +05:30
Lakhan Samani
58749497bd fix: payload example for webhook 2022-08-09 10:04:06 +05:30
Lakhan Samani
5c6e643efb Merge pull request #209 from authorizerdev/feat/send-email-based-on-template
feat: send email based on template
2022-08-09 09:17:29 +05:30
Lakhan Samani
7792cdbc5e fix: template respone & ui 2022-08-09 09:07:47 +05:30
Lakhan Samani
65803c3763 fix: remove todos 2022-08-09 01:53:21 +05:30
Lakhan Samani
81fce1a471 feat: send email based on template 2022-08-09 01:43:37 +05:30
Lakhan Samani
0714b4360b Merge pull request #206 from authorizerdev/feat/2fa
feat: add mutifactor authentication
2022-08-07 11:11:56 +05:30
Lakhan Samani
8f69d5746e Merge pull request #207 from authorizerdev/feat/email-template-ui
feat: email template UI + subject
2022-08-07 11:10:44 +05:30
Lakhan Samani
ebc11906ef Merge branch 'feat/2fa' of https://github.com/authorizerdev/authorizer into feat/2fa 2022-08-03 23:20:37 +05:30
Lakhan Samani
465a92de22 feat: add managing mfa 2022-08-03 23:20:23 +05:30
Lakhan Samani
a890013317 Update generate_otp.go 2022-08-02 18:26:05 +05:30
Lakhan Samani
587828b59b feat: add helper for updating all users 2022-08-02 14:12:36 +05:30
anik-ghosh-au7
85630a59c1 feat: add webhook payload example 2022-08-02 00:56:21 +05:30
anik-ghosh-au7
b4ef196bfb fix: update email template variables 2022-08-01 14:07:06 +05:30
anik-ghosh-au7
099b2a39b4 feat: add delete email template modal 2022-07-30 22:47:00 +05:30
anik-ghosh-au7
2d07baedf4 feat: fix update email template editor 2022-07-30 20:28:36 +05:30
anik-ghosh-au7
8b34e001ef feat: fix update email template editor 2022-07-30 20:15:49 +05:30
anik-ghosh-au7
617dcdde53 feat: fix update email template modal 2022-07-30 18:43:02 +05:30
anik-ghosh-au7
f2fb800323 feat: dashboard add email-template page 2022-07-30 16:05:35 +05:30
Lakhan Samani
236045ac54 feat: add resend otp test 2022-07-30 01:12:20 +05:30
Lakhan Samani
d89be44fe5 feat: add sending otp 2022-07-29 19:49:50 +05:30
Lakhan Samani
db4d711cba feat: add subject to email template 2022-07-29 16:15:57 +05:30
Lakhan Samani
0fc9e8ccaa feat: add EnvKeyIsEmailServiceEnabled 2022-07-29 16:00:12 +05:30
anik-ghosh-au7
4e3d73e767 feat: otp resolvers updated 2022-07-29 13:49:46 +05:30
anik-ghosh-au7
e3c58ffbb0 fix: login resolver multifactor auth 2022-07-28 11:18:06 +05:30
anik-ghosh-au7
f12491e42d fix: auth response schema updated 2022-07-27 15:28:12 +05:30
anik-ghosh-au7
d653fac340 Merge branch 'feat/2fa' of https://github.com/authorizerdev/authorizer into feat/2fa 2022-07-27 12:18:51 +05:30
anik-ghosh-au7
9fae8215d2 feat: dashboard - add actions to update is_multi_factor_auth_enabled 2022-07-27 12:18:32 +05:30
Lakhan Samani
4e23e49de4 fix: syntax 2022-07-25 18:08:07 +05:30
anik-ghosh-au7
ef22318d5c feat: add generate_otp util 2022-07-24 10:40:37 +05:30
anik-ghosh-au7
480438fb7a fix: remove duplicate code in verify otp resolver 2022-07-23 20:04:39 +05:30
Lakhan Samani
8db6649e5c Merge pull request #205 from anik-ghosh-au7/feat/2fa
update: verify otp resolver and test added
2022-07-23 18:37:04 +05:30
anik-ghosh-au7
49cc6033ab update: verify otp resolver and test added 2022-07-23 18:32:31 +05:30
Lakhan Samani
5d903ca170 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/2fa 2022-07-23 16:52:30 +05:30
Lakhan Samani
44280be25a feat: add resolver for verify_otp 2022-07-23 16:44:39 +05:30
Lakhan Samani
f6029fb7bf feat: use upsert for otp + implement otp methods for cassandradb 2022-07-23 16:39:35 +05:30
Lakhan Samani
22ae3bca54 feat: add otp implementation for arangodb 2022-07-23 16:06:52 +05:30
Lakhan Samani
1a27d91957 feat: add otp implementation for mongodb 2022-07-23 16:01:46 +05:30
Lakhan Samani
f6c67243b9 feat: add otp model + implementation for sql 2022-07-23 15:55:06 +05:30
Lakhan Samani
9ba1239c11 Merge pull request #204 from anik-ghosh-au7/main
fix: collections names
2022-07-23 15:46:35 +05:30
anik-ghosh-au7
ed7ed73980 fix: collections names 2022-07-23 15:44:56 +05:30
Lakhan Samani
9ef5f33f7a feat: add is_multi_factor_auth_enabled 2022-07-23 15:26:44 +05:30
Lakhan Samani
0f081ac3c8 Update README.md 2022-07-20 23:08:48 +05:30
Lakhan Samani
3aa0fb20ce Update CONTRIBUTING.md 2022-07-20 23:08:44 +05:30
Lakhan Samani
891c885f20 fix: webhook ui 2022-07-17 17:18:45 +05:30
Lakhan Samani
89606615dc Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-07-17 17:06:03 +05:30
Lakhan Samani
ecab47b2ea Merge pull request #202 from anik-ghosh-au7/feat/webhooks
Feat/webhooks
2022-07-17 17:05:51 +05:30
Lakhan Samani
882756ef3a fix: handle different response 2022-07-17 17:05:35 +05:30
anik-ghosh-au7
a208c87c29 update: webhooks 2022-07-17 16:50:58 +05:30
Lakhan Samani
70ea463f60 feat: handle empty response from webhook endpoint 2022-07-17 16:25:16 +05:30
anik-ghosh-au7
79c94fcaf0 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/webhooks 2022-07-17 16:03:21 +05:30
anik-ghosh-au7
3b925bb072 update: webhooks 2022-07-17 16:03:07 +05:30
anik-ghosh-au7
df17ea8f40 update: webhooks 2022-07-17 14:48:20 +05:30
anik-ghosh-au7
94066d4408 update: webhooks 2022-07-17 14:42:46 +05:30
Lakhan Samani
41468b5b60 Merge pull request #201 from authorizerdev/feat/add-email-template-apis
feat: add email template apis
2022-07-17 14:20:34 +05:30
anik-ghosh-au7
1c61fcc17a update: webhooks 2022-07-17 13:52:31 +05:30
Lakhan Samani
a102924fd7 fix: remove debug logs 2022-07-17 13:39:23 +05:30
anik-ghosh-au7
390846c85f update: webhooks 2022-07-17 13:38:18 +05:30
Lakhan Samani
a48b809a89 feat: add tests for email template resolvers 2022-07-17 13:37:34 +05:30
Lakhan Samani
cd46da60a0 feat: implement resolvers for email template 2022-07-17 12:32:01 +05:30
Lakhan Samani
50f52a99b4 fix: github user emails 2022-07-17 12:07:17 +05:30
anik-ghosh-au7
150b1e5712 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/webhooks 2022-07-17 11:48:54 +05:30
Lakhan Samani
1f7eee43e2 feat: add email template implementation for arangodb provider 2022-07-17 11:37:04 +05:30
Lakhan Samani
7c441fff14 feat: add email template implementation for cassandra provider 2022-07-17 11:21:51 +05:30
Lakhan Samani
647cc1d9bf feat: add email template implementation for mongodb provider 2022-07-17 11:01:47 +05:30
Lakhan Samani
97b1d8d66f Merge branch 'main' into feat/add-email-template-apis 2022-07-17 10:39:02 +05:30
Lakhan Samani
2cce1c4e93 fix: webhook update headers 2022-07-17 10:36:16 +05:30
anik-ghosh-au7
8b1511a07b update: webhooks 2022-07-16 23:10:05 +05:30
anik-ghosh-au7
a69dd95992 update: webhooks 2022-07-16 15:59:21 +05:30
anik-ghosh-au7
d3260f4f32 update: webhooks 2022-07-16 15:24:50 +05:30
anik-ghosh-au7
301bde4da2 update: webhooks 2022-07-16 09:53:29 +05:30
anik-ghosh-au7
913c5c94fb update: webhooks 2022-07-16 09:42:10 +05:30
Lakhan Samani
610896b6f5 fix: refs for email templatE 2022-07-15 22:13:00 +05:30
anik-ghosh-au7
33f79872be Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/webhooks 2022-07-15 22:12:29 +05:30
anik-ghosh-au7
8fc52d76dc fix: TT-69 2022-07-15 22:12:08 +05:30
Lakhan Samani
aa12757155 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/add-email-template-apis 2022-07-15 22:11:58 +05:30
Lakhan Samani
847c364ad1 fix: refs 2022-07-15 22:11:08 +05:30
anik-ghosh-au7
eabc943452 update: webhooks 2022-07-15 13:17:09 +05:30
anik-ghosh-au7
41a0f15e16 update: webhooks 2022-07-15 13:04:32 +05:30
Lakhan Samani
e2294c24d0 feat: add email template implementation for sql provider 2022-07-15 12:35:35 +05:30
anik-ghosh-au7
a3c0a0422c update: webhooks 2022-07-15 12:22:47 +05:30
anik-ghosh-au7
d837b1590a update: webhooks 2022-07-15 12:20:51 +05:30
Lakhan Samani
283e570ebb feat: init email template schema for all providers 2022-07-15 10:23:45 +05:30
Lakhan Samani
14c74f6566 feat: add email template schema 2022-07-15 10:12:24 +05:30
anik-ghosh-au7
8e655daa71 update: webhooks 2022-07-14 23:41:44 +05:30
Lakhan Samani
fed092bb65 fix: invite email template 2022-07-13 21:16:31 +05:30
Lakhan Samani
6d28290605 Merge pull request #199 from authorizerdev/fix/password-changing
fix(update_profile): changing password if not signed up via basic auth
2022-07-13 20:46:56 +05:30
Lakhan Samani
2de0ea57d0 fix(update_profile): changing password if not signed up via basic
Resolves #198
2022-07-13 20:45:21 +05:30
Lakhan Samani
f2886e6da8 fix: disable other db test for quick test 2022-07-12 11:57:46 +05:30
Lakhan Samani
6b57bce6d9 fix: cassandra + mongo + arangodb issues with webhook 2022-07-12 11:48:42 +05:30
Lakhan Samani
bfbeb6add2 fix: couple session deletion with user deletion 2022-07-12 08:42:32 +05:30
Lakhan Samani
1fe0d65874 feat: add support for planetscale
Resolves #195
2022-07-11 22:37:07 +05:30
Lakhan Samani
bfaa0f9d89 fix: make list webhooks params optional 2022-07-11 22:05:44 +05:30
Lakhan Samani
4f5a6c77f8 Merge pull request #194 from authorizerdev/feat/webhook
feat: add webhook apis + integrate in events
2022-07-11 19:56:48 +05:30
Lakhan Samani
018a13ab3c feat: add tests for webhook resolvers 2022-07-11 19:40:54 +05:30
Lakhan Samani
334041d0e4 fix: delete user event flow 2022-07-11 11:13:32 +05:30
Lakhan Samani
6a8309a231 feat: register event for revoke/enable access + delete user 2022-07-11 11:12:30 +05:30
Lakhan Samani
6347b60753 fix: rename revoke refresh token handler for better reading 2022-07-11 11:10:30 +05:30
Lakhan Samani
bbb064b939 feat: add register event 2022-07-11 10:42:42 +05:30
Lakhan Samani
e91a819067 feat: implement resolvers 2022-07-10 21:49:33 +05:30
Lakhan Samani
09c3eafe6b feat: add mongodb database methods for webhook 2022-07-09 12:23:48 +05:30
Lakhan Samani
bb51775d34 feat: add cassandradb database methods for webhook 2022-07-09 12:16:54 +05:30
Lakhan Samani
6d586b16e4 feat: add arangodb database methods for webhook 2022-07-09 11:44:14 +05:30
Lakhan Samani
e8eb62769e feat: add sql database methods for webhook 2022-07-09 11:21:32 +05:30
Lakhan Samani
0ffb3f67f1 fix: index for arangodb 2022-07-08 19:10:37 +05:30
Lakhan Samani
ec62686fbc feat: add database methods for webhookLog 2022-07-08 19:09:23 +05:30
Lakhan Samani
a8064e79a1 feat: add template for webhook db methods 2022-07-06 10:38:21 +05:30
Lakhan Samani
265331801f feat: add database models 2022-07-04 22:37:13 +05:30
Lakhan Samani
6a74a50493 feat: add support for scylladb
Resolves #177
2022-07-04 21:57:14 +05:30
Lakhan Samani
8c27f20534 Merge pull request #193 from authorizerdev/fix/invalidate-session-token
fix: add provider to token creation
2022-07-01 22:14:50 +05:30
Lakhan Samani
29c6003ea3 fix: remove test log 2022-07-01 22:04:58 +05:30
Lakhan Samani
ae34fc7c2b fix: update_env resolver 2022-07-01 22:02:34 +05:30
Lakhan Samani
2a5d5d43b0 fix: add namespace to session token keys 2022-06-29 22:24:00 +05:30
Lakhan Samani
e6a4670ba9 fix: add provider to token creation 2022-06-29 09:54:12 +05:30
Lakhan Samani
64d64b4099 feat: add ability to disable strong password 2022-06-18 15:31:57 +05:30
Lakhan Samani
88f9a10f21 Merge pull request #192 from authorizerdev/feat/apple-login
feat: add apple login
2022-06-16 09:48:02 +05:30
Lakhan Samani
4e08d4f8fd fix: update app 2022-06-16 09:47:16 +05:30
Lakhan Samani
1c4dda9299 fix: remove debug logs 2022-06-16 07:34:10 +05:30
Lakhan Samani
ab18fa5832 fix: use raw base64 url decoding 2022-06-15 21:55:41 +05:30
Lakhan Samani
484d0c0882 chore: update app 2022-06-14 16:39:06 +05:30
Lakhan Samani
be59c3615f fix: add comment for scope 2022-06-14 15:47:08 +05:30
Lakhan Samani
db351f7771 fix: remove debug logs 2022-06-14 15:45:06 +05:30
Lakhan Samani
91c29c4092 fix: redirect 2022-06-14 15:43:23 +05:30
Lakhan Samani
415b97535e fix: update scope param 2022-06-14 15:05:56 +05:30
Lakhan Samani
7d1272d815 fix: update scope for apple login 2022-06-14 14:41:31 +05:30
Lakhan Samani
c9ba0b13f8 fix: update scope for apple login 2022-06-14 13:37:05 +05:30
Lakhan Samani
fadd9f6168 fix: update scope for apple login 2022-06-14 13:11:39 +05:30
Lakhan Samani
395e2e2a85 fix: update scope for apple login 2022-06-14 12:35:23 +05:30
Lakhan Samani
6335084835 fix: add post method support for oauth callback 2022-06-14 12:17:43 +05:30
Lakhan Samani
eab336cd3d fix: apple login params 2022-06-14 12:06:46 +05:30
Lakhan Samani
f4691fca1f fix: id token parsing 2022-06-14 11:38:04 +05:30
Lakhan Samani
341d4fbae5 fix: scope for apple login 2022-06-14 11:21:26 +05:30
Lakhan Samani
e467b45ab1 fix: apple client secret field 2022-06-14 11:11:09 +05:30
Lakhan Samani
7edfad3486 fix: apple client secret field 2022-06-14 10:56:47 +05:30
Lakhan Samani
80578b88ac feat: update app 2022-06-13 07:37:26 +05:30
Lakhan Samani
5646e7a0e7 feat: add test code to process apple user 2022-06-12 18:30:33 +05:30
Lakhan Samani
53a592ef63 feat: add base for apple login 2022-06-12 14:49:48 +05:30
Lakhan Samani
3337dbd0a4 Merge pull request #191 from authorizerdev/fix/session-invalidation
fix: session invalidation
2022-06-12 09:08:57 +05:30
Lakhan Samani
82a2a42f84 fix: user session access 2022-06-12 00:27:21 +05:30
Lakhan Samani
ac49b5bb70 fix: session tests 2022-06-11 19:24:53 +05:30
Lakhan Samani
926ab07c07 fix: session invalidation 2022-06-11 19:10:39 +05:30
Lakhan Samani
7a2dbea019 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-06-09 23:43:28 +05:30
Lakhan Samani
dff50097e8 feat: add support for cockroachdb 2022-06-09 23:43:21 +05:30
Lakhan Samani
aff9d3af20 Merge pull request #187 from authorizerdev/fix-parallel-access
fix: parallel access of env vars
2022-06-09 23:13:34 +05:30
Lakhan Samani
02eb1d6677 fix: add const for test env 2022-06-09 23:13:22 +05:30
Lakhan Samani
78a673e4ad fix: fix parallel access of env vars 2022-06-08 09:50:30 +05:30
Lakhan Samani
e0d8644264 fix: role validation while signup 2022-06-07 08:00:30 +05:30
Lakhan Samani
d8c662eaad fix: dashboard roles 2022-06-07 07:30:01 +05:30
Lakhan Samani
6d1d259f71 Merge pull request #182 from authorizerdev/feat/add-linkedin-login
feat: add linkedin login
2022-06-06 22:09:08 +05:30
Lakhan Samani
2841853d37 feat: add linkedin login 2022-06-06 22:08:32 +05:30
Lakhan Samani
360dd3c3bd fix: redirect uri 2022-06-05 22:46:56 +05:30
Lakhan Samani
c6add0cca6 fix: give higher priority to authorizer url 2022-06-05 22:13:10 +05:30
Lakhan Samani
7ac6252aac fix: app login page signup url
add debug logs
2022-06-05 21:44:16 +05:30
Lakhan Samani
5d2d1c342b fix: allow setting host for cassandradb without prot 2022-06-05 12:13:55 +05:30
Lakhan Samani
6da0a85936 fix: remove unused code 2022-06-04 09:26:02 +05:30
Lakhan Samani
116972d725 feat: add support for ScyllaDB
Resolves #177
2022-06-04 08:59:26 +05:30
Lakhan Samani
d1e1e287db Merge pull request #174 from authorizerdev/fix/memory-store
fix: replica cache consistency
2022-06-01 23:47:55 +05:30
Lakhan Samani
a7f04f8754 fix: fix mutex for testing purpose 2022-05-31 15:06:53 +05:30
Lakhan Samani
69b56c9912 fix: disable mutex for testing purpose 2022-05-31 15:00:11 +05:30
Lakhan Samani
98015708a2 fix: bool flag for redis 2022-05-31 13:27:43 +05:30
Lakhan Samani
1b5a7b8fb0 fix: don't allow redis disabling from dashboard 2022-05-31 13:26:03 +05:30
Lakhan Samani
8b9bcdfdbe fix: message 2022-05-31 13:24:24 +05:30
Lakhan Samani
ba429da05f fix: env query 2022-05-31 13:18:42 +05:30
Lakhan Samani
7c7bb42003 fix: message 2022-05-31 13:14:08 +05:30
Lakhan Samani
eeff88c853 fix: env saving 2022-05-31 13:11:54 +05:30
Lakhan Samani
cf8762b7a0 fix: slice envs 2022-05-31 08:14:03 +05:30
Lakhan Samani
c61c3024ec fix: upgrade tests 2022-05-30 12:47:50 +05:30
Lakhan Samani
7e3bd6a721 fix: import cycle issues 2022-05-30 11:54:16 +05:30
Lakhan Samani
1146468a03 fix: memory store upgrade in token helpers 2022-05-30 11:00:00 +05:30
Lakhan Samani
268b22ffb2 fix: memory store upgrade in resolvers 2022-05-30 09:19:55 +05:30
Lakhan Samani
43359f1dba fix: update store method till handlers 2022-05-29 17:22:46 +05:30
Lakhan Samani
1941cf4299 fix: move sessionstore -> memstore 2022-05-27 23:20:38 +05:30
Lakhan Samani
7b13034081 fix: dashboard 2022-05-25 16:34:54 +05:30
Lakhan Samani
7c16900618 Merge pull request #168 from anik-ghosh-au7/fix/app-routes
update: separate routes for login and signup added
2022-05-25 16:07:15 +05:30
Lakhan Samani
d722fe258d Merge pull request #173 from authorizerdev/feat/logging
feat: add logging system
2022-05-25 15:07:28 +05:30
Lakhan Samani
99dc5ee572 fix: remove unused code 2022-05-25 15:05:51 +05:30
Lakhan Samani
8bee421d0a feat: add flag for log level 2022-05-25 15:04:26 +05:30
Lakhan Samani
714b79e4ab fix: format logs 2022-05-25 12:30:22 +05:30
Lakhan Samani
d886d780b4 fix: replace all logs 2022-05-24 12:50:33 +05:30
Lakhan Samani
d7bb10fd21 feat: add loggging to all resolvers 2022-05-24 12:42:29 +05:30
Lakhan Samani
f5515bec28 fix: merge conflict 2022-05-23 11:54:46 +05:30
Lakhan Samani
b35d86fd40 feat: add logs for http handlers 2022-05-23 11:52:51 +05:30
anik-ghosh-au7
a638f02014 update: seperate routes for login and signup added 2022-05-18 20:04:29 +05:30
Lakhan Samani
2c4bc9adb6 Merge pull request #161 from akash-dutta-au7/fix/env-page-new
Update Dashboard UI
2022-05-15 17:11:13 +05:30
Lakhan Samani
5884802e60 Merge pull request #166 from Vicg853/fix/role-update
Fix/role update
2022-05-15 17:04:42 +05:30
Vicg853
241f977b2a Fixing related files 2022-05-14 23:21:45 -03:00
anik-ghosh-au7
ed855a274a fix: app style remove unnecessary code 2022-05-14 20:20:21 +05:30
Vicg853
049ea64475 Merge branch 'main' of github.com:Vicg853/authorizer into fix/role-update 2022-05-14 05:07:13 -03:00
Vicg853
5e4f34c889 Fixing login isValidRole usage 2022-05-14 04:37:36 -03:00
Lakhan Samani
ab717d956a fix: update role test 2022-05-13 07:49:45 +05:30
Lakhan Samani
6209c4d506 Merge pull request #165 from Vicg853/fix/role-update
Unable to update user role fix
2022-05-13 07:38:45 +05:30
Lakhan Samani
2bc4c74930 fix: remove old logs 2022-05-13 07:28:31 +05:30
Vicg853
1efa419cdf Clean up 2022-05-12 16:43:07 -03:00
Vicg853
4ceb6db4ba Adding possible test error cause comment 2022-05-12 16:40:49 -03:00
Vicg853
9edc8d0fb5 Inverted userRoles by role fix. Roles can now be updated 2022-05-12 16:40:19 -03:00
Lakhan Samani
da0fcb109b feat: setup logours for logging 2022-05-13 00:47:01 +05:30
akash.dutta
3e51a7bd01 login page vertically responsive 2022-05-12 17:06:18 +05:30
akash.dutta
28bed69b2e login page vertically responsive 2022-05-12 15:10:24 +05:30
anik-ghosh-au7
0433d64737 fix: dom nesting bugs 2022-05-12 12:27:25 +05:30
Lakhan Samani
773213e5a4 fix: clean test data 2022-05-11 20:25:57 +05:30
akash.dutta
de44c40de5 login page fixed 2022-05-09 15:46:42 +05:30
akash.dutta
a7fa988bf0 tooltiip added to Invite member button 2022-05-08 12:48:26 +05:30
akash.dutta
538a2d0b59 tooltiip added to Invite member button 2022-05-08 12:43:52 +05:30
akash.dutta
f519f0eb0e tooltiip added to Invite member button 2022-05-08 09:35:50 +05:30
akash.dutta
d5ad4a6e55 blue border on navlink removed 2022-05-07 22:34:26 +05:30
akash.dutta
d9b49ca932 login page responsive 2022-05-07 22:18:04 +05:30
akash.dutta
7c5aab7bf3 all components updated, uncontrolled input error handled 2022-05-07 22:10:29 +05:30
akash.dutta
c783e101d5 test-pull req 2022-05-03 19:53:21 +05:30
akash.dutta
ebccfb18cd test-pull req 2022-05-03 19:50:01 +05:30
Lakhan Samani
b7aeff57af fixes #160 2022-04-30 12:45:08 +05:30
Lakhan Samani
075c287f34 feat: add support for database cert, key, ca-cert 2022-04-23 17:52:02 +05:30
Lakhan Samani
4778827545 Merge pull request #144 from authorizerdev/feat/casandra-db
feat: add support for cassandra db
2022-04-22 21:26:10 +05:30
Lakhan Samani
39c2c364d9 feat: add support for db username, password, host, port 2022-04-22 21:24:39 +05:30
Lakhan Samani
961f2271c1 fix: tests 2022-04-22 19:56:55 +05:30
Lakhan Samani
aaf0831793 feat: add users queries 2022-04-22 16:45:49 +05:30
Lakhan Samani
27cb41c54c feat: add verification_request queries 2022-04-22 11:52:15 +05:30
Lakhan Samani
718b2d535f feat: add session queries 2022-04-21 18:11:15 +05:30
Lakhan Samani
ed6a1ceccc feat: add env queries 2022-04-21 17:54:33 +05:30
Lakhan Samani
fd52d6e5d3 feat: add casandradb provider 2022-04-21 12:36:22 +05:30
Lakhan Samani
325aa88368 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/casandra-db 2022-04-20 23:32:02 +05:30
Lakhan Samani
75e44ff698 fix: cors error for x-authorizer-url 2022-04-10 14:43:19 +05:30
Lakhan Samani
d5f1c5a5eb Resolves #156 2022-04-02 17:34:50 +05:30
Lakhan Samani
39947f1753 Merge pull request #155 from authorizerdev/fix/gateway-based-setup
fix: setting the cookie for proxy setup
2022-03-30 11:51:20 +05:30
Lakhan Samani
4fa9f79c3f fix: setting the cookie for proxy setup 2022-03-30 11:50:22 +05:30
Lakhan Samani
fe73c2f6f8 Update README.md 2022-03-26 07:00:01 +05:30
Lakhan Samani
4a3e3633ea fix: default access token expiry time 2022-03-25 20:29:00 +05:30
Lakhan Samani
dbbe36f6b5 Merge pull request #154 from MedvedewEM/enhancement/access_token_expiry_time
enhancement: add access_token_expiry_time env variable
2022-03-25 18:57:53 +05:30
egor.medvedev
819dd57377 Merge branch 'authorizerdev/authorizer:main' into main 2022-03-25 16:13:46 +03:00
egor.medvedev
044b025ba2 enhancement: add access_token_expiry_time env variable 2022-03-25 15:21:20 +03:00
Lakhan Samani
41b5f00b83 fix: client id make readonly 2022-03-25 00:32:21 +05:30
Lakhan Samani
3c31b7fdc7 Merge pull request #153 from anik-ghosh-au7/develop
fix: update jwt keys persisting old values
2022-03-24 23:34:20 +05:30
Anik Ghosh
7e91c6ca28 fix: update jwt keys persisting old values 2022-03-24 22:53:54 +05:30
Lakhan Samani
b1b43a41ca fix: resetting the keys 2022-03-24 22:19:30 +05:30
Lakhan Samani
f969495178 fix: generate new keys modal 2022-03-24 22:09:32 +05:30
Lakhan Samani
3c4c128931 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-03-24 21:51:00 +05:30
Lakhan Samani
003cec4f48 feat: add tests for revoke and enable access 2022-03-24 21:50:39 +05:30
Lakhan Samani
3e488155dc Merge pull request #152 from anik-ghosh-au7/feat/generate-new-keys
Feat/generate new keys
2022-03-24 21:34:28 +05:30
Anik Ghosh
4f4a3a91e1 generate-keys-modal updated 2022-03-24 21:29:43 +05:30
Anik Ghosh
a3d9783aef mutation to update jwt vars added 2022-03-24 21:08:10 +05:30
Anik Ghosh
7d77396657 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/generate-new-keys 2022-03-24 19:30:32 +05:30
Lakhan Samani
7a18fc6312 fix: add test to resolvers test 2022-03-24 19:23:43 +05:30
Lakhan Samani
90e2709eeb feat: add mutation to generate new jwt secret & keys
Resolves: #150
2022-03-24 19:21:52 +05:30
Anik Ghosh
4c4743ac24 generate-keys-modal added in dashboard 2022-03-24 18:23:22 +05:30
Anik Ghosh
b2541c8e9a feat: update user access (#151)
* feat: update user access

* revoked timestamp field updated

* updates

* updates

* updates
2022-03-24 14:13:55 +05:30
Lakhan Samani
1f3dec6ea6 feat: add validate_jwt_token query
Resolves #149
2022-03-24 13:32:30 +05:30
Lakhan Samani
a6b743465f feat: add provider template 2022-03-19 17:41:27 +05:30
Lakhan Samani
f356b4728d Merge pull request #142 from authorizerdev/feat/add-password-validation
feat: add validation for strong password
2022-03-19 11:31:31 +05:30
Lakhan Samani
ec4ef97766 feat: add validation for strong password 2022-03-17 15:35:07 +05:30
Lakhan Samani
47d67bf3cd fix: update readme.md 2022-03-17 09:33:55 +05:30
Lakhan Samani
0c54da1168 fix: getting started 2022-03-17 09:31:40 +05:30
Lakhan Samani
d6f60ce464 chore: add workflow-dispatch 2022-03-17 00:44:55 +05:30
Lakhan Samani
3aa888b14e fix: use latest authorizer-react 2022-03-17 00:28:11 +05:30
Lakhan Samani
30be32a10b feat: add sample csv 2022-03-17 00:15:47 +05:30
Lakhan Samani
69d781d6cf fix: set password re-direct uri 2022-03-17 00:04:57 +05:30
Lakhan Samani
e4d9c60971 Merge pull request #139 from anik-ghosh-au7/feat/disable-signup
feat: disable user signup
2022-03-16 23:18:43 +05:30
Anik Ghosh
96edb43b67 feat: disable user signup 2022-03-16 22:49:18 +05:30
Lakhan Samani
21fef67c7d Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-03-16 21:52:51 +05:30
Lakhan Samani
9f09823c8b feat: add redirect_uri for signup 2022-03-16 21:52:45 +05:30
Lakhan Samani
1a64149da7 Merge pull request #138 from anik-ghosh-au7/feat/invite-emails
Feat/invite emails
2022-03-16 21:45:34 +05:30
Lakhan Samani
99b846811a fix: token + redirect 2022-03-16 21:44:57 +05:30
Anik Ghosh
df7837f44d updates 2022-03-16 20:22:24 +05:30
Anik Ghosh
d709f53c47 updates 2022-03-16 20:13:18 +05:30
Anik Ghosh
a257b77501 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/invite-emails 2022-03-16 18:07:16 +05:30
Anik Ghosh
2213619ed5 updates 2022-03-16 18:06:51 +05:30
Anik Ghosh
f65ea72944 package-lock.json 2022-03-16 14:10:55 +05:30
Anik Ghosh
32f8c99a71 updates 2022-03-16 14:08:22 +05:30
Anik Ghosh
8ec52a90f1 updates 2022-03-16 14:08:08 +05:30
Anik Ghosh
2498958295 updates 2022-03-16 00:07:58 +05:30
Anik Ghosh
2913fa0603 updates 2022-03-15 23:51:54 +05:30
Anik Ghosh
e126bfddad invite email modal updated 2022-03-15 20:31:54 +05:30
Lakhan Samani
83001b859c Merge pull request #136 from authorizerdev/feat/invite-member
feat: add resolver for inviting members
2022-03-15 12:51:12 +05:30
Lakhan Samani
74a8024131 feat: add integration test for invite_member 2022-03-15 12:09:54 +05:30
Lakhan Samani
5e6ee8d9b0 fix: setup-password flow 2022-03-15 09:57:09 +05:30
Lakhan Samani
3e7150f872 fix: redirect uri 2022-03-15 09:56:50 +05:30
Lakhan Samani
9a19552f72 feat: add resolver for inviting members 2022-03-15 08:53:48 +05:30
Anik Ghosh
ab01ff249d invite email modal added 2022-03-15 01:24:14 +05:30
Lakhan Samani
1b387f7564 fix: getting version in meta api 2022-03-09 18:55:18 +05:30
Lakhan Samani
8e79ab77b2 Merge pull request #131 from authorizerdev/feat/open-id
Add open id authorization flow with PKCE
2022-03-09 17:27:16 +05:30
Lakhan Samani
2bf6b8f91d fix: remove log 2022-03-09 17:24:53 +05:30
Lakhan Samani
776c0fba8b chore: app dependencies 2022-03-09 17:21:55 +05:30
Lakhan Samani
dd64aa2e79 feat: add version info 2022-03-09 11:53:34 +05:30
Lakhan Samani
157b13baa7 fix: basic auth redirect 2022-03-09 10:10:39 +05:30
Lakhan Samani
d1e284116d fix: verification request model 2022-03-09 07:10:07 +05:30
Lakhan Samani
2f9725d8e1 fix: verification request 2022-03-09 06:41:38 +05:30
Lakhan Samani
ee7aea7bee fix: verify email 2022-03-08 22:55:45 +05:30
Lakhan Samani
5d73df0040 fix: magic link login 2022-03-08 22:41:33 +05:30
Lakhan Samani
60cd317e67 fix: add redirect url to logout 2022-03-08 21:32:42 +05:30
Lakhan Samani
f5bdc8db39 fix: refresh token store info 2022-03-08 21:13:23 +05:30
Lakhan Samani
9eca697a91 fix: refresh token param in string 2022-03-08 19:31:19 +05:30
Lakhan Samani
7136ee924d fix: rotate refresh token 2022-03-08 19:18:33 +05:30
Lakhan Samani
fd9eb7c733 fix: oauth state split 2022-03-08 19:13:45 +05:30
Lakhan Samani
917eaeb2ed feat: don't set cookie in case of offline_access 2022-03-08 18:51:46 +05:30
Lakhan Samani
3bb90acc9e feat: add revoke mutation + handler 2022-03-08 18:49:42 +05:30
Lakhan Samani
a69b8e290c feat: add ability to get access token based on refresh token 2022-03-08 14:56:46 +05:30
Lakhan Samani
674eeeea4e chore: bump authorizer-react 2022-03-08 14:20:11 +05:30
Lakhan Samani
8c2bf6ee0d fix: add token information in redirect url 2022-03-08 12:36:26 +05:30
Lakhan Samani
57bc091499 fix state management 2022-03-07 23:44:19 +05:30
Lakhan Samani
128a2a8f75 feat: add support for response mode 2022-03-07 18:49:18 +05:30
Lakhan Samani
7b09a8817c fix: env encryption 2022-03-07 16:16:54 +05:30
Lakhan Samani
1d61840c6d fix: env decryption + remove log 2022-03-07 15:35:33 +05:30
Lakhan Samani
4b25e8941c fix: env decryption 2022-03-07 15:33:39 +05:30
Lakhan Samani
136eda15bf fix: env encryption 2022-03-07 15:29:37 +05:30
Lakhan Samani
eea6349318 chore: update app version 2022-03-07 12:33:42 +05:30
Lakhan Samani
513b5d2948 fix: env client secret 2022-03-07 12:23:45 +05:30
Lakhan Samani
e61dc2f08a fix: oauth login 2022-03-07 08:31:39 +05:30
Lakhan Samani
07552bc0b1 fix: use url safe code verifier 2022-03-05 13:50:59 +05:30
Lakhan Samani
0787a3b494 feat: add token endpoint 2022-03-04 12:56:11 +05:30
Lakhan Samani
2946428ab8 feat: add userinfo + logout 2022-03-04 00:36:27 +05:30
Lakhan Samani
5c7d32ec16 fix: remove compat cookie 2022-03-03 09:21:48 +05:30
Lakhan Samani
f0f2e0b6c8 fix: auth flow 2022-03-02 17:42:31 +05:30
Lakhan Samani
5399ea8f32 feat: add session token 2022-02-28 21:26:49 +05:30
Lakhan Samani
4830a7e9ac feat: add client secret 2022-02-28 13:14:16 +05:30
Lakhan Samani
df1c56bb1c fix: tests 2022-02-28 07:55:01 +05:30
Lakhan Samani
b68d9ce661 fix: update_env resolver 2022-02-26 20:36:22 +05:30
Lakhan Samani
145091dce1 feat: add well-known jwks.json endpoint 2022-02-26 18:14:43 +05:30
Lakhan Samani
ad46210112 fix: report error on initialization 2022-02-26 10:06:26 +05:30
Lakhan Samani
4e19f73845 fix: segregate env setup 2022-02-26 09:44:55 +05:30
Lakhan Samani
332269ecf9 feat: add well-known config endpoint 2022-02-23 11:24:52 +05:30
Lakhan Samani
dfa96f09a0 feat: add required jwt claims 2022-02-22 11:06:47 +05:30
Lakhan Samani
5bf26f7385 fix: setting custom access token script env 2022-02-18 16:45:12 +05:30
Lakhan Samani
1b269dc6db fix: rename session_client -> redis_client 2022-02-18 09:21:02 +05:30
Lakhan Samani
ce9a115a14 Merge pull request #128 from agarwal-nitesh/feat/redis_cluster_client
Add redis cluster client as a session store.
2022-02-18 09:19:24 +05:30
Nitesh Agarwal
f2f4c72aa6 Add redis cluster client as a session store. 2022-02-17 20:49:54 +05:30
Samyak Bhuta
9970eb16c9 Update README
Minor changes.
2022-02-17 15:04:05 +05:30
Lakhan Samani
23e53286bd Merge pull request #124 from anik-ghosh-au7/feat/jwt-types
support for more jwt encryption types added
2022-02-14 18:56:06 +05:30
Anik Ghosh
47acff05e2 support for more jwt encryption types added 2022-02-14 16:00:35 +05:30
Lakhan Samani
5572928619 fix: remove redundunt break statement 2022-02-12 22:55:33 +05:30
Lakhan Samani
85b4cd6339 Add support for maria db 2022-02-12 22:49:53 +05:30
Lakhan Samani
f0d38ab260 Merge pull request #121 from authorizerdev/feat/add-jwt-algos
feat: add jwt algos
2022-02-12 19:37:28 +05:30
Lakhan Samani
1276af43ef Add new line char 2022-02-12 19:36:29 +05:30
Lakhan Samani
66d42fc2bc Add support for public private key from admin apis 2022-02-12 19:34:22 +05:30
Lakhan Samani
1f058f954d Add test for jwt tokens 2022-02-12 19:26:37 +05:30
Lakhan Samani
8259fb515c Add support for more JWT algo methods 2022-02-12 15:54:23 +05:30
Lakhan Samani
6c2a4c3256 Add support for yugabyte
Resolves #119
2022-02-12 13:19:31 +05:30
Lakhan Samani
51532657d7 fix: input labels 2022-02-07 21:15:08 +05:30
Lakhan Samani
fd56fac353 fix: add custom access token script to env sample 2022-02-07 09:42:49 +05:30
Lakhan Samani
260f533b41 fix: resolves #115 2022-02-05 10:09:25 +05:30
Lakhan Samani
c09ca3b810 fix: improve messaging on dashboard 2022-02-05 09:15:36 +05:30
Lakhan Samani
f07fb50eff Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-02-05 09:01:22 +05:30
Lakhan Samani
ea62a20c80 fix: remove test env 2022-02-05 09:01:12 +05:30
Lakhan Samani
2bb0ded20e fix: error log and gin mode 2022-02-05 09:00:56 +05:30
Lakhan Samani
fec43f55f2 chore: update project setup info 2022-02-02 13:18:26 +05:30
Lakhan Samani
271e901398 chore: update project setup info 2022-02-02 13:17:35 +05:30
Lakhan Samani
c6f01ce839 chore: update project setup info in contributing 2022-02-02 13:15:54 +05:30
Lakhan Samani
2dd404c02c chore: update project setup info 2022-02-02 13:15:14 +05:30
Lakhan Samani
2f29bbcee4 fix: oauth callback update user 2022-02-02 12:30:11 +05:30
Lakhan Samani
63a8c82535 chore: update package-lock.json 2022-02-02 11:41:36 +05:30
Lakhan Samani
1f81e45e79 fix: user dashboard created_at + add signup_methods & roles 2022-02-02 11:39:08 +05:30
Lakhan Samani
40dcf67de9 fix: remove logs 2022-01-31 14:53:17 +05:30
Lakhan Samani
32d8b7c038 Merge branch 'main' of https://github.com/authorizerdev/authorizer 2022-01-31 14:30:54 +05:30
Lakhan Samani
2a91f3e7d8 Merge pull request #113 from anik-ghosh-au7/fix/dashboard
Fix/dashboard
2022-01-31 14:30:32 +05:30
Lakhan Samani
36d9861517 Fix getting host 2022-01-31 14:30:13 +05:30
Anik Ghosh
d577a21a9a user dashboard pagination bug removed 2022-01-31 14:26:19 +05:30
Anik Ghosh
115607cb6b Merge branch 'main' of https://github.com/authorizerdev/authorizer into fix/dashboard 2022-01-31 13:29:38 +05:30
Anik Ghosh
adb969ec04 add delete user modal 2022-01-31 13:23:38 +05:30
Lakhan Samani
4e48320cf1 fix: bug with authorizer url 2022-01-31 11:35:24 +05:30
Anik Ghosh
cfe035e96b loader added to user dashboard 2022-01-31 11:33:35 +05:30
Lakhan Samani
34a91f3195 Add log for host url 2022-01-30 23:44:37 +05:30
Lakhan Samani
ea14cc1743 Merge pull request #111 from anik-ghosh-au7/feat/manage-users
Feat/manage users
2022-01-30 23:43:21 +05:30
Anik Ghosh
9d7f5fd9db array input type update 2022-01-30 17:43:38 +05:30
Anik Ghosh
0520056e43 sync with main branch 2022-01-30 10:56:56 +05:30
Anik Ghosh
1821e27692 Merge branch 'main' of https://github.com/authorizerdev/authorizer into feat/manage-users 2022-01-30 10:52:35 +05:30
Anik Ghosh
388530a69c edit user details modal added 2022-01-30 10:39:35 +05:30
Lakhan Samani
1e32d790b3 Remove unsed code 2022-01-30 00:08:51 +05:30
Anik Ghosh
681ffc65f1 user list added 2022-01-29 20:53:53 +05:30
Lakhan Samani
6331ec7b7a Resolves #110 2022-01-29 17:03:21 +05:30
Lakhan Samani
25c9ce03bd fix: default logo 2022-01-27 09:55:05 +05:30
448 changed files with 56057 additions and 46171 deletions

View File

@@ -9,3 +9,4 @@ build
data.db
app/node_modules
app/build
certs/

View File

@@ -1,16 +1,5 @@
ENV=production
DATABASE_URL=data.db
DATABASE_TYPE=sqlite
ADMIN_SECRET=admin
JWT_SECRET=random_string
SENDER_EMAIL=info@authorizer.dev
SMTP_USERNAME=username
SMTP_PASSWORD=password
SMTP_HOST=smtp.mailtrap.io
SMTP_PORT=2525
JWT_TYPE=HS256
ROLES=user
DEFAULT_ROLES=user
PROTECTED_ROLES=admin
JWT_ROLE_CLAIM=role
CUSTOM_ACCESS_TOKEN_SCRIPT=function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}
CUSTOM_ACCESS_TOKEN_SCRIPT="function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}"
DISABLE_PLAYGROUND=true

15
.env.test Normal file
View File

@@ -0,0 +1,15 @@
ENV=test
DATABASE_URL=test.db
DATABASE_TYPE=sqlite
CUSTOM_ACCESS_TOKEN_SCRIPT="function(user,tokenPayload){var data = tokenPayload;data.extra = {'x-extra-id': user.id};return data;}"
SMTP_HOST=smtp.mailtrap.io
SMTP_PORT=2525
SMTP_USERNAME=test
SMTP_PASSWORD=test
SENDER_EMAIL="info@authorizer.dev"
TWILIO_API_KEY=test
TWILIO_API_SECRET=test
TWILIO_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
TWILIO_SENDER=909921212112
SENDER_NAME="Authorizer"
AWS_REGION=ap-south-1

36
.gitea/workflows/main.yml Normal file
View File

@@ -0,0 +1,36 @@
name: "deploy"
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Cloning repo
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Get Repo Name
id: repo_name
run: echo "::set-output name=repo::$(echo ${GITHUB_REPOSITORY##*/})"
- name: Get Branch Name
id: branch_name
run: echo "::set-output name=branch::$(echo ${GITHUB_REF##*/})"
- name: Push branch 'discours-dev' to staging
if: steps.branch_name.outputs.branch == 'discours-dev'
uses: dokku/github-action@master
with:
branch: "main"
git_remote_url: "ssh://dokku@staging.discours.io:22/authorizer"
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Push branch 'discours' to v2.discours.io
if: steps.branch_name.outputs.branch == 'discours'
uses: dokku/github-action@master
with:
branch: "main"
git_remote_url: "ssh://dokku@v2.discours.io:22/authorizer"
ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}
git_push_flags: '--force'

View File

@@ -43,12 +43,32 @@ Please ask as many questions as you need, either directly in the issue or on [Di
### Project Setup for Authorizer core
1. Fork the [authorizer](https://github.com/authorizerdev/authorizer) repository (**Skip this step if you have access to repo**)
2. `git clone https://github.com/authorizerdev/authorizer.git`
3. `cd authorizer`
4. `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/)
5. Build the code `make clean && make`
> Note: if you don't have [`make`](https://www.ibm.com/docs/en/aix/7.2?topic=concepts-make-command), you can `cd` into `server` dir and build using the `go build` command
6. Run binary `./build/server`
2. Clone repo: `git clone https://github.com/authorizerdev/authorizer.git` or use the forked url from step 1
3. Change directory to authorizer: `cd authorizer`
4. Create Env file `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/)
5. Build Dashboard `make build-dashboard`
6. Build App `make build-app`
7. Build Server `make clean && make`
> Note: if you don't have [`make`](https://www.ibm.com/docs/en/aix/7.2?topic=concepts-make-command), you can `cd` into `server` dir and build using the `go build` command. In that case you will have to build `dashboard` & `app` manually using `npm run build` on both dirs.
8. Run binary `./build/server`
### Updating GraphQL schema
- Modify `server/graph/schema.graphqls` file
- Run `make generate-graphql` this will update the models and required methods
- If a new mutation or query is added
- Write the implementation for the new resolver in `server/resolvers/NEW_RESOLVER.GO`
- Update `server/graph/schema.resolvers.go` with the new resolver method
### Adding support for new database
- Run `make generate-db-template dbname=NEW_DB_NAME`
eg `make generate-db-template dbname=dynamodb`
This command will generate a folder in server/db/providers/ with name specified in the above command.
One will have to implement methods present in that folder.
> Note: Connection for database and schema changes are written in `server/db/providers/DB_NAME/provider.go` > `NewProvider` method is called for any given db based on the env variables present.
### Testing
@@ -85,145 +105,145 @@ For manually testing using graphql playground, you can paste following queries a
```gql
mutation Signup {
signup(
params: {
email: "lakhan@yopmail.com"
password: "test"
confirm_password: "test"
given_name: "lakhan"
}
) {
message
user {
id
family_name
given_name
email
email_verified
}
}
signup(
params: {
email: "lakhan@yopmail.com"
password: "test"
confirm_password: "test"
given_name: "lakhan"
}
) {
message
user {
id
family_name
given_name
email
email_verified
}
}
}
mutation ResendEamil {
resend_verify_email(
params: { email: "lakhan@yopmail.com", identifier: "basic_auth_signup" }
) {
message
}
resend_verify_email(
params: { email: "lakhan@yopmail.com", identifier: "basic_auth_signup" }
) {
message
}
}
query GetVerifyRequests {
_verification_requests {
id
token
expires
identifier
}
_verification_requests {
id
token
expires
identifier
}
}
mutation VerifyEmail {
verify_email(params: { token: "" }) {
access_token
expires_at
user {
id
email
given_name
email_verified
}
}
verify_email(params: { token: "" }) {
access_token
expires_at
user {
id
email
given_name
email_verified
}
}
}
mutation Login {
login(params: { email: "lakhan@yopmail.com", password: "test" }) {
access_token
expires_at
user {
id
family_name
given_name
email
}
}
login(params: { email: "lakhan@yopmail.com", password: "test" }) {
access_token
expires_at
user {
id
family_name
given_name
email
}
}
}
query GetSession {
session {
access_token
expires_at
user {
id
given_name
family_name
email
email_verified
signup_methods
created_at
updated_at
}
}
session {
access_token
expires_at
user {
id
given_name
family_name
email
email_verified
signup_methods
created_at
updated_at
}
}
}
mutation ForgotPassword {
forgot_password(params: { email: "lakhan@yopmail.com" }) {
message
}
forgot_password(params: { email: "lakhan@yopmail.com" }) {
message
}
}
mutation ResetPassword {
reset_password(
params: { token: "", password: "test", confirm_password: "test" }
) {
message
}
reset_password(
params: { token: "", password: "test", confirm_password: "test" }
) {
message
}
}
mutation UpdateProfile {
update_profile(params: { family_name: "samani" }) {
message
}
update_profile(params: { family_name: "samani" }) {
message
}
}
query GetUsers {
_users {
id
email
email_verified
given_name
family_name
picture
signup_methods
phone_number
}
_users {
id
email
email_verified
given_name
family_name
picture
signup_methods
phone_number
}
}
mutation MagicLinkLogin {
magic_link_login(params: { email: "test@yopmail.com" }) {
message
}
magic_link_login(params: { email: "test@yopmail.com" }) {
message
}
}
mutation Logout {
logout {
message
}
logout {
message
}
}
mutation UpdateUser {
_update_user(
params: {
id: "dafc9400-d603-4ade-997c-83fcd54bbd67"
roles: ["user", "admin"]
}
) {
email
roles
}
_update_user(
params: {
id: "dafc9400-d603-4ade-997c-83fcd54bbd67"
roles: ["user", "admin"]
}
) {
email
roles
}
}
mutation DeleteUser {
_delete_user(params: { email: "signup.test134523@yopmail.com" }) {
message
}
_delete_user(params: { email: "signup.test134523@yopmail.com" }) {
message
}
}
```

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
# These are supported funding model platforms
github: authorizerdev

View File

@@ -1,22 +1,45 @@
on:
workflow_dispatch:
inputs:
logLevel:
description: 'Log level'
required: true
default: 'warning'
type: choice
options:
- info
- warning
- debug
tags:
description: 'Tags'
required: false
type: boolean
release:
types: [created]
jobs:
releases:
name: Release Authorizer Binary
name: Release Authorizer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v2
with:
node-version: '16'
- # Add support for more platforms with QEMU (optional)
# https://github.com/docker/setup-qemu-action
name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
platforms: linux/amd64,linux/arm64
- uses: actions/setup-go@v2
with:
go-version: '^1.17.3'
go-version: '^1.19.1'
- name: Install dependencies
run: |
sudo apt-get install build-essential wget zip gcc-mingw-w64 && \
sudo apt-get install build-essential wget zip libc6-dev-arm64-cross && \
echo "/usr/bin/x86_64-w64-mingw32-gcc" >> GITHUB_PATH && \
wget --no-check-certificate --progress=dot:mega https://github.com/wangyoucao577/assets-uploader/releases/download/v0.3.0/github-assets-uploader-v0.3.0-linux-amd64.tar.gz -O github-assets-uploader.tar.gz && \
tar -zxf github-assets-uploader.tar.gz && \
@@ -29,25 +52,28 @@ jobs:
run: whereis go
- name: Print Go Version
run: go version
- name: Install gox
run: go install github.com/mitchellh/gox@latest
- name: Set VERSION env
run: echo VERSION=$(basename ${GITHUB_REF}) >> ${GITHUB_ENV}
- name: Copy .env file
run: mv .env.sample .env
- name: Package files for windows
- name: Build package
run: |
make clean && \
CGO_ENABLED=1 GOOS=windows CC=/usr/bin/x86_64-w64-mingw32-gcc make && \
mv build/server build/server.exe && \
zip -vr authorizer-${VERSION}-windows-amd64.zip .env app/build build templates dashboard/build
- name: Package files for linux
run: |
make clean && \
CGO_ENABLED=1 make && \
tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz .env app/build build templates dashboard/build
make build && \
mkdir -p authorizer-${VERSION}-darwin-arm64/build authorizer-${VERSION}-darwin-arm64/app authorizer-${VERSION}-darwin-arm64/dashboard && cp build/darwin/arm64/server authorizer-${VERSION}-darwin-arm64/build/ && cp .env authorizer-${VERSION}-darwin-arm64/.env && cp -rf app/build authorizer-${VERSION}-darwin-arm64/app/build && cp -rf templates authorizer-${VERSION}-darwin-arm64/ && cp -rf dashboard/build authorizer-${VERSION}-darwin-arm64/dashboard/build && tar cvfz authorizer-${VERSION}-darwin-arm64.tar.gz authorizer-${VERSION}-darwin-arm64 && \
mkdir -p authorizer-${VERSION}-darwin-amd64/build authorizer-${VERSION}-darwin-amd64/app authorizer-${VERSION}-darwin-amd64/dashboard && cp build/darwin/amd64/server authorizer-${VERSION}-darwin-amd64/build/ && cp .env authorizer-${VERSION}-darwin-amd64/.env && cp -rf app/build authorizer-${VERSION}-darwin-amd64/app/build && cp -rf templates authorizer-${VERSION}-darwin-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-darwin-amd64/dashboard/build && tar cvfz authorizer-${VERSION}-darwin-amd64.tar.gz authorizer-${VERSION}-darwin-amd64 && \
mkdir -p authorizer-${VERSION}-linux-amd64/build authorizer-${VERSION}-linux-amd64/app authorizer-${VERSION}-linux-amd64/dashboard && cp build/linux/amd64/server authorizer-${VERSION}-linux-amd64/build/ && cp .env authorizer-${VERSION}-linux-amd64/.env && cp -rf app/build authorizer-${VERSION}-linux-amd64/app/build && cp -rf templates authorizer-${VERSION}-linux-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-linux-amd64/dashboard/build && tar cvfz authorizer-${VERSION}-linux-amd64.tar.gz authorizer-${VERSION}-linux-amd64 && \
mkdir -p authorizer-${VERSION}-linux-arm64/build authorizer-${VERSION}-linux-arm64/app authorizer-${VERSION}-linux-arm64/dashboard && cp build/linux/arm64/server authorizer-${VERSION}-linux-arm64/build/ && cp .env authorizer-${VERSION}-linux-arm64/.env && cp -rf app/build authorizer-${VERSION}-linux-arm64/app/build && cp -rf templates authorizer-${VERSION}-linux-arm64/ && cp -rf dashboard/build authorizer-${VERSION}-linux-arm64/dashboard/build && tar cvfz authorizer-${VERSION}-linux-arm64.tar.gz authorizer-${VERSION}-linux-arm64 && \
mkdir -p authorizer-${VERSION}-windows-amd64/build authorizer-${VERSION}-windows-amd64/app authorizer-${VERSION}-windows-amd64/dashboard && cp build/windows/amd64/server.exe authorizer-${VERSION}-windows-amd64/build/ && cp .env authorizer-${VERSION}-windows-amd64/.env && cp -rf app/build authorizer-${VERSION}-windows-amd64/app/build && cp -rf templates authorizer-${VERSION}-windows-amd64/ && cp -rf dashboard/build authorizer-${VERSION}-windows-amd64/dashboard/build && zip -vr authorizer-${VERSION}-windows-amd64.zip authorizer-${VERSION}-windows-amd64
- name: Upload assets
run: |
github-assets-uploader -f authorizer-${VERSION}-windows-amd64.zip -mediatype application/zip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION} && \
github-assets-uploader -f authorizer-${VERSION}-darwin-arm64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
github-assets-uploader -f authorizer-${VERSION}-darwin-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
github-assets-uploader -f authorizer-${VERSION}-linux-amd64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
github-assets-uploader -f authorizer-${VERSION}-linux-arm64.tar.gz -mediatype application/gzip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
github-assets-uploader -f authorizer-${VERSION}-windows-amd64.zip -mediatype application/zip -repo authorizerdev/authorizer -token ${{secrets.RELEASE_TOKEN}} -tag ${VERSION}
- name: Log in to Docker Hub
uses: docker/login-action@v1
with:
@@ -59,6 +85,11 @@ jobs:
uses: docker/metadata-action@v3
with:
images: lakhansamani/authorizer
tags: |
type=schedule
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
- name: Build and push Docker image
uses: docker/build-push-action@v2
@@ -67,5 +98,6 @@ jobs:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64
build-args: |
VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}

11
.gitignore vendored
View File

@@ -8,6 +8,15 @@ dashboard/build
build
.env
data.db
test.db
.DS_Store
.env.local
*.tar.gz
*.tar.gz
.vscode/
.yalc
yalc.lock
certs/
*-shm
*-wal
.idea
*.iml

View File

@@ -1,4 +1,4 @@
FROM golang:1.17-alpine as go-builder
FROM golang:1.21.3-alpine3.18 AS go-builder
WORKDIR /authorizer
COPY server server
COPY Makefile .
@@ -11,7 +11,7 @@ RUN apk add build-base &&\
make clean && make && \
chmod 777 build/server
FROM node:17-alpine3.12 as node-builder
FROM node:20-alpine3.18 AS node-builder
WORKDIR /authorizer
COPY app app
COPY dashboard dashboard
@@ -20,12 +20,16 @@ RUN apk add build-base &&\
make build-app && \
make build-dashboard
FROM alpine:latest
WORKDIR /root/
FROM alpine:3.18
RUN adduser -D -h /authorizer -u 1000 -k /dev/null authorizer
WORKDIR /authorizer
RUN mkdir app dashboard
COPY --from=node-builder /authorizer/app/build app/build
COPY --from=node-builder /authorizer/dashboard/build dashboard/build
COPY --from=go-builder /authorizer/build build
COPY --from=node-builder --chown=nobody:nobody /authorizer/app/build app/build
COPY --from=node-builder --chown=nobody:nobody /authorizer/app/favicon_io app/favicon_io
COPY --from=node-builder --chown=nobody:nobody /authorizer/dashboard/build dashboard/build
COPY --from=node-builder --chown=nobody:nobody /authorizer/dashboard/favicon_io dashboard/favicon_io
COPY --from=go-builder --chown=nobody:nobody /authorizer/build build
COPY templates templates
EXPOSE 8080
USER authorizer
CMD [ "./build/server" ]

View File

@@ -3,6 +3,12 @@ VERSION := $(or $(VERSION),$(DEFAULT_VERSION))
cmd:
cd server && go build -ldflags "-w -X main.VERSION=$(VERSION)" -o '../build/server'
build:
cd server && gox \
-osarch="linux/amd64 linux/arm64 darwin/arm64 darwin/amd64 windows/amd64" \
-ldflags "-w -X main.VERSION=$(VERSION)" \
-output="../build/{{.OS}}/{{.Arch}}/server" \
./...
build-app:
cd app && npm i && npm run build
build-dashboard:
@@ -10,4 +16,44 @@ build-dashboard:
clean:
rm -rf build
test:
cd server && go clean --testcache && go test -v ./test
rm -rf server/test/test.db server/test/test.db-shm server/test/test.db-wal && rm -rf test.db test.db-shm test.db-wal && cd server && go clean --testcache && TEST_DBS="sqlite" go test -p 1 -v ./test
test-mongodb:
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
cd server && go clean --testcache && TEST_DBS="mongodb" go test -p 1 -v ./test
docker rm -vf authorizer_mongodb_db
test-scylladb:
docker run -d --name authorizer_scylla_db -p 9042:9042 scylladb/scylla
cd server && go clean --testcache && TEST_DBS="scylladb" go test -p 1 -v ./test
docker rm -vf authorizer_scylla_db
test-arangodb:
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.10.3
cd server && go clean --testcache && TEST_DBS="arangodb" go test -p 1 -v ./test
docker rm -vf authorizer_arangodb
test-dynamodb:
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest
cd server && go clean --testcache && TEST_DBS="dynamodb" go test -p 1 -v ./test
docker rm -vf dynamodb-local-test
test-couchbase:
docker run -d --name couchbase-local-test -p 8091-8097:8091-8097 -p 11210:11210 -p 11207:11207 -p 18091-18095:18091-18095 -p 18096:18096 -p 18097:18097 couchbase:latest
sh scripts/couchbase-test.sh
cd server && go clean --testcache && TEST_DBS="couchbase" go test -p 1 -v ./test
docker rm -vf couchbase-local-test
test-all-db:
rm -rf server/test/test.db server/test/test.db-shm server/test/test.db-wal && rm -rf test.db test.db-shm test.db-wal
docker run -d --name authorizer_scylla_db -p 9042:9042 scylladb/scylla
docker run -d --name authorizer_mongodb_db -p 27017:27017 mongo:4.4.15
docker run -d --name authorizer_arangodb -p 8529:8529 -e ARANGO_NO_AUTH=1 arangodb/arangodb:3.10.3
docker run -d --name dynamodb-local-test -p 8000:8000 amazon/dynamodb-local:latest
docker run -d --name couchbase-local-test -p 8091-8097:8091-8097 -p 11210:11210 -p 11207:11207 -p 18091-18095:18091-18095 -p 18096:18096 -p 18097:18097 couchbase:latest
sh scripts/couchbase-test.sh
cd server && go clean --testcache && TEST_DBS="sqlite,mongodb,arangodb,scylladb,dynamodb,couchbase" go test -p 1 -v ./test
docker rm -vf authorizer_scylla_db
docker rm -vf authorizer_mongodb_db
docker rm -vf authorizer_arangodb
docker rm -vf dynamodb-local-test
docker rm -vf couchbase-local-test
generate-graphql:
cd server && go run github.com/99designs/gqlgen generate && go mod tidy
generate-db-template:
cp -rf server/db/providers/provider_template server/db/providers/${dbname}
find server/db/providers/${dbname} -type f -exec sed -i -e 's/provider_template/${dbname}/g' {} \;

123
README.md
View File

@@ -1,50 +1,48 @@
<p align="center">
<a href="https://authorizer.dev">
<img alt="Logo" src="https://github.com/authorizerdev/authorizer/blob/main/assets/logo.png" width="60" />
<img alt="Logo" src="https://authorizer.dev/images/logo.png" width="60" />
</a>
</p>
<h1 align="center">
Authorizer
</h1>
**Authorizer** is an open-source authentication and authorization solution for your applications. Bring your database and have complete control over the user information. You can self-host authorizer instances and connect to any database (Currently supports [Postgres](https://www.postgresql.org/), [MySQL](https://www.mysql.com/), [SQLite](https://www.sqlite.org/index.html), [SQLServer](https://www.microsoft.com/en-us/sql-server/), [MongoDB](https://mongodb.com/),[ArangoDB](https://www.arangodb.com/)).
**Authorizer** is an open-source authentication and authorization solution for your applications. Bring your database and have complete control over the user information. You can self-host authorizer instances and connect to any database (Currently supports 11+ databases including [Postgres](https://www.postgresql.org/), [MySQL](https://www.mysql.com/), [SQLite](https://www.sqlite.org/index.html), [SQLServer](https://www.microsoft.com/en-us/sql-server/), [YugaByte](https://www.yugabyte.com/), [MariaDB](https://mariadb.org/), [PlanetScale](https://planetscale.com/), [CassandraDB](https://cassandra.apache.org/_/index.html), [ScyllaDB](https://www.scylladb.com/), [MongoDB](https://mongodb.com/), [ArangoDB](https://www.arangodb.com/)).
## Table of contents
For more information check:
- [Introduction](#introduction)
- [Getting Started](#getting-started)
- [Contributing](https://github.com/authorizerdev/authorizer/blob/main/.github/CONTRIBUTING.md)
- [Docs](http://docs.authorizer.dev/)
- [Join Community](https://discord.gg/Zv2D5h6kkK)
- [Discord Community](https://discord.gg/Zv2D5h6kkK)
- [Contributing Guide](https://github.com/authorizerdev/authorizer/blob/main/.github/CONTRIBUTING.md)
# Introduction
<img src="https://github.com/authorizerdev/authorizer/blob/main/assets/authorizer-architecture.png" style="height:20em"/>
<img src="https://docs.authorizer.dev/images/authorizer-arch.png" style="height:20em"/>
#### We offer the following functionality
- ✅ Sign-in / Sign-up with email ID and password
- ✅ Secure session management
- ✅ Email verification
- ✅ OAuth2 and OpenID compatible APIs
- ✅ APIs to update profile securely
- ✅ Forgot password flow using email
- ✅ Social logins (Google, Github, Facebook, more coming soon)
- ✅ Social logins (Google, Github, Facebook, LinkedIn, Apple more coming soon)
- ✅ Role-based access management
- ✅ Password-less login with email and magic link
- ✅ Password-less login with magic link login
- ✅ Multi factor authentication
- ✅ Email templating
- ✅ Webhooks
## Roadmap
- Support more JWT encryption algorithms (Currently supporting HS256)
- 2 Factor authentication
- Back office (Admin dashboard to manage user)
- Support more database
- VueJS SDK
- Svelte SDK
- [VueJS SDK](https://github.com/authorizerdev/authorizer-vue)
- [Svelte SDK](https://github.com/authorizerdev/authorizer-svelte)
- [Golang SDK](https://github.com/authorizerdev/authorizer-go)
- React Native SDK
- Flutter SDK
- Android Native SDK
- iOS native SDK
- Golang SDK
- Python SDK
- PHP SDK
- WordPress plugin
@@ -59,33 +57,44 @@
# Getting Started
## Trying out Authorizer
## Step 1: Get Authorizer Instance
### Deploy Production Ready Instance
Deploy production ready Authorizer instance using one click deployment options available below
| **Infra provider** | **One-click link** | **Additional information** |
| :----------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------: |
| Railway.app | <a href="https://railway.app/new/template/nwXp1C?referralCode=FEF4uT"><img src="https://railway.app/button.svg" style="height: 44px" alt="Deploy on Railway"></a> | [docs](https://docs.authorizer.dev/deployment/railway) |
| Heroku | <a href="https://heroku.com/deploy?template=https://github.com/authorizerdev/authorizer-heroku"><img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy to Heroku" style="height: 44px;"></a> | [docs](https://docs.authorizer.dev/deployment/heroku) |
| Render | [![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/authorizerdev/authorizer-render) | [docs](https://docs.authorizer.dev/deployment/render) |
| Koyeb | <a target="_blank" href="https://app.koyeb.com/deploy?name=authorizer&type=docker&image=docker.io/lakhansamani/authorizer&env[PORT]=8000&env[DATABASE_TYPE]=postgres&env[DATABASE_URL]=CHANGE_ME&ports=8000;http;/"><img alt="Deploy to Koyeb" src="https://www.koyeb.com/static/images/deploy/button.svg" /></a> | [docs](https://docs.authorizer.dev/deployment/koyeb) |
| RepoCloud | <a href="https://repocloud.io/details/?app_id=174"><img src="https://d16t0pc4846x52.cloudfront.net/deploy.png" alt="Deploy on RepoCloud"></a> | [docs](https://repocloud.io/details/?app_id=174) |
### Deploy Authorizer Using Source Code
This guide helps you practice using Authorizer to evaluate it before you use it in a production environment. It includes instructions for installing the Authorizer server in local or standalone mode.
- [Install using source code](#install-using-source-code)
- [Install using binaries](#install-using-binaries)
- [Install instance on heroku](#install-instance-on-Heroku)
- [Install instance on railway.app](#install-instance-on-railway)
#### Install using source code
## Install using source code
### Prerequisites
#### Prerequisites
- OS: Linux or macOS or windows
- Go: (Golang)(https://golang.org/dl/) >= v1.15
### Project Setup
#### Project Setup
1. Fork the [authorizer](https://github.com/authorizerdev/authorizer) repository (**Skip this step if you have access to repo**)
2. `git clone https://github.com/authorizerdev/authorizer.git`
3. `cd authorizer`
4. `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/)
5. Build the code `make clean && make`
> Note: if you don't have [`make`](https://www.ibm.com/docs/en/aix/7.2?topic=concepts-make-command), you can `cd` into `server` dir and build using the `go build` command
6. Run binary `./build/server`
2. Clone repo: `git clone https://github.com/authorizerdev/authorizer.git` or use the forked url from step 1
3. Change directory to authorizer: `cd authorizer`
4. Create Env file `cp .env.sample .env`. Check all the supported env [here](https://docs.authorizer.dev/core/env/)
5. Build Dashboard `make build-dashboard`
6. Build App `make build-app`
7. Build Server `make clean && make`
> Note: if you don't have [`make`](https://www.ibm.com/docs/en/aix/7.2?topic=concepts-make-command), you can `cd` into `server` dir and build using the `go build` command. In that case you will have to build `dashboard` & `app` manually using `npm run build` on both dirs.
8. Run binary `./build/server`
## Install using binaries
### Deploy Authorizer using binaries
Deploy / Try Authorizer using binaries. With each [Authorizer Release](https://github.com/authorizerdev/authorizer/releases)
binaries are baked with required deployment files and bundled. You can download a specific version of it for the following operating systems:
@@ -93,7 +102,7 @@ binaries are baked with required deployment files and bundled. You can download
- Mac OSX
- Linux
### Step 1: Download and unzip bundle
#### Download and unzip bundle
- Download the Bundle for the specific OS from the [release page](https://github.com/authorizerdev/authorizer/releases)
@@ -113,11 +122,7 @@ binaries are baked with required deployment files and bundled. You can download
cd authorizer
```
### Step 2: Configure environment variables
Required environment variables are pre-configured in `.env` file. But based on the production requirements, please configure more environment variables. You can refer to [environment variables docs](/core/env) for more information.
### Step 3: Start Authorizer
#### Step 3: Start Authorizer
- Run following command to start authorizer
@@ -129,25 +134,20 @@ Required environment variables are pre-configured in `.env` file. But based on t
> Note: For mac users, you might have to give binary the permission to execute. Here is the command you can use to grant permission `xattr -d com.apple.quarantine build/server`
## Install instance on Heroku
## Step 2: Setup Instance
Deploy Authorizer using [heroku](https://github.com/authorizerdev/authorizer-heroku) and quickly play with it in 30seconds
<br/><br/>
[![Deploy to Heroku](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/authorizerdev/authorizer-heroku)
- Open authorizer instance endpoint in browser
- Sign up as an admin with a secure password
- Configure environment variables from authorizer dashboard. Check env [docs](/core/env) for more information
# Install instance on railway
Deploy production ready Authorizer instance using [railway.app](https://github.com/authorizerdev/authorizer-railway) with postgres and redis for free and build with it in 30seconds
<br/>
[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template?template=https%3A%2F%2Fgithub.com%2Fauthorizerdev%2Fauthorizer-railway&plugins=postgresql%2Credis&envs=ENV%2CDATABASE_TYPE%2CADMIN_SECRET%2CCOOKIE_NAME%2CJWT_ROLE_CLAIM%2CJWT_TYPE%2CJWT_SECRET%2CFACEBOOK_CLIENT_ID%2CFACEBOOK_CLIENT_SECRET%2CGOOGLE_CLIENT_ID%2CGOOGLE_CLIENT_SECRET%2CGITHUB_CLIENT_ID%2CGITHUB_CLIENT_SECRET%2CALLOWED_ORIGINS%2CROLES%2CPROTECTED_ROLES%2CDEFAULT_ROLES&optionalEnvs=FACEBOOK_CLIENT_ID%2CFACEBOOK_CLIENT_SECRET%2CGOOGLE_CLIENT_ID%2CGOOGLE_CLIENT_SECRET%2CGITHUB_CLIENT_ID%2CGITHUB_CLIENT_SECRET%2CALLOWED_ORIGINS%2CROLES%2CPROTECTED_ROLES%2CDEFAULT_ROLES&ENVDesc=Deployment+environment&DATABASE_TYPEDesc=With+railway+we+are+deploying+postgres+db&ADMIN_SECRETDesc=Secret+to+access+the+admin+apis&COOKIE_NAMEDesc=Name+of+http+only+cookie+that+will+be+used+as+session&FACEBOOK_CLIENT_IDDesc=Facebook+client+ID+for+facebook+login&FACEBOOK_CLIENT_SECRETDesc=Facebook+client+secret+for+facebook+login&GOOGLE_CLIENT_IDDesc=Google+client+ID+for+google+login&GOOGLE_CLIENT_SECRETDesc=Google+client+secret+for+google+login&GITHUB_CLIENT_IDDesc=Github+client+ID+for+github+login&GITHUB_CLIENT_SECRETDesc=Github+client+secret+for+github+login&ALLOWED_ORIGINSDesc=Whitelist+the+URL+for+which+this+instance+of+authorizer+is+allowed&ROLESDesc=Comma+separated+list+of+roles+that+platform+supports.+Default+role+is+user&PROTECTED_ROLESDesc=Comma+separated+list+of+protected+roles+for+which+sign-up+is+disabled&DEFAULT_ROLESDesc=Default+role+that+should+be+assigned+to+user.+It+should+be+one+from+the+list+of+%60ROLES%60+env.+Default+role+is+user&JWT_ROLE_CLAIMDesc=JWT+key+to+be+used+to+validate+the+role+field.&JWT_TYPEDesc=JWT+encryption+type&JWT_SECRETDesc=Random+string+that+will+be+used+for+encrypting+the+JWT+token&ENVDefault=PRODUCTION&DATABASE_TYPEDefault=postgres&COOKIE_NAMEDefault=authorizer&JWT_TYPEDefault=HS256&JWT_ROLE_CLAIMDefault=role)
> Note: `DATABASE_URL`, `DATABASE_TYPE` and `DATABASE_NAME` are only configurable via platform envs
### Things to consider
- For social logins, you will need respective social platform key and secret
- For having verified users, you will need an SMTP server with an email address and password using which system can send emails. The system will send a verification link to an email address. Once an email is verified then, only able to access it.
> Note: One can always disable the email verification to allow open sign up, which is not recommended for production as anyone can use anyone's email address 😅
- For persisting user sessions, you will need Redis URL (not in case of railway.app). If you do not configure a Redis server, sessions will be persisted until the instance is up or not restarted. For better response time on authorization requests/middleware, we recommend deploying Redis on the same infra/network as your authorizer server.
- For persisting user sessions, you will need Redis URL (not in case of railway app). If you do not configure a Redis server, sessions will be persisted until the instance is up or not restarted. For better response time on authorization requests/middleware, we recommend deploying Redis on the same infra/network as your authorizer server.
## Testing
@@ -166,8 +166,9 @@ This example demonstrates how you can use [`@authorizerdev/authorizer-js`](/auth
<script type="text/javascript">
const authorizerRef = new authorizerdev.Authorizer({
authorizerURL: `AUTHORIZER_URL`,
authorizerURL: `YOUR_AUTHORIZER_INSTANCE_URL`,
redirectURL: window.location.origin,
clientID: 'YOUR_CLIENT_ID', // obtain your client id from authorizer dashboard
});
// use the button selector as per your application
@@ -178,15 +179,19 @@ This example demonstrates how you can use [`@authorizerdev/authorizer-js`](/auth
});
async function onLoad() {
const res = await authorizerRef.browserLogin();
if (res && res.user) {
const res = await authorizerRef.authorize({
response_type: 'code',
use_refresh_token: false,
});
if (res && res.access_token) {
// you can use user information here, eg:
/**
const userSection = document.getElementById('user');
const logoutSection = document.getElementById('logout-section');
logoutSection.classList.toggle('hide');
userSection.innerHTML = `Welcome, ${res.user.email}`;
*/
const user = await authorizerRef.getProfile({
Authorization: `Bearer ${res.access_token}`,
});
const userSection = document.getElementById('user');
const logoutSection = document.getElementById('logout-section');
logoutSection.classList.toggle('hide');
userSection.innerHTML = `Welcome, ${user.email}`;
}
}
onLoad();

6
app/.prettierrc.json Normal file
View File

@@ -0,0 +1,6 @@
{
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all",
"useTabs": true
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
app/favicon_io/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

767
app/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,13 +5,14 @@
"main": "index.js",
"scripts": {
"build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js",
"start": "NODE_ENV=development node ./esbuild.config.js"
"start": "NODE_ENV=development node ./esbuild.config.js",
"format": "prettier --write 'src/**/*.(ts|tsx|js|jsx)'"
},
"keywords": [],
"author": "Lakhan Samani",
"license": "ISC",
"dependencies": {
"@authorizerdev/authorizer-react": "latest",
"@authorizerdev/authorizer-react": "^1.3.2",
"@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9",
"esbuild": "^0.12.17",
@@ -19,9 +20,12 @@
"react-dom": "^17.0.2",
"react-is": "^17.0.2",
"react-router-dom": "^5.2.0",
"styled-components": "^5.3.0",
"typescript": "^4.3.5"
},
"devDependencies": {
"@types/react-router-dom": "^5.1.8"
"@types/react-router-dom": "^5.1.8",
"@types/styled-components": "^5.1.11",
"prettier": "2.7.1"
}
}

626
app/pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,626 @@
lockfileVersion: 5.4
specifiers:
'@authorizerdev/authorizer-react': ^1.1.9
'@types/react': ^17.0.15
'@types/react-dom': ^17.0.9
'@types/react-router-dom': ^5.1.8
'@types/styled-components': ^5.1.11
esbuild: ^0.12.17
prettier: 2.7.1
react: ^17.0.2
react-dom: ^17.0.2
react-is: ^17.0.2
react-router-dom: ^5.2.0
styled-components: ^5.3.0
typescript: ^4.3.5
dependencies:
'@authorizerdev/authorizer-react': 1.1.9_react@17.0.2
'@types/react': 17.0.53
'@types/react-dom': 17.0.19
esbuild: 0.12.29
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
react-is: 17.0.2
react-router-dom: 5.3.4_react@17.0.2
styled-components: 5.3.9_fane7jikarojcev26y27hpbhu4
typescript: 4.9.5
devDependencies:
'@types/react-router-dom': 5.3.3
'@types/styled-components': 5.1.26
prettier: 2.7.1
packages:
/@authorizerdev/authorizer-js/1.2.1:
resolution: {integrity: sha512-/nFARvsHyZUsGFKrcYi8hgpnbThYR/NMJ2BJdQpWy/x7QsBnfLeCChBYWncbYHSIjFCa5PPKKfvhXM56HqVqsw==}
engines: {node: '>=10'}
dependencies:
cross-fetch: 3.1.5
transitivePeerDependencies:
- encoding
dev: false
/@authorizerdev/authorizer-react/1.1.9_react@17.0.2:
resolution: {integrity: sha512-BlB4ixEm9nf+yjZ9OqIWbx5fMTmzeByEsNDAd5iYkt6HB+3Sk53DGiO5h6SgJznzPyqAwl8yg6y/QgbZreDTFA==}
engines: {node: '>=10'}
peerDependencies:
react: '>=16'
dependencies:
'@authorizerdev/authorizer-js': 1.2.1
react: 17.0.2
transitivePeerDependencies:
- encoding
dev: false
/@babel/code-frame/7.18.6:
resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/highlight': 7.18.6
dev: false
/@babel/generator/7.21.3:
resolution: {integrity: sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.3
'@jridgewell/gen-mapping': 0.3.2
'@jridgewell/trace-mapping': 0.3.17
jsesc: 2.5.2
dev: false
/@babel/helper-annotate-as-pure/7.18.6:
resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.3
dev: false
/@babel/helper-environment-visitor/7.18.9:
resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==}
engines: {node: '>=6.9.0'}
dev: false
/@babel/helper-function-name/7.21.0:
resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.20.7
'@babel/types': 7.21.3
dev: false
/@babel/helper-hoist-variables/7.18.6:
resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.3
dev: false
/@babel/helper-module-imports/7.18.6:
resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.3
dev: false
/@babel/helper-split-export-declaration/7.18.6:
resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.21.3
dev: false
/@babel/helper-string-parser/7.19.4:
resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==}
engines: {node: '>=6.9.0'}
dev: false
/@babel/helper-validator-identifier/7.19.1:
resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
engines: {node: '>=6.9.0'}
dev: false
/@babel/highlight/7.18.6:
resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-validator-identifier': 7.19.1
chalk: 2.4.2
js-tokens: 4.0.0
dev: false
/@babel/parser/7.21.3:
resolution: {integrity: sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
'@babel/types': 7.21.3
dev: false
/@babel/runtime/7.21.0:
resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.13.11
dev: false
/@babel/template/7.20.7:
resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/parser': 7.21.3
'@babel/types': 7.21.3
dev: false
/@babel/traverse/7.21.3_supports-color@5.5.0:
resolution: {integrity: sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/generator': 7.21.3
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-function-name': 7.21.0
'@babel/helper-hoist-variables': 7.18.6
'@babel/helper-split-export-declaration': 7.18.6
'@babel/parser': 7.21.3
'@babel/types': 7.21.3
debug: 4.3.4_supports-color@5.5.0
globals: 11.12.0
transitivePeerDependencies:
- supports-color
dev: false
/@babel/types/7.21.3:
resolution: {integrity: sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-string-parser': 7.19.4
'@babel/helper-validator-identifier': 7.19.1
to-fast-properties: 2.0.0
dev: false
/@emotion/is-prop-valid/1.2.0:
resolution: {integrity: sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==}
dependencies:
'@emotion/memoize': 0.8.0
dev: false
/@emotion/memoize/0.8.0:
resolution: {integrity: sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==}
dev: false
/@emotion/stylis/0.8.5:
resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==}
dev: false
/@emotion/unitless/0.7.5:
resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==}
dev: false
/@jridgewell/gen-mapping/0.3.2:
resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
engines: {node: '>=6.0.0'}
dependencies:
'@jridgewell/set-array': 1.1.2
'@jridgewell/sourcemap-codec': 1.4.14
'@jridgewell/trace-mapping': 0.3.17
dev: false
/@jridgewell/resolve-uri/3.1.0:
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
engines: {node: '>=6.0.0'}
dev: false
/@jridgewell/set-array/1.1.2:
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
engines: {node: '>=6.0.0'}
dev: false
/@jridgewell/sourcemap-codec/1.4.14:
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
dev: false
/@jridgewell/trace-mapping/0.3.17:
resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
dependencies:
'@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.14
dev: false
/@types/history/4.7.11:
resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==}
dev: true
/@types/hoist-non-react-statics/3.3.1:
resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==}
dependencies:
'@types/react': 17.0.53
hoist-non-react-statics: 3.3.2
dev: true
/@types/prop-types/15.7.5:
resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
/@types/react-dom/17.0.19:
resolution: {integrity: sha512-PiYG40pnQRdPHnlf7tZnp0aQ6q9tspYr72vD61saO6zFCybLfMqwUCN0va1/P+86DXn18ZWeW30Bk7xlC5eEAQ==}
dependencies:
'@types/react': 17.0.53
dev: false
/@types/react-router-dom/5.3.3:
resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==}
dependencies:
'@types/history': 4.7.11
'@types/react': 17.0.53
'@types/react-router': 5.1.20
dev: true
/@types/react-router/5.1.20:
resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==}
dependencies:
'@types/history': 4.7.11
'@types/react': 17.0.53
dev: true
/@types/react/17.0.53:
resolution: {integrity: sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw==}
dependencies:
'@types/prop-types': 15.7.5
'@types/scheduler': 0.16.3
csstype: 3.1.1
/@types/scheduler/0.16.3:
resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==}
/@types/styled-components/5.1.26:
resolution: {integrity: sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw==}
dependencies:
'@types/hoist-non-react-statics': 3.3.1
'@types/react': 17.0.53
csstype: 3.1.1
dev: true
/ansi-styles/3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
dependencies:
color-convert: 1.9.3
dev: false
/babel-plugin-styled-components/2.0.7_styled-components@5.3.9:
resolution: {integrity: sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==}
peerDependencies:
styled-components: '>= 2'
dependencies:
'@babel/helper-annotate-as-pure': 7.18.6
'@babel/helper-module-imports': 7.18.6
babel-plugin-syntax-jsx: 6.18.0
lodash: 4.17.21
picomatch: 2.3.1
styled-components: 5.3.9_fane7jikarojcev26y27hpbhu4
dev: false
/babel-plugin-syntax-jsx/6.18.0:
resolution: {integrity: sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==}
dev: false
/camelize/1.0.1:
resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}
dev: false
/chalk/2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
dependencies:
ansi-styles: 3.2.1
escape-string-regexp: 1.0.5
supports-color: 5.5.0
dev: false
/color-convert/1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies:
color-name: 1.1.3
dev: false
/color-name/1.1.3:
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
dev: false
/cross-fetch/3.1.5:
resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==}
dependencies:
node-fetch: 2.6.7
transitivePeerDependencies:
- encoding
dev: false
/css-color-keywords/1.0.0:
resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==}
engines: {node: '>=4'}
dev: false
/css-to-react-native/3.2.0:
resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==}
dependencies:
camelize: 1.0.1
css-color-keywords: 1.0.0
postcss-value-parser: 4.2.0
dev: false
/csstype/3.1.1:
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==}
/debug/4.3.4_supports-color@5.5.0:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.2
supports-color: 5.5.0
dev: false
/esbuild/0.12.29:
resolution: {integrity: sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==}
hasBin: true
requiresBuild: true
dev: false
/escape-string-regexp/1.0.5:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
dev: false
/globals/11.12.0:
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
engines: {node: '>=4'}
dev: false
/has-flag/3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
dev: false
/history/4.10.1:
resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==}
dependencies:
'@babel/runtime': 7.21.0
loose-envify: 1.4.0
resolve-pathname: 3.0.0
tiny-invariant: 1.3.1
tiny-warning: 1.0.3
value-equal: 1.0.1
dev: false
/hoist-non-react-statics/3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
dependencies:
react-is: 16.13.1
/isarray/0.0.1:
resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==}
dev: false
/js-tokens/4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
dev: false
/jsesc/2.5.2:
resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
engines: {node: '>=4'}
hasBin: true
dev: false
/lodash/4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
dev: false
/loose-envify/1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
dependencies:
js-tokens: 4.0.0
dev: false
/ms/2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: false
/node-fetch/2.6.7:
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
dependencies:
whatwg-url: 5.0.0
dev: false
/object-assign/4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
dev: false
/path-to-regexp/1.8.0:
resolution: {integrity: sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==}
dependencies:
isarray: 0.0.1
dev: false
/picomatch/2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
dev: false
/postcss-value-parser/4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
dev: false
/prettier/2.7.1:
resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==}
engines: {node: '>=10.13.0'}
hasBin: true
dev: true
/prop-types/15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
react-is: 16.13.1
dev: false
/react-dom/17.0.2_react@17.0.2:
resolution: {integrity: sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==}
peerDependencies:
react: 17.0.2
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
react: 17.0.2
scheduler: 0.20.2
dev: false
/react-is/16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
/react-is/17.0.2:
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
dev: false
/react-router-dom/5.3.4_react@17.0.2:
resolution: {integrity: sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==}
peerDependencies:
react: '>=15'
dependencies:
'@babel/runtime': 7.21.0
history: 4.10.1
loose-envify: 1.4.0
prop-types: 15.8.1
react: 17.0.2
react-router: 5.3.4_react@17.0.2
tiny-invariant: 1.3.1
tiny-warning: 1.0.3
dev: false
/react-router/5.3.4_react@17.0.2:
resolution: {integrity: sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==}
peerDependencies:
react: '>=15'
dependencies:
'@babel/runtime': 7.21.0
history: 4.10.1
hoist-non-react-statics: 3.3.2
loose-envify: 1.4.0
path-to-regexp: 1.8.0
prop-types: 15.8.1
react: 17.0.2
react-is: 16.13.1
tiny-invariant: 1.3.1
tiny-warning: 1.0.3
dev: false
/react/17.0.2:
resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==}
engines: {node: '>=0.10.0'}
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
dev: false
/regenerator-runtime/0.13.11:
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
dev: false
/resolve-pathname/3.0.0:
resolution: {integrity: sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==}
dev: false
/scheduler/0.20.2:
resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==}
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
dev: false
/shallowequal/1.1.0:
resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}
dev: false
/styled-components/5.3.9_fane7jikarojcev26y27hpbhu4:
resolution: {integrity: sha512-Aj3kb13B75DQBo2oRwRa/APdB5rSmwUfN5exyarpX+x/tlM/rwZA2vVk2vQgVSP6WKaZJHWwiFrzgHt+CLtB4A==}
engines: {node: '>=10'}
peerDependencies:
react: '>= 16.8.0'
react-dom: '>= 16.8.0'
react-is: '>= 16.8.0'
dependencies:
'@babel/helper-module-imports': 7.18.6
'@babel/traverse': 7.21.3_supports-color@5.5.0
'@emotion/is-prop-valid': 1.2.0
'@emotion/stylis': 0.8.5
'@emotion/unitless': 0.7.5
babel-plugin-styled-components: 2.0.7_styled-components@5.3.9
css-to-react-native: 3.2.0
hoist-non-react-statics: 3.3.2
react: 17.0.2
react-dom: 17.0.2_react@17.0.2
react-is: 17.0.2
shallowequal: 1.1.0
supports-color: 5.5.0
dev: false
/supports-color/5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
dependencies:
has-flag: 3.0.0
dev: false
/tiny-invariant/1.3.1:
resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==}
dev: false
/tiny-warning/1.0.3:
resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
dev: false
/to-fast-properties/2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
dev: false
/tr46/0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
dev: false
/typescript/4.9.5:
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: false
/value-equal/1.0.1:
resolution: {integrity: sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==}
dev: false
/webidl-conversions/3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
dev: false
/whatwg-url/5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
dependencies:
tr46: 0.0.3
webidl-conversions: 3.0.1
dev: false

View File

@@ -2,10 +2,37 @@ import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { AuthorizerProvider } from '@authorizerdev/authorizer-react';
import Root from './Root';
import { createRandomString } from './utils/common';
declare global {
interface Window {
__authorizer__: any;
}
}
export default function App() {
// @ts-ignore
const globalState: Record<string, string> = window['__authorizer__'];
const searchParams = new URLSearchParams(window.location.search);
const state = searchParams.get('state') || createRandomString();
const scope = searchParams.get('scope')
? searchParams.get('scope')?.toString().split(' ')
: `openid profile email`;
const urlProps: Record<string, any> = {
state,
scope,
};
const redirectURL =
searchParams.get('redirect_uri') || searchParams.get('redirectURL');
if (redirectURL) {
urlProps.redirectURL = redirectURL;
} else {
urlProps.redirectURL = window.location.href;
}
const globalState: Record<string, string> = {
...window['__authorizer__'],
...urlProps,
};
return (
<div
style={{
@@ -26,19 +53,11 @@ export default function App() {
<img
src={`${globalState.organizationLogo}`}
alt="logo"
style={{ height: 60, width: 60, objectFit: 'cover' }}
style={{ height: 60, objectFit: 'cover' }}
/>
<h1>{globalState.organizationName}</h1>
</div>
<div
style={{
width: 400,
margin: `10px auto`,
border: `1px solid #D1D5DB`,
padding: `25px 20px`,
borderRadius: 5,
}}
>
<div className="container">
<BrowserRouter>
<AuthorizerProvider
config={{
@@ -46,7 +65,7 @@ export default function App() {
redirectURL: globalState.redirectURL,
}}
>
<Root />
<Root globalState={globalState} />
</AuthorizerProvider>
</BrowserRouter>
</div>

View File

@@ -1,23 +1,94 @@
import React, { useEffect, lazy, Suspense } from 'react';
import { Switch, Route } from 'react-router-dom';
import { useAuthorizer } from '@authorizerdev/authorizer-react';
import styled, { ThemeProvider } from 'styled-components';
import SetupPassword from './pages/setup-password';
import { hasWindow, createRandomString } from './utils/common';
import { theme } from './theme';
const ResetPassword = lazy(() => import('./pages/rest-password'));
const Login = lazy(() => import('./pages/login'));
const Dashboard = lazy(() => import('./pages/dashboard'));
const SignUp = lazy(() => import('./pages/signup'));
export default function Root() {
const Wrapper = styled.div`
font-family: ${(props) => props.theme.fonts.fontStack};
color: ${(props) => props.theme.colors.textColor};
font-size: ${(props) => props.theme.fonts.mediumText};
box-sizing: border-box;
*,
*:before,
*:after {
box-sizing: inherit;
}
`;
export default function Root({
globalState,
}: {
globalState: Record<string, string>;
}) {
const { token, loading, config } = useAuthorizer();
const searchParams = new URLSearchParams(
hasWindow() ? window.location.search : ``,
);
const state = searchParams.get('state') || createRandomString();
const scope = searchParams.get('scope')
? searchParams.get('scope')?.toString().split(' ')
: ['openid', 'profile', 'email'];
const code = searchParams.get('code') || '';
const nonce = searchParams.get('nonce') || '';
const urlProps: Record<string, any> = {
state,
scope,
};
const redirectURL =
searchParams.get('redirect_uri') || searchParams.get('redirectURL');
if (redirectURL) {
urlProps.redirectURL = redirectURL;
} else {
urlProps.redirectURL = hasWindow() ? window.location.origin : redirectURL;
}
urlProps.redirect_uri = urlProps.redirectURL;
useEffect(() => {
if (token) {
const url = new URL(config.redirectURL || '/app');
let redirectURL = config.redirectURL || '/app';
// let params = `access_token=${token.access_token}&id_token=${token.id_token}&expires_in=${token.expires_in}&state=${globalState.state}`;
// Note: If OIDC breaks in the future, use the above params
let params = `state=${globalState.state}`;
if (code !== '') {
params += `&code=${code}`;
}
if (nonce !== '') {
params += `&nonce=${nonce}`;
}
if (token.refresh_token) {
params += `&refresh_token=${token.refresh_token}`;
}
const url = new URL(redirectURL);
if (redirectURL.includes('?')) {
redirectURL = `${redirectURL}&${params}`;
} else {
redirectURL = `${redirectURL}?${params}`;
}
if (url.origin !== window.location.origin) {
window.location.href = config.redirectURL || '/app';
sessionStorage.removeItem('authorizer_state');
window.location.replace(redirectURL);
}
}
return () => {};
}, [token]);
}, [token, config]);
if (loading) {
return <h1>Loading...</h1>;
@@ -37,14 +108,24 @@ export default function Root() {
return (
<Suspense fallback={<></>}>
<Switch>
<Route path="/app" exact>
<Login />
</Route>
<Route path="/app/reset-password">
<ResetPassword />
</Route>
</Switch>
<ThemeProvider theme={theme}>
<Wrapper>
<Switch>
<Route path="/app" exact>
<Login urlProps={urlProps} />
</Route>
<Route path="/app/signup">
<SignUp urlProps={urlProps} />
</Route>
<Route path="/app/reset-password">
<ResetPassword />
</Route>
<Route path="/app/setup-password">
<SetupPassword />
</Route>
</Switch>
</Wrapper>
</ThemeProvider>
</Suspense>
);
}

View File

@@ -1,5 +1,5 @@
body {
margin: 0;
margin: 10;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
@@ -14,3 +14,17 @@ body {
*:after {
box-sizing: inherit;
}
.container {
box-sizing: content-box;
border: 1px solid #d1d5db;
padding: 25px 20px;
border-radius: 5px;
}
@media only screen and (min-width: 768px) {
.container {
width: 400px;
margin: 0 auto;
}
}

View File

@@ -1,10 +1,98 @@
import React, { Fragment } from 'react';
import { Authorizer } from '@authorizerdev/authorizer-react';
import React, { Fragment, useState } from 'react';
import {
AuthorizerBasicAuthLogin,
AuthorizerForgotPassword,
AuthorizerMagicLinkLogin,
AuthorizerSocialLogin,
useAuthorizer,
} from '@authorizerdev/authorizer-react';
import styled from 'styled-components';
import { Link } from 'react-router-dom';
export default function Login() {
const enum VIEW_TYPES {
LOGIN = 'login',
FORGOT_PASSWORD = 'forgot-password',
}
const Footer = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 15px;
`;
const FooterContent = styled.div`
display: flex;
justify-content: center;
align-items: center;
margin-top: 10px;
`;
export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
const { config } = useAuthorizer();
const [view, setView] = useState<VIEW_TYPES>(VIEW_TYPES.LOGIN);
const isBasicAuth = config.is_basic_authentication_enabled;
return (
<Fragment>
<Authorizer />
{view === VIEW_TYPES.LOGIN && (
<Fragment>
<h1 style={{ textAlign: 'center' }}>Login</h1>
<AuthorizerSocialLogin urlProps={urlProps} />
<br />
{(config.is_basic_authentication_enabled ||
config.is_mobile_basic_authentication_enabled) &&
!config.is_magic_link_login_enabled && (
<AuthorizerBasicAuthLogin urlProps={urlProps} />
)}
{config.is_magic_link_login_enabled && (
<AuthorizerMagicLinkLogin urlProps={urlProps} />
)}
{(config.is_basic_authentication_enabled ||
config.is_mobile_basic_authentication_enabled) &&
!config.is_magic_link_login_enabled && (
<Footer>
<Link
to="#"
onClick={() => setView(VIEW_TYPES.FORGOT_PASSWORD)}
style={{ marginBottom: 10 }}
>
Forgot Password?
</Link>
</Footer>
)}
</Fragment>
)}
{view === VIEW_TYPES.FORGOT_PASSWORD && (
<Fragment>
<h1 style={{ textAlign: 'center' }}>Forgot Password</h1>
<AuthorizerForgotPassword
urlProps={{
...urlProps,
redirect_uri: `${window.location.origin}/app/reset-password`,
}}
onPasswordReset={() => {
setView(VIEW_TYPES.LOGIN);
}}
/>
<Footer>
<Link
to="#"
onClick={() => setView(VIEW_TYPES.LOGIN)}
style={{ marginBottom: 10 }}
>
Back
</Link>
</Footer>
</Fragment>
)}
{config.is_basic_authentication_enabled &&
!config.is_magic_link_login_enabled &&
config.is_sign_up_enabled && (
<FooterContent>
Don't have an account? &nbsp; <Link to="/app/signup"> Sign Up</Link>
</FooterContent>
)}
</Fragment>
);
}

View File

@@ -0,0 +1,12 @@
import React, { Fragment } from 'react';
import { AuthorizerResetPassword } from '@authorizerdev/authorizer-react';
export default function SetupPassword() {
return (
<Fragment>
<h1 style={{ textAlign: 'center' }}>Setup new Password</h1>
<br />
<AuthorizerResetPassword />
</Fragment>
);
}

29
app/src/pages/signup.tsx Normal file
View File

@@ -0,0 +1,29 @@
import React, { Fragment } from 'react';
import { AuthorizerSignup, AuthorizerSocialLogin } from '@authorizerdev/authorizer-react';
import styled from 'styled-components';
import { Link } from 'react-router-dom';
const FooterContent = styled.div`
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
`;
export default function SignUp({
urlProps,
}: {
urlProps: Record<string, any>;
}) {
return (
<Fragment>
<h1 style={{ textAlign: 'center' }}>Sign Up</h1>
<br />
<AuthorizerSocialLogin urlProps={urlProps} />
<AuthorizerSignup urlProps={urlProps} />
<FooterContent>
Already have an account? <Link to="/app"> Login</Link>
</FooterContent>
</Fragment>
);
}

28
app/src/theme.ts Normal file
View File

@@ -0,0 +1,28 @@
// colors: https://tailwindcss.com/docs/customizing-colors
export const theme = {
colors: {
primary: '#3B82F6',
primaryDisabled: '#60A5FA',
gray: '#D1D5DB',
danger: '#DC2626',
success: '#10B981',
textColor: '#374151',
},
fonts: {
// typography
fontStack: '-apple-system, system-ui, sans-serif',
// font sizes
largeText: '18px',
mediumText: '14px',
smallText: '12px',
tinyText: '10px',
},
radius: {
card: '5px',
button: '5px',
input: '5px',
},
};

24
app/src/utils/common.ts Normal file
View File

@@ -0,0 +1,24 @@
export const getCrypto = () => {
//ie 11.x uses msCrypto
return (window.crypto || (window as any).msCrypto) as Crypto;
};
export const createRandomString = () => {
const charset =
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_~.';
let random = '';
const randomValues = Array.from(
getCrypto().getRandomValues(new Uint8Array(43)),
);
randomValues.forEach((v) => (random += charset[v % charset.length]));
return random;
};
export const createQueryParams = (params: any) => {
return Object.keys(params)
.filter((k) => typeof params[k] !== 'undefined')
.map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
.join('&');
};
export const hasWindow = (): boolean => typeof window !== 'undefined';

619
app/yarn.lock Normal file
View File

@@ -0,0 +1,619 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@authorizerdev/authorizer-js@^2.0.3":
version "2.0.3"
resolved "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-2.0.3.tgz"
integrity sha512-uencwr3Ea8mwfxVKDFf2ITRCRSmzvua+O2voRuiWQORtRQTgZQjkN3M+IEkEj+WP9M1iFIl+NDgzECsp8ptC/A==
dependencies:
cross-fetch "^3.1.5"
"@authorizerdev/authorizer-react@^1.3.2":
version "1.3.2"
resolved "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.3.2.tgz"
integrity sha512-3kMAygHBCa8Fc9Oo0lz1k88r+Pd6kx1PSn3NMYLwxQXy2jRt4xWn7iuGn+SDGFs3DzofaN71I61gRwQ+6dO1rw==
dependencies:
"@authorizerdev/authorizer-js" "^2.0.3"
validator "^13.11.0"
"@babel/code-frame@^7.22.13":
version "7.22.13"
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz"
integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
dependencies:
"@babel/highlight" "^7.22.13"
chalk "^2.4.2"
"@babel/generator@^7.23.0":
version "7.23.0"
resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz"
integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
dependencies:
"@babel/types" "^7.23.0"
"@jridgewell/gen-mapping" "^0.3.2"
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
"@babel/helper-annotate-as-pure@^7.16.0":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz"
integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==
dependencies:
"@babel/types" "^7.16.7"
"@babel/helper-environment-visitor@^7.22.20":
version "7.22.20"
resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz"
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
"@babel/helper-function-name@^7.23.0":
version "7.23.0"
resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz"
integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
dependencies:
"@babel/template" "^7.22.15"
"@babel/types" "^7.23.0"
"@babel/helper-hoist-variables@^7.22.5":
version "7.22.5"
resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz"
integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
dependencies:
"@babel/types" "^7.22.5"
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.16.0":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz"
integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==
dependencies:
"@babel/types" "^7.16.7"
"@babel/helper-split-export-declaration@^7.22.6":
version "7.22.6"
resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz"
integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
dependencies:
"@babel/types" "^7.22.5"
"@babel/helper-string-parser@^7.22.5":
version "7.22.5"
resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz"
integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
"@babel/helper-validator-identifier@^7.22.20":
version "7.22.20"
resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz"
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
"@babel/highlight@^7.22.13":
version "7.22.20"
resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz"
integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==
dependencies:
"@babel/helper-validator-identifier" "^7.22.20"
chalk "^2.4.2"
js-tokens "^4.0.0"
"@babel/parser@^7.22.15", "@babel/parser@^7.23.0":
version "7.23.0"
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz"
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1":
version "7.14.8"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz"
integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.22.15":
version "7.22.15"
resolved "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz"
integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
dependencies:
"@babel/code-frame" "^7.22.13"
"@babel/parser" "^7.22.15"
"@babel/types" "^7.22.15"
"@babel/traverse@^7.4.5":
version "7.23.2"
resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz"
integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
dependencies:
"@babel/code-frame" "^7.22.13"
"@babel/generator" "^7.23.0"
"@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-function-name" "^7.23.0"
"@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/parser" "^7.23.0"
"@babel/types" "^7.23.0"
debug "^4.1.0"
globals "^11.1.0"
"@babel/types@^7.16.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0":
version "7.23.0"
resolved "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz"
integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
dependencies:
"@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
"@emotion/is-prop-valid@^0.8.8":
version "0.8.8"
resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz"
integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
dependencies:
"@emotion/memoize" "0.7.4"
"@emotion/memoize@0.7.4":
version "0.7.4"
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz"
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
"@emotion/stylis@^0.8.4":
version "0.8.5"
resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz"
integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
"@emotion/unitless@^0.7.4":
version "0.7.5"
resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz"
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
"@jridgewell/gen-mapping@^0.3.2":
version "0.3.3"
resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz"
integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==
dependencies:
"@jridgewell/set-array" "^1.0.1"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/resolve-uri@^3.1.0":
version "3.1.1"
resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz"
integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
"@jridgewell/set-array@^1.0.1":
version "1.1.2"
resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
version "1.4.15"
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
version "0.3.20"
resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz"
integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==
dependencies:
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
"@types/history@*":
version "4.7.9"
resolved "https://registry.npmjs.org/@types/history/-/history-4.7.9.tgz"
integrity sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==
"@types/hoist-non-react-statics@*":
version "3.3.1"
resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
"@types/prop-types@*":
version "15.7.4"
resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz"
integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
"@types/react-dom@^17.0.9":
version "17.0.9"
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.9.tgz"
integrity sha512-wIvGxLfgpVDSAMH5utdL9Ngm5Owu0VsGmldro3ORLXV8CShrL8awVj06NuEXFQ5xyaYfdca7Sgbk/50Ri1GdPg==
dependencies:
"@types/react" "*"
"@types/react-router-dom@^5.1.8":
version "5.1.8"
resolved "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.8.tgz"
integrity sha512-03xHyncBzG0PmDmf8pf3rehtjY0NpUj7TIN46FrT5n1ZWHPZvXz32gUyNboJ+xsL8cpg8bQVLcllptcQHvocrw==
dependencies:
"@types/history" "*"
"@types/react" "*"
"@types/react-router" "*"
"@types/react-router@*":
version "5.1.16"
resolved "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.16.tgz"
integrity sha512-8d7nR/fNSqlTFGHti0R3F9WwIertOaaA1UEB8/jr5l5mDMOs4CidEgvvYMw4ivqrBK+vtVLxyTj2P+Pr/dtgzg==
dependencies:
"@types/history" "*"
"@types/react" "*"
"@types/react@*", "@types/react@^17.0.15":
version "17.0.15"
resolved "https://registry.npmjs.org/@types/react/-/react-17.0.15.tgz"
integrity sha512-uTKHDK9STXFHLaKv6IMnwp52fm0hwU+N89w/p9grdUqcFA6WuqDyPhaWopbNyE1k/VhgzmHl8pu1L4wITtmlLw==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/scheduler@*":
version "0.16.2"
resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz"
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
"@types/styled-components@^5.1.11":
version "5.1.25"
resolved "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.25.tgz"
integrity sha512-fgwl+0Pa8pdkwXRoVPP9JbqF0Ivo9llnmsm+7TCI330kbPIFd9qv1Lrhr37shf4tnxCOSu+/IgqM7uJXLWZZNQ==
dependencies:
"@types/hoist-non-react-statics" "*"
"@types/react" "*"
csstype "^3.0.2"
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz"
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
dependencies:
color-convert "^1.9.0"
"babel-plugin-styled-components@>= 1.12.0":
version "2.0.2"
resolved "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.2.tgz"
integrity sha512-7eG5NE8rChnNTDxa6LQfynwgHTVOYYaHJbUYSlOhk8QBXIQiMBKq4gyfHBBKPrxUcVBXVJL61ihduCpCQbuNbw==
dependencies:
"@babel/helper-annotate-as-pure" "^7.16.0"
"@babel/helper-module-imports" "^7.16.0"
babel-plugin-syntax-jsx "^6.18.0"
lodash "^4.17.11"
babel-plugin-syntax-jsx@^6.18.0:
version "6.18.0"
resolved "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz"
integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=
camelize@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz"
integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=
chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
color-name "1.1.3"
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
cross-fetch@^3.1.5:
version "3.1.8"
resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz"
integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==
dependencies:
node-fetch "^2.6.12"
css-color-keywords@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz"
integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=
css-to-react-native@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz"
integrity sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==
dependencies:
camelize "^1.0.0"
css-color-keywords "^1.0.0"
postcss-value-parser "^4.0.2"
csstype@^3.0.2:
version "3.0.8"
resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz"
integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==
debug@^4.1.0:
version "4.3.3"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
dependencies:
ms "2.1.2"
esbuild@^0.12.17:
version "0.12.17"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.12.17.tgz"
integrity sha512-GshKJyVYUnlSXIZj/NheC2O0Kblh42CS7P1wJyTbbIHevTG4jYMS9NNw8EOd8dDWD0dzydYHS01MpZoUcQXB4g==
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
globals@^11.1.0:
version "11.12.0"
resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
history@^4.9.0:
version "4.10.1"
resolved "https://registry.npmjs.org/history/-/history-4.10.1.tgz"
integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
dependencies:
"@babel/runtime" "^7.1.2"
loose-envify "^1.2.0"
resolve-pathname "^3.0.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
value-equal "^1.0.1"
hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0:
version "3.3.2"
resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
dependencies:
react-is "^16.7.0"
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz"
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
lodash@^4.17.11:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
mini-create-react-context@^0.4.0:
version "0.4.1"
resolved "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz"
integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==
dependencies:
"@babel/runtime" "^7.12.1"
tiny-warning "^1.0.3"
ms@2.1.2:
version "2.1.2"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
node-fetch@^2.6.12:
version "2.7.0"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
dependencies:
whatwg-url "^5.0.0"
object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
path-to-regexp@^1.7.0:
version "1.8.0"
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz"
integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
dependencies:
isarray "0.0.1"
postcss-value-parser@^4.0.2:
version "4.2.0"
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
prettier@2.7.1:
version "2.7.1"
resolved "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz"
integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==
prop-types@^15.0.0, prop-types@^15.6.2:
version "15.7.2"
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
dependencies:
loose-envify "^1.4.0"
object-assign "^4.1.1"
react-is "^16.8.1"
react-dom@^17.0.2, "react-dom@>= 16.8.0":
version "17.0.2"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz"
integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
scheduler "^0.20.2"
react-is@^16.6.0:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^16.8.1:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^17.0.2, "react-is@>= 16.8.0":
version "17.0.2"
resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-router-dom@^5.2.0:
version "5.2.0"
resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz"
integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==
dependencies:
"@babel/runtime" "^7.1.2"
history "^4.9.0"
loose-envify "^1.3.1"
prop-types "^15.6.2"
react-router "5.2.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
react-router@5.2.0:
version "5.2.0"
resolved "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz"
integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==
dependencies:
"@babel/runtime" "^7.1.2"
history "^4.9.0"
hoist-non-react-statics "^3.1.0"
loose-envify "^1.3.1"
mini-create-react-context "^0.4.0"
path-to-regexp "^1.7.0"
prop-types "^15.6.2"
react-is "^16.6.0"
tiny-invariant "^1.0.2"
tiny-warning "^1.0.0"
"react@^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", react@^17.0.2, "react@>= 16.8.0", react@>=15, react@>=16, react@17.0.2:
version "17.0.2"
resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz"
integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
regenerator-runtime@^0.13.4:
version "0.13.9"
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
resolve-pathname@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz"
integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
scheduler@^0.20.2:
version "0.20.2"
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz"
integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
shallowequal@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz"
integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
styled-components@^5.3.0, "styled-components@>= 2":
version "5.3.3"
resolved "https://registry.npmjs.org/styled-components/-/styled-components-5.3.3.tgz"
integrity sha512-++4iHwBM7ZN+x6DtPPWkCI4vdtwumQ+inA/DdAsqYd4SVgUKJie5vXyzotA00ttcFdQkCng7zc6grwlfIfw+lw==
dependencies:
"@babel/helper-module-imports" "^7.0.0"
"@babel/traverse" "^7.4.5"
"@emotion/is-prop-valid" "^0.8.8"
"@emotion/stylis" "^0.8.4"
"@emotion/unitless" "^0.7.4"
babel-plugin-styled-components ">= 1.12.0"
css-to-react-native "^3.0.0"
hoist-non-react-statics "^3.0.0"
shallowequal "^1.1.0"
supports-color "^5.5.0"
supports-color@^5.3.0, supports-color@^5.5.0:
version "5.5.0"
resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
tiny-invariant@^1.0.2:
version "1.1.0"
resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz"
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
tiny-warning@^1.0.0, tiny-warning@^1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz"
integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
typescript@^4.3.5:
version "4.3.5"
resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz"
integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
validator@^13.11.0:
version "13.11.0"
resolved "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz"
integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==
value-equal@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz"
integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz"
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"

View File

@@ -0,0 +1,6 @@
{
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all",
"useTabs": true
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -5,27 +5,38 @@
"main": "index.js",
"scripts": {
"build": "rm -rf build && NODE_ENV=production node ./esbuild.config.js",
"start": "NODE_ENV=development node ./esbuild.config.js"
"start": "NODE_ENV=development node ./esbuild.config.js",
"format": "prettier --write --use-tabs 'src/**/*.(ts|tsx|js|jsx)'"
},
"keywords": [],
"author": "Lakhan Samani",
"license": "ISC",
"dependencies": {
"@chakra-ui/react": "^1.7.3",
"@emotion/core": "^11.0.0",
"@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0",
"@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11",
"@types/react-router-dom": "^5.3.2",
"dayjs": "^1.10.7",
"esbuild": "^0.14.9",
"focus-visible": "^5.2.0",
"framer-motion": "^5.5.5",
"graphql": "^16.2.0",
"lodash": "^4.17.21",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-draft-wysiwyg": "^1.15.0",
"react-dropzone": "^12.0.4",
"react-email-editor": "^1.6.1",
"react-icons": "^4.3.1",
"react-router-dom": "^6.2.1",
"typescript": "^4.5.4",
"urql": "^2.0.6"
},
"devDependencies": {
"@types/react-email-editor": "^1.1.7",
"prettier": "2.7.1"
}
}

BIN
dashboard/public/roblox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
foo@bar.com,test@authorizer.dev
1 foo bar.com,test authorizer.dev

View File

@@ -1,4 +1,5 @@
import * as React from 'react';
import { Fragment } from 'react';
import { ChakraProvider, extendTheme } from '@chakra-ui/react';
import { BrowserRouter } from 'react-router-dom';
import { createClient, Provider } from 'urql';
@@ -10,6 +11,9 @@ const queryClient = createClient({
fetchOptions: () => {
return {
credentials: 'include',
headers: {
'x-authorizer-url': window.location.origin,
},
};
},
requestPolicy: 'network-only',
@@ -19,8 +23,8 @@ const theme = extendTheme({
styles: {
global: {
'html, body, #root': {
fontFamily: 'Avenir, Helvetica, Arial, sans-serif',
height: '100%',
outline: 'none',
},
},
},
@@ -33,14 +37,16 @@ const theme = extendTheme({
export default function App() {
return (
<ChakraProvider theme={theme}>
<Provider value={queryClient}>
<BrowserRouter basename="/dashboard">
<AuthContextProvider>
<AppRoutes />
</AuthContextProvider>
</BrowserRouter>
</Provider>
</ChakraProvider>
<Fragment>
<ChakraProvider theme={theme}>
<Provider value={queryClient}>
<BrowserRouter basename="/dashboard">
<AuthContextProvider>
<AppRoutes />
</AuthContextProvider>
</BrowserRouter>
</Provider>
</ChakraProvider>
</Fragment>
);
}

View File

@@ -0,0 +1,106 @@
import React from 'react';
import {
Button,
Center,
Flex,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
Text,
useToast,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaRegTrashAlt } from 'react-icons/fa';
import { DeleteEmailTemplate } from '../graphql/mutation';
import { capitalizeFirstLetter } from '../utils';
interface deleteEmailTemplateModalInputPropTypes {
emailTemplateId: string;
eventName: string;
fetchEmailTemplatesData: Function;
}
const DeleteEmailTemplateModal = ({
emailTemplateId,
eventName,
fetchEmailTemplatesData,
}: deleteEmailTemplateModalInputPropTypes) => {
const client = useClient();
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const deleteHandler = async () => {
const res = await client
.mutation(DeleteEmailTemplate, { params: { id: emailTemplateId } })
.toPromise();
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'top-right',
});
return;
} else if (res.data?._delete_email_template) {
toast({
title: capitalizeFirstLetter(res.data?._delete_email_template.message),
isClosable: true,
status: 'success',
position: 'top-right',
});
}
onClose();
fetchEmailTemplatesData();
};
return (
<>
<MenuItem onClick={onOpen}>Delete</MenuItem>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Delete Email Template</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text fontSize="md">Are you sure?</Text>
<Flex
padding="5%"
marginTop="5%"
marginBottom="2%"
border="1px solid #ff7875"
borderRadius="5px"
flexDirection="column"
>
<Text fontSize="sm">
Email template for event <b>{eventName}</b> will be deleted
permanently!
</Text>
</Flex>
</ModalBody>
<ModalFooter>
<Button
leftIcon={<FaRegTrashAlt />}
colorScheme="red"
variant="solid"
onClick={deleteHandler}
isDisabled={false}
>
<Center h="100%" pt="5%">
Delete
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default DeleteEmailTemplateModal;

View File

@@ -0,0 +1,112 @@
import React from 'react';
import {
Button,
Center,
Flex,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
Text,
useToast,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaRegTrashAlt } from 'react-icons/fa';
import { DeleteUser } from '../graphql/mutation';
import { capitalizeFirstLetter } from '../utils';
interface userDataTypes {
id: string;
email: string;
}
const DeleteUserModal = ({
user,
updateUserList,
}: {
user: userDataTypes;
updateUserList: Function;
}) => {
const client = useClient();
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const [userData, setUserData] = React.useState<userDataTypes>({
id: '',
email: '',
});
React.useEffect(() => {
setUserData(user);
}, []);
const deleteHandler = async () => {
const res = await client
.mutation(DeleteUser, { params: { email: userData.email } })
.toPromise();
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'top-right',
});
return;
} else if (res.data?._delete_user) {
toast({
title: capitalizeFirstLetter(res.data?._delete_user.message),
isClosable: true,
status: 'success',
position: 'top-right',
});
}
onClose();
updateUserList();
};
return (
<>
<MenuItem onClick={onOpen}>Delete User</MenuItem>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Delete User</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text fontSize="md">Are you sure?</Text>
<Flex
padding="5%"
marginTop="5%"
marginBottom="2%"
border="1px solid #ff7875"
borderRadius="5px"
flexDirection="column"
>
<Text fontSize="sm">
User <b>{user.email}</b> will be deleted permanently!
</Text>
</Flex>
</ModalBody>
<ModalFooter>
<Button
leftIcon={<FaRegTrashAlt />}
colorScheme="red"
variant="solid"
onClick={deleteHandler}
isDisabled={false}
>
<Center h="100%" pt="5%">
Delete
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default DeleteUserModal;

View File

@@ -0,0 +1,106 @@
import React from 'react';
import {
Button,
Center,
Flex,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
Text,
useToast,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaRegTrashAlt } from 'react-icons/fa';
import { DeleteWebhook } from '../graphql/mutation';
import { capitalizeFirstLetter } from '../utils';
interface deleteWebhookModalInputPropTypes {
webhookId: string;
eventName: string;
fetchWebookData: Function;
}
const DeleteWebhookModal = ({
webhookId,
eventName,
fetchWebookData,
}: deleteWebhookModalInputPropTypes) => {
const client = useClient();
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const deleteHandler = async () => {
const res = await client
.mutation(DeleteWebhook, { params: { id: webhookId } })
.toPromise();
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'top-right',
});
return;
} else if (res.data?._delete_webhook) {
toast({
title: capitalizeFirstLetter(res.data?._delete_webhook.message),
isClosable: true,
status: 'success',
position: 'top-right',
});
}
onClose();
fetchWebookData();
};
return (
<>
<MenuItem onClick={onOpen}>Delete</MenuItem>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Delete Webhook</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Text fontSize="md">Are you sure?</Text>
<Flex
padding="5%"
marginTop="5%"
marginBottom="2%"
border="1px solid #ff7875"
borderRadius="5px"
flexDirection="column"
>
<Text fontSize="sm">
Webhook for event <b>{eventName}</b> will be deleted
permanently!
</Text>
</Flex>
</ModalBody>
<ModalFooter>
<Button
leftIcon={<FaRegTrashAlt />}
colorScheme="red"
variant="solid"
onClick={deleteHandler}
isDisabled={false}
>
<Center h="100%" pt="5%">
Delete
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default DeleteWebhookModal;

View File

@@ -0,0 +1,263 @@
import React, { useState } from 'react';
import {
Button,
Center,
Flex,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Stack,
useDisclosure,
Text,
useToast,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaSave } from 'react-icons/fa';
import InputField from './InputField';
import {
DateInputType,
MultiSelectInputType,
SelectInputType,
TextInputType,
} from '../constants';
import { getObjectDiff } from '../utils';
import { UpdateUser } from '../graphql/mutation';
import { GetAvailableRolesQuery } from '../graphql/queries';
const GenderTypes = {
Undisclosed: null,
Male: 'Male',
Female: 'Female',
};
interface userDataTypes {
id: string;
email: string;
given_name: string;
family_name: string;
middle_name: string;
nickname: string;
gender: string;
birthdate: string;
phone_number: string;
picture: string;
roles: [string] | [];
}
const EditUserModal = ({
user,
updateUserList,
}: {
user: userDataTypes;
updateUserList: Function;
}) => {
const client = useClient();
const toast = useToast();
const [availableRoles, setAvailableRoles] = useState<string[]>([]);
const { isOpen, onOpen, onClose } = useDisclosure();
const [userData, setUserData] = useState<userDataTypes>({
id: '',
email: '',
given_name: '',
family_name: '',
middle_name: '',
nickname: '',
gender: '',
birthdate: '',
phone_number: '',
picture: '',
roles: [],
});
React.useEffect(() => {
setUserData(user);
fetchAvailableRoles();
}, []);
const fetchAvailableRoles = async () => {
const res = await client.query(GetAvailableRolesQuery).toPromise();
if (res.data?._env?.ROLES && res.data?._env?.PROTECTED_ROLES) {
setAvailableRoles([
...res.data._env.ROLES,
...res.data._env.PROTECTED_ROLES,
]);
}
};
const saveHandler = async () => {
const diff = getObjectDiff(user, userData);
const updatedUserData = diff.reduce(
(acc: any, property: string) => ({
...acc,
// @ts-ignore
[property]: userData[property],
}),
{},
);
const res = await client
.mutation(UpdateUser, { params: { ...updatedUserData, id: userData.id } })
.toPromise();
if (res.error) {
toast({
title: 'User data update failed',
isClosable: true,
status: 'error',
position: 'top-right',
});
} else if (res.data?._update_user?.id) {
toast({
title: 'User data update successful',
isClosable: true,
status: 'success',
position: 'top-right',
});
}
onClose();
updateUserList();
};
return (
<>
<MenuItem onClick={onOpen}>Edit User Details</MenuItem>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Edit User Details</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Stack>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Given Name:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={TextInputType.GIVEN_NAME}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Middle Name:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={TextInputType.MIDDLE_NAME}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Family Name:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={TextInputType.FAMILY_NAME}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Birth Date:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={DateInputType.BIRTHDATE}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Nickname:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={TextInputType.NICKNAME}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Gender:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={SelectInputType.GENDER}
value={userData.gender}
options={GenderTypes}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Phone Number:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={TextInputType.PHONE_NUMBER}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Picture:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
inputType={TextInputType.PICTURE}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Roles:</Text>
</Flex>
<Center w="70%">
<InputField
variables={userData}
setVariables={setUserData}
availableRoles={availableRoles}
inputType={MultiSelectInputType.USER_ROLES}
/>
</Center>
</Flex>
</Stack>
</ModalBody>
<ModalFooter>
<Button
leftIcon={<FaSave />}
colorScheme="blue"
variant="solid"
onClick={saveHandler}
isDisabled={false}
>
<Center h="100%" pt="5%">
Save
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default EditUserModal;

View File

@@ -0,0 +1,65 @@
import React from 'react';
import { Flex, Stack, Text, useMediaQuery } from '@chakra-ui/react';
import InputField from '../../components/InputField';
import { TextInputType, TextAreaInputType } from '../../constants';
const AccessToken = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Access Token
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '50%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">Access Token Expiry Time:</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.ACCESS_TOKEN_EXPIRY_TIME}
placeholder="0h15m0s"
/>
</Flex>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '60%'}
justifyContent="start"
direction="column"
>
<Text fontSize="sm">Custom Scripts:</Text>
<Text fontSize="xs" color="blackAlpha.500">
(Used to add custom fields in ID token)
</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
variables={variables}
setVariables={setVariables}
inputType={TextAreaInputType.CUSTOM_ACCESS_TOKEN_SCRIPT}
placeholder="Add script here"
minH="25vh"
/>
</Flex>
</Flex>
</Stack>
</div>
);
};
export default AccessToken;

View File

@@ -0,0 +1,89 @@
import React from 'react';
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
import InputField from '../../components/InputField';
import { TextInputType } from '../../constants';
const DatabaseCredentials = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Database Credentials
</Text>
<Stack spacing={6} padding="3% 0">
<Text fontStyle="italic" fontSize="sm" color="blackAlpha.500" mt={3}>
Note: Database related environment variables cannot be updated from
dashboard. Please use .env file or OS environment variables to update
it.
</Text>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">DataBase Name:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.DATABASE_NAME}
isDisabled={true}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">DataBase Type:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.DATABASE_TYPE}
isDisabled={true}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">DataBase URL:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.DATABASE_URL}
isDisabled={true}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default DatabaseCredentials;

View File

@@ -0,0 +1,35 @@
import React from 'react';
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
import InputField from '../../components/InputField';
import { ArrayInputType } from '../../constants';
const DomainWhiteListing = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Domain White Listing
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Allowed Origins:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
variables={variables}
setVariables={setVariables}
inputType={ArrayInputType.ALLOWED_ORIGINS}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default DomainWhiteListing;

View File

@@ -0,0 +1,150 @@
import React from 'react';
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
import InputField from '../../components/InputField';
import { TextInputType, HiddenInputType } from '../../constants';
const EmailConfigurations = ({
variables,
setVariables,
fieldVisibility,
setFieldVisibility,
}: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Email Configurations
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">SMTP Host:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SMTP_HOST}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">SMTP Port:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SMTP_PORT}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">SMTP Local Name:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SMTP_LOCAL_NAME}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">SMTP Username:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SMTP_USERNAME}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">SMTP Password:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.SMTP_PASSWORD}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">From Email:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SENDER_EMAIL}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Sender Name:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.SENDER_NAME}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default EmailConfigurations;

View File

@@ -0,0 +1,229 @@
import React from 'react';
import { Divider, Flex, Stack, Text } from '@chakra-ui/react';
import InputField from '../InputField';
import { SwitchInputType } from '../../constants';
const Features = ({ variables, setVariables }: any) => {
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Features
</Text>
<Stack spacing={6}>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Login Page:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_LOGIN_PAGE}
hasReversedValue
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Email Verification:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_EMAIL_VERIFICATION}
hasReversedValue
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Magic Login Link:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_MAGIC_LINK_LOGIN}
hasReversedValue
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Email Basic Authentication:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_BASIC_AUTHENTICATION}
hasReversedValue
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Mobile Basic Authentication:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_MOBILE_BASIC_AUTHENTICATION}
hasReversedValue
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Sign Up:</Text>
</Flex>
<Flex justifyContent="start" mb={3}>
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_SIGN_UP}
hasReversedValue
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Strong Password:</Text>
</Flex>
<Flex justifyContent="start" mb={3}>
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_STRONG_PASSWORD}
hasReversedValue
/>
</Flex>
</Flex>
<Flex alignItems="center">
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">Multi Factor Authentication (MFA):</Text>
<Text fontSize="x-small">
Note: Enabling this will ignore Enforcing MFA shown below and will
also ignore the user MFA setting.
</Text>
</Flex>
<Flex justifyContent="start" mb={3}>
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_MULTI_FACTOR_AUTHENTICATION}
hasReversedValue
/>
</Flex>
</Flex>
{!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && (
<Flex alignItems="center">
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">Time Based OTP (TOTP):</Text>
<Text fontSize="x-small">Note: to enable totp mfa</Text>
</Flex>
<Flex justifyContent="start" mb={3}>
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_TOTP_LOGIN}
hasReversedValue
/>
</Flex>
</Flex>
)}
{!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && (
<Flex alignItems="center">
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">EMAIL OTP:</Text>
<Text fontSize="x-small">Note: to enable email otp mfa</Text>
</Flex>
<Flex justifyContent="start" mb={3}>
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_MAIL_OTP_LOGIN}
hasReversedValue
/>
</Flex>
</Flex>
)}
<Flex alignItems="center">
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">
Enforce Multi Factor Authentication (MFA):
</Text>
<Text fontSize="x-small">
Note: If you disable enforcing after it was enabled, it will still
keep MFA enabled for older users.
</Text>
</Flex>
<Flex justifyContent="start" mb={3}>
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.ENFORCE_MULTI_FACTOR_AUTHENTICATION}
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Playground:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.DISABLE_PLAYGROUND}
hasReversedValue
/>
</Flex>
</Flex>
</Stack>
<Divider paddingY={5} />
<Text fontSize="md" paddingTop={5} fontWeight="bold" mb={5}>
Cookie Security Features
</Text>
<Stack spacing={6}>
<Flex>
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">Use Secure App Cookie:</Text>
<Text fontSize="x-small">
Note: If you set this to insecure, it will set{' '}
<code>sameSite</code> property of cookie to <code>lax</code> mode
</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.APP_COOKIE_SECURE}
/>
</Flex>
</Flex>
<Flex>
<Flex w="100%" alignItems="baseline" flexDir="column">
<Text fontSize="sm">Use Secure Admin Cookie:</Text>
</Flex>
<Flex justifyContent="start">
<InputField
variables={variables}
setVariables={setVariables}
inputType={SwitchInputType.ADMIN_COOKIE_SECURE}
/>
</Flex>
</Flex>
</Stack>
</div>
);
};
export default Features;

View File

@@ -0,0 +1,200 @@
import React from 'react';
import {
Flex,
Stack,
Center,
Text,
useMediaQuery,
Button,
useToast,
} from '@chakra-ui/react';
import {
HiddenInputType,
TextInputType,
TextAreaInputType,
} from '../../constants';
import GenerateKeysModal from '../GenerateKeysModal';
import InputField from '../InputField';
import { copyTextToClipboard } from '../../utils';
const JSTConfigurations = ({
variables,
setVariables,
fieldVisibility,
setFieldVisibility,
SelectInputType,
getData,
HMACEncryptionType,
RSAEncryptionType,
ECDSAEncryptionType,
}: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
const toast = useToast();
const copyJSON = async () => {
try {
await copyTextToClipboard(
JSON.stringify({
type: variables.JWT_TYPE,
key: variables.JWT_PUBLIC_KEY || variables.JWT_SECRET,
}),
);
toast({
title: `JWT config copied successfully`,
isClosable: true,
status: 'success',
position: 'top-right',
});
} catch (err) {
console.error({
message: `Failed to copy JWT config`,
error: err,
});
toast({
title: `Failed to copy JWT config`,
isClosable: true,
status: 'error',
position: 'top-right',
});
}
};
return (
<div>
<Flex
borderRadius={5}
width="100%"
justifyContent="space-between"
alignItems="center"
paddingTop="2%"
>
<Text
fontSize={isNotSmallerScreen ? 'md' : 'sm'}
fontWeight="bold"
mb={5}
>
JWT (JSON Web Tokens) Configurations
</Text>
<Flex mb={7}>
<Button
colorScheme="blue"
h="1.75rem"
size="sm"
variant="ghost"
onClick={copyJSON}
>
Copy As JSON Config
</Button>
<GenerateKeysModal jwtType={variables.JWT_TYPE} getData={getData} />
</Flex>
</Flex>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Type:</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={SelectInputType}
value={SelectInputType}
options={{
...HMACEncryptionType,
...RSAEncryptionType,
...ECDSAEncryptionType,
}}
/>
</Flex>
</Flex>
{Object.values(HMACEncryptionType).includes(variables.JWT_TYPE) ? (
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Secret</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.JWT_SECRET}
/>
</Center>
</Flex>
) : (
<>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Public Key</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextAreaInputType.JWT_PUBLIC_KEY}
placeholder="Add public key here"
minH="25vh"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Private Key</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextAreaInputType.JWT_PRIVATE_KEY}
placeholder="Add private key here"
minH="25vh"
/>
</Center>
</Flex>
</>
)}
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm" orientation="vertical">
JWT Role Claim:
</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.JWT_ROLE_CLAIM}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default JSTConfigurations;

View File

@@ -0,0 +1,525 @@
import React from 'react';
import InputField from '../InputField';
import {
Flex,
Stack,
Center,
Text,
Box,
Divider,
useMediaQuery,
} from '@chakra-ui/react';
import {
FaGoogle,
FaGithub,
FaFacebookF,
FaLinkedin,
FaApple,
FaTwitter,
FaMicrosoft,
FaTwitch,
FaDiscord,
} from 'react-icons/fa';
import {
TextInputType,
HiddenInputType,
ResponseModes,
ResponseTypes,
SelectInputType,
} from '../../constants';
const OAuthConfig = ({
envVariables,
setVariables,
fieldVisibility,
setFieldVisibility,
}: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:667px)');
return (
<div>
<Box>
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={6}>
Authorizer Config
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Client ID</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
variables={envVariables}
setVariables={() => {}}
inputType={TextInputType.CLIENT_ID}
placeholder="Client ID"
readOnly={true}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Client Secret</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.CLIENT_SECRET}
placeholder="Client Secret"
readOnly={true}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Default Response Type:</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={SelectInputType.DEFAULT_AUTHORIZE_RESPONSE_TYPE}
value={SelectInputType}
options={ResponseTypes}
/>
</Flex>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Default Response Mode:</Text>
</Flex>
<Flex
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={SelectInputType.DEFAULT_AUTHORIZE_RESPONSE_MODE}
value={SelectInputType}
options={ResponseModes}
/>
</Flex>
</Flex>
</Stack>
<Divider mt={5} mb={2} color="blackAlpha.700" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={4}>
Social Media Logins
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #ff3e30"
borderRadius="5px"
>
<FaGoogle style={{ color: '#ff3e30' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.GOOGLE_CLIENT_ID}
placeholder="Google Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.GOOGLE_CLIENT_SECRET}
placeholder="Google Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #171515"
borderRadius="5px"
>
<FaGithub style={{ color: '#171515' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.GITHUB_CLIENT_ID}
placeholder="Github Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.GITHUB_CLIENT_SECRET}
placeholder="Github Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaFacebookF style={{ color: '#3b5998' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.FACEBOOK_CLIENT_ID}
placeholder="Facebook Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.FACEBOOK_CLIENT_SECRET}
placeholder="Facebook Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaLinkedin style={{ color: '#3b5998' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.LINKEDIN_CLIENT_ID}
placeholder="LinkedIn Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.LINKEDIN_CLIENT_SECRET}
placeholder="LinkedIn Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaApple style={{ color: '#3b5998' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.APPLE_CLIENT_ID}
placeholder="Apple Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.APPLE_CLIENT_SECRET}
placeholder="Apple Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaDiscord style={{ color: '#7289da' }} />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.DISCORD_CLIENT_ID}
placeholder="Discord Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.DISCORD_CLIENT_SECRET}
placeholder="Discord Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaTwitter />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.TWITTER_CLIENT_ID}
placeholder="Twitter Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.TWITTER_CLIENT_SECRET}
placeholder="Twitter Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaMicrosoft />
</Center>
<Center
w={isNotSmallerScreen ? '35%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID}
placeholder="Microsoft Active Directory TenantID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '35%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.MICROSOFT_CLIENT_ID}
placeholder="Microsoft Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.MICROSOFT_CLIENT_SECRET}
placeholder="Microsoft Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<FaTwitch />
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.TWITCH_CLIENT_ID}
placeholder="Twitch Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.TWITCH_CLIENT_SECRET}
placeholder="Twitch Client Secret"
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Center
w={isNotSmallerScreen ? '55px' : '35px'}
h="35px"
marginRight="1.5%"
border="1px solid #3b5998"
borderRadius="5px"
>
<img
src="https://authorizer.dev/_next/image?url=%2Fimages%2Froblox.png&w=25&q=25"
alt="Roblox"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
marginRight="1.5%"
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
inputType={TextInputType.ROBLOX_CLIENT_ID}
placeholder="Roblox Client ID"
/>
</Center>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={envVariables}
setVariables={setVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.ROBLOX_CLIENT_SECRET}
placeholder="Roblox Client Secret"
/>
</Center>
</Flex>
</Stack>
</Box>
</div>
);
};
export default OAuthConfig;

View File

@@ -0,0 +1,60 @@
import React from 'react';
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
import InputField from '../InputField';
import { TextInputType } from '../../constants';
const OrganizationInfo = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Organization Information
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">Organization Name:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.ORGANIZATION_NAME}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">Organization Logo:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={TextInputType.ORGANIZATION_LOGO}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default OrganizationInfo;

View File

@@ -0,0 +1,68 @@
import React from 'react';
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
import { ArrayInputType } from '../../constants';
import InputField from '../InputField';
const Roles = ({ variables, setVariables }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Roles
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Roles:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
overflow="hidden"
>
<InputField
borderRadius={7}
variables={variables}
setVariables={setVariables}
inputType={ArrayInputType.ROLES}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Default Roles:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
variables={variables}
setVariables={setVariables}
inputType={ArrayInputType.DEFAULT_ROLES}
/>
</Center>
</Flex>
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Protected Roles:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '2'}
>
<InputField
variables={variables}
setVariables={setVariables}
inputType={ArrayInputType.PROTECTED_ROLES}
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default Roles;

View File

@@ -0,0 +1,138 @@
import React from 'react';
import {
Flex,
Stack,
Center,
Text,
Input,
InputGroup,
InputRightElement,
useMediaQuery,
} from '@chakra-ui/react';
import { FaRegEyeSlash, FaRegEye } from 'react-icons/fa';
import InputField from '../InputField';
import { HiddenInputType } from '../../constants';
const SecurityAdminSecret = ({
variables,
setVariables,
fieldVisibility,
setFieldVisibility,
validateAdminSecretHandler,
adminSecret,
}: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Security (Admin Secret)
</Text>
<Stack
spacing={6}
padding="0 5%"
marginTop="3%"
border="1px solid #ff7875"
borderRadius="5px"
>
<Flex
marginTop={isNotSmallerScreen ? '3%' : '5%'}
direction={isNotSmallerScreen ? 'row' : 'column'}
>
<Flex
mt={3}
w={isNotSmallerScreen ? '30%' : '40%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">Old Admin Secret:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputGroup size="sm">
<Input
borderRadius={5}
size="sm"
placeholder="Enter Old Admin Secret"
value={adminSecret.value as string}
onChange={(event: any) => validateAdminSecretHandler(event)}
type={
!fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET]
? 'password'
: 'text'
}
/>
<InputRightElement
right="5px"
children={
<Flex>
{fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET] ? (
<Center
w="25px"
margin="0 1.5%"
cursor="pointer"
onClick={() =>
setFieldVisibility({
...fieldVisibility,
[HiddenInputType.OLD_ADMIN_SECRET]: false,
})
}
>
<FaRegEyeSlash color="#bfbfbf" />
</Center>
) : (
<Center
w="25px"
margin="0 1.5%"
cursor="pointer"
onClick={() =>
setFieldVisibility({
...fieldVisibility,
[HiddenInputType.OLD_ADMIN_SECRET]: true,
})
}
>
<FaRegEye color="#bfbfbf" />
</Center>
)}
</Flex>
}
/>
</InputGroup>
</Center>
</Flex>
<Flex
paddingBottom="3%"
direction={isNotSmallerScreen ? 'row' : 'column'}
>
<Flex
w={isNotSmallerScreen ? '30%' : '50%'}
justifyContent="start"
alignItems="center"
>
<Text fontSize="sm">New Admin Secret:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
borderRadius={5}
mb={3}
variables={variables}
setVariables={setVariables}
inputType={HiddenInputType.ADMIN_SECRET}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
isDisabled={adminSecret.disableInputField}
placeholder="Enter New Admin Secret"
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default SecurityAdminSecret;

View File

@@ -0,0 +1,42 @@
import React from 'react';
import { Flex, Stack, Center, Text, useMediaQuery } from '@chakra-ui/react';
import InputField from '../InputField';
const SessionStorage = ({ variables, setVariables, RedisURL }: any) => {
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<div>
{' '}
<Text fontSize="md" paddingTop="2%" fontWeight="bold" mb={5}>
Session Storage
</Text>
<Text fontStyle="italic" fontSize="sm" color="blackAlpha.500" mt={3}>
Note: Redis related environment variables cannot be updated from
dashboard. Please use .env file or OS environment variables to update
it.
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Redis URL:</Text>
</Flex>
<Center
w={isNotSmallerScreen ? '70%' : '100%'}
mt={isNotSmallerScreen ? '0' : '3'}
>
<InputField
disabled
borderRadius={5}
variables={variables}
setVariables={setVariables}
inputType={RedisURL}
placeholder="Redis URL"
/>
</Center>
</Flex>
</Stack>
</div>
);
};
export default SessionStorage;

View File

@@ -0,0 +1,247 @@
import React from 'react';
import {
Button,
Center,
Flex,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
Text,
useToast,
Input,
Spinner,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaSave } from 'react-icons/fa';
import {
ECDSAEncryptionType,
HMACEncryptionType,
RSAEncryptionType,
SelectInputType,
TextAreaInputType,
} from '../constants';
import InputField from './InputField';
import { GenerateKeys, UpdateEnvVariables } from '../graphql/mutation';
interface propTypes {
jwtType: string;
getData: Function;
}
interface stateVarTypes {
JWT_TYPE: string;
JWT_SECRET: string;
JWT_PRIVATE_KEY: string;
JWT_PUBLIC_KEY: string;
}
const initState: stateVarTypes = {
JWT_TYPE: '',
JWT_SECRET: '',
JWT_PRIVATE_KEY: '',
JWT_PUBLIC_KEY: '',
};
const GenerateKeysModal = ({ jwtType, getData }: propTypes) => {
const client = useClient();
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const [stateVariables, setStateVariables] = React.useState<stateVarTypes>({
...initState,
});
const [isLoading, setIsLoading] = React.useState(false);
React.useEffect(() => {
if (isOpen) {
setStateVariables({ ...initState, JWT_TYPE: jwtType });
}
}, [isOpen]);
const fetchKeys = async () => {
setIsLoading(true);
try {
const res = await client
.mutation(GenerateKeys, { params: { type: stateVariables.JWT_TYPE } })
.toPromise();
if (res?.error) {
toast({
title: 'Error occurred generating jwt keys',
isClosable: true,
status: 'error',
position: 'top-right',
});
closeHandler();
} else {
setStateVariables({
...stateVariables,
JWT_SECRET: res?.data?._generate_jwt_keys?.secret || '',
JWT_PRIVATE_KEY: res?.data?._generate_jwt_keys?.private_key || '',
JWT_PUBLIC_KEY: res?.data?._generate_jwt_keys?.public_key || '',
});
}
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
}
};
React.useEffect(() => {
if (isOpen && stateVariables.JWT_TYPE) {
fetchKeys();
}
}, [stateVariables.JWT_TYPE]);
const saveHandler = async () => {
const res = await client
.mutation(UpdateEnvVariables, { params: { ...stateVariables } })
.toPromise();
if (res.error) {
toast({
title: 'Error occurred setting jwt keys',
isClosable: true,
status: 'error',
position: 'top-right',
});
return;
}
toast({
title: 'JWT keys updated successfully',
isClosable: true,
status: 'success',
position: 'top-right',
});
closeHandler();
};
const closeHandler = () => {
setStateVariables({ ...initState });
getData();
onClose();
};
return (
<>
<Button
colorScheme="blue"
h="1.75rem"
size="sm"
variant="ghost"
onClick={onOpen}
>
Generate new keys
</Button>
<Modal isOpen={isOpen} onClose={closeHandler}>
<ModalOverlay />
<ModalContent>
<ModalHeader>New JWT keys</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Type:</Text>
</Flex>
<InputField
variables={stateVariables}
setVariables={setStateVariables}
inputType={SelectInputType.JWT_TYPE}
value={SelectInputType.JWT_TYPE}
options={{
...HMACEncryptionType,
...RSAEncryptionType,
...ECDSAEncryptionType,
}}
/>
</Flex>
{isLoading ? (
<Center minH="25vh">
<Spinner />
</Center>
) : (
<>
{Object.values(HMACEncryptionType).includes(
stateVariables.JWT_TYPE,
) ? (
<Flex marginTop="8">
<Flex w="23%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Secret</Text>
</Flex>
<Center w="77%">
<Input
size="sm"
value={stateVariables.JWT_SECRET}
onChange={(event: any) =>
setStateVariables({
...stateVariables,
JWT_SECRET: event.target.value,
})
}
readOnly
/>
</Center>
</Flex>
) : (
<>
<Flex marginTop="8">
<Flex w="23%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Public Key</Text>
</Flex>
<Center w="77%">
<InputField
variables={stateVariables}
setVariables={setStateVariables}
inputType={TextAreaInputType.JWT_PUBLIC_KEY}
placeholder="Add public key here"
minH="25vh"
readOnly
/>
</Center>
</Flex>
<Flex marginTop="8">
<Flex w="23%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Private Key</Text>
</Flex>
<Center w="77%">
<InputField
variables={stateVariables}
setVariables={setStateVariables}
inputType={TextAreaInputType.JWT_PRIVATE_KEY}
placeholder="Add private key here"
minH="25vh"
readOnly
/>
</Center>
</Flex>
</>
)}
</>
)}
</ModalBody>
<ModalFooter>
<Button
leftIcon={<FaSave />}
colorScheme="blue"
variant="solid"
onClick={saveHandler}
isDisabled={isLoading}
>
<Center h="100%" pt="5%">
Apply
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default GenerateKeysModal;

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import {
Box,
Flex,
@@ -12,7 +12,13 @@ import {
Select,
Textarea,
Switch,
Code,
Text,
MenuButton,
MenuList,
MenuItemOption,
MenuOptionGroup,
Button,
Menu,
} from '@chakra-ui/react';
import {
FaRegClone,
@@ -20,6 +26,7 @@ import {
FaRegEyeSlash,
FaPlus,
FaTimes,
FaAngleDown,
} from 'react-icons/fa';
import {
ArrayInputOperations,
@@ -29,56 +36,65 @@ import {
TextInputType,
TextAreaInputType,
SwitchInputType,
DateInputType,
MultiSelectInputType,
} from '../constants';
import { copyTextToClipboard } from '../utils';
const InputField = ({
inputType,
envVariables,
setEnvVariables,
variables,
setVariables,
fieldVisibility,
setFieldVisibility,
availableRoles,
// This prop is added to improve the user experience for the boolean ENV variable having `DISABLE_` prefix, as those values need to be considered in inverted form.
hasReversedValue,
...downshiftProps
}: any) => {
const props = {
size: 'sm',
...downshiftProps,
};
const [inputFieldVisibility, setInputFieldVisibility] = React.useState<
const [availableUserRoles, setAvailableUserRoles] =
useState<string[]>(availableRoles);
const [inputFieldVisibility, setInputFieldVisibility] = useState<
Record<string, boolean>
>({
ROLES: false,
DEFAULT_ROLES: false,
PROTECTED_ROLES: false,
ALLOWED_ORIGINS: false,
roles: false,
});
const [inputData, setInputData] = React.useState<Record<string, string>>({
const [inputData, setInputData] = useState<Record<string, string>>({
ROLES: '',
DEFAULT_ROLES: '',
PROTECTED_ROLES: '',
ALLOWED_ORIGINS: '',
roles: '',
});
const updateInputHandler = (
type: string,
operation: any,
role: string = ''
role: string = '',
) => {
if (operation === ArrayInputOperations.APPEND) {
if (inputData[type] !== '') {
setEnvVariables({
...envVariables,
[type]: [...envVariables[type], inputData[type]],
setVariables({
...variables,
[type]: [...variables[type], inputData[type]],
});
setInputData({ ...inputData, [type]: '' });
}
setInputFieldVisibility({ ...inputFieldVisibility, [type]: false });
}
if (operation === ArrayInputOperations.REMOVE) {
let updatedEnvVars = envVariables[type].filter(
(item: string) => item !== role
let updatedEnvVars = variables[type].filter(
(item: string) => item !== role,
);
setEnvVariables({
...envVariables,
setVariables({
...variables,
[type]: updatedEnvVars,
});
}
@@ -88,14 +104,14 @@ const InputField = ({
<InputGroup size="sm">
<Input
{...props}
value={envVariables[inputType]}
value={variables[inputType] ? variables[inputType] : ''}
onChange={(
event: Event & {
target: HTMLInputElement;
}
},
) =>
setEnvVariables({
...envVariables,
setVariables({
...variables,
[inputType]: event.target.value,
})
}
@@ -103,7 +119,7 @@ const InputField = ({
<InputRightElement
children={<FaRegClone color="#bfbfbf" />}
cursor="pointer"
onClick={() => copyTextToClipboard(envVariables[inputType])}
onClick={() => copyTextToClipboard(variables[inputType])}
/>
</InputGroup>
);
@@ -113,14 +129,14 @@ const InputField = ({
<InputGroup size="sm">
<Input
{...props}
value={envVariables[inputType]}
value={variables[inputType] || ''}
onChange={(
event: Event & {
target: HTMLInputElement;
}
},
) =>
setEnvVariables({
...envVariables,
setVariables({
...variables,
[inputType]: event.target.value,
})
}
@@ -163,7 +179,7 @@ const InputField = ({
w="25px"
margin="0 1.5%"
cursor="pointer"
onClick={() => copyTextToClipboard(envVariables[inputType])}
onClick={() => copyTextToClipboard(variables[inputType])}
>
<FaRegClone color="#bfbfbf" />
</Center>
@@ -178,13 +194,15 @@ const InputField = ({
<Flex
border="1px solid #e2e8f0"
w="100%"
h="45px"
wrap="wrap"
overflow="scroll"
padding="1%"
borderRadius={5}
paddingTop="0.5%"
overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'}
overflowY="hidden"
justifyContent="start"
alignItems="center"
>
{envVariables[inputType].map((role: string, index: number) => (
<Box key={index} margin="1" role="group">
{variables[inputType].map((role: string, index: number) => (
<Box key={index} margin="0.5%" role="group">
<Tag
size="sm"
variant="outline"
@@ -202,7 +220,7 @@ const InputField = ({
updateInputHandler(
inputType,
ArrayInputOperations.REMOVE,
role
role,
)
}
/>
@@ -210,12 +228,13 @@ const InputField = ({
</Box>
))}
{inputFieldVisibility[inputType] ? (
<Box ml="1.15%">
<Box ml="1%" mb="0.75%">
<Input
type="text"
size="xs"
minW="150px"
placeholder="add a new value"
value={inputData[inputType]}
value={inputData[inputType] || ''}
onChange={(e: any) => {
setInputData({ ...inputData, [inputType]: e.target.value });
}}
@@ -231,7 +250,7 @@ const InputField = ({
</Box>
) : (
<Box
margin="1"
marginLeft="0.5%"
cursor="pointer"
onClick={() =>
setInputFieldVisibility({
@@ -254,43 +273,160 @@ const InputField = ({
);
}
if (Object.values(SelectInputType).includes(inputType)) {
const { options, ...rest } = props;
return (
<Select size="sm" {...props}>
{[envVariables[inputType]].map((value: string) => (
<option value="value" key={value}>
{value}
<Select
size="sm"
{...rest}
value={variables[inputType] ? variables[inputType] : ''}
onChange={(e) =>
setVariables({ ...variables, [inputType]: e.target.value })
}
>
{Object.entries(options).map(([key, value]: any) => (
<option value={value} key={key}>
{key}
</option>
))}
</Select>
);
}
if (Object.values(MultiSelectInputType).includes(inputType)) {
return (
<Flex w="100%" style={{ position: 'relative' }}>
<Flex
border="1px solid #e2e8f0"
w="100%"
borderRadius="var(--chakra-radii-sm)"
p="1% 0 0 2.5%"
overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'}
overflowY="hidden"
justifyContent="space-between"
alignItems="center"
>
<Flex justifyContent="start" alignItems="center" w="100%" wrap="wrap">
{variables[inputType].map((role: string, index: number) => (
<Box key={index} margin="0.5%" role="group">
<Tag
size="sm"
variant="outline"
colorScheme="gray"
minW="fit-content"
>
<TagLabel cursor="default">{role}</TagLabel>
<TagRightIcon
boxSize="12px"
as={FaTimes}
display="none"
cursor="pointer"
_groupHover={{ display: 'block' }}
onClick={() =>
updateInputHandler(
inputType,
ArrayInputOperations.REMOVE,
role,
)
}
/>
</Tag>
</Box>
))}
</Flex>
<Menu matchWidth={true}>
<MenuButton px="10px" py="7.5px">
<FaAngleDown />
</MenuButton>
<MenuList
position="absolute"
top="0"
right="0"
zIndex="10"
maxH="150"
overflowX="scroll"
>
<MenuOptionGroup
title={undefined}
value={variables[inputType]}
type="checkbox"
onChange={(values: string[] | string) => {
setVariables({
...variables,
[inputType]: values,
});
}}
>
{availableUserRoles.map((role) => {
return (
<MenuItemOption
key={`multiselect-menu-${role}`}
value={role}
>
{role}
</MenuItemOption>
);
})}
</MenuOptionGroup>
</MenuList>
</Menu>
</Flex>
</Flex>
);
}
if (Object.values(TextAreaInputType).includes(inputType)) {
return (
<Textarea
{...props}
size="lg"
value={inputData[inputType]}
onChange={(e: any) => {
setInputData({ ...inputData, [inputType]: e.target.value });
}}
fontSize={14}
value={variables[inputType] ? variables[inputType] : ''}
onChange={(
event: Event & {
target: HTMLInputElement;
},
) =>
setVariables({
...variables,
[inputType]: event.target.value,
})
}
/>
);
}
if (Object.values(SwitchInputType).includes(inputType)) {
return (
<Flex w="25%" justifyContent="space-between">
<Code h="75%">Off</Code>
<Text h="75%" fontWeight="bold" marginRight="2">
Off
</Text>
<Switch
size="md"
isChecked={envVariables[inputType]}
isChecked={
hasReversedValue ? !variables[inputType] : variables[inputType]
}
onChange={() => {
setEnvVariables({
...envVariables,
[inputType]: !envVariables[inputType],
setVariables({
...variables,
[inputType]: !variables[inputType],
});
}}
/>
<Code h="75%">On</Code>
<Text h="75%" fontWeight="bold" marginLeft="2">
On
</Text>
</Flex>
);
}
if (Object.values(DateInputType).includes(inputType)) {
return (
<Flex border="1px solid #e2e8f0" w="100%" h="33px" padding="1%">
<input
type="date"
style={{ width: '100%', paddingLeft: '2.5%' }}
value={variables[inputType] ? variables[inputType] : ''}
onChange={(e) =>
setVariables({ ...variables, [inputType]: e.target.value })
}
/>
</Flex>
);
}

View File

@@ -0,0 +1,385 @@
import React, { useState, useCallback, useEffect } from 'react';
import {
Button,
Center,
Flex,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
useToast,
Tabs,
TabList,
Tab,
TabPanels,
TabPanel,
InputGroup,
Input,
InputRightElement,
Text,
Link,
Tooltip,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import { FaUserPlus, FaMinusCircle, FaPlus, FaUpload } from 'react-icons/fa';
import { useDropzone } from 'react-dropzone';
import { validateEmail, validateURI } from '../utils';
import { InviteMembers } from '../graphql/mutation';
import { ArrayInputOperations } from '../constants';
import parseCSV from '../utils/parseCSV';
interface stateDataTypes {
value: string;
isInvalid: boolean;
}
interface requestParamTypes {
emails: string[];
redirect_uri?: string;
}
const initData: stateDataTypes = {
value: '',
isInvalid: false,
};
const InviteMembersModal = ({
updateUserList,
disabled = true,
}: {
updateUserList: Function;
disabled: boolean;
}) => {
const client = useClient();
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const [tabIndex, setTabIndex] = useState<number>(0);
const [redirectURI, setRedirectURI] = useState<stateDataTypes>({
...initData,
});
const [emails, setEmails] = useState<stateDataTypes[]>([{ ...initData }]);
const [disableSendButton, setDisableSendButton] = useState<boolean>(false);
const [loading, setLoading] = React.useState<boolean>(false);
useEffect(() => {
if (redirectURI.isInvalid) {
setDisableSendButton(true);
} else if (emails.some((emailData) => emailData.isInvalid)) {
setDisableSendButton(true);
} else {
setDisableSendButton(false);
}
}, [redirectURI, emails]);
useEffect(() => {
return () => {
setRedirectURI({ ...initData });
setEmails([{ ...initData }]);
};
}, []);
const sendInviteHandler = async () => {
setLoading(true);
try {
const emailList = emails
.filter((emailData) => !emailData.isInvalid)
.map((emailData) => emailData.value);
const params: requestParamTypes = {
emails: emailList,
};
if (redirectURI.value !== '' && !redirectURI.isInvalid) {
params.redirect_uri = redirectURI.value;
}
if (emailList.length > 0) {
const res = await client
.mutation(InviteMembers, {
params,
})
.toPromise();
if (res.error) {
throw new Error('Internal server error');
return;
}
toast({
title: 'Invites sent successfully!',
isClosable: true,
status: 'success',
position: 'top-right',
});
setLoading(false);
updateUserList();
} else {
throw new Error('Please add emails');
}
} catch (error: any) {
toast({
title: error?.message || 'Error occurred, try again!',
isClosable: true,
status: 'error',
position: 'top-right',
});
setLoading(false);
}
closeModalHandler();
};
const updateEmailListHandler = (operation: string, index: number = 0) => {
switch (operation) {
case ArrayInputOperations.APPEND:
setEmails([...emails, { ...initData }]);
break;
case ArrayInputOperations.REMOVE:
const updatedEmailList = [...emails];
updatedEmailList.splice(index, 1);
setEmails(updatedEmailList);
break;
default:
break;
}
};
const inputChangeHandler = (value: string, index: number) => {
const updatedEmailList = [...emails];
updatedEmailList[index].value = value;
updatedEmailList[index].isInvalid = !validateEmail(value);
setEmails(updatedEmailList);
};
const changeTabsHandler = (index: number) => {
setTabIndex(index);
};
const onDrop = useCallback(async (acceptedFiles) => {
const result = await parseCSV(acceptedFiles[0], ',');
setEmails(result);
changeTabsHandler(0);
}, []);
const setRedirectURIHandler = (value: string) => {
const updatedRedirectURI: stateDataTypes = {
value: '',
isInvalid: false,
};
updatedRedirectURI.value = value;
updatedRedirectURI.isInvalid = !validateURI(value);
setRedirectURI(updatedRedirectURI);
};
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept: 'text/csv',
});
const closeModalHandler = () => {
setRedirectURI({
value: '',
isInvalid: false,
});
setEmails([
{
value: '',
isInvalid: false,
},
]);
onClose();
};
return (
<>
<Button
leftIcon={<FaUserPlus />}
colorScheme="blue"
variant="solid"
onClick={onOpen}
isDisabled={disabled}
size="sm"
>
<Center h="100%">
{disabled ? (
<Tooltip
mr={8}
mt={1}
hasArrow
bg="gray.300"
color="black"
label="Email verification is disabled, refer to 'Features' tab within 'Environment' to enable it."
>
Invite Members
</Tooltip>
) : (
'Invite Members'
)}
</Center>{' '}
</Button>
<Modal isOpen={isOpen} onClose={closeModalHandler} size="xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>Invite Members</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Tabs
isFitted
variant="enclosed"
index={tabIndex}
onChange={changeTabsHandler}
>
<TabList>
<Tab>Enter emails</Tab>
<Tab>Upload CSV</Tab>
</TabList>
<TabPanels
border="1px"
borderTop="0"
borderBottomRadius="5px"
borderColor="inherit"
>
<TabPanel>
<Flex flexDirection="column">
<Flex
width="100%"
justifyContent="start"
alignItems="center"
marginBottom="2%"
>
<Flex marginLeft="2.5%">Redirect URI</Flex>
</Flex>
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
marginBottom="2%"
>
<InputGroup size="md" marginBottom="2.5%">
<Input
pr="4.5rem"
type="text"
placeholder="https://domain.com/sign-up"
value={redirectURI.value}
isInvalid={redirectURI.isInvalid}
onChange={(e) =>
setRedirectURIHandler(e.currentTarget.value)
}
/>
</InputGroup>
</Flex>
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
marginBottom="2%"
>
<Flex marginLeft="2.5%">Emails</Flex>
<Flex>
<Button
leftIcon={<FaPlus />}
colorScheme="blue"
h="1.75rem"
size="sm"
variant="ghost"
onClick={() =>
updateEmailListHandler(ArrayInputOperations.APPEND)
}
>
Add more emails
</Button>
</Flex>
</Flex>
<Flex flexDirection="column" maxH={250} overflowY="scroll">
{emails.map((emailData, index) => (
<Flex
key={`email-data-${index}`}
justifyContent="center"
alignItems="center"
>
<InputGroup size="md" marginBottom="2.5%">
<Input
pr="4.5rem"
type="text"
placeholder="name@domain.com"
value={emailData.value}
isInvalid={emailData.isInvalid}
onChange={(e) =>
inputChangeHandler(e.currentTarget.value, index)
}
/>
<InputRightElement width="3rem">
<Button
h="1.75rem"
size="sm"
colorScheme="blackAlpha"
variant="ghost"
onClick={() =>
updateEmailListHandler(
ArrayInputOperations.REMOVE,
index,
)
}
>
<FaMinusCircle />
</Button>
</InputRightElement>
</InputGroup>
</Flex>
))}
</Flex>
</Flex>
</TabPanel>
<TabPanel>
<Flex
justify="center"
align="center"
textAlign="center"
bg="#f0f0f0"
h={230}
p={50}
m={2}
borderRadius={5}
{...getRootProps()}
>
<input {...getInputProps()} />
{isDragActive ? (
<Text>Drop the files here...</Text>
) : (
<Flex
flexDirection="column"
justifyContent="center"
alignItems="center"
>
<Center boxSize="20" color="blackAlpha.500">
<FaUpload fontSize="40" />
</Center>
<Text>
Drag 'n' drop the csv file here, or click to select.
</Text>
<Text size="xs">
Download{' '}
<Link
href={`/dashboard/public/sample.csv`}
download="sample.csv"
color="blue.600"
onClick={(e) => e.stopPropagation()}
>
{' '}
sample.csv
</Link>{' '}
and modify it.{' '}
</Text>
</Flex>
)}
</Flex>
</TabPanel>
</TabPanels>
</Tabs>
</ModalBody>
<ModalFooter>
<Button
colorScheme="blue"
variant="solid"
onClick={sendInviteHandler}
isDisabled={disableSendButton || loading}
>
<Center h="100%" pt="5%">
Send
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default InviteMembersModal;

View File

@@ -1,4 +1,4 @@
import React, { ReactNode } from 'react';
import React, { Fragment, ReactNode } from 'react';
import {
IconButton,
Box,
@@ -17,32 +17,104 @@ import {
MenuButton,
MenuItem,
MenuList,
Accordion,
AccordionButton,
AccordionPanel,
AccordionItem,
useMediaQuery,
} from '@chakra-ui/react';
import {
FiHome,
FiUser,
FiCode,
FiSettings,
FiMenu,
FiUser,
FiUsers,
FiChevronDown,
FiLink,
FiFileText,
} from 'react-icons/fi';
import { BiCustomize } from 'react-icons/bi';
import { AiOutlineKey } from 'react-icons/ai';
import { SiOpenaccess, SiJsonwebtokens } from 'react-icons/si';
import { MdSecurity } from 'react-icons/md';
import { RiDatabase2Line } from 'react-icons/ri';
import { BsCheck2Circle } from 'react-icons/bs';
import { HiOutlineMail, HiOutlineOfficeBuilding } from 'react-icons/hi';
import { IconType } from 'react-icons';
import { ReactText } from 'react';
import { useMutation } from 'urql';
import { useMutation, useQuery } from 'urql';
import { NavLink, useNavigate, useLocation } from 'react-router-dom';
import { useAuthContext } from '../contexts/AuthContext';
import { AdminLogout } from '../graphql/mutation';
import { MetaQuery } from '../graphql/queries';
interface SubRoutes {
name: string;
icon: IconType;
route: string;
}
interface LinkItemProps {
name: string;
icon: IconType;
route: string;
subRoutes?: SubRoutes[];
}
const LinkItems: Array<LinkItemProps> = [
{ name: 'Home', icon: FiHome, route: '/' },
{
name: 'Environment ',
icon: FiSettings,
route: '/',
subRoutes: [
{
name: 'OAuth Config',
icon: AiOutlineKey,
route: '/oauth-setting',
},
{ name: 'Roles', icon: FiUser, route: '/roles' },
{
name: 'JWT Secrets',
icon: SiJsonwebtokens,
route: '/jwt-config',
},
{
name: 'Session Storage',
icon: RiDatabase2Line,
route: '/session-storage',
},
{
name: 'Email Configurations',
icon: HiOutlineMail,
route: '/email-config',
},
{
name: 'Domain White Listing',
icon: BsCheck2Circle,
route: '/whitelist-variables',
},
{
name: 'Organization Info',
icon: HiOutlineOfficeBuilding,
route: '/organization-info',
},
{ name: 'Access Token', icon: SiOpenaccess, route: '/access-token' },
{
name: 'Features',
icon: BiCustomize,
route: '/features',
},
{ name: 'Database', icon: RiDatabase2Line, route: '/db-cred' },
{
name: ' Security',
icon: MdSecurity,
route: '/admin-secret',
},
],
},
{ name: 'Users', icon: FiUsers, route: '/users' },
{ name: 'Environment Variables', icon: FiSettings, route: '/environment' },
{ name: 'Webhooks', icon: FiLink, route: '/webhooks' },
{ name: 'Email Templates', icon: FiFileText, route: '/email-templates' },
];
interface SidebarProps extends BoxProps {
@@ -51,20 +123,28 @@ interface SidebarProps extends BoxProps {
export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
const { pathname } = useLocation();
const [{ data }] = useQuery({ query: MetaQuery });
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<Box
transition="3s ease"
bg={useColorModeValue('white', 'gray.900')}
borderRight="1px"
borderRightColor={useColorModeValue('gray.200', 'gray.700')}
w={{ base: 'full', md: 60 }}
w={{ base: 'full', md: '64' }}
pos="fixed"
h="full"
{...rest}
>
<Flex h="20" alignItems="center" mx="8" justifyContent="space-between">
<Flex
h="20"
alignItems="center"
mx="18"
justifyContent="space-between"
flexDirection="row"
>
<NavLink to="/">
<Flex alignItems="center">
<Flex alignItems="center" mt="6">
<Image
src="https://authorizer.dev/images/logo.png"
alt="logo"
@@ -77,34 +157,104 @@ export const Sidebar = ({ onClose, ...rest }: SidebarProps) => {
</NavLink>
<CloseButton display={{ base: 'flex', md: 'none' }} onClick={onClose} />
</Flex>
{LinkItems.map((link) => (
<NavLink key={link.name} to={link.route}>
<NavItem
icon={link.icon}
color={pathname === link.route ? 'blue.500' : ''}
>
{link.name}
</NavItem>
</NavLink>
))}
<Link
href="/playground"
target="_blank"
style={{
textDecoration: 'none',
}}
_focus={{ _boxShadow: 'none' }}
>
<NavItem icon={FiCode}>API Playground</NavItem>
</Link>
<Accordion defaultIndex={[0]} allowMultiple>
<AccordionItem textAlign="center" border="none" w="100%">
{LinkItems.map((link) =>
link?.subRoutes ? (
<div key={link.name}>
<AccordionButton _focus={{ boxShadow: 'none' }}>
<Text as="div" fontSize="md">
<NavItem
icon={link.icon}
color={pathname === link.route ? 'blue.500' : ''}
style={{ outline: 'unset' }}
height={12}
ml={-1}
mb={isNotSmallerScreen ? -1 : -4}
w={isNotSmallerScreen ? '100%' : '310%'}
>
<Fragment>
{link.name}
<Box display={{ base: 'none', md: 'flex' }} ml={12}>
<FiChevronDown />
</Box>
</Fragment>
</NavItem>
</Text>
</AccordionButton>
<AccordionPanel>
{link.subRoutes?.map((sublink) => (
<NavLink
key={sublink.name}
to={sublink.route}
onClick={onClose}
>
{' '}
<Text as="div" fontSize="xs" ml={2}>
<NavItem
icon={sublink.icon}
color={pathname === sublink.route ? 'blue.500' : ''}
height={8}
>
{sublink.name}
</NavItem>{' '}
</Text>
</NavLink>
))}
</AccordionPanel>
</div>
) : (
<NavLink key={link.name} to={link.route}>
{' '}
<Text as="div" fontSize="md" w="100%" mt={-2}>
<NavItem
icon={link.icon}
color={pathname === link.route ? 'blue.500' : ''}
height={12}
onClick={onClose}
>
{link.name}
</NavItem>{' '}
</Text>
</NavLink>
),
)}
<Link
href="/playground"
target="_blank"
style={{
textDecoration: 'none',
}}
_focus={{ _boxShadow: 'none' }}
>
<NavItem icon={FiCode}>API Playground</NavItem>
</Link>
</AccordionItem>
</Accordion>
{data?.meta?.version && (
<Flex alignContent="center">
{' '}
<Text
color="gray.400"
fontSize="sm"
textAlign="center"
position="absolute"
bottom="5"
left="7"
>
Current Version: {data.meta.version}
</Text>
</Flex>
)}
</Box>
);
};
interface NavItemProps extends FlexProps {
icon: IconType;
children: ReactText;
children: ReactText | JSX.Element | JSX.Element[];
}
export const NavItem = ({ icon, children, ...rest }: NavItemProps) => {
return (
@@ -152,7 +302,7 @@ export const MobileNav = ({ onOpen, ...rest }: MobileProps) => {
return (
<Flex
ml={{ base: 0, md: 60 }}
ml={{ base: 0, md: 64 }}
px={{ base: 4, md: 4 }}
height="20"
position="fixed"
@@ -189,7 +339,7 @@ export const MobileNav = ({ onOpen, ...rest }: MobileProps) => {
transition="all 0.3s"
_focus={{ boxShadow: 'none' }}
>
<HStack>
<HStack mr={5}>
<FiUser />
<VStack
display={{ base: 'none', md: 'flex' }}

View File

@@ -0,0 +1,565 @@
import React, { useEffect, useRef, useState } from 'react';
import {
Button,
Center,
Flex,
Input,
InputGroup,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Select,
Text,
useDisclosure,
useToast,
Alert,
AlertIcon,
Collapse,
Box,
TableContainer,
Table,
Thead,
Tr,
Th,
Tbody,
Td,
Code,
Radio,
RadioGroup,
Stack,
Textarea,
} from '@chakra-ui/react';
import { FaPlus, FaAngleDown, FaAngleUp } from 'react-icons/fa';
import { useClient } from 'urql';
import EmailEditor from 'react-email-editor';
import {
UpdateModalViews,
EmailTemplateInputDataFields,
emailTemplateEventNames,
emailTemplateVariables,
EmailTemplateEditors,
} from '../constants';
import { capitalizeFirstLetter } from '../utils';
import { AddEmailTemplate, EditEmailTemplate } from '../graphql/mutation';
interface selectedEmailTemplateDataTypes {
[EmailTemplateInputDataFields.ID]: string;
[EmailTemplateInputDataFields.EVENT_NAME]: string;
[EmailTemplateInputDataFields.SUBJECT]: string;
[EmailTemplateInputDataFields.CREATED_AT]: number;
[EmailTemplateInputDataFields.TEMPLATE]: string;
[EmailTemplateInputDataFields.DESIGN]: string;
}
interface UpdateEmailTemplateInputPropTypes {
view: UpdateModalViews;
selectedTemplate?: selectedEmailTemplateDataTypes;
fetchEmailTemplatesData: Function;
}
interface templateVariableDataTypes {
text: string;
value: string;
description: string;
}
interface emailTemplateDataType {
[EmailTemplateInputDataFields.EVENT_NAME]: string;
[EmailTemplateInputDataFields.SUBJECT]: string;
[EmailTemplateInputDataFields.TEMPLATE]: string;
[EmailTemplateInputDataFields.DESIGN]: string;
}
interface validatorDataType {
[EmailTemplateInputDataFields.SUBJECT]: boolean;
}
const initTemplateData: emailTemplateDataType = {
[EmailTemplateInputDataFields.EVENT_NAME]: emailTemplateEventNames.Signup,
[EmailTemplateInputDataFields.SUBJECT]: '',
[EmailTemplateInputDataFields.TEMPLATE]: '',
[EmailTemplateInputDataFields.DESIGN]: '',
};
const initTemplateValidatorData: validatorDataType = {
[EmailTemplateInputDataFields.SUBJECT]: true,
};
const UpdateEmailTemplate = ({
view,
selectedTemplate,
fetchEmailTemplatesData,
}: UpdateEmailTemplateInputPropTypes) => {
const client = useClient();
const toast = useToast();
const emailEditorRef = useRef(null);
const { isOpen, onOpen, onClose } = useDisclosure();
const [loading, setLoading] = useState<boolean>(false);
const [editor, setEditor] = useState<string>(
EmailTemplateEditors.PLAIN_HTML_EDITOR,
);
const [templateVariables, setTemplateVariables] = useState<
templateVariableDataTypes[]
>([]);
const [templateData, setTemplateData] = useState<emailTemplateDataType>({
...initTemplateData,
});
const [validator, setValidator] = useState<validatorDataType>({
...initTemplateValidatorData,
});
const [isDynamicVariableInfoOpen, setIsDynamicVariableInfoOpen] =
useState<boolean>(false);
const onReady = () => {
if (selectedTemplate) {
const { design } = selectedTemplate;
try {
if (design) {
const designData = JSON.parse(design);
// @ts-ignore
emailEditorRef.current.editor.loadDesign(designData);
}
} catch (error) {
console.error(error);
onClose();
}
}
};
const inputChangehandler = (inputType: string, value: any) => {
if (inputType !== EmailTemplateInputDataFields.EVENT_NAME) {
setValidator({
...validator,
[inputType]: value?.trim().length,
});
}
setTemplateData({ ...templateData, [inputType]: value });
};
const validateData = () => {
return (
!loading &&
templateData[EmailTemplateInputDataFields.EVENT_NAME].length > 0 &&
templateData[EmailTemplateInputDataFields.SUBJECT].length > 0 &&
validator[EmailTemplateInputDataFields.SUBJECT]
);
};
const updateTemplate = async (params: emailTemplateDataType) => {
let res: any = {};
if (
view === UpdateModalViews.Edit &&
selectedTemplate?.[EmailTemplateInputDataFields.ID]
) {
res = await client
.mutation(EditEmailTemplate, {
params: {
...params,
id: selectedTemplate[EmailTemplateInputDataFields.ID],
},
})
.toPromise();
} else {
res = await client.mutation(AddEmailTemplate, { params }).toPromise();
}
setLoading(false);
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'top-right',
});
} else if (
res.data?._add_email_template ||
res.data?._update_email_template
) {
toast({
title: capitalizeFirstLetter(
res.data?._add_email_template?.message ||
res.data?._update_email_template?.message,
),
isClosable: true,
status: 'success',
position: 'top-right',
});
setTemplateData({
...initTemplateData,
});
setValidator({ ...initTemplateValidatorData });
fetchEmailTemplatesData();
}
};
const saveData = async () => {
if (!validateData()) return;
setLoading(true);
let params: emailTemplateDataType = {
[EmailTemplateInputDataFields.EVENT_NAME]:
templateData[EmailTemplateInputDataFields.EVENT_NAME],
[EmailTemplateInputDataFields.SUBJECT]:
templateData[EmailTemplateInputDataFields.SUBJECT],
[EmailTemplateInputDataFields.TEMPLATE]:
templateData[EmailTemplateInputDataFields.TEMPLATE],
[EmailTemplateInputDataFields.DESIGN]: '',
};
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
// @ts-ignore
await emailEditorRef.current.editor.exportHtml(async (data) => {
const { design, html } = data;
if (!html || !design) {
setLoading(false);
return;
}
params = {
...params,
[EmailTemplateInputDataFields.TEMPLATE]: html.trim(),
[EmailTemplateInputDataFields.DESIGN]: JSON.stringify(design),
};
await updateTemplate(params);
});
} else {
await updateTemplate(params);
}
view === UpdateModalViews.ADD && onClose();
};
const resetData = () => {
if (selectedTemplate) {
setTemplateData(selectedTemplate);
} else {
setTemplateData({ ...initTemplateData });
}
};
// set template data if edit modal is open
useEffect(() => {
if (
isOpen &&
view === UpdateModalViews.Edit &&
selectedTemplate &&
Object.keys(selectedTemplate || {}).length
) {
const { id, created_at, ...rest } = selectedTemplate;
setTemplateData(rest);
}
}, [isOpen]);
// set template variables
useEffect(() => {
const updatedTemplateVariables = Object.entries(
emailTemplateVariables,
).reduce((acc, [key, val]): any => {
if (
(templateData[EmailTemplateInputDataFields.EVENT_NAME] !==
emailTemplateEventNames['Verify Otp'] &&
val === emailTemplateVariables.otp) ||
(templateData[EmailTemplateInputDataFields.EVENT_NAME] ===
emailTemplateEventNames['Verify Otp'] &&
val === emailTemplateVariables.verification_url)
) {
return acc;
}
return [
...acc,
{
text: key,
value: val.value,
description: val.description,
},
];
}, []);
setTemplateVariables(updatedTemplateVariables);
}, [templateData[EmailTemplateInputDataFields.EVENT_NAME]]);
// change editor
useEffect(() => {
if (isOpen && selectedTemplate) {
const { design } = selectedTemplate;
if (design) {
setEditor(EmailTemplateEditors.UNLAYER_EDITOR);
} else {
setEditor(EmailTemplateEditors.PLAIN_HTML_EDITOR);
}
}
}, [isOpen, selectedTemplate]);
// reset fields when editor is changed
useEffect(() => {
if (selectedTemplate?.design) {
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
setTemplateData({
...templateData,
[EmailTemplateInputDataFields.TEMPLATE]: selectedTemplate.template,
[EmailTemplateInputDataFields.DESIGN]: selectedTemplate.design,
});
} else {
setTemplateData({
...templateData,
[EmailTemplateInputDataFields.TEMPLATE]: '',
[EmailTemplateInputDataFields.DESIGN]: '',
});
}
} else if (selectedTemplate?.template) {
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
setTemplateData({
...templateData,
[EmailTemplateInputDataFields.TEMPLATE]: '',
[EmailTemplateInputDataFields.DESIGN]: '',
});
} else {
setTemplateData({
...templateData,
[EmailTemplateInputDataFields.TEMPLATE]: selectedTemplate?.template,
[EmailTemplateInputDataFields.DESIGN]: '',
});
}
}
}, [editor]);
return (
<>
{view === UpdateModalViews.ADD ? (
<Button
leftIcon={<FaPlus />}
colorScheme="blue"
variant="solid"
onClick={onOpen}
isDisabled={false}
size="sm"
>
<Center h="100%">Add Template</Center>{' '}
</Button>
) : (
<MenuItem onClick={onOpen}>Edit</MenuItem>
)}
<Modal
isOpen={isOpen}
onClose={() => {
resetData();
onClose();
}}
size="6xl"
>
<ModalOverlay />
<ModalContent>
<ModalHeader>
{view === UpdateModalViews.ADD
? 'Add New Email Template'
: 'Edit Email Template'}
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Flex
flexDirection="column"
border="1px"
borderRadius="md"
borderColor="gray.200"
p="5"
>
<Alert
status="info"
onClick={() =>
setIsDynamicVariableInfoOpen(!isDynamicVariableInfoOpen)
}
borderRadius="5"
marginBottom={5}
cursor="pointer"
fontSize="sm"
>
<AlertIcon />
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
>
<Box width="85%">
<b>Note:</b> You can add set of dynamic variables to subject
and email body. Click here to see the set of dynamic
variables.
</Box>
{isDynamicVariableInfoOpen ? <FaAngleUp /> : <FaAngleDown />}
</Flex>
</Alert>
<Collapse
style={{
width: '100%',
}}
in={isDynamicVariableInfoOpen}
>
<TableContainer
background="gray.100"
borderRadius={5}
height={200}
width="100%"
overflowY="auto"
overflowWrap="break-word"
>
<Table variant="simple">
<Thead>
<Tr>
<Th>Variable</Th>
<Th>Description</Th>
</Tr>
</Thead>
<Tbody>
{templateVariables.map((i) => (
<Tr key={i.text}>
<Td>
<Code fontSize="sm">{`{{.${i.text}}}`}</Code>
</Td>
<Td>
<Text
size="sm"
fontSize="sm"
overflowWrap="break-word"
width="100%"
>
{i.description}
</Text>
</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
</Collapse>
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
marginBottom="2%"
>
<Flex flex="1">Event Name</Flex>
<Flex flex="3">
<Select
size="md"
value={
templateData[EmailTemplateInputDataFields.EVENT_NAME]
}
onChange={(e) =>
inputChangehandler(
EmailTemplateInputDataFields.EVENT_NAME,
e.currentTarget.value,
)
}
>
{Object.entries(emailTemplateEventNames).map(
([key, value]: any) => (
<option value={value} key={key}>
{key}
</option>
),
)}
</Select>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="start"
alignItems="center"
marginBottom="2%"
>
<Flex flex="1">Subject</Flex>
<Flex flex="3">
<InputGroup size="md">
<Input
pr="4.5rem"
type="text"
placeholder="Subject Line"
value={templateData[EmailTemplateInputDataFields.SUBJECT]}
isInvalid={
!validator[EmailTemplateInputDataFields.SUBJECT]
}
onChange={(e) =>
inputChangehandler(
EmailTemplateInputDataFields.SUBJECT,
e.currentTarget.value,
)
}
/>
</InputGroup>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="flex-start"
alignItems="center"
marginBottom="2%"
>
<Flex flex="1">Template Body</Flex>
<Flex flex="3">
<RadioGroup
onChange={(value) => setEditor(value)}
value={editor}
>
<Stack direction="row" spacing="50px">
<Radio value={EmailTemplateEditors.PLAIN_HTML_EDITOR}>
Plain HTML
</Radio>
<Radio value={EmailTemplateEditors.UNLAYER_EDITOR}>
Unlayer Editor
</Radio>
</Stack>
</RadioGroup>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="flex-start"
alignItems="center"
border="1px solid"
borderColor="gray.200"
>
{editor === EmailTemplateEditors.UNLAYER_EDITOR ? (
<EmailEditor ref={emailEditorRef} onReady={onReady} />
) : (
<Textarea
value={templateData.template}
onChange={(e) => {
setTemplateData({
...templateData,
[EmailTemplateInputDataFields.TEMPLATE]: e.target.value,
});
}}
placeholder="Template HTML"
border="0"
height="500px"
/>
)}
</Flex>
</Flex>
</ModalBody>
<ModalFooter>
<Button
variant="outline"
onClick={resetData}
isDisabled={loading}
marginRight="5"
>
Reset
</Button>
<Button
colorScheme="blue"
variant="solid"
isLoading={loading}
onClick={saveData}
isDisabled={!validateData()}
>
<Center h="100%" pt="5%">
Save
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default UpdateEmailTemplate;

View File

@@ -0,0 +1,697 @@
import React, { useEffect, useState } from 'react';
import {
Button,
Center,
Code,
Collapse,
Flex,
Input,
InputGroup,
InputRightElement,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Select,
Switch,
Text,
useDisclosure,
useToast,
Alert,
AlertIcon,
Divider,
} from '@chakra-ui/react';
import {
FaAngleDown,
FaAngleUp,
FaMinusCircle,
FaPlus,
FaRegClone,
} from 'react-icons/fa';
import { useClient } from 'urql';
import {
webhookEventNames,
ArrayInputOperations,
WebhookInputDataFields,
WebhookInputHeaderFields,
UpdateModalViews,
webhookVerifiedStatus,
webhookPayloadExample,
} from '../constants';
import {
capitalizeFirstLetter,
copyTextToClipboard,
validateURI,
} from '../utils';
import { AddWebhook, EditWebhook, TestEndpoint } from '../graphql/mutation';
import { BiCheckCircle, BiError, BiErrorCircle } from 'react-icons/bi';
interface headersDataType {
[WebhookInputHeaderFields.KEY]: string;
[WebhookInputHeaderFields.VALUE]: string;
}
interface headersValidatorDataType {
[WebhookInputHeaderFields.KEY]: boolean;
[WebhookInputHeaderFields.VALUE]: boolean;
}
interface selecetdWebhookDataTypes {
[WebhookInputDataFields.ID]: string;
[WebhookInputDataFields.EVENT_NAME]: string;
[WebhookInputDataFields.EVENT_DESCRIPTION]?: string;
[WebhookInputDataFields.ENDPOINT]: string;
[WebhookInputDataFields.ENABLED]: boolean;
[WebhookInputDataFields.HEADERS]?: Record<string, string>;
}
interface UpdateWebhookModalInputPropTypes {
view: UpdateModalViews;
selectedWebhook?: selecetdWebhookDataTypes;
fetchWebookData: Function;
}
const initHeadersData: headersDataType = {
[WebhookInputHeaderFields.KEY]: '',
[WebhookInputHeaderFields.VALUE]: '',
};
const initHeadersValidatorData: headersValidatorDataType = {
[WebhookInputHeaderFields.KEY]: true,
[WebhookInputHeaderFields.VALUE]: true,
};
interface webhookDataType {
[WebhookInputDataFields.EVENT_NAME]: string;
[WebhookInputDataFields.EVENT_DESCRIPTION]?: string;
[WebhookInputDataFields.ENDPOINT]: string;
[WebhookInputDataFields.ENABLED]: boolean;
[WebhookInputDataFields.HEADERS]: headersDataType[];
}
interface validatorDataType {
[WebhookInputDataFields.ENDPOINT]: boolean;
[WebhookInputDataFields.HEADERS]: headersValidatorDataType[];
}
const initWebhookData: webhookDataType = {
[WebhookInputDataFields.EVENT_NAME]: webhookEventNames['User login'],
[WebhookInputDataFields.EVENT_DESCRIPTION]: '',
[WebhookInputDataFields.ENDPOINT]: '',
[WebhookInputDataFields.ENABLED]: true,
[WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }],
};
const initWebhookValidatorData: validatorDataType = {
[WebhookInputDataFields.ENDPOINT]: true,
[WebhookInputDataFields.HEADERS]: [{ ...initHeadersValidatorData }],
};
const UpdateWebhookModal = ({
view,
selectedWebhook,
fetchWebookData,
}: UpdateWebhookModalInputPropTypes) => {
const client = useClient();
const toast = useToast();
const { isOpen, onOpen, onClose } = useDisclosure();
const [loading, setLoading] = useState<boolean>(false);
const [verifyingEndpoint, setVerifyingEndpoint] = useState<boolean>(false);
const [isShowingPayload, setIsShowingPayload] = useState<boolean>(false);
const [webhook, setWebhook] = useState<webhookDataType>({
...initWebhookData,
});
const [validator, setValidator] = useState<validatorDataType>({
...initWebhookValidatorData,
});
const [verifiedStatus, setVerifiedStatus] = useState<webhookVerifiedStatus>(
webhookVerifiedStatus.PENDING,
);
const inputChangehandler = (
inputType: string,
value: any,
headerInputType: string = WebhookInputHeaderFields.KEY,
headerIndex: number = 0,
) => {
if (
verifiedStatus !== webhookVerifiedStatus.PENDING &&
inputType !== WebhookInputDataFields.ENABLED
) {
setVerifiedStatus(webhookVerifiedStatus.PENDING);
}
switch (inputType) {
case WebhookInputDataFields.EVENT_NAME:
setWebhook({ ...webhook, [inputType]: value });
break;
case WebhookInputDataFields.EVENT_DESCRIPTION:
setWebhook({ ...webhook, [inputType]: value });
break;
case WebhookInputDataFields.ENDPOINT:
setWebhook({ ...webhook, [inputType]: value });
setValidator({
...validator,
[WebhookInputDataFields.ENDPOINT]: validateURI(value),
});
break;
case WebhookInputDataFields.ENABLED:
setWebhook({ ...webhook, [inputType]: value });
break;
case WebhookInputDataFields.HEADERS:
const updatedHeaders: any = [
...webhook[WebhookInputDataFields.HEADERS],
];
const updatedHeadersValidatorData: any = [
...validator[WebhookInputDataFields.HEADERS],
];
const otherHeaderInputType =
headerInputType === WebhookInputHeaderFields.KEY
? WebhookInputHeaderFields.VALUE
: WebhookInputHeaderFields.KEY;
updatedHeaders[headerIndex][headerInputType] = value;
updatedHeadersValidatorData[headerIndex][headerInputType] =
value.length > 0
? updatedHeaders[headerIndex][otherHeaderInputType].length > 0
: updatedHeaders[headerIndex][otherHeaderInputType].length === 0;
updatedHeadersValidatorData[headerIndex][otherHeaderInputType] =
value.length > 0
? updatedHeaders[headerIndex][otherHeaderInputType].length > 0
: updatedHeaders[headerIndex][otherHeaderInputType].length === 0;
setWebhook({ ...webhook, [inputType]: updatedHeaders });
setValidator({
...validator,
[inputType]: updatedHeadersValidatorData,
});
break;
default:
break;
}
};
const updateHeaders = (operation: string, index: number = 0) => {
if (verifiedStatus !== webhookVerifiedStatus.PENDING) {
setVerifiedStatus(webhookVerifiedStatus.PENDING);
}
switch (operation) {
case ArrayInputOperations.APPEND:
setWebhook({
...webhook,
[WebhookInputDataFields.HEADERS]: [
...(webhook?.[WebhookInputDataFields.HEADERS] || []),
{ ...initHeadersData },
],
});
setValidator({
...validator,
[WebhookInputDataFields.HEADERS]: [
...(validator?.[WebhookInputDataFields.HEADERS] || []),
{ ...initHeadersValidatorData },
],
});
break;
case ArrayInputOperations.REMOVE:
if (webhook?.[WebhookInputDataFields.HEADERS]?.length) {
const updatedHeaders = [...webhook[WebhookInputDataFields.HEADERS]];
updatedHeaders.splice(index, 1);
setWebhook({
...webhook,
[WebhookInputDataFields.HEADERS]: updatedHeaders,
});
}
if (validator?.[WebhookInputDataFields.HEADERS]?.length) {
const updatedHeadersData = [
...validator[WebhookInputDataFields.HEADERS],
];
updatedHeadersData.splice(index, 1);
setValidator({
...validator,
[WebhookInputDataFields.HEADERS]: updatedHeadersData,
});
}
break;
default:
break;
}
};
const validateData = () => {
return (
!loading &&
!verifyingEndpoint &&
webhook[WebhookInputDataFields.EVENT_NAME].length > 0 &&
webhook[WebhookInputDataFields.ENDPOINT].length > 0 &&
validator[WebhookInputDataFields.ENDPOINT] &&
!validator[WebhookInputDataFields.HEADERS].some(
(headerData: headersValidatorDataType) =>
!headerData.key || !headerData.value,
)
);
};
const getParams = () => {
let params: any = {
[WebhookInputDataFields.EVENT_NAME]:
webhook[WebhookInputDataFields.EVENT_NAME],
[WebhookInputDataFields.EVENT_DESCRIPTION]:
webhook[WebhookInputDataFields.EVENT_DESCRIPTION],
[WebhookInputDataFields.ENDPOINT]:
webhook[WebhookInputDataFields.ENDPOINT],
[WebhookInputDataFields.ENABLED]: webhook[WebhookInputDataFields.ENABLED],
[WebhookInputDataFields.HEADERS]: {},
};
if (webhook[WebhookInputDataFields.HEADERS].length) {
const headers = webhook[WebhookInputDataFields.HEADERS].reduce(
(acc, data) => {
return data.key ? { ...acc, [data.key]: data.value } : acc;
},
{},
);
if (Object.keys(headers).length) {
params[WebhookInputDataFields.HEADERS] = headers;
}
}
return params;
};
const saveData = async () => {
if (!validateData()) return;
setLoading(true);
const params = getParams();
let res: any = {};
if (
view === UpdateModalViews.Edit &&
selectedWebhook?.[WebhookInputDataFields.ID]
) {
res = await client
.mutation(EditWebhook, {
params: {
...params,
id: selectedWebhook[WebhookInputDataFields.ID],
},
})
.toPromise();
} else {
res = await client.mutation(AddWebhook, { params }).toPromise();
}
setLoading(false);
if (res.error) {
toast({
title: capitalizeFirstLetter(res.error.message),
isClosable: true,
status: 'error',
position: 'top-right',
});
} else if (res.data?._add_webhook || res.data?._update_webhook) {
toast({
title: capitalizeFirstLetter(
res.data?._add_webhook?.message || res.data?._update_webhook?.message,
),
isClosable: true,
status: 'success',
position: 'top-right',
});
setWebhook({
...initWebhookData,
[WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }],
});
setValidator({ ...initWebhookValidatorData });
fetchWebookData();
}
view === UpdateModalViews.ADD && onClose();
};
useEffect(() => {
if (
isOpen &&
view === UpdateModalViews.Edit &&
selectedWebhook &&
Object.keys(selectedWebhook || {}).length
) {
const { headers, ...rest } = selectedWebhook;
const headerItems = Object.entries(headers || {});
if (headerItems.length) {
let formattedHeadersData = headerItems.map((headerData) => {
return {
[WebhookInputHeaderFields.KEY]: headerData[0],
[WebhookInputHeaderFields.VALUE]: headerData[1],
};
});
setWebhook({
...rest,
[WebhookInputDataFields.HEADERS]: formattedHeadersData,
});
setValidator({
...validator,
[WebhookInputDataFields.HEADERS]: new Array(
formattedHeadersData.length,
)
.fill({})
.map(() => ({ ...initHeadersValidatorData })),
});
} else {
setWebhook({
...rest,
[WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }],
});
}
}
}, [isOpen]);
const verifyEndpoint = async () => {
if (!validateData()) return;
setVerifyingEndpoint(true);
const { [WebhookInputDataFields.ENABLED]: _, ...params } = getParams();
const res = await client.mutation(TestEndpoint, { params }).toPromise();
if (
res.data?._test_endpoint?.http_status >= 200 &&
res.data?._test_endpoint?.http_status < 400
) {
setVerifiedStatus(webhookVerifiedStatus.VERIFIED);
} else {
setVerifiedStatus(webhookVerifiedStatus.NOT_VERIFIED);
}
setVerifyingEndpoint(false);
};
return (
<>
{view === UpdateModalViews.ADD ? (
<Button
leftIcon={<FaPlus />}
colorScheme="blue"
variant="solid"
onClick={onOpen}
isDisabled={false}
size="sm"
>
<Center h="100%">Add Webhook</Center>{' '}
</Button>
) : (
<MenuItem onClick={onOpen}>Edit</MenuItem>
)}
<Modal isOpen={isOpen} onClose={onClose} size="3xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>
{view === UpdateModalViews.ADD ? 'Add New Webhook' : 'Edit Webhook'}
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Flex
flexDirection="column"
border="1px"
borderRadius="md"
borderColor="gray.200"
p="5"
>
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
marginBottom="2%"
>
<Flex flex="1">Event Name</Flex>
<Flex flex="3">
<Select
size="md"
value={
webhook[WebhookInputDataFields.EVENT_NAME].split('-')[0]
}
onChange={(e) =>
inputChangehandler(
WebhookInputDataFields.EVENT_NAME,
e.currentTarget.value,
)
}
>
{Object.entries(webhookEventNames).map(
([key, value]: any) => (
<option value={value} key={key}>
{key}
</option>
),
)}
</Select>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="start"
alignItems="center"
marginBottom="5%"
>
<Flex flex="1">Event Description</Flex>
<Flex flex="3">
<InputGroup size="md">
<Input
pr="4.5rem"
type="text"
placeholder="User event"
value={webhook[WebhookInputDataFields.EVENT_DESCRIPTION]}
onChange={(e) =>
inputChangehandler(
WebhookInputDataFields.EVENT_DESCRIPTION,
e.currentTarget.value,
)
}
/>
</InputGroup>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="start"
alignItems="center"
marginBottom="5%"
>
<Flex flex="1">Endpoint</Flex>
<Flex flex="3">
<InputGroup size="md">
<Input
pr="4.5rem"
type="text"
placeholder="https://domain.com/webhook"
value={webhook[WebhookInputDataFields.ENDPOINT]}
isInvalid={!validator[WebhookInputDataFields.ENDPOINT]}
onChange={(e) =>
inputChangehandler(
WebhookInputDataFields.ENDPOINT,
e.currentTarget.value,
)
}
/>
</InputGroup>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
marginBottom="5%"
>
<Flex flex="1">Enabled</Flex>
<Flex w="25%" justifyContent="space-between">
<Text h="75%" fontWeight="bold" marginRight="2">
Off
</Text>
<Switch
size="md"
isChecked={webhook[WebhookInputDataFields.ENABLED]}
onChange={() =>
inputChangehandler(
WebhookInputDataFields.ENABLED,
!webhook[WebhookInputDataFields.ENABLED],
)
}
/>
<Text h="75%" fontWeight="bold" marginLeft="2">
On
</Text>
</Flex>
</Flex>
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
marginBottom="5%"
>
<Flex>Headers</Flex>
<Flex>
<Button
leftIcon={<FaPlus />}
colorScheme="blue"
h="1.75rem"
size="sm"
variant="ghost"
paddingRight="0"
onClick={() => updateHeaders(ArrayInputOperations.APPEND)}
>
Add more Headers
</Button>
</Flex>
</Flex>
<Flex flexDirection="column" maxH={220} overflowY="auto">
{webhook[WebhookInputDataFields.HEADERS]?.map(
(headerData, index) => (
<Flex
key={`header-data-${index}`}
justifyContent="center"
alignItems="center"
>
<InputGroup size="md" marginBottom="2.5%">
<Input
type="text"
placeholder="key"
value={headerData[WebhookInputHeaderFields.KEY]}
isInvalid={
!validator[WebhookInputDataFields.HEADERS][index]?.[
WebhookInputHeaderFields.KEY
]
}
onChange={(e) =>
inputChangehandler(
WebhookInputDataFields.HEADERS,
e.target.value,
WebhookInputHeaderFields.KEY,
index,
)
}
width="30%"
marginRight="2%"
/>
<Center marginRight="2%">
<Text fontWeight="bold">:</Text>
</Center>
<Input
type="text"
placeholder="value"
value={headerData[WebhookInputHeaderFields.VALUE]}
isInvalid={
!validator[WebhookInputDataFields.HEADERS][index]?.[
WebhookInputHeaderFields.VALUE
]
}
onChange={(e) =>
inputChangehandler(
WebhookInputDataFields.HEADERS,
e.target.value,
WebhookInputHeaderFields.VALUE,
index,
)
}
width="65%"
/>
<InputRightElement width="3rem">
<Button
width="6rem"
colorScheme="blackAlpha"
variant="ghost"
padding="0"
onClick={() =>
updateHeaders(ArrayInputOperations.REMOVE, index)
}
>
<FaMinusCircle />
</Button>
</InputRightElement>
</InputGroup>
</Flex>
),
)}
</Flex>
<Divider marginY={5} />
<Alert
status="info"
onClick={() => setIsShowingPayload(!isShowingPayload)}
borderRadius="5"
cursor="pointer"
fontSize="sm"
>
<AlertIcon />
<Flex
width="100%"
justifyContent="space-between"
alignItems="center"
>
Checkout the example payload
{isShowingPayload ? <FaAngleUp /> : <FaAngleDown />}
</Flex>
</Alert>
<Collapse
style={{
marginTop: 10,
width: '100%',
}}
in={isShowingPayload}
>
<Code
width="inherit"
borderRadius={5}
padding={2}
position="relative"
>
<pre style={{ overflow: 'auto' }}>
{webhookPayloadExample}
</pre>
{isShowingPayload && (
<Flex
position="absolute"
top={4}
right={4}
cursor="pointer"
onClick={() => copyTextToClipboard(webhookPayloadExample)}
>
<FaRegClone color="#bfbfbf" />
</Flex>
)}
</Code>
</Collapse>
</Flex>
</ModalBody>
<ModalFooter>
<Button
colorScheme={
verifiedStatus === webhookVerifiedStatus.VERIFIED
? 'green'
: verifiedStatus === webhookVerifiedStatus.PENDING
? 'yellow'
: 'red'
}
variant="outline"
onClick={verifyEndpoint}
isLoading={verifyingEndpoint}
isDisabled={!validateData()}
marginRight="5"
leftIcon={
verifiedStatus === webhookVerifiedStatus.VERIFIED ? (
<BiCheckCircle />
) : verifiedStatus === webhookVerifiedStatus.PENDING ? (
<BiErrorCircle />
) : (
<BiError />
)
}
>
{verifiedStatus === webhookVerifiedStatus.VERIFIED
? 'Endpoint Verified'
: verifiedStatus === webhookVerifiedStatus.PENDING
? 'Test Endpoint'
: 'Endpoint Not Verified'}
</Button>
<Button
colorScheme="blue"
variant="solid"
onClick={saveData}
isDisabled={!validateData()}
>
<Center h="100%" pt="5%">
Save
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default UpdateWebhookModal;

View File

@@ -0,0 +1,426 @@
import React, { useEffect, useState } from 'react';
import dayjs from 'dayjs';
import {
Button,
Center,
Flex,
MenuItem,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
useDisclosure,
Text,
Spinner,
Table,
Th,
Thead,
Tr,
Tbody,
IconButton,
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
Select,
TableCaption,
Tooltip,
Td,
Tag,
} from '@chakra-ui/react';
import { useClient } from 'urql';
import {
FaAngleDoubleLeft,
FaAngleDoubleRight,
FaAngleLeft,
FaAngleRight,
FaExclamationCircle,
FaRegClone,
} from 'react-icons/fa';
import { copyTextToClipboard } from '../utils';
import { WebhookLogsQuery } from '../graphql/queries';
import { pageLimits } from '../constants';
interface paginationPropTypes {
limit: number;
page: number;
offset: number;
total: number;
maxPages: number;
}
interface deleteWebhookModalInputPropTypes {
webhookId: string;
eventName: string;
}
interface webhookLogsDataTypes {
id: string;
http_status: number;
request: string;
response: string;
created_at: number;
}
const ViewWebhookLogsModal = ({
webhookId,
eventName,
}: deleteWebhookModalInputPropTypes) => {
const client = useClient();
const { isOpen, onOpen, onClose } = useDisclosure();
const [loading, setLoading] = useState<boolean>(false);
const [webhookLogs, setWebhookLogs] = useState<webhookLogsDataTypes[]>([]);
const [paginationProps, setPaginationProps] = useState<paginationPropTypes>({
limit: 5,
page: 1,
offset: 0,
total: 0,
maxPages: 1,
});
const getMaxPages = (pagination: paginationPropTypes) => {
const { limit, total } = pagination;
if (total > 1) {
return total % limit === 0
? total / limit
: parseInt(`${total / limit}`) + 1;
} else return 1;
};
const fetchWebhookLogsData = async () => {
setLoading(true);
const res = await client
.query(WebhookLogsQuery, {
params: {
webhook_id: webhookId,
pagination: {
limit: paginationProps.limit,
page: paginationProps.page,
},
},
})
.toPromise();
if (res.data?._webhook_logs) {
const { pagination, webhook_logs } = res.data?._webhook_logs;
const maxPages = getMaxPages(pagination);
if (webhook_logs?.length) {
setWebhookLogs(webhook_logs);
setPaginationProps({ ...paginationProps, ...pagination, maxPages });
} else {
if (paginationProps.page !== 1) {
setPaginationProps({
...paginationProps,
...pagination,
maxPages,
page: 1,
});
}
}
}
setLoading(false);
};
const paginationHandler = (value: Record<string, number>) => {
setPaginationProps({ ...paginationProps, ...value });
};
useEffect(() => {
isOpen && fetchWebhookLogsData();
}, [isOpen, paginationProps.page, paginationProps.limit]);
return (
<>
<MenuItem onClick={onOpen}>View Logs</MenuItem>
<Modal isOpen={isOpen} onClose={onClose} size="4xl">
<ModalOverlay />
<ModalContent>
<ModalHeader>Webhook Logs - {eventName}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Flex
flexDirection="column"
border="1px"
borderRadius="md"
borderColor="gray.200"
p="5"
>
{!loading ? (
webhookLogs.length ? (
<Table variant="simple">
<Thead>
<Tr>
<Th>ID</Th>
<Th>Created At</Th>
<Th>Http Status</Th>
<Th>Request</Th>
<Th>Response</Th>
</Tr>
</Thead>
<Tbody>
{webhookLogs.map((logData: webhookLogsDataTypes) => (
<Tr key={logData.id} style={{ fontSize: 14 }}>
<Td>
<Text fontSize="sm">{`${logData.id.substring(
0,
5,
)}***${logData.id.substring(
logData.id.length - 5,
logData.id.length,
)}`}</Text>
</Td>
<Td>
{dayjs(logData.created_at * 1000).format(
'MMM DD, YYYY',
)}
</Td>
<Td>
<Tag
size="sm"
variant="outline"
colorScheme={
logData.http_status >= 400 ? 'red' : 'green'
}
>
{logData.http_status}
</Tag>
</Td>
<Td>
<Flex alignItems="center">
<Tooltip
bg="gray.300"
color="black"
label={logData.request || 'null'}
>
<Tag
size="sm"
variant="outline"
colorScheme={
logData.request ? 'gray' : 'yellow'
}
>
{logData.request ? 'Payload' : 'No Data'}
</Tag>
</Tooltip>
{logData.request && (
<Button
size="xs"
variant="outline"
marginLeft="5px"
h="21px"
onClick={() =>
copyTextToClipboard(logData.request)
}
>
<FaRegClone color="#bfbfbf" />
</Button>
)}
</Flex>
</Td>
<Td>
<Flex alignItems="center">
<Tooltip
bg="gray.300"
color="black"
label={logData.response || 'null'}
>
<Tag
size="sm"
variant="outline"
colorScheme={
logData.response ? 'gray' : 'yellow'
}
>
{logData.response ? 'Preview' : 'No Data'}
</Tag>
</Tooltip>
{logData.response && (
<Button
size="xs"
variant="outline"
marginLeft="5px"
h="21px"
onClick={() =>
copyTextToClipboard(logData.response)
}
>
<FaRegClone color="#bfbfbf" />
</Button>
)}
</Flex>
</Td>
</Tr>
))}
</Tbody>
{(paginationProps.maxPages > 1 ||
paginationProps.total >= 5) && (
<TableCaption>
<Flex
justifyContent="space-between"
alignItems="center"
m="2% 0"
>
<Flex flex="1">
<Tooltip label="First Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: 1,
})
}
isDisabled={paginationProps.page <= 1}
mr={4}
icon={<FaAngleDoubleLeft />}
/>
</Tooltip>
<Tooltip label="Previous Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page - 1,
})
}
isDisabled={paginationProps.page <= 1}
icon={<FaAngleLeft />}
/>
</Tooltip>
</Flex>
<Flex
flex="8"
justifyContent="space-evenly"
alignItems="center"
>
<Text mr={8}>
Page{' '}
<Text fontWeight="bold" as="span">
{paginationProps.page}
</Text>{' '}
of{' '}
<Text fontWeight="bold" as="span">
{paginationProps.maxPages}
</Text>
</Text>
<Flex alignItems="center">
<Text flexShrink="0">Go to page:</Text>{' '}
<NumberInput
ml={2}
mr={8}
w={28}
min={1}
max={paginationProps.maxPages}
onChange={(value) =>
paginationHandler({
page: parseInt(value),
})
}
value={paginationProps.page}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Flex>
<Select
w={32}
value={paginationProps.limit}
onChange={(e) =>
paginationHandler({
page: 1,
limit: parseInt(e.target.value),
})
}
>
{pageLimits.map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</Select>
</Flex>
<Flex flex="1">
<Tooltip label="Next Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page + 1,
})
}
isDisabled={
paginationProps.page >=
paginationProps.maxPages
}
icon={<FaAngleRight />}
/>
</Tooltip>
<Tooltip label="Last Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.maxPages,
})
}
isDisabled={
paginationProps.page >=
paginationProps.maxPages
}
ml={4}
icon={<FaAngleDoubleRight />}
/>
</Tooltip>
</Flex>
</Flex>
</TableCaption>
)}
</Table>
) : (
<Flex
flexDirection="column"
minH="25vh"
justifyContent="center"
alignItems="center"
>
<Center w="50px" marginRight="1.5%">
<FaExclamationCircle
style={{ color: '#f0f0f0', fontSize: 70 }}
/>
</Center>
<Text
fontSize="2xl"
paddingRight="1%"
fontWeight="bold"
color="#d9d9d9"
>
No Data
</Text>
</Flex>
)
) : (
<Center minH="25vh">
<Spinner />
</Center>
)}
</Flex>
</ModalBody>
<ModalFooter>
<Button
colorScheme="blue"
variant="solid"
onClick={onClose}
isDisabled={false}
>
<Center h="100%" pt="5%">
Close
</Center>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
export default ViewWebhookLogsModal;

View File

@@ -2,26 +2,52 @@ export const LOGO_URL =
'https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png';
export const TextInputType = {
ACCESS_TOKEN_EXPIRY_TIME: 'ACCESS_TOKEN_EXPIRY_TIME',
CLIENT_ID: 'CLIENT_ID',
GOOGLE_CLIENT_ID: 'GOOGLE_CLIENT_ID',
GITHUB_CLIENT_ID: 'GITHUB_CLIENT_ID',
FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID',
LINKEDIN_CLIENT_ID: 'LINKEDIN_CLIENT_ID',
APPLE_CLIENT_ID: 'APPLE_CLIENT_ID',
DISCORD_CLIENT_ID: 'DISCORD_CLIENT_ID',
TWITTER_CLIENT_ID: 'TWITTER_CLIENT_ID',
MICROSOFT_CLIENT_ID: 'MICROSOFT_CLIENT_ID',
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: 'MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID',
TWITCH_CLIENT_ID: 'TWITCH_CLIENT_ID',
ROBLOX_CLIENT_ID: 'ROBLOX_CLIENT_ID',
JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM',
REDIS_URL: 'REDIS_URL',
SMTP_HOST: 'SMTP_HOST',
SMTP_PORT: 'SMTP_PORT',
SMTP_USERNAME: 'SMTP_USERNAME',
SMTP_LOCAL_NAME: 'SMTP_LOCAL_NAME',
SENDER_EMAIL: 'SENDER_EMAIL',
SENDER_NAME: 'SENDER_NAME',
ORGANIZATION_NAME: 'ORGANIZATION_NAME',
ORGANIZATION_LOGO: 'ORGANIZATION_LOGO',
DATABASE_NAME: 'DATABASE_NAME',
DATABASE_TYPE: 'DATABASE_TYPE',
DATABASE_URL: 'DATABASE_URL',
GIVEN_NAME: 'given_name',
MIDDLE_NAME: 'middle_name',
FAMILY_NAME: 'family_name',
NICKNAME: 'nickname',
PHONE_NUMBER: 'phone_number',
PICTURE: 'picture',
};
export const HiddenInputType = {
CLIENT_SECRET: 'CLIENT_SECRET',
GOOGLE_CLIENT_SECRET: 'GOOGLE_CLIENT_SECRET',
GITHUB_CLIENT_SECRET: 'GITHUB_CLIENT_SECRET',
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET',
LINKEDIN_CLIENT_SECRET: 'LINKEDIN_CLIENT_SECRET',
APPLE_CLIENT_SECRET: 'APPLE_CLIENT_SECRET',
DISCORD_CLIENT_SECRET: 'DISCORD_CLIENT_SECRET',
TWITTER_CLIENT_SECRET: 'TWITTER_CLIENT_SECRET',
MICROSOFT_CLIENT_SECRET: 'MICROSOFT_CLIENT_SECRET',
TWITCH_CLIENT_SECRET: 'TWITCH_CLIENT_SECRET',
ROBLOX_CLIENT_SECRET: 'ROBLOX_CLIENT_SECRET',
JWT_SECRET: 'JWT_SECRET',
SMTP_PASSWORD: 'SMTP_PASSWORD',
ADMIN_SECRET: 'ADMIN_SECRET',
@@ -37,20 +63,329 @@ export const ArrayInputType = {
export const SelectInputType = {
JWT_TYPE: 'JWT_TYPE',
GENDER: 'gender',
DEFAULT_AUTHORIZE_RESPONSE_TYPE: 'DEFAULT_AUTHORIZE_RESPONSE_TYPE',
DEFAULT_AUTHORIZE_RESPONSE_MODE: 'DEFAULT_AUTHORIZE_RESPONSE_MODE',
};
export const MultiSelectInputType = {
USER_ROLES: 'roles',
};
export const TextAreaInputType = {
CUSTOM_ACCESS_TOKEN_SCRIPT: 'CUSTOM_ACCESS_TOKEN_SCRIPT',
JWT_PRIVATE_KEY: 'JWT_PRIVATE_KEY',
JWT_PUBLIC_KEY: 'JWT_PUBLIC_KEY',
};
export const SwitchInputType = {
APP_COOKIE_SECURE: 'APP_COOKIE_SECURE',
ADMIN_COOKIE_SECURE: 'ADMIN_COOKIE_SECURE',
DISABLE_LOGIN_PAGE: 'DISABLE_LOGIN_PAGE',
DISABLE_MAGIC_LINK_LOGIN: 'DISABLE_MAGIC_LINK_LOGIN',
DISABLE_EMAIL_VERIFICATION: 'DISABLE_EMAIL_VERIFICATION',
DISABLE_BASIC_AUTHENTICATION: 'DISABLE_BASIC_AUTHENTICATION',
DISABLE_MOBILE_BASIC_AUTHENTICATION: 'DISABLE_MOBILE_BASIC_AUTHENTICATION',
DISABLE_SIGN_UP: 'DISABLE_SIGN_UP',
DISABLE_REDIS_FOR_ENV: 'DISABLE_REDIS_FOR_ENV',
DISABLE_STRONG_PASSWORD: 'DISABLE_STRONG_PASSWORD',
DISABLE_MULTI_FACTOR_AUTHENTICATION: 'DISABLE_MULTI_FACTOR_AUTHENTICATION',
ENFORCE_MULTI_FACTOR_AUTHENTICATION: 'ENFORCE_MULTI_FACTOR_AUTHENTICATION',
DISABLE_PLAYGROUND: 'DISABLE_PLAYGROUND',
DISABLE_TOTP_LOGIN: 'DISABLE_TOTP_LOGIN',
DISABLE_MAIL_OTP_LOGIN: 'DISABLE_MAIL_OTP_LOGIN',
};
export const DateInputType = {
BIRTHDATE: 'birthdate',
};
export const ArrayInputOperations = {
APPEND: 'APPEND',
REMOVE: 'REMOVE',
};
export const HMACEncryptionType = {
HS256: 'HS256',
HS384: 'HS384',
HS512: 'HS512',
};
export const RSAEncryptionType = {
RS256: 'RS256',
RS384: 'RS384',
RS512: 'RS512',
};
export const ECDSAEncryptionType = {
ES256: 'ES256',
ES384: 'ES384',
ES512: 'ES512',
};
export interface envVarTypes {
GOOGLE_CLIENT_ID: string;
GOOGLE_CLIENT_SECRET: string;
GITHUB_CLIENT_ID: string;
GITHUB_CLIENT_SECRET: string;
FACEBOOK_CLIENT_ID: string;
FACEBOOK_CLIENT_SECRET: string;
LINKEDIN_CLIENT_ID: string;
LINKEDIN_CLIENT_SECRET: string;
APPLE_CLIENT_ID: string;
APPLE_CLIENT_SECRET: string;
DISCORD_CLIENT_ID: string;
DISCORD_CLIENT_SECRET: string;
TWITTER_CLIENT_ID: string;
TWITTER_CLIENT_SECRET: string;
MICROSOFT_CLIENT_ID: string;
MICROSOFT_CLIENT_SECRET: string;
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: string;
TWITCH_CLIENT_ID: string;
TWITCH_CLIENT_SECRET: string;
ROBLOX_CLIENT_ID: string;
ROBLOX_CLIENT_SECRET: string;
ROLES: [string] | [];
DEFAULT_ROLES: [string] | [];
PROTECTED_ROLES: [string] | [];
JWT_TYPE: string;
JWT_SECRET: string;
JWT_ROLE_CLAIM: string;
JWT_PRIVATE_KEY: string;
JWT_PUBLIC_KEY: string;
REDIS_URL: string;
SMTP_HOST: string;
SMTP_PORT: string;
SMTP_USERNAME: string;
SMTP_PASSWORD: string;
SMTP_LOCAL_NAME: string;
SENDER_EMAIL: string;
SENDER_NAME: string;
ALLOWED_ORIGINS: [string] | [];
ORGANIZATION_NAME: string;
ORGANIZATION_LOGO: string;
CUSTOM_ACCESS_TOKEN_SCRIPT: string;
ADMIN_SECRET: string;
APP_COOKIE_SECURE: boolean;
ADMIN_COOKIE_SECURE: boolean;
DISABLE_LOGIN_PAGE: boolean;
DISABLE_MAGIC_LINK_LOGIN: boolean;
DISABLE_EMAIL_VERIFICATION: boolean;
DISABLE_BASIC_AUTHENTICATION: boolean;
DISABLE_MOBILE_BASIC_AUTHENTICATION: boolean;
DISABLE_SIGN_UP: boolean;
DISABLE_STRONG_PASSWORD: boolean;
OLD_ADMIN_SECRET: string;
DATABASE_NAME: string;
DATABASE_TYPE: string;
DATABASE_URL: string;
ACCESS_TOKEN_EXPIRY_TIME: string;
DISABLE_MULTI_FACTOR_AUTHENTICATION: boolean;
ENFORCE_MULTI_FACTOR_AUTHENTICATION: boolean;
DEFAULT_AUTHORIZE_RESPONSE_TYPE: string;
DEFAULT_AUTHORIZE_RESPONSE_MODE: string;
DISABLE_PLAYGROUND: boolean;
DISABLE_TOTP_LOGIN: boolean;
DISABLE_MAIL_OTP_LOGIN: boolean;
}
export const envSubViews = {
INSTANCE_INFO: 'instance-info',
ROLES: 'roles',
JWT_CONFIG: 'jwt-config',
SESSION_STORAGE: 'session-storage',
EMAIL_CONFIG: 'email-config',
WHITELIST_VARIABLES: 'whitelist-variables',
ORGANIZATION_INFO: 'organization-info',
ACCESS_TOKEN: 'access-token',
FEATURES: 'features',
ADMIN_SECRET: 'admin-secret',
DB_CRED: 'db-cred',
};
export enum WebhookInputDataFields {
ID = 'id',
EVENT_DESCRIPTION = 'event_description',
EVENT_NAME = 'event_name',
ENDPOINT = 'endpoint',
ENABLED = 'enabled',
HEADERS = 'headers',
}
export enum EmailTemplateInputDataFields {
ID = 'id',
EVENT_NAME = 'event_name',
SUBJECT = 'subject',
CREATED_AT = 'created_at',
TEMPLATE = 'template',
DESIGN = 'design',
}
export enum WebhookInputHeaderFields {
KEY = 'key',
VALUE = 'value',
}
export enum UpdateModalViews {
ADD = 'add',
Edit = 'edit',
}
export const pageLimits: number[] = [5, 10, 15];
export const webhookEventNames = {
'User signup': 'user.signup',
'User created': 'user.created',
'User login': 'user.login',
'User deleted': 'user.deleted',
'User access enabled': 'user.access_enabled',
'User access revoked': 'user.access_revoked',
'User deactivated': 'user.deactivated',
};
export const emailTemplateEventNames = {
Signup: 'basic_auth_signup',
'Magic Link Login': 'magic_link_login',
'Update Email': 'update_email',
'Forgot Password': 'forgot_password',
'Verify Otp': 'verify_otp',
'Invite member': 'invite_member',
};
export enum webhookVerifiedStatus {
VERIFIED = 'verified',
NOT_VERIFIED = 'not_verified',
PENDING = 'verification_pending',
}
export const emailTemplateVariables = {
'user.id': {
description: `User identifier`,
value: '{.user.id}}',
},
'user.email': {
description: 'User email address',
value: '{.user.email}}',
},
'user.given_name': {
description: `User first name`,
value: '{.user.given_name}}',
},
'user.family_name': {
description: `User last name`,
value: '{.user.family_name}}',
},
'user.middle_name': {
description: `Middle name of user`,
value: '{.user.middle_name}}',
},
'user.nickname': {
description: `Nick name of user`,
value: '{.user.nickname}}',
},
'user.preferred_username': {
description: `Username, by default it is email`,
value: '{.user.preferred_username}}',
},
'user.signup_methods': {
description: `Comma separated list of methods using which user has signed up`,
value: '{.user.signup_methods}}',
},
'user.email_verified': {
description: `Whether email is verified or not`,
value: '{.user.email_verified}}',
},
'user.picture': {
description: `URL of the user profile picture`,
value: '{.user.picture}}',
},
'user.roles': {
description: `Comma separated list of roles assigned to user`,
value: '{.user.roles}}',
},
'user.gender': {
description: `Gender of user`,
value: '{.user.gender}}',
},
'user.birthdate': {
description: `BirthDate of user`,
value: '{.user.birthdate}}',
},
'user.phone_number': {
description: `Phone number of user`,
value: '{.user.phone_number}}',
},
'user.phone_number_verified': {
description: `Whether phone number is verified or not`,
value: '{.user.phone_number_verified}}',
},
'user.created_at': {
description: `User created at time`,
value: '{.user.created_at}}',
},
'user.updated_at': {
description: `Last updated time at user`,
value: '{.user.updated_at}}',
},
'organization.name': {
description: `Organization name`,
value: '{.organization.name}}',
},
'organization.logo': {
description: `Organization logo`,
value: '{.organization.logo}}',
},
verification_url: {
description: `Verification URL in case of events other than verify otp`,
value: '{.verification_url}}',
},
otp: {
description: `OTP sent during login with Multi factor authentication`,
value: '{.otp}}',
},
};
export const webhookPayloadExample: string = `{
"event_name":"user.login",
"user":{
"birthdate":null,
"created_at":1657524721,
"email":"lakhan.m.samani@gmail.com",
"email_verified":true,
"family_name":"Samani",
"gender":null,
"given_name":"Lakhan",
"id":"466d0b31-1b87-420e-bea5-09d05d79c586",
"middle_name":null,
"nickname":null,
"phone_number":null,
"phone_number_verified":false,
"picture":"https://lh3.googleusercontent.com/a-/AFdZucppvU6a2zIDkX0wvhhapVjT0ZMKDlYCkQDi3NxcUg=s96-c",
"preferred_username":"lakhan.m.samani@gmail.com",
"revoked_timestamp":null,
"roles":[
"user"
],
"signup_methods":"google",
"updated_at":1657526492
},
"auth_recipe":"google"
}`;
export enum EmailTemplateEditors {
UNLAYER_EDITOR = 'unlayer_editor',
PLAIN_HTML_EDITOR = 'plain_html_editor',
}
export const ResponseTypes = {
token: 'token',
code: 'code',
id_token: 'id_token',
};
export const ResponseModes = {
query: 'query',
form_post: 'form_post',
fragment: 'fragment',
web_message: 'web_message',
};

View File

@@ -23,11 +23,116 @@ export const AdminLogout = `
`;
export const UpdateEnvVariables = `
mutation updateEnvVariables(
$params: UpdateEnvInput!
) {
mutation updateEnvVariables($params: UpdateEnvInput!) {
_update_env(params: $params) {
message
}
}
`;
export const UpdateUser = `
mutation updateUser($params: UpdateUserInput!) {
_update_user(params: $params) {
id
}
}
`;
export const DeleteUser = `
mutation deleteUser($params: DeleteUserInput!) {
_delete_user(params: $params) {
message
}
}
`;
export const InviteMembers = `
mutation inviteMembers($params: InviteMemberInput!) {
_invite_members(params: $params) {
message
}
}
`;
export const RevokeAccess = `
mutation revokeAccess($param: UpdateAccessInput!) {
_revoke_access(param: $param) {
message
}
}
`;
export const EnableAccess = `
mutation revokeAccess($param: UpdateAccessInput!) {
_enable_access(param: $param) {
message
}
}
`;
export const GenerateKeys = `
mutation generateKeys($params: GenerateJWTKeysInput!) {
_generate_jwt_keys(params: $params) {
secret
public_key
private_key
}
}
`;
export const AddWebhook = `
mutation addWebhook($params: AddWebhookRequest!) {
_add_webhook(params: $params) {
message
}
}
`;
export const EditWebhook = `
mutation editWebhook($params: UpdateWebhookRequest!) {
_update_webhook(params: $params) {
message
}
}
`;
export const DeleteWebhook = `
mutation deleteWebhook($params: WebhookRequest!) {
_delete_webhook(params: $params) {
message
}
}
`;
export const TestEndpoint = `
mutation testEndpoint($params: TestEndpointRequest!) {
_test_endpoint(params: $params) {
http_status
response
}
}
`;
export const AddEmailTemplate = `
mutation addEmailTemplate($params: AddEmailTemplateRequest!) {
_add_email_template(params: $params) {
message
}
}
`;
export const EditEmailTemplate = `
mutation editEmailTemplate($params: UpdateEmailTemplateRequest!) {
_update_email_template(params: $params) {
message
}
}
`;
export const DeleteEmailTemplate = `
mutation deleteEmailTemplate($params: DeleteEmailTemplateRequest!) {
_delete_email_template(params: $params) {
message
}
}
`;

View File

@@ -1,3 +1,12 @@
export const MetaQuery = `
query MetaQuery {
meta {
version
client_id
}
}
`;
export const AdminSessionQuery = `
query {
_admin_session{
@@ -9,36 +18,182 @@ export const AdminSessionQuery = `
export const EnvVariablesQuery = `
query {
_env{
GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET,
GITHUB_CLIENT_ID,
GITHUB_CLIENT_SECRET,
FACEBOOK_CLIENT_ID,
FACEBOOK_CLIENT_SECRET,
ROLES,
DEFAULT_ROLES,
PROTECTED_ROLES,
JWT_TYPE,
JWT_SECRET,
JWT_ROLE_CLAIM,
REDIS_URL,
SMTP_HOST,
SMTP_PORT,
SMTP_USERNAME,
SMTP_PASSWORD,
SENDER_EMAIL,
ALLOWED_ORIGINS,
ORGANIZATION_NAME,
ORGANIZATION_LOGO,
ADMIN_SECRET,
DISABLE_LOGIN_PAGE,
DISABLE_MAGIC_LINK_LOGIN,
DISABLE_EMAIL_VERIFICATION,
DISABLE_BASIC_AUTHENTICATION,
CUSTOM_ACCESS_TOKEN_SCRIPT,
DATABASE_NAME,
DATABASE_TYPE,
DATABASE_URL,
CLIENT_ID
CLIENT_SECRET
GOOGLE_CLIENT_ID
GOOGLE_CLIENT_SECRET
GITHUB_CLIENT_ID
GITHUB_CLIENT_SECRET
FACEBOOK_CLIENT_ID
FACEBOOK_CLIENT_SECRET
LINKEDIN_CLIENT_ID
LINKEDIN_CLIENT_SECRET
APPLE_CLIENT_ID
APPLE_CLIENT_SECRET
DISCORD_CLIENT_ID
DISCORD_CLIENT_SECRET
TWITTER_CLIENT_ID
TWITTER_CLIENT_SECRET
MICROSOFT_CLIENT_ID
MICROSOFT_CLIENT_SECRET
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID
TWITCH_CLIENT_ID
TWITCH_CLIENT_SECRET
ROBLOX_CLIENT_ID
ROBLOX_CLIENT_SECRET
DEFAULT_ROLES
PROTECTED_ROLES
ROLES
JWT_TYPE
JWT_SECRET
JWT_ROLE_CLAIM
JWT_PRIVATE_KEY
JWT_PUBLIC_KEY
REDIS_URL
SMTP_HOST
SMTP_PORT
SMTP_USERNAME
SMTP_PASSWORD
SMTP_LOCAL_NAME
SENDER_EMAIL
SENDER_NAME
ALLOWED_ORIGINS
ORGANIZATION_NAME
ORGANIZATION_LOGO
ADMIN_SECRET
APP_COOKIE_SECURE
ADMIN_COOKIE_SECURE
DISABLE_LOGIN_PAGE
DISABLE_MAGIC_LINK_LOGIN
DISABLE_EMAIL_VERIFICATION
DISABLE_BASIC_AUTHENTICATION
DISABLE_MOBILE_BASIC_AUTHENTICATION
DISABLE_SIGN_UP
DISABLE_STRONG_PASSWORD
DISABLE_REDIS_FOR_ENV
CUSTOM_ACCESS_TOKEN_SCRIPT
DATABASE_NAME
DATABASE_TYPE
DATABASE_URL
ACCESS_TOKEN_EXPIRY_TIME
DISABLE_MULTI_FACTOR_AUTHENTICATION
ENFORCE_MULTI_FACTOR_AUTHENTICATION
DEFAULT_AUTHORIZE_RESPONSE_TYPE
DEFAULT_AUTHORIZE_RESPONSE_MODE
DISABLE_PLAYGROUND
DISABLE_TOTP_LOGIN
DISABLE_MAIL_OTP_LOGIN
}
}
`;
export const UserDetailsQuery = `
query($params: PaginatedInput) {
_users(params: $params) {
pagination {
limit
page
offset
total
}
users {
id
email
email_verified
phone_number_verified
given_name
family_name
middle_name
nickname
gender
birthdate
phone_number
picture
signup_methods
roles
created_at
revoked_timestamp
is_multi_factor_auth_enabled
}
}
}
`;
export const EmailVerificationQuery = `
query {
_env{
DISABLE_EMAIL_VERIFICATION
}
}
`;
export const WebhooksDataQuery = `
query getWebhooksData($params: PaginatedInput!) {
_webhooks(params: $params){
webhooks{
id
event_description
event_name
endpoint
enabled
headers
}
pagination{
limit
page
offset
total
}
}
}
`;
export const EmailTemplatesQuery = `
query getEmailTemplates($params: PaginatedInput!) {
_email_templates(params: $params) {
email_templates {
id
event_name
subject
created_at
template
design
}
pagination {
limit
page
offset
total
}
}
}
`;
export const WebhookLogsQuery = `
query getWebhookLogs($params: ListWebhookLogRequest!) {
_webhook_logs(params: $params) {
webhook_logs {
id
http_status
request
response
created_at
}
pagination {
limit
page
offset
total
}
}
}
`;
export const GetAvailableRolesQuery = `
query {
_env {
ROLES
PROTECTED_ROLES
}
}
`;

View File

@@ -2,4 +2,9 @@ import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
ReactDOM.render(
<div>
<App />
</div>,
document.getElementById('root'),
);

View File

@@ -1,18 +1,28 @@
import { Box, Center, Flex, Image, Text } from '@chakra-ui/react';
import {
Box,
Flex,
Image,
Text,
Spinner,
useMediaQuery,
} from '@chakra-ui/react';
import React from 'react';
import { LOGO_URL } from '../constants';
import { useQuery } from 'urql';
import { MetaQuery } from '../graphql/queries';
export function AuthLayout({ children }: { children: React.ReactNode }) {
const [{ fetching, data }] = useQuery({ query: MetaQuery });
const [isNotSmallerScreen] = useMediaQuery('(min-width:600px)');
return (
<Flex
flexWrap="wrap"
h="100%"
h="100vh"
bg="gray.100"
alignItems="center"
justifyContent="center"
flexDirection="column"
direction={['column', 'column']}
padding={['2%', '2%', '2%', '2%']}
>
<Flex alignItems="center">
<Flex alignItems="center" maxW="100%">
<Image
src="https://authorizer.dev/images/logo.png"
alt="logo"
@@ -23,9 +33,26 @@ export function AuthLayout({ children }: { children: React.ReactNode }) {
</Text>
</Flex>
<Box p="6" m="5" rounded="5" bg="white" w="500px" shadow="xl">
{children}
</Box>
{fetching ? (
<Spinner />
) : (
<>
<Box
p="6"
m="5"
rounded="5"
bg="white"
w={isNotSmallerScreen ? '500px' : '450px'}
shadow="xl"
maxW="100%"
>
{children}
</Box>
<Text color="gray.600" fontSize="sm">
Current Version: {data.meta.version}
</Text>
</>
)}
</Flex>
);
}

View File

@@ -31,7 +31,7 @@ export function DashboardLayout({ children }: { children: ReactNode }) {
</Drawer>
{/* mobilenav */}
<MobileNav onOpen={onOpen} />
<Box ml={{ base: 0, md: 60 }} p="4" pt="24">
<Box ml={{ base: 0, md: '64' }} p="4" pt="24">
{children}
</Box>
</Box>

View File

@@ -6,7 +6,6 @@ import {
useToast,
VStack,
Text,
Divider,
} from '@chakra-ui/react';
import React, { useEffect } from 'react';
import { useMutation } from 'urql';
@@ -58,7 +57,7 @@ export default function Auth() {
title: capitalizeFirstLetter(error.message),
isClosable: true,
status: 'error',
position: 'bottom-right',
position: 'top-right',
});
});
}
@@ -73,17 +72,25 @@ export default function Auth() {
fontWeight="bold"
mb="2"
>
Hi there 👋 <br />
Hello Admin 👋 <br />
</Text>
<Text fontSize="large" textAlign="center" color="gray.500" mb="8">
Welcome to Authorizer Administrative Dashboard
Welcome to Admin Dashboard
</Text>
<form onSubmit={handleSubmit}>
<VStack spacing="5" justify="space-between">
<FormControl isRequired>
{/* <FormLabel htmlFor="admin-secret">
{isLogin ? 'Enter' : 'Configure'} Admin Secret
</FormLabel> */}
<FormLabel htmlFor="admin-username">Username</FormLabel>
<Input
size="lg"
id="admin-username"
placeholder="Username"
disabled
value="admin"
/>
</FormControl>
<FormControl isRequired>
<FormLabel htmlFor="admin-secret">Password</FormLabel>
<Input
size="lg"
id="admin-secret"
@@ -94,10 +101,10 @@ export default function Auth() {
</FormControl>
<Button
isLoading={signUpResult.fetching || loginResult.fetching}
loadingText="Submitting"
colorScheme="blue"
size="lg"
w="100%"
d="block"
type="submit"
>
{isLogin ? 'Login' : 'Sign up'}
@@ -111,10 +118,7 @@ export default function Auth() {
</Text>
) : (
<Text color="gray.600" fontSize="sm">
<b>Note:</b> You can also configure admin secret by setting{' '}
<code>ADMIN_SECRET</code> environment variable. For more
information, please refer to the{' '}
<a href="https://docs.authorizer.dev/core/env/">documentation</a>.
<b>Note:</b> Configure the password to start using your dashboard.
</Text>
)}
</VStack>

View File

@@ -0,0 +1,348 @@
import React, { useEffect, useState } from 'react';
import { useClient } from 'urql';
import {
Box,
Button,
Center,
Flex,
IconButton,
Menu,
MenuButton,
MenuList,
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
Select,
Spinner,
Table,
TableCaption,
Tbody,
Td,
Text,
Th,
Thead,
Tooltip,
Tr,
} from '@chakra-ui/react';
import {
FaAngleDoubleLeft,
FaAngleDoubleRight,
FaAngleDown,
FaAngleLeft,
FaAngleRight,
FaExclamationCircle,
} from 'react-icons/fa';
import UpdateEmailTemplateModal from '../components/UpdateEmailTemplateModal';
import {
pageLimits,
UpdateModalViews,
EmailTemplateInputDataFields,
} from '../constants';
import { EmailTemplatesQuery } from '../graphql/queries';
import dayjs from 'dayjs';
import DeleteEmailTemplateModal from '../components/DeleteEmailTemplateModal';
interface paginationPropTypes {
limit: number;
page: number;
offset: number;
total: number;
maxPages: number;
}
interface EmailTemplateDataType {
[EmailTemplateInputDataFields.ID]: string;
[EmailTemplateInputDataFields.EVENT_NAME]: string;
[EmailTemplateInputDataFields.SUBJECT]: string;
[EmailTemplateInputDataFields.CREATED_AT]: number;
[EmailTemplateInputDataFields.TEMPLATE]: string;
[EmailTemplateInputDataFields.DESIGN]: string;
}
const EmailTemplates = () => {
const client = useClient();
const [loading, setLoading] = useState<boolean>(false);
const [emailTemplatesData, setEmailTemplatesData] = useState<
EmailTemplateDataType[]
>([]);
const [paginationProps, setPaginationProps] = useState<paginationPropTypes>({
limit: 5,
page: 1,
offset: 0,
total: 0,
maxPages: 1,
});
const getMaxPages = (pagination: paginationPropTypes) => {
const { limit, total } = pagination;
if (total > 1) {
return total % limit === 0
? total / limit
: parseInt(`${total / limit}`) + 1;
} else return 1;
};
const fetchEmailTemplatesData = async () => {
setLoading(true);
const res = await client
.query(EmailTemplatesQuery, {
params: {
pagination: {
limit: paginationProps.limit,
page: paginationProps.page,
},
},
})
.toPromise();
if (res.data?._email_templates) {
const { pagination, email_templates: emailTemplates } =
res.data?._email_templates;
const maxPages = getMaxPages(pagination);
if (emailTemplates?.length) {
setEmailTemplatesData(emailTemplates);
setPaginationProps({ ...paginationProps, ...pagination, maxPages });
} else {
if (paginationProps.page !== 1) {
setPaginationProps({
...paginationProps,
...pagination,
maxPages,
page: 1,
});
}
}
}
setLoading(false);
};
const paginationHandler = (value: Record<string, number>) => {
setPaginationProps({ ...paginationProps, ...value });
};
useEffect(() => {
fetchEmailTemplatesData();
}, [paginationProps.page, paginationProps.limit]);
return (
<Box m="5" py="5" px="10" bg="white" rounded="md">
<Flex margin="2% 0" justifyContent="space-between" alignItems="center">
<Text fontSize="md" fontWeight="bold">
Email Templates
</Text>
<UpdateEmailTemplateModal
view={UpdateModalViews.ADD}
fetchEmailTemplatesData={fetchEmailTemplatesData}
/>
</Flex>
{!loading ? (
emailTemplatesData.length ? (
<Table variant="simple">
<Thead>
<Tr>
<Th>Event Name</Th>
<Th>Subject</Th>
<Th>Created At</Th>
<Th>Actions</Th>
</Tr>
</Thead>
<Tbody>
{emailTemplatesData.map((templateData: EmailTemplateDataType) => (
<Tr
key={templateData[EmailTemplateInputDataFields.ID]}
style={{ fontSize: 14 }}
>
<Td maxW="300">
{templateData[EmailTemplateInputDataFields.EVENT_NAME]}
</Td>
<Td>{templateData[EmailTemplateInputDataFields.SUBJECT]}</Td>
<Td>
{dayjs(templateData.created_at * 1000).format(
'MMM DD, YYYY',
)}
</Td>
<Td>
<Menu>
<MenuButton as={Button} variant="unstyled" size="sm">
<Flex
justifyContent="space-between"
alignItems="center"
>
<Text fontSize="sm" fontWeight="light">
Menu
</Text>
<FaAngleDown style={{ marginLeft: 10 }} />
</Flex>
</MenuButton>
<MenuList>
<UpdateEmailTemplateModal
view={UpdateModalViews.Edit}
selectedTemplate={templateData}
fetchEmailTemplatesData={fetchEmailTemplatesData}
/>
<DeleteEmailTemplateModal
emailTemplateId={
templateData[EmailTemplateInputDataFields.ID]
}
eventName={
templateData[
EmailTemplateInputDataFields.EVENT_NAME
]
}
fetchEmailTemplatesData={fetchEmailTemplatesData}
/>
</MenuList>
</Menu>
</Td>
</Tr>
))}
</Tbody>
{(paginationProps.maxPages > 1 || paginationProps.total >= 5) && (
<TableCaption>
<Flex
justifyContent="space-between"
alignItems="center"
m="2% 0"
>
<Flex flex="1">
<Tooltip label="First Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: 1,
})
}
isDisabled={paginationProps.page <= 1}
mr={4}
icon={<FaAngleDoubleLeft />}
/>
</Tooltip>
<Tooltip label="Previous Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page - 1,
})
}
isDisabled={paginationProps.page <= 1}
icon={<FaAngleLeft />}
/>
</Tooltip>
</Flex>
<Flex
flex="8"
justifyContent="space-evenly"
alignItems="center"
>
<Text mr={8}>
Page{' '}
<Text fontWeight="bold" as="span">
{paginationProps.page}
</Text>{' '}
of{' '}
<Text fontWeight="bold" as="span">
{paginationProps.maxPages}
</Text>
</Text>
<Flex alignItems="center">
<Text flexShrink="0">Go to page:</Text>{' '}
<NumberInput
ml={2}
mr={8}
w={28}
min={1}
max={paginationProps.maxPages}
onChange={(value) =>
paginationHandler({
page: parseInt(value),
})
}
value={paginationProps.page}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Flex>
<Select
w={32}
value={paginationProps.limit}
onChange={(e) =>
paginationHandler({
page: 1,
limit: parseInt(e.target.value),
})
}
>
{pageLimits.map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</Select>
</Flex>
<Flex flex="1">
<Tooltip label="Next Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page + 1,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
icon={<FaAngleRight />}
/>
</Tooltip>
<Tooltip label="Last Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.maxPages,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
ml={4}
icon={<FaAngleDoubleRight />}
/>
</Tooltip>
</Flex>
</Flex>
</TableCaption>
)}
</Table>
) : (
<Flex
flexDirection="column"
minH="25vh"
justifyContent="center"
alignItems="center"
>
<Center w="50px" marginRight="1.5%">
<FaExclamationCircle style={{ color: '#f0f0f0', fontSize: 70 }} />
</Center>
<Text
fontSize="2xl"
paddingRight="1%"
fontWeight="bold"
color="#d9d9d9"
>
No Data
</Text>
</Flex>
)
) : (
<Center minH="25vh">
<Spinner />
</Center>
)}
</Box>
);
};
export default EmailTemplates;

View File

@@ -1,75 +1,35 @@
import React, { useEffect } from 'react';
import {
Box,
Divider,
Flex,
Stack,
Center,
Text,
Button,
Input,
InputGroup,
InputRightElement,
useToast,
} from '@chakra-ui/react';
import { useParams } from 'react-router-dom';
import { Box, Flex, Stack, Button, useToast } from '@chakra-ui/react';
import { useClient } from 'urql';
import {
FaGoogle,
FaGithub,
FaFacebookF,
FaSave,
FaRegEyeSlash,
FaRegEye,
} from 'react-icons/fa';
import { FaSave } from 'react-icons/fa';
import _ from 'lodash';
import InputField from '../components/InputField';
import { EnvVariablesQuery } from '../graphql/queries';
import {
ArrayInputType,
SelectInputType,
HiddenInputType,
TextInputType,
TextAreaInputType,
SwitchInputType,
HMACEncryptionType,
RSAEncryptionType,
ECDSAEncryptionType,
envVarTypes,
envSubViews,
} from '../constants';
import { UpdateEnvVariables } from '../graphql/mutation';
import { getObjectDiff, capitalizeFirstLetter } from '../utils';
import OAuthConfig from '../components/EnvComponents/OAuthConfig';
import Roles from '../components/EnvComponents/Roles';
import JWTConfigurations from '../components/EnvComponents/JWTConfiguration';
import SessionStorage from '../components/EnvComponents/SessionStorage';
import EmailConfigurations from '../components/EnvComponents/EmailConfiguration';
import DomainWhiteListing from '../components/EnvComponents/DomainWhitelisting';
import OrganizationInfo from '../components/EnvComponents/OrganizationInfo';
import AccessToken from '../components/EnvComponents/AccessToken';
import Features from '../components/EnvComponents/Features';
import SecurityAdminSecret from '../components/EnvComponents/SecurityAdminSecret';
import DatabaseCredentials from '../components/EnvComponents/DatabaseCredentials';
interface envVarTypes {
GOOGLE_CLIENT_ID: string;
GOOGLE_CLIENT_SECRET: string;
GITHUB_CLIENT_ID: string;
GITHUB_CLIENT_SECRET: string;
FACEBOOK_CLIENT_ID: string;
FACEBOOK_CLIENT_SECRET: string;
ROLES: [string] | [];
DEFAULT_ROLES: [string] | [];
PROTECTED_ROLES: [string] | [];
JWT_TYPE: string;
JWT_SECRET: string;
JWT_ROLE_CLAIM: string;
REDIS_URL: string;
SMTP_HOST: string;
SMTP_PORT: string;
SMTP_USERNAME: string;
SMTP_PASSWORD: string;
SENDER_EMAIL: string;
ALLOWED_ORIGINS: [string] | [];
ORGANIZATION_NAME: string;
ORGANIZATION_LOGO: string;
CUSTOM_ACCESS_TOKEN_SCRIPT: string;
ADMIN_SECRET: string;
DISABLE_LOGIN_PAGE: boolean;
DISABLE_MAGIC_LINK_LOGIN: boolean;
DISABLE_EMAIL_VERIFICATION: boolean;
DISABLE_BASIC_AUTHENTICATION: boolean;
OLD_ADMIN_SECRET: string;
DATABASE_NAME: string;
DATABASE_TYPE: string;
DATABASE_URL: string;
}
export default function Environment() {
const Environment = () => {
const client = useClient();
const toast = useToast();
const [adminSecret, setAdminSecret] = React.useState<
@@ -86,31 +46,63 @@ export default function Environment() {
GITHUB_CLIENT_SECRET: '',
FACEBOOK_CLIENT_ID: '',
FACEBOOK_CLIENT_SECRET: '',
LINKEDIN_CLIENT_ID: '',
LINKEDIN_CLIENT_SECRET: '',
APPLE_CLIENT_ID: '',
APPLE_CLIENT_SECRET: '',
DISCORD_CLIENT_ID: '',
DISCORD_CLIENT_SECRET: '',
TWITTER_CLIENT_ID: '',
TWITTER_CLIENT_SECRET: '',
MICROSOFT_CLIENT_ID: '',
MICROSOFT_CLIENT_SECRET: '',
MICROSOFT_ACTIVE_DIRECTORY_TENANT_ID: '',
TWITCH_CLIENT_ID: '',
TWITCH_CLIENT_SECRET: '',
ROBLOX_CLIENT_ID: '',
ROBLOX_CLIENT_SECRET: '',
ROLES: [],
DEFAULT_ROLES: [],
PROTECTED_ROLES: [],
JWT_TYPE: '',
JWT_SECRET: '',
JWT_ROLE_CLAIM: '',
JWT_PRIVATE_KEY: '',
JWT_PUBLIC_KEY: '',
REDIS_URL: '',
SMTP_HOST: '',
SMTP_PORT: '',
SMTP_USERNAME: '',
SMTP_PASSWORD: '',
SMTP_LOCAL_NAME: '',
SENDER_EMAIL: '',
SENDER_NAME: '',
ALLOWED_ORIGINS: [],
ORGANIZATION_NAME: '',
ORGANIZATION_LOGO: '',
CUSTOM_ACCESS_TOKEN_SCRIPT: '',
ADMIN_SECRET: '',
APP_COOKIE_SECURE: false,
ADMIN_COOKIE_SECURE: false,
DISABLE_LOGIN_PAGE: false,
DISABLE_MAGIC_LINK_LOGIN: false,
DISABLE_EMAIL_VERIFICATION: false,
DISABLE_BASIC_AUTHENTICATION: false,
DISABLE_MOBILE_BASIC_AUTHENTICATION: false,
DISABLE_SIGN_UP: false,
DISABLE_STRONG_PASSWORD: false,
OLD_ADMIN_SECRET: '',
DATABASE_NAME: '',
DATABASE_TYPE: '',
DATABASE_URL: '',
ACCESS_TOKEN_EXPIRY_TIME: '',
DISABLE_MULTI_FACTOR_AUTHENTICATION: false,
ENFORCE_MULTI_FACTOR_AUTHENTICATION: false,
DEFAULT_AUTHORIZE_RESPONSE_TYPE: '',
DEFAULT_AUTHORIZE_RESPONSE_MODE: '',
DISABLE_PLAYGROUND: false,
DISABLE_TOTP_LOGIN: false,
DISABLE_MAIL_OTP_LOGIN: true,
});
const [fieldVisibility, setFieldVisibility] = React.useState<
@@ -119,39 +111,39 @@ export default function Environment() {
GOOGLE_CLIENT_SECRET: false,
GITHUB_CLIENT_SECRET: false,
FACEBOOK_CLIENT_SECRET: false,
LINKEDIN_CLIENT_SECRET: false,
APPLE_CLIENT_SECRET: false,
DISCORD_CLIENT_SECRET: false,
TWITTER_CLIENT_SECRET: false,
TWITCH_CLIENT_SECRET: false,
JWT_SECRET: false,
SMTP_PASSWORD: false,
ADMIN_SECRET: false,
OLD_ADMIN_SECRET: false,
});
const { sec } = useParams();
async function getData() {
const {
data: { _env: envData },
} = await client.query(EnvVariablesQuery).toPromise();
setLoading(false);
setEnvVariables({
...envData,
OLD_ADMIN_SECRET: envData.ADMIN_SECRET,
ADMIN_SECRET: '',
});
setAdminSecret({
value: '',
disableInputField: true,
});
}
useEffect(() => {
let isMounted = true;
async function getData() {
const {
data: { _env: envData },
} = await client.query(EnvVariablesQuery).toPromise();
if (isMounted) {
setLoading(false);
setEnvVariables({
...envData,
OLD_ADMIN_SECRET: envData.ADMIN_SECRET,
ADMIN_SECRET: '',
});
setAdminSecret({
value: '',
disableInputField: true,
});
}
}
getData();
return () => {
isMounted = false;
};
}, []);
}, [sec]);
const validateAdminSecretHandler = (event: any) => {
if (envVariables.OLD_ADMIN_SECRET === event.target.value) {
@@ -177,7 +169,6 @@ export default function Environment() {
const {
data: { _env: envData },
} = await client.query(EnvVariablesQuery).toPromise();
const diff = getObjectDiff(envVariables, envData);
const updatedEnvVariables = diff.reduce(
(acc: any, property: string) => ({
@@ -185,7 +176,7 @@ export default function Environment() {
// @ts-ignore
[property]: envVariables[property],
}),
{}
{},
);
if (
updatedEnvVariables[HiddenInputType.ADMIN_SECRET] === '' ||
@@ -222,540 +213,122 @@ export default function Environment() {
disableInputField: true,
});
getData();
toast({
title: `Successfully updated ${
Object.keys(updatedEnvVariables).length
} variables`,
isClosable: true,
status: 'success',
position: 'bottom-right',
position: 'top-right',
});
};
const renderComponent = (tab: any) => {
switch (tab) {
case envSubViews.INSTANCE_INFO:
return (
<OAuthConfig
envVariables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
/>
);
case envSubViews.ROLES:
return (
<Roles variables={envVariables} setVariables={setEnvVariables} />
);
case envSubViews.JWT_CONFIG:
return (
<JWTConfigurations
variables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
SelectInputType={SelectInputType.JWT_TYPE}
HMACEncryptionType={HMACEncryptionType}
RSAEncryptionType={RSAEncryptionType}
ECDSAEncryptionType={ECDSAEncryptionType}
getData={getData}
/>
);
case envSubViews.SESSION_STORAGE:
return (
<SessionStorage
variables={envVariables}
setVariables={setEnvVariables}
RedisURL={TextInputType.REDIS_URL}
/>
);
case envSubViews.EMAIL_CONFIG:
return (
<EmailConfigurations
variables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
/>
);
case envSubViews.WHITELIST_VARIABLES:
return (
<DomainWhiteListing
variables={envVariables}
setVariables={setEnvVariables}
/>
);
case envSubViews.ORGANIZATION_INFO:
return (
<OrganizationInfo
variables={envVariables}
setVariables={setEnvVariables}
/>
);
case envSubViews.ACCESS_TOKEN:
return (
<AccessToken
variables={envVariables}
setVariables={setEnvVariables}
/>
);
case envSubViews.FEATURES:
return (
<Features variables={envVariables} setVariables={setEnvVariables} />
);
case envSubViews.ADMIN_SECRET:
return (
<SecurityAdminSecret
variables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
validateAdminSecretHandler={validateAdminSecretHandler}
adminSecret={adminSecret}
/>
);
case envSubViews.DB_CRED:
return (
<DatabaseCredentials
variables={envVariables}
setVariables={setEnvVariables}
/>
);
default:
return (
<OAuthConfig
envVariables={envVariables}
setVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
/>
);
}
};
return (
<Box m="5" py="5" px="10" bg="white" rounded="md">
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Social Media Logins
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Center
w="50px"
marginRight="1.5%"
border="1px solid #e2e8f0"
borderRadius="5px"
>
<FaGoogle style={{ color: '#8c8c8c' }} />
</Center>
<Center w="45%" marginRight="1.5%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={TextInputType.GOOGLE_CLIENT_ID}
placeholder="Google Client ID"
/>
</Center>
<Center w="45%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.GOOGLE_CLIENT_SECRET}
placeholder="Google Secret"
/>
</Center>
</Flex>
<Flex>
<Center
w="50px"
marginRight="1.5%"
border="1px solid #e2e8f0"
borderRadius="5px"
>
<FaGithub style={{ color: '#8c8c8c' }} />
</Center>
<Center w="45%" marginRight="1.5%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={TextInputType.GITHUB_CLIENT_ID}
placeholder="Github Client ID"
/>
</Center>
<Center w="45%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.GITHUB_CLIENT_SECRET}
placeholder="Github Secret"
/>
</Center>
</Flex>
<Flex>
<Center
w="50px"
marginRight="1.5%"
border="1px solid #e2e8f0"
borderRadius="5px"
>
<FaFacebookF style={{ color: '#8c8c8c' }} />
</Center>
<Center w="45%" marginRight="1.5%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={TextInputType.FACEBOOK_CLIENT_ID}
placeholder="Facebook Client ID"
/>
</Center>
<Center w="45%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.FACEBOOK_CLIENT_SECRET}
placeholder="Facebook Secret"
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Roles
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Roles:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={ArrayInputType.ROLES}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Default Roles:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={ArrayInputType.DEFAULT_ROLES}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Protected Roles:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={ArrayInputType.PROTECTED_ROLES}
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
JWT (JSON Web Tokens) Configurations
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Type:</Text>
</Flex>
<Center w="70%">
<Flex w="100%" justifyContent="space-between">
<Flex flex="2">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={SelectInputType.JWT_TYPE}
isDisabled={true}
defaultValue={SelectInputType.JWT_TYPE}
/>
</Flex>
<Flex flex="3" justifyContent="center" alignItems="center">
<Text fontSize="sm">
More JWT types will be enabled in upcoming releases.
</Text>
</Flex>
</Flex>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Secret</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.JWT_SECRET}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">JWT Role Claim:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={TextInputType.JWT_ROLE_CLAIM}
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Session Storage
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Redis URL:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={TextInputType.REDIS_URL}
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Email Configurations
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">SMTP Host:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={TextInputType.SMTP_HOST}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Port:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={TextInputType.SMTP_PORT}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Username:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={TextInputType.SMTP_USERNAME}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Password:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
inputType={HiddenInputType.SMTP_PASSWORD}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">From Email:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={TextInputType.SENDER_EMAIL}
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
White Listing
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Allowed Origins:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={ArrayInputType.ALLOWED_ORIGINS}
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Organization Information
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Organization Name:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={TextInputType.ORGANIZATION_NAME}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Organization Logo:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={TextInputType.ORGANIZATION_LOGO}
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Custom Scripts
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Center w="100%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={TextAreaInputType.CUSTOM_ACCESS_TOKEN_SCRIPT}
placeholder="Add script here"
minH="25vh"
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Disable Features
</Text>
<Stack spacing={6} padding="2% 0%">
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Disable Login Page:</Text>
</Flex>
<Flex justifyContent="start" w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={SwitchInputType.DISABLE_LOGIN_PAGE}
/>
</Flex>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Disable Email Verification:</Text>
</Flex>
<Flex justifyContent="start" w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={SwitchInputType.DISABLE_EMAIL_VERIFICATION}
/>
</Flex>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Disable Magic Login Link:</Text>
</Flex>
<Flex justifyContent="start" w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={SwitchInputType.DISABLE_MAGIC_LINK_LOGIN}
/>
</Flex>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Disable Basic Authentication:</Text>
</Flex>
<Flex justifyContent="start" w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={SwitchInputType.DISABLE_BASIC_AUTHENTICATION}
/>
</Flex>
</Flex>
</Stack>
<Divider marginTop="2%" marginBottom="2%" />
<Text fontSize="md" paddingTop="2%" fontWeight="bold">
Danger
</Text>
<Stack
spacing={6}
padding="0 5%"
marginTop="3%"
border="1px solid #ff7875"
borderRadius="5px"
>
<Stack spacing={6} padding="3% 0">
<Text fontStyle="italic" fontSize="sm" color="gray.600">
Note: Database related environment variables cannot be updated from
dashboard :(
</Text>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">DataBase Name:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={TextInputType.DATABASE_NAME}
isDisabled={true}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">DataBase Type:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={TextInputType.DATABASE_TYPE}
isDisabled={true}
/>
</Center>
</Flex>
<Flex>
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">DataBase URL:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={TextInputType.DATABASE_URL}
isDisabled={true}
/>
</Center>
</Flex>
</Stack>
<Flex marginTop="3%">
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">Old Admin Secret:</Text>
</Flex>
<Center w="70%">
<InputGroup size="sm">
<Input
size="sm"
placeholder="Enter Old Admin Secret"
value={adminSecret.value as string}
onChange={(event: any) => validateAdminSecretHandler(event)}
type={
!fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET]
? 'password'
: 'text'
}
/>
<InputRightElement
right="5px"
children={
<Flex>
{fieldVisibility[HiddenInputType.OLD_ADMIN_SECRET] ? (
<Center
w="25px"
margin="0 1.5%"
cursor="pointer"
onClick={() =>
setFieldVisibility({
...fieldVisibility,
[HiddenInputType.OLD_ADMIN_SECRET]: false,
})
}
>
<FaRegEyeSlash color="#bfbfbf" />
</Center>
) : (
<Center
w="25px"
margin="0 1.5%"
cursor="pointer"
onClick={() =>
setFieldVisibility({
...fieldVisibility,
[HiddenInputType.OLD_ADMIN_SECRET]: true,
})
}
>
<FaRegEye color="#bfbfbf" />
</Center>
)}
</Flex>
}
/>
</InputGroup>
</Center>
</Flex>
<Flex paddingBottom="3%">
<Flex w="30%" justifyContent="start" alignItems="center">
<Text fontSize="sm">New Admin Secret:</Text>
</Flex>
<Center w="70%">
<InputField
envVariables={envVariables}
setEnvVariables={setEnvVariables}
inputType={HiddenInputType.ADMIN_SECRET}
fieldVisibility={fieldVisibility}
setFieldVisibility={setFieldVisibility}
isDisabled={adminSecret.disableInputField}
placeholder="Enter New Admin Secret"
/>
</Center>
</Flex>
</Stack>
<Divider marginTop="5%" marginBottom="2%" />
<Stack spacing={6} padding="1% 0">
{renderComponent(sec)}
<Stack spacing={6} padding="1% 0" mt={4}>
<Flex justifyContent="end" alignItems="center">
<Button
leftIcon={<FaSave />}
@@ -770,4 +343,6 @@ export default function Environment() {
</Stack>
</Box>
);
}
};
export default Environment;

View File

@@ -1,6 +1,607 @@
import { Box } from '@chakra-ui/react';
import React from 'react';
import { useClient } from 'urql';
import dayjs from 'dayjs';
import {
Box,
Flex,
IconButton,
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
Select,
Table,
Tag,
Tbody,
Td,
Text,
TableCaption,
Th,
Thead,
Tooltip,
Tr,
Button,
Center,
Menu,
MenuButton,
MenuList,
MenuItem,
useToast,
Spinner,
TableContainer,
} from '@chakra-ui/react';
import {
FaAngleLeft,
FaAngleRight,
FaAngleDoubleLeft,
FaAngleDoubleRight,
FaExclamationCircle,
FaAngleDown,
} from 'react-icons/fa';
import { EmailVerificationQuery, UserDetailsQuery } from '../graphql/queries';
import { EnableAccess, RevokeAccess, UpdateUser } from '../graphql/mutation';
import EditUserModal from '../components/EditUserModal';
import DeleteUserModal from '../components/DeleteUserModal';
import InviteMembersModal from '../components/InviteMembersModal';
interface paginationPropTypes {
limit: number;
page: number;
offset: number;
total: number;
maxPages: number;
}
interface userDataTypes {
id: string;
email: string;
email_verified: boolean;
given_name: string;
family_name: string;
middle_name: string;
nickname: string;
gender: string;
birthdate: string;
phone_number: string;
picture: string;
signup_methods: string;
roles: [string];
created_at: number;
revoked_timestamp: number;
is_multi_factor_auth_enabled?: boolean;
}
const enum updateAccessActions {
REVOKE = 'REVOKE',
ENABLE = 'ENABLE',
}
const getMaxPages = (pagination: paginationPropTypes) => {
const { limit, total } = pagination;
if (total > 1) {
return total % limit === 0
? total / limit
: parseInt(`${total / limit}`) + 1;
} else return 1;
};
const getLimits = (pagination: paginationPropTypes) => {
const { total } = pagination;
const limits = [5];
if (total > 10) {
for (let i = 10; i <= total && limits.length <= 10; i += 5) {
limits.push(i);
}
}
return limits;
};
export default function Users() {
return <Box>Welcome to Users Page</Box>;
const client = useClient();
const toast = useToast();
const [paginationProps, setPaginationProps] =
React.useState<paginationPropTypes>({
limit: 5,
page: 1,
offset: 0,
total: 0,
maxPages: 1,
});
const [userList, setUserList] = React.useState<userDataTypes[]>([]);
const [loading, setLoading] = React.useState<boolean>(false);
const [disableInviteMembers, setDisableInviteMembers] =
React.useState<boolean>(true);
const updateUserList = async () => {
setLoading(true);
const { data } = await client
.query(UserDetailsQuery, {
params: {
pagination: {
limit: paginationProps.limit,
page: paginationProps.page,
},
},
})
.toPromise();
if (data?._users) {
const { pagination, users } = data._users;
const maxPages = getMaxPages(pagination);
if (users && users.length > 0) {
setPaginationProps({ ...paginationProps, ...pagination, maxPages });
setUserList(users);
} else {
if (paginationProps.page !== 1) {
setPaginationProps({
...paginationProps,
...pagination,
maxPages,
page: 1,
});
}
}
}
setLoading(false);
};
const checkEmailVerification = async () => {
setLoading(true);
const { data } = await client.query(EmailVerificationQuery).toPromise();
if (data?._env) {
const { DISABLE_EMAIL_VERIFICATION } = data._env;
setDisableInviteMembers(DISABLE_EMAIL_VERIFICATION);
}
setLoading(false);
};
React.useEffect(() => {
updateUserList();
checkEmailVerification();
}, []);
React.useEffect(() => {
updateUserList();
}, [paginationProps.page, paginationProps.limit]);
const paginationHandler = (value: Record<string, number>) => {
setPaginationProps({ ...paginationProps, ...value });
};
const userVerificationHandler = async (user: userDataTypes) => {
const { id, email, phone_number } = user;
let params = {};
if (email) {
params = {
id,
email,
email_verified: true,
};
}
if (phone_number) {
params = {
id,
phone_number,
phone_number_verified: true,
};
}
const res = await client
.mutation(UpdateUser, {
params,
})
.toPromise();
if (res.error) {
toast({
title: 'User verification failed',
isClosable: true,
status: 'error',
position: 'top-right',
});
} else if (res.data?._update_user?.id) {
toast({
title: 'User verification successful',
isClosable: true,
status: 'success',
position: 'top-right',
});
}
updateUserList();
};
const updateAccessHandler = async (
id: string,
action: updateAccessActions,
) => {
switch (action) {
case updateAccessActions.ENABLE:
const enableAccessRes = await client
.mutation(EnableAccess, {
param: {
user_id: id,
},
})
.toPromise();
if (enableAccessRes.error) {
toast({
title: 'User access enable failed',
isClosable: true,
status: 'error',
position: 'top-right',
});
} else {
toast({
title: 'User access enabled successfully',
isClosable: true,
status: 'success',
position: 'top-right',
});
}
updateUserList();
break;
case updateAccessActions.REVOKE:
const revokeAccessRes = await client
.mutation(RevokeAccess, {
param: {
user_id: id,
},
})
.toPromise();
if (revokeAccessRes.error) {
toast({
title: 'User access revoke failed',
isClosable: true,
status: 'error',
position: 'top-right',
});
} else {
toast({
title: 'User access revoked successfully',
isClosable: true,
status: 'success',
position: 'top-right',
});
}
updateUserList();
break;
default:
break;
}
};
const multiFactorAuthUpdateHandler = async (user: userDataTypes) => {
const res = await client
.mutation(UpdateUser, {
params: {
id: user.id,
is_multi_factor_auth_enabled: !user.is_multi_factor_auth_enabled,
},
})
.toPromise();
if (res.data?._update_user?.id) {
toast({
title: `Multi factor authentication ${
user.is_multi_factor_auth_enabled ? 'disabled' : 'enabled'
} for user`,
isClosable: true,
status: 'success',
position: 'top-right',
});
updateUserList();
return;
}
toast({
title: 'Multi factor authentication update failed for user',
isClosable: true,
status: 'error',
position: 'top-right',
});
};
return (
<Box m="5" py="5" px="10" bg="white" rounded="md">
<Flex margin="2% 0" justifyContent="space-between" alignItems="center">
<Text fontSize="md" fontWeight="bold">
Users
</Text>
<InviteMembersModal
disabled={disableInviteMembers}
updateUserList={updateUserList}
/>
</Flex>
{!loading ? (
userList.length > 0 ? (
<TableContainer>
<Table variant="simple">
<Thead>
<Tr>
<Th>Email / Phone</Th>
<Th>Created At</Th>
<Th>Signup Methods</Th>
<Th>Roles</Th>
<Th>Verified</Th>
<Th>Access</Th>
<Th>
<Tooltip label="MultiFactor Authentication Enabled / Disabled">
MFA
</Tooltip>
</Th>
<Th>Actions</Th>
</Tr>
</Thead>
<Tbody>
{userList.map((user: userDataTypes) => {
const {
email_verified,
phone_number_verified,
created_at,
...rest
}: any = user;
return (
<Tr key={user.id} style={{ fontSize: 14 }}>
<Td maxW="300">{user.email || user.phone_number}</Td>
<Td>
{dayjs(user.created_at * 1000).format('MMM DD, YYYY')}
</Td>
<Td>{user.signup_methods}</Td>
<Td>{user.roles.join(', ')}</Td>
<Td>
<Tag
size="sm"
variant="outline"
colorScheme={
user.email_verified || user.phone_number_verified
? 'green'
: 'yellow'
}
>
{(
user.email_verified || user.phone_number_verified
).toString()}
</Tag>
</Td>
<Td>
<Tag
size="sm"
variant="outline"
colorScheme={user.revoked_timestamp ? 'red' : 'green'}
>
{user.revoked_timestamp ? 'Revoked' : 'Enabled'}
</Tag>
</Td>
<Td>
<Tag
size="sm"
variant="outline"
colorScheme={
user.is_multi_factor_auth_enabled ? 'green' : 'red'
}
>
{user.is_multi_factor_auth_enabled
? 'Enabled'
: 'Disabled'}
</Tag>
</Td>
<Td>
<Menu>
<MenuButton as={Button} variant="unstyled" size="sm">
<Flex
justifyContent="space-between"
alignItems="center"
>
<Text fontSize="sm" fontWeight="light">
Menu
</Text>
<FaAngleDown style={{ marginLeft: 10 }} />
</Flex>
</MenuButton>
<MenuList>
{!user.email_verified &&
!user.phone_number_verified && (
<MenuItem
onClick={() => userVerificationHandler(user)}
>
Verify User
</MenuItem>
)}
<EditUserModal
user={rest}
updateUserList={updateUserList}
/>
<DeleteUserModal
user={rest}
updateUserList={updateUserList}
/>
{user.revoked_timestamp ? (
<MenuItem
onClick={() =>
updateAccessHandler(
user.id,
updateAccessActions.ENABLE,
)
}
>
Enable Access
</MenuItem>
) : (
<MenuItem
onClick={() =>
updateAccessHandler(
user.id,
updateAccessActions.REVOKE,
)
}
>
Revoke Access
</MenuItem>
)}
{user.is_multi_factor_auth_enabled ? (
<MenuItem
onClick={() =>
multiFactorAuthUpdateHandler(user)
}
>
Disable MultiFactor Authentication
</MenuItem>
) : (
<MenuItem
onClick={() =>
multiFactorAuthUpdateHandler(user)
}
>
Enable MultiFactor Authentication
</MenuItem>
)}
</MenuList>
</Menu>
</Td>
</Tr>
);
})}
</Tbody>
{(paginationProps.maxPages > 1 || paginationProps.total >= 5) && (
<TableCaption>
<Flex
justifyContent="space-between"
alignItems="center"
m="2% 0"
>
<Flex flex="1">
<Tooltip label="First Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: 1,
})
}
isDisabled={paginationProps.page <= 1}
mr={4}
icon={<FaAngleDoubleLeft />}
/>
</Tooltip>
<Tooltip label="Previous Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page - 1,
})
}
isDisabled={paginationProps.page <= 1}
icon={<FaAngleLeft />}
/>
</Tooltip>
</Flex>
<Flex
flex="8"
justifyContent="space-evenly"
alignItems="center"
>
<Text mr={8}>
Page{' '}
<Text fontWeight="bold" as="span">
{paginationProps.page}
</Text>{' '}
of{' '}
<Text fontWeight="bold" as="span">
{paginationProps.maxPages}
</Text>
</Text>
<Flex alignItems="center">
<Text flexShrink="0">Go to page:</Text>{' '}
<NumberInput
ml={2}
mr={8}
w={28}
min={1}
max={paginationProps.maxPages}
onChange={(value) =>
paginationHandler({
page: parseInt(value),
})
}
value={paginationProps.page}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Flex>
<Select
w={32}
value={paginationProps.limit}
onChange={(e) =>
paginationHandler({
page: 1,
limit: parseInt(e.target.value),
})
}
>
{getLimits(paginationProps).map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</Select>
</Flex>
<Flex flex="1">
<Tooltip label="Next Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page + 1,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
icon={<FaAngleRight />}
/>
</Tooltip>
<Tooltip label="Last Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.maxPages,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
ml={4}
icon={<FaAngleDoubleRight />}
/>
</Tooltip>
</Flex>
</Flex>
</TableCaption>
)}
</Table>
</TableContainer>
) : (
<Flex
flexDirection="column"
minH="25vh"
justifyContent="center"
alignItems="center"
>
<Center w="50px" marginRight="1.5%">
<FaExclamationCircle style={{ color: '#f0f0f0', fontSize: 70 }} />
</Center>
<Text
fontSize="2xl"
paddingRight="1%"
fontWeight="bold"
color="#d9d9d9"
>
No Data
</Text>
</Flex>
)
) : (
<Center minH="25vh">
<Spinner />
</Center>
)}
</Box>
);
}

View File

@@ -0,0 +1,374 @@
import React, { useEffect, useState } from 'react';
import { useClient } from 'urql';
import {
Box,
Button,
Center,
Flex,
IconButton,
Menu,
MenuButton,
MenuList,
NumberDecrementStepper,
NumberIncrementStepper,
NumberInput,
NumberInputField,
NumberInputStepper,
Select,
Spinner,
Table,
TableCaption,
Tag,
Tbody,
Td,
Text,
Th,
Thead,
Tooltip,
Tr,
} from '@chakra-ui/react';
import {
FaAngleDoubleLeft,
FaAngleDoubleRight,
FaAngleDown,
FaAngleLeft,
FaAngleRight,
FaExclamationCircle,
} from 'react-icons/fa';
import UpdateWebhookModal from '../components/UpdateWebhookModal';
import {
pageLimits,
WebhookInputDataFields,
UpdateModalViews,
} from '../constants';
import { WebhooksDataQuery } from '../graphql/queries';
import DeleteWebhookModal from '../components/DeleteWebhookModal';
import ViewWebhookLogsModal from '../components/ViewWebhookLogsModal';
interface paginationPropTypes {
limit: number;
page: number;
offset: number;
total: number;
maxPages: number;
}
interface webhookDataTypes {
[WebhookInputDataFields.ID]: string;
[WebhookInputDataFields.EVENT_NAME]: string;
[WebhookInputDataFields.EVENT_DESCRIPTION]?: string;
[WebhookInputDataFields.ENDPOINT]: string;
[WebhookInputDataFields.ENABLED]: boolean;
[WebhookInputDataFields.HEADERS]?: Record<string, string>;
}
const Webhooks = () => {
const client = useClient();
const [loading, setLoading] = useState<boolean>(false);
const [webhookData, setWebhookData] = useState<webhookDataTypes[]>([]);
const [paginationProps, setPaginationProps] = useState<paginationPropTypes>({
limit: 5,
page: 1,
offset: 0,
total: 0,
maxPages: 1,
});
const getMaxPages = (pagination: paginationPropTypes) => {
const { limit, total } = pagination;
if (total > 1) {
return total % limit === 0
? total / limit
: parseInt(`${total / limit}`) + 1;
} else return 1;
};
const fetchWebookData = async () => {
setLoading(true);
const res = await client
.query(WebhooksDataQuery, {
params: {
pagination: {
limit: paginationProps.limit,
page: paginationProps.page,
},
},
})
.toPromise();
if (res.data?._webhooks) {
const { pagination, webhooks } = res.data?._webhooks;
const maxPages = getMaxPages(pagination);
if (webhooks?.length) {
setWebhookData(webhooks);
setPaginationProps({ ...paginationProps, ...pagination, maxPages });
} else {
if (paginationProps.page !== 1) {
setPaginationProps({
...paginationProps,
...pagination,
maxPages,
page: 1,
});
}
}
}
setLoading(false);
};
const paginationHandler = (value: Record<string, number>) => {
setPaginationProps({ ...paginationProps, ...value });
};
useEffect(() => {
fetchWebookData();
}, [paginationProps.page, paginationProps.limit]);
return (
<Box m="5" py="5" px="10" bg="white" rounded="md">
<Flex margin="2% 0" justifyContent="space-between" alignItems="center">
<Text fontSize="md" fontWeight="bold">
Webhooks
</Text>
<UpdateWebhookModal
view={UpdateModalViews.ADD}
fetchWebookData={fetchWebookData}
/>
</Flex>
{!loading ? (
webhookData.length ? (
<Table variant="simple">
<Thead>
<Tr>
<Th>Event Name</Th>
<Th>Event Description</Th>
<Th>Endpoint</Th>
<Th>Enabled</Th>
<Th>Headers</Th>
<Th>Actions</Th>
</Tr>
</Thead>
<Tbody>
{webhookData.map((webhook: webhookDataTypes) => (
<Tr
key={webhook[WebhookInputDataFields.ID]}
style={{ fontSize: 14 }}
>
<Td maxW="300">
{webhook[WebhookInputDataFields.EVENT_NAME].split('-')[0]}
</Td>
<Td maxW="300">
{webhook[WebhookInputDataFields.EVENT_DESCRIPTION]}
</Td>
<Td>{webhook[WebhookInputDataFields.ENDPOINT]}</Td>
<Td>
<Tag
size="sm"
variant="outline"
colorScheme={
webhook[WebhookInputDataFields.ENABLED]
? 'green'
: 'yellow'
}
>
{webhook[WebhookInputDataFields.ENABLED].toString()}
</Tag>
</Td>
<Td>
<Tooltip
bg="gray.300"
color="black"
label={JSON.stringify(
webhook[WebhookInputDataFields.HEADERS],
null,
' ',
)}
>
<Tag size="sm" variant="outline" colorScheme="gray">
{Object.keys(
webhook[WebhookInputDataFields.HEADERS] || {},
)?.length.toString()}
</Tag>
</Tooltip>
</Td>
<Td>
<Menu>
<MenuButton as={Button} variant="unstyled" size="sm">
<Flex
justifyContent="space-between"
alignItems="center"
>
<Text fontSize="sm" fontWeight="light">
Menu
</Text>
<FaAngleDown style={{ marginLeft: 10 }} />
</Flex>
</MenuButton>
<MenuList>
<UpdateWebhookModal
view={UpdateModalViews.Edit}
selectedWebhook={webhook}
fetchWebookData={fetchWebookData}
/>
<DeleteWebhookModal
webhookId={webhook[WebhookInputDataFields.ID]}
eventName={webhook[WebhookInputDataFields.EVENT_NAME]}
fetchWebookData={fetchWebookData}
/>
<ViewWebhookLogsModal
webhookId={webhook[WebhookInputDataFields.ID]}
eventName={webhook[WebhookInputDataFields.EVENT_NAME]}
/>
</MenuList>
</Menu>
</Td>
</Tr>
))}
</Tbody>
{(paginationProps.maxPages > 1 || paginationProps.total >= 5) && (
<TableCaption>
<Flex
justifyContent="space-between"
alignItems="center"
m="2% 0"
>
<Flex flex="1">
<Tooltip label="First Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: 1,
})
}
isDisabled={paginationProps.page <= 1}
mr={4}
icon={<FaAngleDoubleLeft />}
/>
</Tooltip>
<Tooltip label="Previous Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page - 1,
})
}
isDisabled={paginationProps.page <= 1}
icon={<FaAngleLeft />}
/>
</Tooltip>
</Flex>
<Flex
flex="8"
justifyContent="space-evenly"
alignItems="center"
>
<Text mr={8}>
Page{' '}
<Text fontWeight="bold" as="span">
{paginationProps.page}
</Text>{' '}
of{' '}
<Text fontWeight="bold" as="span">
{paginationProps.maxPages}
</Text>
</Text>
<Flex alignItems="center">
<Text>Go to page:</Text>{' '}
<NumberInput
ml={2}
mr={8}
w={28}
min={1}
max={paginationProps.maxPages}
onChange={(value) =>
paginationHandler({
page: parseInt(value),
})
}
value={paginationProps.page}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Flex>
<Select
w={32}
value={paginationProps.limit}
onChange={(e) =>
paginationHandler({
page: 1,
limit: parseInt(e.target.value),
})
}
>
{pageLimits.map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</Select>
</Flex>
<Flex flex="1">
<Tooltip label="Next Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.page + 1,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
icon={<FaAngleRight />}
/>
</Tooltip>
<Tooltip label="Last Page">
<IconButton
aria-label="icon button"
onClick={() =>
paginationHandler({
page: paginationProps.maxPages,
})
}
isDisabled={
paginationProps.page >= paginationProps.maxPages
}
ml={4}
icon={<FaAngleDoubleRight />}
/>
</Tooltip>
</Flex>
</Flex>
</TableCaption>
)}
</Table>
) : (
<Flex
flexDirection="column"
minH="25vh"
justifyContent="center"
alignItems="center"
>
<Center w="50px" marginRight="1.5%">
<FaExclamationCircle style={{ color: '#f0f0f0', fontSize: 70 }} />
</Center>
<Text
fontSize="2xl"
paddingRight="1%"
fontWeight="bold"
color="#d9d9d9"
>
No Data
</Text>
</Flex>
)
) : (
<Center minH="25vh">
<Spinner />
</Center>
)}
</Box>
);
};
export default Webhooks;

View File

@@ -3,33 +3,41 @@ import { Outlet, Route, Routes } from 'react-router-dom';
import { useAuthContext } from '../contexts/AuthContext';
import { DashboardLayout } from '../layouts/DashboardLayout';
import EmailTemplates from '../pages/EmailTemplates';
const Auth = lazy(() => import('../pages/Auth'));
const Environment = lazy(() => import('../pages/Environment'));
const Home = lazy(() => import('../pages/Home'));
const Users = lazy(() => import('../pages/Users'));
const Webhooks = lazy(() => import('../pages/Webhooks'));
export const AppRoutes = () => {
const { isLoggedIn } = useAuthContext();
if (isLoggedIn) {
return (
<Suspense fallback={<></>}>
<Routes>
<Route
element={
<DashboardLayout>
<Outlet />
</DashboardLayout>
}
>
<Route path="/" element={<Home />} />
<Route path="users" element={<Users />} />
<Route path="environment" element={<Environment />} />
<Route path="*" element={<Home />} />
</Route>
</Routes>
</Suspense>
<div>
<Suspense fallback={<></>}>
<Routes>
<Route
element={
<DashboardLayout>
<Outlet />
</DashboardLayout>
}
>
<Route path="/" element={<Outlet />}>
<Route index element={<Environment />} />
<Route path="/:sec" element={<Environment />} />
</Route>
<Route path="users" element={<Users />} />
<Route path="webhooks" element={<Webhooks />} />
<Route path="email-templates" element={<EmailTemplates />} />
<Route path="*" element={<Home />} />
</Route>
</Routes>
</Suspense>
</div>
);
}
return (

View File

@@ -29,26 +29,30 @@ const fallbackCopyTextToClipboard = (text: string) => {
document.body.removeChild(textArea);
};
export const copyTextToClipboard = (text: string) => {
export const copyTextToClipboard = async (text: string) => {
if (!navigator.clipboard) {
fallbackCopyTextToClipboard(text);
return;
}
navigator.clipboard.writeText(text).then(
() => {
console.log('Async: Copying to clipboard was successful!');
},
(err) => {
console.error('Async: Could not copy text: ', err);
}
);
try {
navigator.clipboard.writeText(text);
} catch (err) {
throw err;
}
};
export const getObjectDiff = (obj1: any, obj2: any) => {
const diff = Object.keys(obj1).reduce((result, key) => {
if (!obj2.hasOwnProperty(key)) {
result.push(key);
} else if (_.isEqual(obj1[key], obj2[key])) {
} else if (
_.isEqual(obj1[key], obj2[key]) ||
(obj1[key] === null && obj2[key] === '') ||
(obj1[key] &&
Array.isArray(obj1[key]) &&
obj1[key].length === 0 &&
obj2[key] === null)
) {
const resultKeyIndex = result.indexOf(key);
result.splice(resultKeyIndex, 1);
}
@@ -57,3 +61,25 @@ export const getObjectDiff = (obj1: any, obj2: any) => {
return diff;
};
export const validateEmail = (email: string) => {
if (!email || email === '') return true;
return email
.toLowerCase()
.match(
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
)
? true
: false;
};
export const validateURI = (uri: string) => {
if (!uri || uri === '') return true;
return uri
.toLowerCase()
.match(
/(?:^|\s)((https?:\/\/)?(?:localhost|[\w-]+(?:\.[\w-]+)+)(:\d+)?(\/\S*)?)/,
)
? true
: false;
};

View File

@@ -0,0 +1,39 @@
import _flatten from 'lodash/flatten';
import { validateEmail } from '.';
interface dataTypes {
value: string;
isInvalid: boolean;
}
const parseCSV = (file: File, delimiter: string): Promise<dataTypes[]> => {
return new Promise((resolve) => {
const reader = new FileReader();
// When the FileReader has loaded the file...
reader.onload = (e: any) => {
// Split the result to an array of lines
const lines = e.target.result.split('\n');
// Split the lines themselves by the specified
// delimiter, such as a comma
let result = lines.map((line: string) => line.split(delimiter));
// As the FileReader reads asynchronously,
// we can't just return the result; instead,
// we're passing it to a callback function
result = _flatten(result);
resolve(
result.map((email: string) => {
return {
value: email.trim(),
isInvalid: !validateEmail(email.trim()),
};
}),
);
};
// Read the file content as a single string
reader.readAsText(file);
});
};
export default parseCSV;

1887
dashboard/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

20
go.mod Normal file
View File

@@ -0,0 +1,20 @@
module server
go 1.21.5
require (
github.com/99designs/gqlgen v0.17.43 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sosodev/duration v1.1.0 // indirect
github.com/urfave/cli/v2 v2.25.5 // indirect
github.com/vektah/gqlparser/v2 v2.5.11 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.9.3 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

31
go.sum Normal file
View File

@@ -0,0 +1,31 @@
github.com/99designs/gqlgen v0.17.43 h1:I4SYg6ahjowErAQcHFVKy5EcWuwJ3+Xw9z2fLpuFCPo=
github.com/99designs/gqlgen v0.17.43/go.mod h1:lO0Zjy8MkZgBdv4T1U91x09r0e0WFOdhVUutlQs1Rsc=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sosodev/duration v1.1.0 h1:kQcaiGbJaIsRqgQy7VGlZrVw1giWO+lDoX3MCPnpVO4=
github.com/sosodev/duration v1.1.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc=
github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8=
github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

13
node_modules/.package-lock.json generated vendored
View File

@@ -1,13 +0,0 @@
{
"name": "authorizer",
"lockfileVersion": 2,
"requires": true,
"packages": {
"node_modules/@types/node": {
"version": "17.0.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.10.tgz",
"integrity": "sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog==",
"dev": true
}
}
}

21
node_modules/@types/node/LICENSE generated vendored
View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE

16
node_modules/@types/node/README.md generated vendored
View File

@@ -1,16 +0,0 @@
# Installation
> `npm install --save @types/node`
# Summary
This package contains type definitions for Node.js (https://nodejs.org/).
# Details
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node.
### Additional Details
* Last updated: Tue, 18 Jan 2022 18:31:37 GMT
* Dependencies: none
* Global values: `AbortController`, `AbortSignal`, `__dirname`, `__filename`, `console`, `exports`, `gc`, `global`, `module`, `process`, `require`
# Credits
These definitions were written by [Microsoft TypeScript](https://github.com/Microsoft), [DefinitelyTyped](https://github.com/DefinitelyTyped), [Alberto Schiabel](https://github.com/jkomyno), [Alvis HT Tang](https://github.com/alvis), [Andrew Makarov](https://github.com/r3nya), [Benjamin Toueg](https://github.com/btoueg), [Chigozirim C.](https://github.com/smac89), [David Junger](https://github.com/touffy), [Deividas Bakanas](https://github.com/DeividasBakanas), [Eugene Y. Q. Shen](https://github.com/eyqs), [Hannes Magnusson](https://github.com/Hannes-Magnusson-CK), [Huw](https://github.com/hoo29), [Kelvin Jin](https://github.com/kjin), [Klaus Meinhardt](https://github.com/ajafff), [Lishude](https://github.com/islishude), [Mariusz Wiktorczyk](https://github.com/mwiktorczyk), [Mohsen Azimi](https://github.com/mohsen1), [Nicolas Even](https://github.com/n-e), [Nikita Galkin](https://github.com/galkin), [Parambir Singh](https://github.com/parambirs), [Sebastian Silbermann](https://github.com/eps1lon), [Seth Westphal](https://github.com/westy92), [Simon Schick](https://github.com/SimonSchick), [Thomas den Hollander](https://github.com/ThomasdenH), [Wilco Bakker](https://github.com/WilcoBakker), [wwwy3y3](https://github.com/wwwy3y3), [Samuel Ainsworth](https://github.com/samuela), [Kyle Uehlein](https://github.com/kuehlein), [Thanik Bhongbhibhat](https://github.com/bhongy), [Marcin Kopacz](https://github.com/chyzwar), [Trivikram Kamat](https://github.com/trivikr), [Junxiao Shi](https://github.com/yoursunny), [Ilia Baryshnikov](https://github.com/qwelias), [ExE Boss](https://github.com/ExE-Boss), [Piotr Błażejewicz](https://github.com/peterblazejewicz), [Anna Henningsen](https://github.com/addaleax), [Victor Perin](https://github.com/victorperin), [Yongsheng Zhang](https://github.com/ZYSzys), [NodeJS Contributors](https://github.com/NodeJS), [Linus Unnebäck](https://github.com/LinusU), and [wafuwafu13](https://github.com/wafuwafu13).

912
node_modules/@types/node/assert.d.ts generated vendored
View File

@@ -1,912 +0,0 @@
/**
* The `assert` module provides a set of assertion functions for verifying
* invariants.
* @see [source](https://github.com/nodejs/node/blob/v17.0.0/lib/assert.js)
*/
declare module 'assert' {
/**
* An alias of {@link ok}.
* @since v0.5.9
* @param value The input that is checked for being truthy.
*/
function assert(value: unknown, message?: string | Error): asserts value;
namespace assert {
/**
* Indicates the failure of an assertion. All errors thrown by the `assert` module
* will be instances of the `AssertionError` class.
*/
class AssertionError extends Error {
actual: unknown;
expected: unknown;
operator: string;
generatedMessage: boolean;
code: 'ERR_ASSERTION';
constructor(options?: {
/** If provided, the error message is set to this value. */
message?: string | undefined;
/** The `actual` property on the error instance. */
actual?: unknown | undefined;
/** The `expected` property on the error instance. */
expected?: unknown | undefined;
/** The `operator` property on the error instance. */
operator?: string | undefined;
/** If provided, the generated stack trace omits frames before this function. */
// tslint:disable-next-line:ban-types
stackStartFn?: Function | undefined;
});
}
/**
* This feature is currently experimental and behavior might still change.
* @since v14.2.0, v12.19.0
* @experimental
*/
class CallTracker {
/**
* The wrapper function is expected to be called exactly `exact` times. If the
* function has not been called exactly `exact` times when `tracker.verify()` is called, then `tracker.verify()` will throw an
* error.
*
* ```js
* import assert from 'assert';
*
* // Creates call tracker.
* const tracker = new assert.CallTracker();
*
* function func() {}
*
* // Returns a function that wraps func() that must be called exact times
* // before tracker.verify().
* const callsfunc = tracker.calls(func);
* ```
* @since v14.2.0, v12.19.0
* @param [fn='A no-op function']
* @param [exact=1]
* @return that wraps `fn`.
*/
calls(exact?: number): () => void;
calls<Func extends (...args: any[]) => any>(fn?: Func, exact?: number): Func;
/**
* The arrays contains information about the expected and actual number of calls of
* the functions that have not been called the expected number of times.
*
* ```js
* import assert from 'assert';
*
* // Creates call tracker.
* const tracker = new assert.CallTracker();
*
* function func() {}
*
* function foo() {}
*
* // Returns a function that wraps func() that must be called exact times
* // before tracker.verify().
* const callsfunc = tracker.calls(func, 2);
*
* // Returns an array containing information on callsfunc()
* tracker.report();
* // [
* // {
* // message: 'Expected the func function to be executed 2 time(s) but was
* // executed 0 time(s).',
* // actual: 0,
* // expected: 2,
* // operator: 'func',
* // stack: stack trace
* // }
* // ]
* ```
* @since v14.2.0, v12.19.0
* @return of objects containing information about the wrapper functions returned by `calls`.
*/
report(): CallTrackerReportInformation[];
/**
* Iterates through the list of functions passed to `tracker.calls()` and will throw an error for functions that
* have not been called the expected number of times.
*
* ```js
* import assert from 'assert';
*
* // Creates call tracker.
* const tracker = new assert.CallTracker();
*
* function func() {}
*
* // Returns a function that wraps func() that must be called exact times
* // before tracker.verify().
* const callsfunc = tracker.calls(func, 2);
*
* callsfunc();
*
* // Will throw an error since callsfunc() was only called once.
* tracker.verify();
* ```
* @since v14.2.0, v12.19.0
*/
verify(): void;
}
interface CallTrackerReportInformation {
message: string;
/** The actual number of times the function was called. */
actual: number;
/** The number of times the function was expected to be called. */
expected: number;
/** The name of the function that is wrapped. */
operator: string;
/** A stack trace of the function. */
stack: object;
}
type AssertPredicate = RegExp | (new () => object) | ((thrown: unknown) => boolean) | object | Error;
/**
* Throws an `AssertionError` with the provided error message or a default
* error message. If the `message` parameter is an instance of an `Error` then
* it will be thrown instead of the `AssertionError`.
*
* ```js
* import assert from 'assert/strict';
*
* assert.fail();
* // AssertionError [ERR_ASSERTION]: Failed
*
* assert.fail('boom');
* // AssertionError [ERR_ASSERTION]: boom
*
* assert.fail(new TypeError('need array'));
* // TypeError: need array
* ```
*
* Using `assert.fail()` with more than two arguments is possible but deprecated.
* See below for further details.
* @since v0.1.21
* @param [message='Failed']
*/
function fail(message?: string | Error): never;
/** @deprecated since v10.0.0 - use fail([message]) or other assert functions instead. */
function fail(
actual: unknown,
expected: unknown,
message?: string | Error,
operator?: string,
// tslint:disable-next-line:ban-types
stackStartFn?: Function
): never;
/**
* Tests if `value` is truthy. It is equivalent to`assert.equal(!!value, true, message)`.
*
* If `value` is not truthy, an `AssertionError` is thrown with a `message`property set equal to the value of the `message` parameter. If the `message`parameter is `undefined`, a default
* error message is assigned. If the `message`parameter is an instance of an `Error` then it will be thrown instead of the`AssertionError`.
* If no arguments are passed in at all `message` will be set to the string:`` 'No value argument passed to `assert.ok()`' ``.
*
* Be aware that in the `repl` the error message will be different to the one
* thrown in a file! See below for further details.
*
* ```js
* import assert from 'assert/strict';
*
* assert.ok(true);
* // OK
* assert.ok(1);
* // OK
*
* assert.ok();
* // AssertionError: No value argument passed to `assert.ok()`
*
* assert.ok(false, 'it\'s false');
* // AssertionError: it's false
*
* // In the repl:
* assert.ok(typeof 123 === 'string');
* // AssertionError: false == true
*
* // In a file (e.g. test.js):
* assert.ok(typeof 123 === 'string');
* // AssertionError: The expression evaluated to a falsy value:
* //
* // assert.ok(typeof 123 === 'string')
*
* assert.ok(false);
* // AssertionError: The expression evaluated to a falsy value:
* //
* // assert.ok(false)
*
* assert.ok(0);
* // AssertionError: The expression evaluated to a falsy value:
* //
* // assert.ok(0)
* ```
*
* ```js
* import assert from 'assert/strict';
*
* // Using `assert()` works the same:
* assert(0);
* // AssertionError: The expression evaluated to a falsy value:
* //
* // assert(0)
* ```
* @since v0.1.21
*/
function ok(value: unknown, message?: string | Error): asserts value;
/**
* **Strict assertion mode**
*
* An alias of {@link strictEqual}.
*
* **Legacy assertion mode**
*
* > Stability: 3 - Legacy: Use {@link strictEqual} instead.
*
* Tests shallow, coercive equality between the `actual` and `expected` parameters
* using the [Abstract Equality Comparison](https://tc39.github.io/ecma262/#sec-abstract-equality-comparison) ( `==` ). `NaN` is special handled
* and treated as being identical in case both sides are `NaN`.
*
* ```js
* import assert from 'assert';
*
* assert.equal(1, 1);
* // OK, 1 == 1
* assert.equal(1, '1');
* // OK, 1 == '1'
* assert.equal(NaN, NaN);
* // OK
*
* assert.equal(1, 2);
* // AssertionError: 1 == 2
* assert.equal({ a: { b: 1 } }, { a: { b: 1 } });
* // AssertionError: { a: { b: 1 } } == { a: { b: 1 } }
* ```
*
* If the values are not equal, an `AssertionError` is thrown with a `message`property set equal to the value of the `message` parameter. If the `message`parameter is undefined, a default
* error message is assigned. If the `message`parameter is an instance of an `Error` then it will be thrown instead of the`AssertionError`.
* @since v0.1.21
*/
function equal(actual: unknown, expected: unknown, message?: string | Error): void;
/**
* **Strict assertion mode**
*
* An alias of {@link notStrictEqual}.
*
* **Legacy assertion mode**
*
* > Stability: 3 - Legacy: Use {@link notStrictEqual} instead.
*
* Tests shallow, coercive inequality with the [Abstract Equality Comparison](https://tc39.github.io/ecma262/#sec-abstract-equality-comparison)(`!=` ). `NaN` is special handled and treated as
* being identical in case both
* sides are `NaN`.
*
* ```js
* import assert from 'assert';
*
* assert.notEqual(1, 2);
* // OK
*
* assert.notEqual(1, 1);
* // AssertionError: 1 != 1
*
* assert.notEqual(1, '1');
* // AssertionError: 1 != '1'
* ```
*
* If the values are equal, an `AssertionError` is thrown with a `message`property set equal to the value of the `message` parameter. If the `message`parameter is undefined, a default error
* message is assigned. If the `message`parameter is an instance of an `Error` then it will be thrown instead of the`AssertionError`.
* @since v0.1.21
*/
function notEqual(actual: unknown, expected: unknown, message?: string | Error): void;
/**
* **Strict assertion mode**
*
* An alias of {@link deepStrictEqual}.
*
* **Legacy assertion mode**
*
* > Stability: 3 - Legacy: Use {@link deepStrictEqual} instead.
*
* Tests for deep equality between the `actual` and `expected` parameters. Consider
* using {@link deepStrictEqual} instead. {@link deepEqual} can have
* surprising results.
*
* _Deep equality_ means that the enumerable "own" properties of child objects
* are also recursively evaluated by the following rules.
* @since v0.1.21
*/
function deepEqual(actual: unknown, expected: unknown, message?: string | Error): void;
/**
* **Strict assertion mode**
*
* An alias of {@link notDeepStrictEqual}.
*
* **Legacy assertion mode**
*
* > Stability: 3 - Legacy: Use {@link notDeepStrictEqual} instead.
*
* Tests for any deep inequality. Opposite of {@link deepEqual}.
*
* ```js
* import assert from 'assert';
*
* const obj1 = {
* a: {
* b: 1
* }
* };
* const obj2 = {
* a: {
* b: 2
* }
* };
* const obj3 = {
* a: {
* b: 1
* }
* };
* const obj4 = Object.create(obj1);
*
* assert.notDeepEqual(obj1, obj1);
* // AssertionError: { a: { b: 1 } } notDeepEqual { a: { b: 1 } }
*
* assert.notDeepEqual(obj1, obj2);
* // OK
*
* assert.notDeepEqual(obj1, obj3);
* // AssertionError: { a: { b: 1 } } notDeepEqual { a: { b: 1 } }
*
* assert.notDeepEqual(obj1, obj4);
* // OK
* ```
*
* If the values are deeply equal, an `AssertionError` is thrown with a`message` property set equal to the value of the `message` parameter. If the`message` parameter is undefined, a default
* error message is assigned. If the`message` parameter is an instance of an `Error` then it will be thrown
* instead of the `AssertionError`.
* @since v0.1.21
*/
function notDeepEqual(actual: unknown, expected: unknown, message?: string | Error): void;
/**
* Tests strict equality between the `actual` and `expected` parameters as
* determined by the [SameValue Comparison](https://tc39.github.io/ecma262/#sec-samevalue).
*
* ```js
* import assert from 'assert/strict';
*
* assert.strictEqual(1, 2);
* // AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal:
* //
* // 1 !== 2
*
* assert.strictEqual(1, 1);
* // OK
*
* assert.strictEqual('Hello foobar', 'Hello World!');
* // AssertionError [ERR_ASSERTION]: Expected inputs to be strictly equal:
* // + actual - expected
* //
* // + 'Hello foobar'
* // - 'Hello World!'
* // ^
*
* const apples = 1;
* const oranges = 2;
* assert.strictEqual(apples, oranges, `apples ${apples} !== oranges ${oranges}`);
* // AssertionError [ERR_ASSERTION]: apples 1 !== oranges 2
*
* assert.strictEqual(1, '1', new TypeError('Inputs are not identical'));
* // TypeError: Inputs are not identical
* ```
*
* If the values are not strictly equal, an `AssertionError` is thrown with a`message` property set equal to the value of the `message` parameter. If the`message` parameter is undefined, a
* default error message is assigned. If the`message` parameter is an instance of an `Error` then it will be thrown
* instead of the `AssertionError`.
* @since v0.1.21
*/
function strictEqual<T>(actual: unknown, expected: T, message?: string | Error): asserts actual is T;
/**
* Tests strict inequality between the `actual` and `expected` parameters as
* determined by the [SameValue Comparison](https://tc39.github.io/ecma262/#sec-samevalue).
*
* ```js
* import assert from 'assert/strict';
*
* assert.notStrictEqual(1, 2);
* // OK
*
* assert.notStrictEqual(1, 1);
* // AssertionError [ERR_ASSERTION]: Expected "actual" to be strictly unequal to:
* //
* // 1
*
* assert.notStrictEqual(1, '1');
* // OK
* ```
*
* If the values are strictly equal, an `AssertionError` is thrown with a`message` property set equal to the value of the `message` parameter. If the`message` parameter is undefined, a
* default error message is assigned. If the`message` parameter is an instance of an `Error` then it will be thrown
* instead of the `AssertionError`.
* @since v0.1.21
*/
function notStrictEqual(actual: unknown, expected: unknown, message?: string | Error): void;
/**
* Tests for deep equality between the `actual` and `expected` parameters.
* "Deep" equality means that the enumerable "own" properties of child objects
* are recursively evaluated also by the following rules.
* @since v1.2.0
*/
function deepStrictEqual<T>(actual: unknown, expected: T, message?: string | Error): asserts actual is T;
/**
* Tests for deep strict inequality. Opposite of {@link deepStrictEqual}.
*
* ```js
* import assert from 'assert/strict';
*
* assert.notDeepStrictEqual({ a: 1 }, { a: '1' });
* // OK
* ```
*
* If the values are deeply and strictly equal, an `AssertionError` is thrown
* with a `message` property set equal to the value of the `message` parameter. If
* the `message` parameter is undefined, a default error message is assigned. If
* the `message` parameter is an instance of an `Error` then it will be thrown
* instead of the `AssertionError`.
* @since v1.2.0
*/
function notDeepStrictEqual(actual: unknown, expected: unknown, message?: string | Error): void;
/**
* Expects the function `fn` to throw an error.
*
* If specified, `error` can be a [`Class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes),
* [`RegExp`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions), a validation function,
* a validation object where each property will be tested for strict deep equality,
* or an instance of error where each property will be tested for strict deep
* equality including the non-enumerable `message` and `name` properties. When
* using an object, it is also possible to use a regular expression, when
* validating against a string property. See below for examples.
*
* If specified, `message` will be appended to the message provided by the`AssertionError` if the `fn` call fails to throw or in case the error validation
* fails.
*
* Custom validation object/error instance:
*
* ```js
* import assert from 'assert/strict';
*
* const err = new TypeError('Wrong value');
* err.code = 404;
* err.foo = 'bar';
* err.info = {
* nested: true,
* baz: 'text'
* };
* err.reg = /abc/i;
*
* assert.throws(
* () => {
* throw err;
* },
* {
* name: 'TypeError',
* message: 'Wrong value',
* info: {
* nested: true,
* baz: 'text'
* }
* // Only properties on the validation object will be tested for.
* // Using nested objects requires all properties to be present. Otherwise
* // the validation is going to fail.
* }
* );
*
* // Using regular expressions to validate error properties:
* throws(
* () => {
* throw err;
* },
* {
* // The `name` and `message` properties are strings and using regular
* // expressions on those will match against the string. If they fail, an
* // error is thrown.
* name: /^TypeError$/,
* message: /Wrong/,
* foo: 'bar',
* info: {
* nested: true,
* // It is not possible to use regular expressions for nested properties!
* baz: 'text'
* },
* // The `reg` property contains a regular expression and only if the
* // validation object contains an identical regular expression, it is going
* // to pass.
* reg: /abc/i
* }
* );
*
* // Fails due to the different `message` and `name` properties:
* throws(
* () => {
* const otherErr = new Error('Not found');
* // Copy all enumerable properties from `err` to `otherErr`.
* for (const [key, value] of Object.entries(err)) {
* otherErr[key] = value;
* }
* throw otherErr;
* },
* // The error's `message` and `name` properties will also be checked when using
* // an error as validation object.
* err
* );
* ```
*
* Validate instanceof using constructor:
*
* ```js
* import assert from 'assert/strict';
*
* assert.throws(
* () => {
* throw new Error('Wrong value');
* },
* Error
* );
* ```
*
* Validate error message using [`RegExp`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions):
*
* Using a regular expression runs `.toString` on the error object, and will
* therefore also include the error name.
*
* ```js
* import assert from 'assert/strict';
*
* assert.throws(
* () => {
* throw new Error('Wrong value');
* },
* /^Error: Wrong value$/
* );
* ```
*
* Custom error validation:
*
* The function must return `true` to indicate all internal validations passed.
* It will otherwise fail with an `AssertionError`.
*
* ```js
* import assert from 'assert/strict';
*
* assert.throws(
* () => {
* throw new Error('Wrong value');
* },
* (err) => {
* assert(err instanceof Error);
* assert(/value/.test(err));
* // Avoid returning anything from validation functions besides `true`.
* // Otherwise, it's not clear what part of the validation failed. Instead,
* // throw an error about the specific validation that failed (as done in this
* // example) and add as much helpful debugging information to that error as
* // possible.
* return true;
* },
* 'unexpected error'
* );
* ```
*
* `error` cannot be a string. If a string is provided as the second
* argument, then `error` is assumed to be omitted and the string will be used for`message` instead. This can lead to easy-to-miss mistakes. Using the same
* message as the thrown error message is going to result in an`ERR_AMBIGUOUS_ARGUMENT` error. Please read the example below carefully if using
* a string as the second argument gets considered:
*
* ```js
* import assert from 'assert/strict';
*
* function throwingFirst() {
* throw new Error('First');
* }
*
* function throwingSecond() {
* throw new Error('Second');
* }
*
* function notThrowing() {}
*
* // The second argument is a string and the input function threw an Error.
* // The first case will not throw as it does not match for the error message
* // thrown by the input function!
* assert.throws(throwingFirst, 'Second');
* // In the next example the message has no benefit over the message from the
* // error and since it is not clear if the user intended to actually match
* // against the error message, Node.js throws an `ERR_AMBIGUOUS_ARGUMENT` error.
* assert.throws(throwingSecond, 'Second');
* // TypeError [ERR_AMBIGUOUS_ARGUMENT]
*
* // The string is only used (as message) in case the function does not throw:
* assert.throws(notThrowing, 'Second');
* // AssertionError [ERR_ASSERTION]: Missing expected exception: Second
*
* // If it was intended to match for the error message do this instead:
* // It does not throw because the error messages match.
* assert.throws(throwingSecond, /Second$/);
*
* // If the error message does not match, an AssertionError is thrown.
* assert.throws(throwingFirst, /Second$/);
* // AssertionError [ERR_ASSERTION]
* ```
*
* Due to the confusing error-prone notation, avoid a string as the second
* argument.
* @since v0.1.21
*/
function throws(block: () => unknown, message?: string | Error): void;
function throws(block: () => unknown, error: AssertPredicate, message?: string | Error): void;
/**
* Asserts that the function `fn` does not throw an error.
*
* Using `assert.doesNotThrow()` is actually not useful because there
* is no benefit in catching an error and then rethrowing it. Instead, consider
* adding a comment next to the specific code path that should not throw and keep
* error messages as expressive as possible.
*
* When `assert.doesNotThrow()` is called, it will immediately call the `fn`function.
*
* If an error is thrown and it is the same type as that specified by the `error`parameter, then an `AssertionError` is thrown. If the error is of a
* different type, or if the `error` parameter is undefined, the error is
* propagated back to the caller.
*
* If specified, `error` can be a [`Class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes),
* [`RegExp`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) or a validation
* function. See {@link throws} for more details.
*
* The following, for instance, will throw the `TypeError` because there is no
* matching error type in the assertion:
*
* ```js
* import assert from 'assert/strict';
*
* assert.doesNotThrow(
* () => {
* throw new TypeError('Wrong value');
* },
* SyntaxError
* );
* ```
*
* However, the following will result in an `AssertionError` with the message
* 'Got unwanted exception...':
*
* ```js
* import assert from 'assert/strict';
*
* assert.doesNotThrow(
* () => {
* throw new TypeError('Wrong value');
* },
* TypeError
* );
* ```
*
* If an `AssertionError` is thrown and a value is provided for the `message`parameter, the value of `message` will be appended to the `AssertionError` message:
*
* ```js
* import assert from 'assert/strict';
*
* assert.doesNotThrow(
* () => {
* throw new TypeError('Wrong value');
* },
* /Wrong value/,
* 'Whoops'
* );
* // Throws: AssertionError: Got unwanted exception: Whoops
* ```
* @since v0.1.21
*/
function doesNotThrow(block: () => unknown, message?: string | Error): void;
function doesNotThrow(block: () => unknown, error: AssertPredicate, message?: string | Error): void;
/**
* Throws `value` if `value` is not `undefined` or `null`. This is useful when
* testing the `error` argument in callbacks. The stack trace contains all frames
* from the error passed to `ifError()` including the potential new frames for`ifError()` itself.
*
* ```js
* import assert from 'assert/strict';
*
* assert.ifError(null);
* // OK
* assert.ifError(0);
* // AssertionError [ERR_ASSERTION]: ifError got unwanted exception: 0
* assert.ifError('error');
* // AssertionError [ERR_ASSERTION]: ifError got unwanted exception: 'error'
* assert.ifError(new Error());
* // AssertionError [ERR_ASSERTION]: ifError got unwanted exception: Error
*
* // Create some random error frames.
* let err;
* (function errorFrame() {
* err = new Error('test error');
* })();
*
* (function ifErrorFrame() {
* assert.ifError(err);
* })();
* // AssertionError [ERR_ASSERTION]: ifError got unwanted exception: test error
* // at ifErrorFrame
* // at errorFrame
* ```
* @since v0.1.97
*/
function ifError(value: unknown): asserts value is null | undefined;
/**
* Awaits the `asyncFn` promise or, if `asyncFn` is a function, immediately
* calls the function and awaits the returned promise to complete. It will then
* check that the promise is rejected.
*
* If `asyncFn` is a function and it throws an error synchronously,`assert.rejects()` will return a rejected `Promise` with that error. If the
* function does not return a promise, `assert.rejects()` will return a rejected`Promise` with an `ERR_INVALID_RETURN_VALUE` error. In both cases the error
* handler is skipped.
*
* Besides the async nature to await the completion behaves identically to {@link throws}.
*
* If specified, `error` can be a [`Class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes),
* [`RegExp`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions), a validation function,
* an object where each property will be tested for, or an instance of error where
* each property will be tested for including the non-enumerable `message` and`name` properties.
*
* If specified, `message` will be the message provided by the `AssertionError` if the `asyncFn` fails to reject.
*
* ```js
* import assert from 'assert/strict';
*
* await assert.rejects(
* async () => {
* throw new TypeError('Wrong value');
* },
* {
* name: 'TypeError',
* message: 'Wrong value'
* }
* );
* ```
*
* ```js
* import assert from 'assert/strict';
*
* await assert.rejects(
* async () => {
* throw new TypeError('Wrong value');
* },
* (err) => {
* assert.strictEqual(err.name, 'TypeError');
* assert.strictEqual(err.message, 'Wrong value');
* return true;
* }
* );
* ```
*
* ```js
* import assert from 'assert/strict';
*
* assert.rejects(
* Promise.reject(new Error('Wrong value')),
* Error
* ).then(() => {
* // ...
* });
* ```
*
* `error` cannot be a string. If a string is provided as the second
* argument, then `error` is assumed to be omitted and the string will be used for`message` instead. This can lead to easy-to-miss mistakes. Please read the
* example in {@link throws} carefully if using a string as the second
* argument gets considered.
* @since v10.0.0
*/
function rejects(block: (() => Promise<unknown>) | Promise<unknown>, message?: string | Error): Promise<void>;
function rejects(block: (() => Promise<unknown>) | Promise<unknown>, error: AssertPredicate, message?: string | Error): Promise<void>;
/**
* Awaits the `asyncFn` promise or, if `asyncFn` is a function, immediately
* calls the function and awaits the returned promise to complete. It will then
* check that the promise is not rejected.
*
* If `asyncFn` is a function and it throws an error synchronously,`assert.doesNotReject()` will return a rejected `Promise` with that error. If
* the function does not return a promise, `assert.doesNotReject()` will return a
* rejected `Promise` with an `ERR_INVALID_RETURN_VALUE` error. In both cases
* the error handler is skipped.
*
* Using `assert.doesNotReject()` is actually not useful because there is little
* benefit in catching a rejection and then rejecting it again. Instead, consider
* adding a comment next to the specific code path that should not reject and keep
* error messages as expressive as possible.
*
* If specified, `error` can be a [`Class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes),
* [`RegExp`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) or a validation
* function. See {@link throws} for more details.
*
* Besides the async nature to await the completion behaves identically to {@link doesNotThrow}.
*
* ```js
* import assert from 'assert/strict';
*
* await assert.doesNotReject(
* async () => {
* throw new TypeError('Wrong value');
* },
* SyntaxError
* );
* ```
*
* ```js
* import assert from 'assert/strict';
*
* assert.doesNotReject(Promise.reject(new TypeError('Wrong value')))
* .then(() => {
* // ...
* });
* ```
* @since v10.0.0
*/
function doesNotReject(block: (() => Promise<unknown>) | Promise<unknown>, message?: string | Error): Promise<void>;
function doesNotReject(block: (() => Promise<unknown>) | Promise<unknown>, error: AssertPredicate, message?: string | Error): Promise<void>;
/**
* Expects the `string` input to match the regular expression.
*
* ```js
* import assert from 'assert/strict';
*
* assert.match('I will fail', /pass/);
* // AssertionError [ERR_ASSERTION]: The input did not match the regular ...
*
* assert.match(123, /pass/);
* // AssertionError [ERR_ASSERTION]: The "string" argument must be of type string.
*
* assert.match('I will pass', /pass/);
* // OK
* ```
*
* If the values do not match, or if the `string` argument is of another type than`string`, an `AssertionError` is thrown with a `message` property set equal
* to the value of the `message` parameter. If the `message` parameter is
* undefined, a default error message is assigned. If the `message` parameter is an
* instance of an `Error` then it will be thrown instead of the `AssertionError`.
* @since v13.6.0, v12.16.0
*/
function match(value: string, regExp: RegExp, message?: string | Error): void;
/**
* Expects the `string` input not to match the regular expression.
*
* ```js
* import assert from 'assert/strict';
*
* assert.doesNotMatch('I will fail', /fail/);
* // AssertionError [ERR_ASSERTION]: The input was expected to not match the ...
*
* assert.doesNotMatch(123, /pass/);
* // AssertionError [ERR_ASSERTION]: The "string" argument must be of type string.
*
* assert.doesNotMatch('I will pass', /different/);
* // OK
* ```
*
* If the values do match, or if the `string` argument is of another type than`string`, an `AssertionError` is thrown with a `message` property set equal
* to the value of the `message` parameter. If the `message` parameter is
* undefined, a default error message is assigned. If the `message` parameter is an
* instance of an `Error` then it will be thrown instead of the `AssertionError`.
* @since v13.6.0, v12.16.0
*/
function doesNotMatch(value: string, regExp: RegExp, message?: string | Error): void;
const strict: Omit<typeof assert, 'equal' | 'notEqual' | 'deepEqual' | 'notDeepEqual' | 'ok' | 'strictEqual' | 'deepStrictEqual' | 'ifError' | 'strict'> & {
(value: unknown, message?: string | Error): asserts value;
equal: typeof strictEqual;
notEqual: typeof notStrictEqual;
deepEqual: typeof deepStrictEqual;
notDeepEqual: typeof notDeepStrictEqual;
// Mapped types and assertion functions are incompatible?
// TS2775: Assertions require every name in the call target
// to be declared with an explicit type annotation.
ok: typeof ok;
strictEqual: typeof strictEqual;
deepStrictEqual: typeof deepStrictEqual;
ifError: typeof ifError;
strict: typeof strict;
};
}
export = assert;
}
declare module 'node:assert' {
import assert = require('assert');
export = assert;
}

View File

@@ -1,8 +0,0 @@
declare module 'assert/strict' {
import { strict } from 'node:assert';
export = strict;
}
declare module 'node:assert/strict' {
import { strict } from 'node:assert';
export = strict;
}

View File

@@ -1,501 +0,0 @@
/**
* The `async_hooks` module provides an API to track asynchronous resources. It
* can be accessed using:
*
* ```js
* import async_hooks from 'async_hooks';
* ```
* @experimental
* @see [source](https://github.com/nodejs/node/blob/v17.0.0/lib/async_hooks.js)
*/
declare module 'async_hooks' {
/**
* ```js
* import { executionAsyncId } from 'async_hooks';
*
* console.log(executionAsyncId()); // 1 - bootstrap
* fs.open(path, 'r', (err, fd) => {
* console.log(executionAsyncId()); // 6 - open()
* });
* ```
*
* The ID returned from `executionAsyncId()` is related to execution timing, not
* causality (which is covered by `triggerAsyncId()`):
*
* ```js
* const server = net.createServer((conn) => {
* // Returns the ID of the server, not of the new connection, because the
* // callback runs in the execution scope of the server's MakeCallback().
* async_hooks.executionAsyncId();
*
* }).listen(port, () => {
* // Returns the ID of a TickObject (process.nextTick()) because all
* // callbacks passed to .listen() are wrapped in a nextTick().
* async_hooks.executionAsyncId();
* });
* ```
*
* Promise contexts may not get precise `executionAsyncIds` by default.
* See the section on `promise execution tracking`.
* @since v8.1.0
* @return The `asyncId` of the current execution context. Useful to track when something calls.
*/
function executionAsyncId(): number;
/**
* Resource objects returned by `executionAsyncResource()` are most often internal
* Node.js handle objects with undocumented APIs. Using any functions or properties
* on the object is likely to crash your application and should be avoided.
*
* Using `executionAsyncResource()` in the top-level execution context will
* return an empty object as there is no handle or request object to use,
* but having an object representing the top-level can be helpful.
*
* ```js
* import { open } from 'fs';
* import { executionAsyncId, executionAsyncResource } from 'async_hooks';
*
* console.log(executionAsyncId(), executionAsyncResource()); // 1 {}
* open(new URL(import.meta.url), 'r', (err, fd) => {
* console.log(executionAsyncId(), executionAsyncResource()); // 7 FSReqWrap
* });
* ```
*
* This can be used to implement continuation local storage without the
* use of a tracking `Map` to store the metadata:
*
* ```js
* import { createServer } from 'http';
* import {
* executionAsyncId,
* executionAsyncResource,
* createHook
* } from 'async_hooks';
* const sym = Symbol('state'); // Private symbol to avoid pollution
*
* createHook({
* init(asyncId, type, triggerAsyncId, resource) {
* const cr = executionAsyncResource();
* if (cr) {
* resource[sym] = cr[sym];
* }
* }
* }).enable();
*
* const server = createServer((req, res) => {
* executionAsyncResource()[sym] = { state: req.url };
* setTimeout(function() {
* res.end(JSON.stringify(executionAsyncResource()[sym]));
* }, 100);
* }).listen(3000);
* ```
* @since v13.9.0, v12.17.0
* @return The resource representing the current execution. Useful to store data within the resource.
*/
function executionAsyncResource(): object;
/**
* ```js
* const server = net.createServer((conn) => {
* // The resource that caused (or triggered) this callback to be called
* // was that of the new connection. Thus the return value of triggerAsyncId()
* // is the asyncId of "conn".
* async_hooks.triggerAsyncId();
*
* }).listen(port, () => {
* // Even though all callbacks passed to .listen() are wrapped in a nextTick()
* // the callback itself exists because the call to the server's .listen()
* // was made. So the return value would be the ID of the server.
* async_hooks.triggerAsyncId();
* });
* ```
*
* Promise contexts may not get valid `triggerAsyncId`s by default. See
* the section on `promise execution tracking`.
* @return The ID of the resource responsible for calling the callback that is currently being executed.
*/
function triggerAsyncId(): number;
interface HookCallbacks {
/**
* Called when a class is constructed that has the possibility to emit an asynchronous event.
* @param asyncId a unique ID for the async resource
* @param type the type of the async resource
* @param triggerAsyncId the unique ID of the async resource in whose execution context this async resource was created
* @param resource reference to the resource representing the async operation, needs to be released during destroy
*/
init?(asyncId: number, type: string, triggerAsyncId: number, resource: object): void;
/**
* When an asynchronous operation is initiated or completes a callback is called to notify the user.
* The before callback is called just before said callback is executed.
* @param asyncId the unique identifier assigned to the resource about to execute the callback.
*/
before?(asyncId: number): void;
/**
* Called immediately after the callback specified in before is completed.
* @param asyncId the unique identifier assigned to the resource which has executed the callback.
*/
after?(asyncId: number): void;
/**
* Called when a promise has resolve() called. This may not be in the same execution id
* as the promise itself.
* @param asyncId the unique id for the promise that was resolve()d.
*/
promiseResolve?(asyncId: number): void;
/**
* Called after the resource corresponding to asyncId is destroyed
* @param asyncId a unique ID for the async resource
*/
destroy?(asyncId: number): void;
}
interface AsyncHook {
/**
* Enable the callbacks for a given AsyncHook instance. If no callbacks are provided enabling is a noop.
*/
enable(): this;
/**
* Disable the callbacks for a given AsyncHook instance from the global pool of AsyncHook callbacks to be executed. Once a hook has been disabled it will not be called again until enabled.
*/
disable(): this;
}
/**
* Registers functions to be called for different lifetime events of each async
* operation.
*
* The callbacks `init()`/`before()`/`after()`/`destroy()` are called for the
* respective asynchronous event during a resource's lifetime.
*
* All callbacks are optional. For example, if only resource cleanup needs to
* be tracked, then only the `destroy` callback needs to be passed. The
* specifics of all functions that can be passed to `callbacks` is in the `Hook Callbacks` section.
*
* ```js
* import { createHook } from 'async_hooks';
*
* const asyncHook = createHook({
* init(asyncId, type, triggerAsyncId, resource) { },
* destroy(asyncId) { }
* });
* ```
*
* The callbacks will be inherited via the prototype chain:
*
* ```js
* class MyAsyncCallbacks {
* init(asyncId, type, triggerAsyncId, resource) { }
* destroy(asyncId) {}
* }
*
* class MyAddedCallbacks extends MyAsyncCallbacks {
* before(asyncId) { }
* after(asyncId) { }
* }
*
* const asyncHook = async_hooks.createHook(new MyAddedCallbacks());
* ```
*
* Because promises are asynchronous resources whose lifecycle is tracked
* via the async hooks mechanism, the `init()`, `before()`, `after()`, and`destroy()` callbacks _must not_ be async functions that return promises.
* @since v8.1.0
* @param callbacks The `Hook Callbacks` to register
* @return Instance used for disabling and enabling hooks
*/
function createHook(callbacks: HookCallbacks): AsyncHook;
interface AsyncResourceOptions {
/**
* The ID of the execution context that created this async event.
* @default executionAsyncId()
*/
triggerAsyncId?: number | undefined;
/**
* Disables automatic `emitDestroy` when the object is garbage collected.
* This usually does not need to be set (even if `emitDestroy` is called
* manually), unless the resource's `asyncId` is retrieved and the
* sensitive API's `emitDestroy` is called with it.
* @default false
*/
requireManualDestroy?: boolean | undefined;
}
/**
* The class `AsyncResource` is designed to be extended by the embedder's async
* resources. Using this, users can easily trigger the lifetime events of their
* own resources.
*
* The `init` hook will trigger when an `AsyncResource` is instantiated.
*
* The following is an overview of the `AsyncResource` API.
*
* ```js
* import { AsyncResource, executionAsyncId } from 'async_hooks';
*
* // AsyncResource() is meant to be extended. Instantiating a
* // new AsyncResource() also triggers init. If triggerAsyncId is omitted then
* // async_hook.executionAsyncId() is used.
* const asyncResource = new AsyncResource(
* type, { triggerAsyncId: executionAsyncId(), requireManualDestroy: false }
* );
*
* // Run a function in the execution context of the resource. This will
* // * establish the context of the resource
* // * trigger the AsyncHooks before callbacks
* // * call the provided function `fn` with the supplied arguments
* // * trigger the AsyncHooks after callbacks
* // * restore the original execution context
* asyncResource.runInAsyncScope(fn, thisArg, ...args);
*
* // Call AsyncHooks destroy callbacks.
* asyncResource.emitDestroy();
*
* // Return the unique ID assigned to the AsyncResource instance.
* asyncResource.asyncId();
*
* // Return the trigger ID for the AsyncResource instance.
* asyncResource.triggerAsyncId();
* ```
*/
class AsyncResource {
/**
* AsyncResource() is meant to be extended. Instantiating a
* new AsyncResource() also triggers init. If triggerAsyncId is omitted then
* async_hook.executionAsyncId() is used.
* @param type The type of async event.
* @param triggerAsyncId The ID of the execution context that created
* this async event (default: `executionAsyncId()`), or an
* AsyncResourceOptions object (since 9.3)
*/
constructor(type: string, triggerAsyncId?: number | AsyncResourceOptions);
/**
* Binds the given function to the current execution context.
*
* The returned function will have an `asyncResource` property referencing
* the `AsyncResource` to which the function is bound.
* @since v14.8.0, v12.19.0
* @param fn The function to bind to the current execution context.
* @param type An optional name to associate with the underlying `AsyncResource`.
*/
static bind<Func extends (this: ThisArg, ...args: any[]) => any, ThisArg>(
fn: Func,
type?: string,
thisArg?: ThisArg
): Func & {
asyncResource: AsyncResource;
};
/**
* Binds the given function to execute to this `AsyncResource`'s scope.
*
* The returned function will have an `asyncResource` property referencing
* the `AsyncResource` to which the function is bound.
* @since v14.8.0, v12.19.0
* @param fn The function to bind to the current `AsyncResource`.
*/
bind<Func extends (...args: any[]) => any>(
fn: Func
): Func & {
asyncResource: AsyncResource;
};
/**
* Call the provided function with the provided arguments in the execution context
* of the async resource. This will establish the context, trigger the AsyncHooks
* before callbacks, call the function, trigger the AsyncHooks after callbacks, and
* then restore the original execution context.
* @since v9.6.0
* @param fn The function to call in the execution context of this async resource.
* @param thisArg The receiver to be used for the function call.
* @param args Optional arguments to pass to the function.
*/
runInAsyncScope<This, Result>(fn: (this: This, ...args: any[]) => Result, thisArg?: This, ...args: any[]): Result;
/**
* Call all `destroy` hooks. This should only ever be called once. An error will
* be thrown if it is called more than once. This **must** be manually called. If
* the resource is left to be collected by the GC then the `destroy` hooks will
* never be called.
* @return A reference to `asyncResource`.
*/
emitDestroy(): this;
/**
* @return The unique `asyncId` assigned to the resource.
*/
asyncId(): number;
/**
*
* @return The same `triggerAsyncId` that is passed to the `AsyncResource` constructor.
*/
triggerAsyncId(): number;
}
/**
* This class creates stores that stay coherent through asynchronous operations.
*
* While you can create your own implementation on top of the `async_hooks` module,`AsyncLocalStorage` should be preferred as it is a performant and memory safe
* implementation that involves significant optimizations that are non-obvious to
* implement.
*
* The following example uses `AsyncLocalStorage` to build a simple logger
* that assigns IDs to incoming HTTP requests and includes them in messages
* logged within each request.
*
* ```js
* import http from 'http';
* import { AsyncLocalStorage } from 'async_hooks';
*
* const asyncLocalStorage = new AsyncLocalStorage();
*
* function logWithId(msg) {
* const id = asyncLocalStorage.getStore();
* console.log(`${id !== undefined ? id : '-'}:`, msg);
* }
*
* let idSeq = 0;
* http.createServer((req, res) => {
* asyncLocalStorage.run(idSeq++, () => {
* logWithId('start');
* // Imagine any chain of async operations here
* setImmediate(() => {
* logWithId('finish');
* res.end();
* });
* });
* }).listen(8080);
*
* http.get('http://localhost:8080');
* http.get('http://localhost:8080');
* // Prints:
* // 0: start
* // 1: start
* // 0: finish
* // 1: finish
* ```
*
* Each instance of `AsyncLocalStorage` maintains an independent storage context.
* Multiple instances can safely exist simultaneously without risk of interfering
* with each other data.
* @since v13.10.0, v12.17.0
*/
class AsyncLocalStorage<T> {
/**
* Disables the instance of `AsyncLocalStorage`. All subsequent calls
* to `asyncLocalStorage.getStore()` will return `undefined` until`asyncLocalStorage.run()` or `asyncLocalStorage.enterWith()` is called again.
*
* When calling `asyncLocalStorage.disable()`, all current contexts linked to the
* instance will be exited.
*
* Calling `asyncLocalStorage.disable()` is required before the`asyncLocalStorage` can be garbage collected. This does not apply to stores
* provided by the `asyncLocalStorage`, as those objects are garbage collected
* along with the corresponding async resources.
*
* Use this method when the `asyncLocalStorage` is not in use anymore
* in the current process.
* @since v13.10.0, v12.17.0
* @experimental
*/
disable(): void;
/**
* Returns the current store.
* If called outside of an asynchronous context initialized by
* calling `asyncLocalStorage.run()` or `asyncLocalStorage.enterWith()`, it
* returns `undefined`.
* @since v13.10.0, v12.17.0
*/
getStore(): T | undefined;
/**
* Runs a function synchronously within a context and returns its
* return value. The store is not accessible outside of the callback function.
* The store is accessible to any asynchronous operations created within the
* callback.
*
* The optional `args` are passed to the callback function.
*
* If the callback function throws an error, the error is thrown by `run()` too.
* The stacktrace is not impacted by this call and the context is exited.
*
* Example:
*
* ```js
* const store = { id: 2 };
* try {
* asyncLocalStorage.run(store, () => {
* asyncLocalStorage.getStore(); // Returns the store object
* setTimeout(() => {
* asyncLocalStorage.getStore(); // Returns the store object
* }, 200);
* throw new Error();
* });
* } catch (e) {
* asyncLocalStorage.getStore(); // Returns undefined
* // The error will be caught here
* }
* ```
* @since v13.10.0, v12.17.0
*/
run<R, TArgs extends any[]>(store: T, callback: (...args: TArgs) => R, ...args: TArgs): R;
/**
* Runs a function synchronously outside of a context and returns its
* return value. The store is not accessible within the callback function or
* the asynchronous operations created within the callback. Any `getStore()`call done within the callback function will always return `undefined`.
*
* The optional `args` are passed to the callback function.
*
* If the callback function throws an error, the error is thrown by `exit()` too.
* The stacktrace is not impacted by this call and the context is re-entered.
*
* Example:
*
* ```js
* // Within a call to run
* try {
* asyncLocalStorage.getStore(); // Returns the store object or value
* asyncLocalStorage.exit(() => {
* asyncLocalStorage.getStore(); // Returns undefined
* throw new Error();
* });
* } catch (e) {
* asyncLocalStorage.getStore(); // Returns the same object or value
* // The error will be caught here
* }
* ```
* @since v13.10.0, v12.17.0
* @experimental
*/
exit<R, TArgs extends any[]>(callback: (...args: TArgs) => R, ...args: TArgs): R;
/**
* Transitions into the context for the remainder of the current
* synchronous execution and then persists the store through any following
* asynchronous calls.
*
* Example:
*
* ```js
* const store = { id: 1 };
* // Replaces previous store with the given store object
* asyncLocalStorage.enterWith(store);
* asyncLocalStorage.getStore(); // Returns the store object
* someAsyncOperation(() => {
* asyncLocalStorage.getStore(); // Returns the same object
* });
* ```
*
* This transition will continue for the _entire_ synchronous execution.
* This means that if, for example, the context is entered within an event
* handler subsequent event handlers will also run within that context unless
* specifically bound to another context with an `AsyncResource`. That is why`run()` should be preferred over `enterWith()` unless there are strong reasons
* to use the latter method.
*
* ```js
* const store = { id: 1 };
*
* emitter.on('my-event', () => {
* asyncLocalStorage.enterWith(store);
* });
* emitter.on('my-event', () => {
* asyncLocalStorage.getStore(); // Returns the same object
* });
*
* asyncLocalStorage.getStore(); // Returns undefined
* emitter.emit('my-event');
* asyncLocalStorage.getStore(); // Returns the same object
* ```
* @since v13.11.0, v12.17.0
* @experimental
*/
enterWith(store: T): void;
}
}
declare module 'node:async_hooks' {
export * from 'async_hooks';
}

2142
node_modules/@types/node/buffer.d.ts generated vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

414
node_modules/@types/node/cluster.d.ts generated vendored
View File

@@ -1,414 +0,0 @@
/**
* A single instance of Node.js runs in a single thread. To take advantage of
* multi-core systems, the user will sometimes want to launch a cluster of Node.js
* processes to handle the load.
*
* The cluster module allows easy creation of child processes that all share
* server ports.
*
* ```js
* import cluster from 'cluster';
* import http from 'http';
* import { cpus } from 'os';
* import process from 'process';
*
* const numCPUs = cpus().length;
*
* if (cluster.isPrimary) {
* console.log(`Primary ${process.pid} is running`);
*
* // Fork workers.
* for (let i = 0; i < numCPUs; i++) {
* cluster.fork();
* }
*
* cluster.on('exit', (worker, code, signal) => {
* console.log(`worker ${worker.process.pid} died`);
* });
* } else {
* // Workers can share any TCP connection
* // In this case it is an HTTP server
* http.createServer((req, res) => {
* res.writeHead(200);
* res.end('hello world\n');
* }).listen(8000);
*
* console.log(`Worker ${process.pid} started`);
* }
* ```
*
* Running Node.js will now share port 8000 between the workers:
*
* ```console
* $ node server.js
* Primary 3596 is running
* Worker 4324 started
* Worker 4520 started
* Worker 6056 started
* Worker 5644 started
* ```
*
* On Windows, it is not yet possible to set up a named pipe server in a worker.
* @see [source](https://github.com/nodejs/node/blob/v17.0.0/lib/cluster.js)
*/
declare module 'cluster' {
import * as child from 'node:child_process';
import EventEmitter = require('node:events');
import * as net from 'node:net';
export interface ClusterSettings {
execArgv?: string[] | undefined; // default: process.execArgv
exec?: string | undefined;
args?: string[] | undefined;
silent?: boolean | undefined;
stdio?: any[] | undefined;
uid?: number | undefined;
gid?: number | undefined;
inspectPort?: number | (() => number) | undefined;
}
export interface Address {
address: string;
port: number;
addressType: number | 'udp4' | 'udp6'; // 4, 6, -1, "udp4", "udp6"
}
/**
* A `Worker` object contains all public information and method about a worker.
* In the primary it can be obtained using `cluster.workers`. In a worker
* it can be obtained using `cluster.worker`.
* @since v0.7.0
*/
export class Worker extends EventEmitter {
/**
* Each new worker is given its own unique id, this id is stored in the`id`.
*
* While a worker is alive, this is the key that indexes it in`cluster.workers`.
* @since v0.8.0
*/
id: number;
/**
* All workers are created using `child_process.fork()`, the returned object
* from this function is stored as `.process`. In a worker, the global `process`is stored.
*
* See: `Child Process module`.
*
* Workers will call `process.exit(0)` if the `'disconnect'` event occurs
* on `process` and `.exitedAfterDisconnect` is not `true`. This protects against
* accidental disconnection.
* @since v0.7.0
*/
process: child.ChildProcess;
/**
* Send a message to a worker or primary, optionally with a handle.
*
* In the primary this sends a message to a specific worker. It is identical to `ChildProcess.send()`.
*
* In a worker this sends a message to the primary. It is identical to`process.send()`.
*
* This example will echo back all messages from the primary:
*
* ```js
* if (cluster.isPrimary) {
* const worker = cluster.fork();
* worker.send('hi there');
*
* } else if (cluster.isWorker) {
* process.on('message', (msg) => {
* process.send(msg);
* });
* }
* ```
* @since v0.7.0
* @param options The `options` argument, if present, is an object used to parameterize the sending of certain types of handles. `options` supports the following properties:
*/
send(message: child.Serializable, callback?: (error: Error | null) => void): boolean;
send(message: child.Serializable, sendHandle: child.SendHandle, callback?: (error: Error | null) => void): boolean;
send(message: child.Serializable, sendHandle: child.SendHandle, options?: child.MessageOptions, callback?: (error: Error | null) => void): boolean;
/**
* This function will kill the worker. In the primary, it does this
* by disconnecting the `worker.process`, and once disconnected, killing
* with `signal`. In the worker, it does it by disconnecting the channel,
* and then exiting with code `0`.
*
* Because `kill()` attempts to gracefully disconnect the worker process, it is
* susceptible to waiting indefinitely for the disconnect to complete. For example,
* if the worker enters an infinite loop, a graceful disconnect will never occur.
* If the graceful disconnect behavior is not needed, use `worker.process.kill()`.
*
* Causes `.exitedAfterDisconnect` to be set.
*
* This method is aliased as `worker.destroy()` for backward compatibility.
*
* In a worker, `process.kill()` exists, but it is not this function;
* it is `kill()`.
* @since v0.9.12
* @param [signal='SIGTERM'] Name of the kill signal to send to the worker process.
*/
kill(signal?: string): void;
destroy(signal?: string): void;
/**
* In a worker, this function will close all servers, wait for the `'close'` event
* on those servers, and then disconnect the IPC channel.
*
* In the primary, an internal message is sent to the worker causing it to call`.disconnect()` on itself.
*
* Causes `.exitedAfterDisconnect` to be set.
*
* After a server is closed, it will no longer accept new connections,
* but connections may be accepted by any other listening worker. Existing
* connections will be allowed to close as usual. When no more connections exist,
* see `server.close()`, the IPC channel to the worker will close allowing it
* to die gracefully.
*
* The above applies _only_ to server connections, client connections are not
* automatically closed by workers, and disconnect does not wait for them to close
* before exiting.
*
* In a worker, `process.disconnect` exists, but it is not this function;
* it is `disconnect()`.
*
* Because long living server connections may block workers from disconnecting, it
* may be useful to send a message, so application specific actions may be taken to
* close them. It also may be useful to implement a timeout, killing a worker if
* the `'disconnect'` event has not been emitted after some time.
*
* ```js
* if (cluster.isPrimary) {
* const worker = cluster.fork();
* let timeout;
*
* worker.on('listening', (address) => {
* worker.send('shutdown');
* worker.disconnect();
* timeout = setTimeout(() => {
* worker.kill();
* }, 2000);
* });
*
* worker.on('disconnect', () => {
* clearTimeout(timeout);
* });
*
* } else if (cluster.isWorker) {
* const net = require('net');
* const server = net.createServer((socket) => {
* // Connections never end
* });
*
* server.listen(8000);
*
* process.on('message', (msg) => {
* if (msg === 'shutdown') {
* // Initiate graceful close of any connections to server
* }
* });
* }
* ```
* @since v0.7.7
* @return A reference to `worker`.
*/
disconnect(): void;
/**
* This function returns `true` if the worker is connected to its primary via its
* IPC channel, `false` otherwise. A worker is connected to its primary after it
* has been created. It is disconnected after the `'disconnect'` event is emitted.
* @since v0.11.14
*/
isConnected(): boolean;
/**
* This function returns `true` if the worker's process has terminated (either
* because of exiting or being signaled). Otherwise, it returns `false`.
*
* ```js
* import cluster from 'cluster';
* import http from 'http';
* import { cpus } from 'os';
* import process from 'process';
*
* const numCPUs = cpus().length;
*
* if (cluster.isPrimary) {
* console.log(`Primary ${process.pid} is running`);
*
* // Fork workers.
* for (let i = 0; i < numCPUs; i++) {
* cluster.fork();
* }
*
* cluster.on('fork', (worker) => {
* console.log('worker is dead:', worker.isDead());
* });
*
* cluster.on('exit', (worker, code, signal) => {
* console.log('worker is dead:', worker.isDead());
* });
* } else {
* // Workers can share any TCP connection. In this case, it is an HTTP server.
* http.createServer((req, res) => {
* res.writeHead(200);
* res.end(`Current process\n ${process.pid}`);
* process.kill(process.pid);
* }).listen(8000);
* }
* ```
* @since v0.11.14
*/
isDead(): boolean;
/**
* This property is `true` if the worker exited due to `.kill()` or`.disconnect()`. If the worker exited any other way, it is `false`. If the
* worker has not exited, it is `undefined`.
*
* The boolean `worker.exitedAfterDisconnect` allows distinguishing between
* voluntary and accidental exit, the primary may choose not to respawn a worker
* based on this value.
*
* ```js
* cluster.on('exit', (worker, code, signal) => {
* if (worker.exitedAfterDisconnect === true) {
* console.log('Oh, it was just voluntary no need to worry');
* }
* });
*
* // kill worker
* worker.kill();
* ```
* @since v6.0.0
*/
exitedAfterDisconnect: boolean;
/**
* events.EventEmitter
* 1. disconnect
* 2. error
* 3. exit
* 4. listening
* 5. message
* 6. online
*/
addListener(event: string, listener: (...args: any[]) => void): this;
addListener(event: 'disconnect', listener: () => void): this;
addListener(event: 'error', listener: (error: Error) => void): this;
addListener(event: 'exit', listener: (code: number, signal: string) => void): this;
addListener(event: 'listening', listener: (address: Address) => void): this;
addListener(event: 'message', listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
addListener(event: 'online', listener: () => void): this;
emit(event: string | symbol, ...args: any[]): boolean;
emit(event: 'disconnect'): boolean;
emit(event: 'error', error: Error): boolean;
emit(event: 'exit', code: number, signal: string): boolean;
emit(event: 'listening', address: Address): boolean;
emit(event: 'message', message: any, handle: net.Socket | net.Server): boolean;
emit(event: 'online'): boolean;
on(event: string, listener: (...args: any[]) => void): this;
on(event: 'disconnect', listener: () => void): this;
on(event: 'error', listener: (error: Error) => void): this;
on(event: 'exit', listener: (code: number, signal: string) => void): this;
on(event: 'listening', listener: (address: Address) => void): this;
on(event: 'message', listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
on(event: 'online', listener: () => void): this;
once(event: string, listener: (...args: any[]) => void): this;
once(event: 'disconnect', listener: () => void): this;
once(event: 'error', listener: (error: Error) => void): this;
once(event: 'exit', listener: (code: number, signal: string) => void): this;
once(event: 'listening', listener: (address: Address) => void): this;
once(event: 'message', listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
once(event: 'online', listener: () => void): this;
prependListener(event: string, listener: (...args: any[]) => void): this;
prependListener(event: 'disconnect', listener: () => void): this;
prependListener(event: 'error', listener: (error: Error) => void): this;
prependListener(event: 'exit', listener: (code: number, signal: string) => void): this;
prependListener(event: 'listening', listener: (address: Address) => void): this;
prependListener(event: 'message', listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
prependListener(event: 'online', listener: () => void): this;
prependOnceListener(event: string, listener: (...args: any[]) => void): this;
prependOnceListener(event: 'disconnect', listener: () => void): this;
prependOnceListener(event: 'error', listener: (error: Error) => void): this;
prependOnceListener(event: 'exit', listener: (code: number, signal: string) => void): this;
prependOnceListener(event: 'listening', listener: (address: Address) => void): this;
prependOnceListener(event: 'message', listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
prependOnceListener(event: 'online', listener: () => void): this;
}
export interface Cluster extends EventEmitter {
disconnect(callback?: () => void): void;
fork(env?: any): Worker;
/** @deprecated since v16.0.0 - use isPrimary. */
readonly isMaster: boolean;
readonly isPrimary: boolean;
readonly isWorker: boolean;
schedulingPolicy: number;
readonly settings: ClusterSettings;
/** @deprecated since v16.0.0 - use setupPrimary. */
setupMaster(settings?: ClusterSettings): void;
/**
* `setupPrimary` is used to change the default 'fork' behavior. Once called, the settings will be present in cluster.settings.
*/
setupPrimary(settings?: ClusterSettings): void;
readonly worker?: Worker | undefined;
readonly workers?: NodeJS.Dict<Worker> | undefined;
readonly SCHED_NONE: number;
readonly SCHED_RR: number;
/**
* events.EventEmitter
* 1. disconnect
* 2. exit
* 3. fork
* 4. listening
* 5. message
* 6. online
* 7. setup
*/
addListener(event: string, listener: (...args: any[]) => void): this;
addListener(event: 'disconnect', listener: (worker: Worker) => void): this;
addListener(event: 'exit', listener: (worker: Worker, code: number, signal: string) => void): this;
addListener(event: 'fork', listener: (worker: Worker) => void): this;
addListener(event: 'listening', listener: (worker: Worker, address: Address) => void): this;
addListener(event: 'message', listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
addListener(event: 'online', listener: (worker: Worker) => void): this;
addListener(event: 'setup', listener: (settings: ClusterSettings) => void): this;
emit(event: string | symbol, ...args: any[]): boolean;
emit(event: 'disconnect', worker: Worker): boolean;
emit(event: 'exit', worker: Worker, code: number, signal: string): boolean;
emit(event: 'fork', worker: Worker): boolean;
emit(event: 'listening', worker: Worker, address: Address): boolean;
emit(event: 'message', worker: Worker, message: any, handle: net.Socket | net.Server): boolean;
emit(event: 'online', worker: Worker): boolean;
emit(event: 'setup', settings: ClusterSettings): boolean;
on(event: string, listener: (...args: any[]) => void): this;
on(event: 'disconnect', listener: (worker: Worker) => void): this;
on(event: 'exit', listener: (worker: Worker, code: number, signal: string) => void): this;
on(event: 'fork', listener: (worker: Worker) => void): this;
on(event: 'listening', listener: (worker: Worker, address: Address) => void): this;
on(event: 'message', listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
on(event: 'online', listener: (worker: Worker) => void): this;
on(event: 'setup', listener: (settings: ClusterSettings) => void): this;
once(event: string, listener: (...args: any[]) => void): this;
once(event: 'disconnect', listener: (worker: Worker) => void): this;
once(event: 'exit', listener: (worker: Worker, code: number, signal: string) => void): this;
once(event: 'fork', listener: (worker: Worker) => void): this;
once(event: 'listening', listener: (worker: Worker, address: Address) => void): this;
once(event: 'message', listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
once(event: 'online', listener: (worker: Worker) => void): this;
once(event: 'setup', listener: (settings: ClusterSettings) => void): this;
prependListener(event: string, listener: (...args: any[]) => void): this;
prependListener(event: 'disconnect', listener: (worker: Worker) => void): this;
prependListener(event: 'exit', listener: (worker: Worker, code: number, signal: string) => void): this;
prependListener(event: 'fork', listener: (worker: Worker) => void): this;
prependListener(event: 'listening', listener: (worker: Worker, address: Address) => void): this;
// the handle is a net.Socket or net.Server object, or undefined.
prependListener(event: 'message', listener: (worker: Worker, message: any, handle?: net.Socket | net.Server) => void): this;
prependListener(event: 'online', listener: (worker: Worker) => void): this;
prependListener(event: 'setup', listener: (settings: ClusterSettings) => void): this;
prependOnceListener(event: string, listener: (...args: any[]) => void): this;
prependOnceListener(event: 'disconnect', listener: (worker: Worker) => void): this;
prependOnceListener(event: 'exit', listener: (worker: Worker, code: number, signal: string) => void): this;
prependOnceListener(event: 'fork', listener: (worker: Worker) => void): this;
prependOnceListener(event: 'listening', listener: (worker: Worker, address: Address) => void): this;
// the handle is a net.Socket or net.Server object, or undefined.
prependOnceListener(event: 'message', listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this;
prependOnceListener(event: 'online', listener: (worker: Worker) => void): this;
prependOnceListener(event: 'setup', listener: (settings: ClusterSettings) => void): this;
}
const cluster: Cluster;
export default cluster;
}
declare module 'node:cluster' {
export * from 'cluster';
export { default as default } from 'cluster';
}

412
node_modules/@types/node/console.d.ts generated vendored
View File

@@ -1,412 +0,0 @@
/**
* The `console` module provides a simple debugging console that is similar to the
* JavaScript console mechanism provided by web browsers.
*
* The module exports two specific components:
*
* * A `Console` class with methods such as `console.log()`, `console.error()` and`console.warn()` that can be used to write to any Node.js stream.
* * A global `console` instance configured to write to `process.stdout` and `process.stderr`. The global `console` can be used without calling`require('console')`.
*
* _**Warning**_: The global console object's methods are neither consistently
* synchronous like the browser APIs they resemble, nor are they consistently
* asynchronous like all other Node.js streams. See the `note on process I/O` for
* more information.
*
* Example using the global `console`:
*
* ```js
* console.log('hello world');
* // Prints: hello world, to stdout
* console.log('hello %s', 'world');
* // Prints: hello world, to stdout
* console.error(new Error('Whoops, something bad happened'));
* // Prints error message and stack trace to stderr:
* // Error: Whoops, something bad happened
* // at [eval]:5:15
* // at Script.runInThisContext (node:vm:132:18)
* // at Object.runInThisContext (node:vm:309:38)
* // at node:internal/process/execution:77:19
* // at [eval]-wrapper:6:22
* // at evalScript (node:internal/process/execution:76:60)
* // at node:internal/main/eval_string:23:3
*
* const name = 'Will Robinson';
* console.warn(`Danger ${name}! Danger!`);
* // Prints: Danger Will Robinson! Danger!, to stderr
* ```
*
* Example using the `Console` class:
*
* ```js
* const out = getStreamSomehow();
* const err = getStreamSomehow();
* const myConsole = new console.Console(out, err);
*
* myConsole.log('hello world');
* // Prints: hello world, to out
* myConsole.log('hello %s', 'world');
* // Prints: hello world, to out
* myConsole.error(new Error('Whoops, something bad happened'));
* // Prints: [Error: Whoops, something bad happened], to err
*
* const name = 'Will Robinson';
* myConsole.warn(`Danger ${name}! Danger!`);
* // Prints: Danger Will Robinson! Danger!, to err
* ```
* @see [source](https://github.com/nodejs/node/blob/v17.0.0/lib/console.js)
*/
declare module 'console' {
import console = require('node:console');
export = console;
}
declare module 'node:console' {
import { InspectOptions } from 'node:util';
global {
// This needs to be global to avoid TS2403 in case lib.dom.d.ts is present in the same build
interface Console {
Console: console.ConsoleConstructor;
/**
* `console.assert()` writes a message if `value` is [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) or omitted. It only
* writes a message and does not otherwise affect execution. The output always
* starts with `"Assertion failed"`. If provided, `message` is formatted using `util.format()`.
*
* If `value` is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy), nothing happens.
*
* ```js
* console.assert(true, 'does nothing');
*
* console.assert(false, 'Whoops %s work', 'didn\'t');
* // Assertion failed: Whoops didn't work
*
* console.assert();
* // Assertion failed
* ```
* @since v0.1.101
* @param value The value tested for being truthy.
* @param message All arguments besides `value` are used as error message.
*/
assert(value: any, message?: string, ...optionalParams: any[]): void;
/**
* When `stdout` is a TTY, calling `console.clear()` will attempt to clear the
* TTY. When `stdout` is not a TTY, this method does nothing.
*
* The specific operation of `console.clear()` can vary across operating systems
* and terminal types. For most Linux operating systems, `console.clear()`operates similarly to the `clear` shell command. On Windows, `console.clear()`will clear only the output in the
* current terminal viewport for the Node.js
* binary.
* @since v8.3.0
*/
clear(): void;
/**
* Maintains an internal counter specific to `label` and outputs to `stdout` the
* number of times `console.count()` has been called with the given `label`.
*
* ```js
* > console.count()
* default: 1
* undefined
* > console.count('default')
* default: 2
* undefined
* > console.count('abc')
* abc: 1
* undefined
* > console.count('xyz')
* xyz: 1
* undefined
* > console.count('abc')
* abc: 2
* undefined
* > console.count()
* default: 3
* undefined
* >
* ```
* @since v8.3.0
* @param label The display label for the counter.
*/
count(label?: string): void;
/**
* Resets the internal counter specific to `label`.
*
* ```js
* > console.count('abc');
* abc: 1
* undefined
* > console.countReset('abc');
* undefined
* > console.count('abc');
* abc: 1
* undefined
* >
* ```
* @since v8.3.0
* @param label The display label for the counter.
*/
countReset(label?: string): void;
/**
* The `console.debug()` function is an alias for {@link log}.
* @since v8.0.0
*/
debug(message?: any, ...optionalParams: any[]): void;
/**
* Uses `util.inspect()` on `obj` and prints the resulting string to `stdout`.
* This function bypasses any custom `inspect()` function defined on `obj`.
* @since v0.1.101
*/
dir(obj: any, options?: InspectOptions): void;
/**
* This method calls `console.log()` passing it the arguments received.
* This method does not produce any XML formatting.
* @since v8.0.0
*/
dirxml(...data: any[]): void;
/**
* Prints to `stderr` with newline. Multiple arguments can be passed, with the
* first used as the primary message and all additional used as substitution
* values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to `util.format()`).
*
* ```js
* const code = 5;
* console.error('error #%d', code);
* // Prints: error #5, to stderr
* console.error('error', code);
* // Prints: error 5, to stderr
* ```
*
* If formatting elements (e.g. `%d`) are not found in the first string then `util.inspect()` is called on each argument and the resulting string
* values are concatenated. See `util.format()` for more information.
* @since v0.1.100
*/
error(message?: any, ...optionalParams: any[]): void;
/**
* Increases indentation of subsequent lines by spaces for `groupIndentation`length.
*
* If one or more `label`s are provided, those are printed first without the
* additional indentation.
* @since v8.5.0
*/
group(...label: any[]): void;
/**
* An alias for {@link group}.
* @since v8.5.0
*/
groupCollapsed(...label: any[]): void;
/**
* Decreases indentation of subsequent lines by spaces for `groupIndentation`length.
* @since v8.5.0
*/
groupEnd(): void;
/**
* The `console.info()` function is an alias for {@link log}.
* @since v0.1.100
*/
info(message?: any, ...optionalParams: any[]): void;
/**
* Prints to `stdout` with newline. Multiple arguments can be passed, with the
* first used as the primary message and all additional used as substitution
* values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to `util.format()`).
*
* ```js
* const count = 5;
* console.log('count: %d', count);
* // Prints: count: 5, to stdout
* console.log('count:', count);
* // Prints: count: 5, to stdout
* ```
*
* See `util.format()` for more information.
* @since v0.1.100
*/
log(message?: any, ...optionalParams: any[]): void;
/**
* Try to construct a table with the columns of the properties of `tabularData`(or use `properties`) and rows of `tabularData` and log it. Falls back to just
* logging the argument if it cant be parsed as tabular.
*
* ```js
* // These can't be parsed as tabular data
* console.table(Symbol());
* // Symbol()
*
* console.table(undefined);
* // undefined
*
* console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }]);
* // ┌─────────┬─────┬─────┐
* // │ (index) │ a │ b │
* // ├─────────┼─────┼─────┤
* // │ 0 │ 1 │ 'Y' │
* // │ 1 │ 'Z' │ 2 │
* // └─────────┴─────┴─────┘
*
* console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }], ['a']);
* // ┌─────────┬─────┐
* // │ (index) │ a │
* // ├─────────┼─────┤
* // │ 0 │ 1 │
* // │ 1 │ 'Z' │
* // └─────────┴─────┘
* ```
* @since v10.0.0
* @param properties Alternate properties for constructing the table.
*/
table(tabularData: any, properties?: ReadonlyArray<string>): void;
/**
* Starts a timer that can be used to compute the duration of an operation. Timers
* are identified by a unique `label`. Use the same `label` when calling {@link timeEnd} to stop the timer and output the elapsed time in
* suitable time units to `stdout`. For example, if the elapsed
* time is 3869ms, `console.timeEnd()` displays "3.869s".
* @since v0.1.104
*/
time(label?: string): void;
/**
* Stops a timer that was previously started by calling {@link time} and
* prints the result to `stdout`:
*
* ```js
* console.time('100-elements');
* for (let i = 0; i < 100; i++) {}
* console.timeEnd('100-elements');
* // prints 100-elements: 225.438ms
* ```
* @since v0.1.104
*/
timeEnd(label?: string): void;
/**
* For a timer that was previously started by calling {@link time}, prints
* the elapsed time and other `data` arguments to `stdout`:
*
* ```js
* console.time('process');
* const value = expensiveProcess1(); // Returns 42
* console.timeLog('process', value);
* // Prints "process: 365.227ms 42".
* doExpensiveProcess2(value);
* console.timeEnd('process');
* ```
* @since v10.7.0
*/
timeLog(label?: string, ...data: any[]): void;
/**
* Prints to `stderr` the string `'Trace: '`, followed by the `util.format()` formatted message and stack trace to the current position in the code.
*
* ```js
* console.trace('Show me');
* // Prints: (stack trace will vary based on where trace is called)
* // Trace: Show me
* // at repl:2:9
* // at REPLServer.defaultEval (repl.js:248:27)
* // at bound (domain.js:287:14)
* // at REPLServer.runBound [as eval] (domain.js:300:12)
* // at REPLServer.<anonymous> (repl.js:412:12)
* // at emitOne (events.js:82:20)
* // at REPLServer.emit (events.js:169:7)
* // at REPLServer.Interface._onLine (readline.js:210:10)
* // at REPLServer.Interface._line (readline.js:549:8)
* // at REPLServer.Interface._ttyWrite (readline.js:826:14)
* ```
* @since v0.1.104
*/
trace(message?: any, ...optionalParams: any[]): void;
/**
* The `console.warn()` function is an alias for {@link error}.
* @since v0.1.100
*/
warn(message?: any, ...optionalParams: any[]): void;
// --- Inspector mode only ---
/**
* This method does not display anything unless used in the inspector.
* Starts a JavaScript CPU profile with an optional label.
*/
profile(label?: string): void;
/**
* This method does not display anything unless used in the inspector.
* Stops the current JavaScript CPU profiling session if one has been started and prints the report to the Profiles panel of the inspector.
*/
profileEnd(label?: string): void;
/**
* This method does not display anything unless used in the inspector.
* Adds an event with the label `label` to the Timeline panel of the inspector.
*/
timeStamp(label?: string): void;
}
/**
* The `console` module provides a simple debugging console that is similar to the
* JavaScript console mechanism provided by web browsers.
*
* The module exports two specific components:
*
* * A `Console` class with methods such as `console.log()`, `console.error()` and`console.warn()` that can be used to write to any Node.js stream.
* * A global `console` instance configured to write to `process.stdout` and `process.stderr`. The global `console` can be used without calling`require('console')`.
*
* _**Warning**_: The global console object's methods are neither consistently
* synchronous like the browser APIs they resemble, nor are they consistently
* asynchronous like all other Node.js streams. See the `note on process I/O` for
* more information.
*
* Example using the global `console`:
*
* ```js
* console.log('hello world');
* // Prints: hello world, to stdout
* console.log('hello %s', 'world');
* // Prints: hello world, to stdout
* console.error(new Error('Whoops, something bad happened'));
* // Prints error message and stack trace to stderr:
* // Error: Whoops, something bad happened
* // at [eval]:5:15
* // at Script.runInThisContext (node:vm:132:18)
* // at Object.runInThisContext (node:vm:309:38)
* // at node:internal/process/execution:77:19
* // at [eval]-wrapper:6:22
* // at evalScript (node:internal/process/execution:76:60)
* // at node:internal/main/eval_string:23:3
*
* const name = 'Will Robinson';
* console.warn(`Danger ${name}! Danger!`);
* // Prints: Danger Will Robinson! Danger!, to stderr
* ```
*
* Example using the `Console` class:
*
* ```js
* const out = getStreamSomehow();
* const err = getStreamSomehow();
* const myConsole = new console.Console(out, err);
*
* myConsole.log('hello world');
* // Prints: hello world, to out
* myConsole.log('hello %s', 'world');
* // Prints: hello world, to out
* myConsole.error(new Error('Whoops, something bad happened'));
* // Prints: [Error: Whoops, something bad happened], to err
*
* const name = 'Will Robinson';
* myConsole.warn(`Danger ${name}! Danger!`);
* // Prints: Danger Will Robinson! Danger!, to err
* ```
* @see [source](https://github.com/nodejs/node/blob/v16.4.2/lib/console.js)
*/
namespace console {
interface ConsoleConstructorOptions {
stdout: NodeJS.WritableStream;
stderr?: NodeJS.WritableStream | undefined;
ignoreErrors?: boolean | undefined;
colorMode?: boolean | 'auto' | undefined;
inspectOptions?: InspectOptions | undefined;
/**
* Set group indentation
* @default 2
*/
groupIndentation?: number | undefined;
}
interface ConsoleConstructor {
prototype: Console;
new (stdout: NodeJS.WritableStream, stderr?: NodeJS.WritableStream, ignoreErrors?: boolean): Console;
new (options: ConsoleConstructorOptions): Console;
}
}
var console: Console;
}
export = globalThis.console;
}

View File

@@ -1,18 +0,0 @@
/** @deprecated since v6.3.0 - use constants property exposed by the relevant module instead. */
declare module 'constants' {
import { constants as osConstants, SignalConstants } from 'node:os';
import { constants as cryptoConstants } from 'node:crypto';
import { constants as fsConstants } from 'node:fs';
const exp: typeof osConstants.errno &
typeof osConstants.priority &
SignalConstants &
typeof cryptoConstants &
typeof fsConstants;
export = exp;
}
declare module 'node:constants' {
import constants = require('constants');
export = constants;
}

3307
node_modules/@types/node/crypto.d.ts generated vendored

File diff suppressed because it is too large Load Diff

545
node_modules/@types/node/dgram.d.ts generated vendored
View File

@@ -1,545 +0,0 @@
/**
* The `dgram` module provides an implementation of UDP datagram sockets.
*
* ```js
* import dgram from 'dgram';
*
* const server = dgram.createSocket('udp4');
*
* server.on('error', (err) => {
* console.log(`server error:\n${err.stack}`);
* server.close();
* });
*
* server.on('message', (msg, rinfo) => {
* console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
* });
*
* server.on('listening', () => {
* const address = server.address();
* console.log(`server listening ${address.address}:${address.port}`);
* });
*
* server.bind(41234);
* // Prints: server listening 0.0.0.0:41234
* ```
* @see [source](https://github.com/nodejs/node/blob/v17.0.0/lib/dgram.js)
*/
declare module 'dgram' {
import { AddressInfo } from 'node:net';
import * as dns from 'node:dns';
import { EventEmitter, Abortable } from 'node:events';
interface RemoteInfo {
address: string;
family: 'IPv4' | 'IPv6';
port: number;
size: number;
}
interface BindOptions {
port?: number | undefined;
address?: string | undefined;
exclusive?: boolean | undefined;
fd?: number | undefined;
}
type SocketType = 'udp4' | 'udp6';
interface SocketOptions extends Abortable {
type: SocketType;
reuseAddr?: boolean | undefined;
/**
* @default false
*/
ipv6Only?: boolean | undefined;
recvBufferSize?: number | undefined;
sendBufferSize?: number | undefined;
lookup?: ((hostname: string, options: dns.LookupOneOptions, callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void) => void) | undefined;
}
/**
* Creates a `dgram.Socket` object. Once the socket is created, calling `socket.bind()` will instruct the socket to begin listening for datagram
* messages. When `address` and `port` are not passed to `socket.bind()` the
* method will bind the socket to the "all interfaces" address on a random port
* (it does the right thing for both `udp4` and `udp6` sockets). The bound address
* and port can be retrieved using `socket.address().address` and `socket.address().port`.
*
* If the `signal` option is enabled, calling `.abort()` on the corresponding`AbortController` is similar to calling `.close()` on the socket:
*
* ```js
* const controller = new AbortController();
* const { signal } = controller;
* const server = dgram.createSocket({ type: 'udp4', signal });
* server.on('message', (msg, rinfo) => {
* console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
* });
* // Later, when you want to close the server.
* controller.abort();
* ```
* @since v0.11.13
* @param options Available options are:
* @param callback Attached as a listener for `'message'` events. Optional.
*/
function createSocket(type: SocketType, callback?: (msg: Buffer, rinfo: RemoteInfo) => void): Socket;
function createSocket(options: SocketOptions, callback?: (msg: Buffer, rinfo: RemoteInfo) => void): Socket;
/**
* Encapsulates the datagram functionality.
*
* New instances of `dgram.Socket` are created using {@link createSocket}.
* The `new` keyword is not to be used to create `dgram.Socket` instances.
* @since v0.1.99
*/
class Socket extends EventEmitter {
/**
* Tells the kernel to join a multicast group at the given `multicastAddress` and`multicastInterface` using the `IP_ADD_MEMBERSHIP` socket option. If the`multicastInterface` argument is not
* specified, the operating system will choose
* one interface and will add membership to it. To add membership to every
* available interface, call `addMembership` multiple times, once per interface.
*
* When called on an unbound socket, this method will implicitly bind to a random
* port, listening on all interfaces.
*
* When sharing a UDP socket across multiple `cluster` workers, the`socket.addMembership()` function must be called only once or an`EADDRINUSE` error will occur:
*
* ```js
* import cluster from 'cluster';
* import dgram from 'dgram';
*
* if (cluster.isPrimary) {
* cluster.fork(); // Works ok.
* cluster.fork(); // Fails with EADDRINUSE.
* } else {
* const s = dgram.createSocket('udp4');
* s.bind(1234, () => {
* s.addMembership('224.0.0.114');
* });
* }
* ```
* @since v0.6.9
*/
addMembership(multicastAddress: string, multicastInterface?: string): void;
/**
* Returns an object containing the address information for a socket.
* For UDP sockets, this object will contain `address`, `family` and `port`properties.
*
* This method throws `EBADF` if called on an unbound socket.
* @since v0.1.99
*/
address(): AddressInfo;
/**
* For UDP sockets, causes the `dgram.Socket` to listen for datagram
* messages on a named `port` and optional `address`. If `port` is not
* specified or is `0`, the operating system will attempt to bind to a
* random port. If `address` is not specified, the operating system will
* attempt to listen on all addresses. Once binding is complete, a`'listening'` event is emitted and the optional `callback` function is
* called.
*
* Specifying both a `'listening'` event listener and passing a`callback` to the `socket.bind()` method is not harmful but not very
* useful.
*
* A bound datagram socket keeps the Node.js process running to receive
* datagram messages.
*
* If binding fails, an `'error'` event is generated. In rare case (e.g.
* attempting to bind with a closed socket), an `Error` may be thrown.
*
* Example of a UDP server listening on port 41234:
*
* ```js
* import dgram from 'dgram';
*
* const server = dgram.createSocket('udp4');
*
* server.on('error', (err) => {
* console.log(`server error:\n${err.stack}`);
* server.close();
* });
*
* server.on('message', (msg, rinfo) => {
* console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
* });
*
* server.on('listening', () => {
* const address = server.address();
* console.log(`server listening ${address.address}:${address.port}`);
* });
*
* server.bind(41234);
* // Prints: server listening 0.0.0.0:41234
* ```
* @since v0.1.99
* @param callback with no parameters. Called when binding is complete.
*/
bind(port?: number, address?: string, callback?: () => void): this;
bind(port?: number, callback?: () => void): this;
bind(callback?: () => void): this;
bind(options: BindOptions, callback?: () => void): this;
/**
* Close the underlying socket and stop listening for data on it. If a callback is
* provided, it is added as a listener for the `'close'` event.
* @since v0.1.99
* @param callback Called when the socket has been closed.
*/
close(callback?: () => void): this;
/**
* Associates the `dgram.Socket` to a remote address and port. Every
* message sent by this handle is automatically sent to that destination. Also,
* the socket will only receive messages from that remote peer.
* Trying to call `connect()` on an already connected socket will result
* in an `ERR_SOCKET_DGRAM_IS_CONNECTED` exception. If `address` is not
* provided, `'127.0.0.1'` (for `udp4` sockets) or `'::1'` (for `udp6` sockets)
* will be used by default. Once the connection is complete, a `'connect'` event
* is emitted and the optional `callback` function is called. In case of failure,
* the `callback` is called or, failing this, an `'error'` event is emitted.
* @since v12.0.0
* @param callback Called when the connection is completed or on error.
*/
connect(port: number, address?: string, callback?: () => void): void;
connect(port: number, callback: () => void): void;
/**
* A synchronous function that disassociates a connected `dgram.Socket` from
* its remote address. Trying to call `disconnect()` on an unbound or already
* disconnected socket will result in an `ERR_SOCKET_DGRAM_NOT_CONNECTED` exception.
* @since v12.0.0
*/
disconnect(): void;
/**
* Instructs the kernel to leave a multicast group at `multicastAddress` using the`IP_DROP_MEMBERSHIP` socket option. This method is automatically called by the
* kernel when the socket is closed or the process terminates, so most apps will
* never have reason to call this.
*
* If `multicastInterface` is not specified, the operating system will attempt to
* drop membership on all valid interfaces.
* @since v0.6.9
*/
dropMembership(multicastAddress: string, multicastInterface?: string): void;
/**
* This method throws `ERR_SOCKET_BUFFER_SIZE` if called on an unbound socket.
* @since v8.7.0
* @return the `SO_RCVBUF` socket receive buffer size in bytes.
*/
getRecvBufferSize(): number;
/**
* This method throws `ERR_SOCKET_BUFFER_SIZE` if called on an unbound socket.
* @since v8.7.0
* @return the `SO_SNDBUF` socket send buffer size in bytes.
*/
getSendBufferSize(): number;
/**
* By default, binding a socket will cause it to block the Node.js process from
* exiting as long as the socket is open. The `socket.unref()` method can be used
* to exclude the socket from the reference counting that keeps the Node.js
* process active. The `socket.ref()` method adds the socket back to the reference
* counting and restores the default behavior.
*
* Calling `socket.ref()` multiples times will have no additional effect.
*
* The `socket.ref()` method returns a reference to the socket so calls can be
* chained.
* @since v0.9.1
*/
ref(): this;
/**
* Returns an object containing the `address`, `family`, and `port` of the remote
* endpoint. This method throws an `ERR_SOCKET_DGRAM_NOT_CONNECTED` exception
* if the socket is not connected.
* @since v12.0.0
*/
remoteAddress(): AddressInfo;
/**
* Broadcasts a datagram on the socket.
* For connectionless sockets, the destination `port` and `address` must be
* specified. Connected sockets, on the other hand, will use their associated
* remote endpoint, so the `port` and `address` arguments must not be set.
*
* The `msg` argument contains the message to be sent.
* Depending on its type, different behavior can apply. If `msg` is a `Buffer`,
* any `TypedArray` or a `DataView`,
* the `offset` and `length` specify the offset within the `Buffer` where the
* message begins and the number of bytes in the message, respectively.
* If `msg` is a `String`, then it is automatically converted to a `Buffer`with `'utf8'` encoding. With messages that
* contain multi-byte characters, `offset` and `length` will be calculated with
* respect to `byte length` and not the character position.
* If `msg` is an array, `offset` and `length` must not be specified.
*
* The `address` argument is a string. If the value of `address` is a host name,
* DNS will be used to resolve the address of the host. If `address` is not
* provided or otherwise nullish, `'127.0.0.1'` (for `udp4` sockets) or `'::1'`(for `udp6` sockets) will be used by default.
*
* If the socket has not been previously bound with a call to `bind`, the socket
* is assigned a random port number and is bound to the "all interfaces" address
* (`'0.0.0.0'` for `udp4` sockets, `'::0'` for `udp6` sockets.)
*
* An optional `callback` function may be specified to as a way of reporting
* DNS errors or for determining when it is safe to reuse the `buf` object.
* DNS lookups delay the time to send for at least one tick of the
* Node.js event loop.
*
* The only way to know for sure that the datagram has been sent is by using a`callback`. If an error occurs and a `callback` is given, the error will be
* passed as the first argument to the `callback`. If a `callback` is not given,
* the error is emitted as an `'error'` event on the `socket` object.
*
* Offset and length are optional but both _must_ be set if either are used.
* They are supported only when the first argument is a `Buffer`, a `TypedArray`,
* or a `DataView`.
*
* This method throws `ERR_SOCKET_BAD_PORT` if called on an unbound socket.
*
* Example of sending a UDP packet to a port on `localhost`;
*
* ```js
* import dgram from 'dgram';
* import { Buffer } from 'buffer';
*
* const message = Buffer.from('Some bytes');
* const client = dgram.createSocket('udp4');
* client.send(message, 41234, 'localhost', (err) => {
* client.close();
* });
* ```
*
* Example of sending a UDP packet composed of multiple buffers to a port on`127.0.0.1`;
*
* ```js
* import dgram from 'dgram';
* import { Buffer } from 'buffer';
*
* const buf1 = Buffer.from('Some ');
* const buf2 = Buffer.from('bytes');
* const client = dgram.createSocket('udp4');
* client.send([buf1, buf2], 41234, (err) => {
* client.close();
* });
* ```
*
* Sending multiple buffers might be faster or slower depending on the
* application and operating system. Run benchmarks to
* determine the optimal strategy on a case-by-case basis. Generally speaking,
* however, sending multiple buffers is faster.
*
* Example of sending a UDP packet using a socket connected to a port on`localhost`:
*
* ```js
* import dgram from 'dgram';
* import { Buffer } from 'buffer';
*
* const message = Buffer.from('Some bytes');
* const client = dgram.createSocket('udp4');
* client.connect(41234, 'localhost', (err) => {
* client.send(message, (err) => {
* client.close();
* });
* });
* ```
* @since v0.1.99
* @param msg Message to be sent.
* @param offset Offset in the buffer where the message starts.
* @param length Number of bytes in the message.
* @param port Destination port.
* @param address Destination host name or IP address.
* @param callback Called when the message has been sent.
*/
send(msg: string | Uint8Array | ReadonlyArray<any>, port?: number, address?: string, callback?: (error: Error | null, bytes: number) => void): void;
send(msg: string | Uint8Array | ReadonlyArray<any>, port?: number, callback?: (error: Error | null, bytes: number) => void): void;
send(msg: string | Uint8Array | ReadonlyArray<any>, callback?: (error: Error | null, bytes: number) => void): void;
send(msg: string | Uint8Array, offset: number, length: number, port?: number, address?: string, callback?: (error: Error | null, bytes: number) => void): void;
send(msg: string | Uint8Array, offset: number, length: number, port?: number, callback?: (error: Error | null, bytes: number) => void): void;
send(msg: string | Uint8Array, offset: number, length: number, callback?: (error: Error | null, bytes: number) => void): void;
/**
* Sets or clears the `SO_BROADCAST` socket option. When set to `true`, UDP
* packets may be sent to a local interface's broadcast address.
*
* This method throws `EBADF` if called on an unbound socket.
* @since v0.6.9
*/
setBroadcast(flag: boolean): void;
/**
* _All references to scope in this section are referring to [IPv6 Zone Indices](https://en.wikipedia.org/wiki/IPv6_address#Scoped_literal_IPv6_addresses), which are defined by [RFC
* 4007](https://tools.ietf.org/html/rfc4007). In string form, an IP_
* _with a scope index is written as `'IP%scope'` where scope is an interface name_
* _or interface number._
*
* Sets the default outgoing multicast interface of the socket to a chosen
* interface or back to system interface selection. The `multicastInterface` must
* be a valid string representation of an IP from the socket's family.
*
* For IPv4 sockets, this should be the IP configured for the desired physical
* interface. All packets sent to multicast on the socket will be sent on the
* interface determined by the most recent successful use of this call.
*
* For IPv6 sockets, `multicastInterface` should include a scope to indicate the
* interface as in the examples that follow. In IPv6, individual `send` calls can
* also use explicit scope in addresses, so only packets sent to a multicast
* address without specifying an explicit scope are affected by the most recent
* successful use of this call.
*
* This method throws `EBADF` if called on an unbound socket.
*
* #### Example: IPv6 outgoing multicast interface
*
* On most systems, where scope format uses the interface name:
*
* ```js
* const socket = dgram.createSocket('udp6');
*
* socket.bind(1234, () => {
* socket.setMulticastInterface('::%eth1');
* });
* ```
*
* On Windows, where scope format uses an interface number:
*
* ```js
* const socket = dgram.createSocket('udp6');
*
* socket.bind(1234, () => {
* socket.setMulticastInterface('::%2');
* });
* ```
*
* #### Example: IPv4 outgoing multicast interface
*
* All systems use an IP of the host on the desired physical interface:
*
* ```js
* const socket = dgram.createSocket('udp4');
*
* socket.bind(1234, () => {
* socket.setMulticastInterface('10.0.0.2');
* });
* ```
* @since v8.6.0
*/
setMulticastInterface(multicastInterface: string): void;
/**
* Sets or clears the `IP_MULTICAST_LOOP` socket option. When set to `true`,
* multicast packets will also be received on the local interface.
*
* This method throws `EBADF` if called on an unbound socket.
* @since v0.3.8
*/
setMulticastLoopback(flag: boolean): boolean;
/**
* Sets the `IP_MULTICAST_TTL` socket option. While TTL generally stands for
* "Time to Live", in this context it specifies the number of IP hops that a
* packet is allowed to travel through, specifically for multicast traffic. Each
* router or gateway that forwards a packet decrements the TTL. If the TTL is
* decremented to 0 by a router, it will not be forwarded.
*
* The `ttl` argument may be between 0 and 255\. The default on most systems is `1`.
*
* This method throws `EBADF` if called on an unbound socket.
* @since v0.3.8
*/
setMulticastTTL(ttl: number): number;
/**
* Sets the `SO_RCVBUF` socket option. Sets the maximum socket receive buffer
* in bytes.
*
* This method throws `ERR_SOCKET_BUFFER_SIZE` if called on an unbound socket.
* @since v8.7.0
*/
setRecvBufferSize(size: number): void;
/**
* Sets the `SO_SNDBUF` socket option. Sets the maximum socket send buffer
* in bytes.
*
* This method throws `ERR_SOCKET_BUFFER_SIZE` if called on an unbound socket.
* @since v8.7.0
*/
setSendBufferSize(size: number): void;
/**
* Sets the `IP_TTL` socket option. While TTL generally stands for "Time to Live",
* in this context it specifies the number of IP hops that a packet is allowed to
* travel through. Each router or gateway that forwards a packet decrements the
* TTL. If the TTL is decremented to 0 by a router, it will not be forwarded.
* Changing TTL values is typically done for network probes or when multicasting.
*
* The `ttl` argument may be between between 1 and 255\. The default on most systems
* is 64.
*
* This method throws `EBADF` if called on an unbound socket.
* @since v0.1.101
*/
setTTL(ttl: number): number;
/**
* By default, binding a socket will cause it to block the Node.js process from
* exiting as long as the socket is open. The `socket.unref()` method can be used
* to exclude the socket from the reference counting that keeps the Node.js
* process active, allowing the process to exit even if the socket is still
* listening.
*
* Calling `socket.unref()` multiple times will have no addition effect.
*
* The `socket.unref()` method returns a reference to the socket so calls can be
* chained.
* @since v0.9.1
*/
unref(): this;
/**
* Tells the kernel to join a source-specific multicast channel at the given`sourceAddress` and `groupAddress`, using the `multicastInterface` with the`IP_ADD_SOURCE_MEMBERSHIP` socket
* option. If the `multicastInterface` argument
* is not specified, the operating system will choose one interface and will add
* membership to it. To add membership to every available interface, call`socket.addSourceSpecificMembership()` multiple times, once per interface.
*
* When called on an unbound socket, this method will implicitly bind to a random
* port, listening on all interfaces.
* @since v13.1.0, v12.16.0
*/
addSourceSpecificMembership(sourceAddress: string, groupAddress: string, multicastInterface?: string): void;
/**
* Instructs the kernel to leave a source-specific multicast channel at the given`sourceAddress` and `groupAddress` using the `IP_DROP_SOURCE_MEMBERSHIP`socket option. This method is
* automatically called by the kernel when the
* socket is closed or the process terminates, so most apps will never have
* reason to call this.
*
* If `multicastInterface` is not specified, the operating system will attempt to
* drop membership on all valid interfaces.
* @since v13.1.0, v12.16.0
*/
dropSourceSpecificMembership(sourceAddress: string, groupAddress: string, multicastInterface?: string): void;
/**
* events.EventEmitter
* 1. close
* 2. connect
* 3. error
* 4. listening
* 5. message
*/
addListener(event: string, listener: (...args: any[]) => void): this;
addListener(event: 'close', listener: () => void): this;
addListener(event: 'connect', listener: () => void): this;
addListener(event: 'error', listener: (err: Error) => void): this;
addListener(event: 'listening', listener: () => void): this;
addListener(event: 'message', listener: (msg: Buffer, rinfo: RemoteInfo) => void): this;
emit(event: string | symbol, ...args: any[]): boolean;
emit(event: 'close'): boolean;
emit(event: 'connect'): boolean;
emit(event: 'error', err: Error): boolean;
emit(event: 'listening'): boolean;
emit(event: 'message', msg: Buffer, rinfo: RemoteInfo): boolean;
on(event: string, listener: (...args: any[]) => void): this;
on(event: 'close', listener: () => void): this;
on(event: 'connect', listener: () => void): this;
on(event: 'error', listener: (err: Error) => void): this;
on(event: 'listening', listener: () => void): this;
on(event: 'message', listener: (msg: Buffer, rinfo: RemoteInfo) => void): this;
once(event: string, listener: (...args: any[]) => void): this;
once(event: 'close', listener: () => void): this;
once(event: 'connect', listener: () => void): this;
once(event: 'error', listener: (err: Error) => void): this;
once(event: 'listening', listener: () => void): this;
once(event: 'message', listener: (msg: Buffer, rinfo: RemoteInfo) => void): this;
prependListener(event: string, listener: (...args: any[]) => void): this;
prependListener(event: 'close', listener: () => void): this;
prependListener(event: 'connect', listener: () => void): this;
prependListener(event: 'error', listener: (err: Error) => void): this;
prependListener(event: 'listening', listener: () => void): this;
prependListener(event: 'message', listener: (msg: Buffer, rinfo: RemoteInfo) => void): this;
prependOnceListener(event: string, listener: (...args: any[]) => void): this;
prependOnceListener(event: 'close', listener: () => void): this;
prependOnceListener(event: 'connect', listener: () => void): this;
prependOnceListener(event: 'error', listener: (err: Error) => void): this;
prependOnceListener(event: 'listening', listener: () => void): this;
prependOnceListener(event: 'message', listener: (msg: Buffer, rinfo: RemoteInfo) => void): this;
}
}
declare module 'node:dgram' {
export * from 'dgram';
}

View File

@@ -1,134 +0,0 @@
/**
* The `diagnostics_channel` module provides an API to create named channels
* to report arbitrary message data for diagnostics purposes.
*
* It can be accessed using:
*
* ```js
* import diagnostics_channel from 'diagnostics_channel';
* ```
*
* It is intended that a module writer wanting to report diagnostics messages
* will create one or many top-level channels to report messages through.
* Channels may also be acquired at runtime but it is not encouraged
* due to the additional overhead of doing so. Channels may be exported for
* convenience, but as long as the name is known it can be acquired anywhere.
*
* If you intend for your module to produce diagnostics data for others to
* consume it is recommended that you include documentation of what named
* channels are used along with the shape of the message data. Channel names
* should generally include the module name to avoid collisions with data from
* other modules.
* @experimental
* @see [source](https://github.com/nodejs/node/blob/v17.0.0/lib/diagnostics_channel.js)
*/
declare module 'diagnostics_channel' {
/**
* Check if there are active subscribers to the named channel. This is helpful if
* the message you want to send might be expensive to prepare.
*
* This API is optional but helpful when trying to publish messages from very
* performance-sensitive code.
*
* ```js
* import diagnostics_channel from 'diagnostics_channel';
*
* if (diagnostics_channel.hasSubscribers('my-channel')) {
* // There are subscribers, prepare and publish message
* }
* ```
* @since v15.1.0, v14.17.0
* @param name The channel name
* @return If there are active subscribers
*/
function hasSubscribers(name: string): boolean;
/**
* This is the primary entry-point for anyone wanting to interact with a named
* channel. It produces a channel object which is optimized to reduce overhead at
* publish time as much as possible.
*
* ```js
* import diagnostics_channel from 'diagnostics_channel';
*
* const channel = diagnostics_channel.channel('my-channel');
* ```
* @since v15.1.0, v14.17.0
* @param name The channel name
* @return The named channel object
*/
function channel(name: string): Channel;
type ChannelListener = (name: string, message: unknown) => void;
/**
* The class `Channel` represents an individual named channel within the data
* pipeline. It is use to track subscribers and to publish messages when there
* are subscribers present. It exists as a separate object to avoid channel
* lookups at publish time, enabling very fast publish speeds and allowing
* for heavy use while incurring very minimal cost. Channels are created with {@link channel}, constructing a channel directly
* with `new Channel(name)` is not supported.
* @since v15.1.0, v14.17.0
*/
class Channel {
readonly name: string;
/**
* Check if there are active subscribers to this channel. This is helpful if
* the message you want to send might be expensive to prepare.
*
* This API is optional but helpful when trying to publish messages from very
* performance-sensitive code.
*
* ```js
* import diagnostics_channel from 'diagnostics_channel';
*
* const channel = diagnostics_channel.channel('my-channel');
*
* if (channel.hasSubscribers) {
* // There are subscribers, prepare and publish message
* }
* ```
* @since v15.1.0, v14.17.0
*/
readonly hasSubscribers: boolean;
private constructor(name: string);
/**
* Register a message handler to subscribe to this channel. This message handler
* will be run synchronously whenever a message is published to the channel. Any
* errors thrown in the message handler will trigger an `'uncaughtException'`.
*
* ```js
* import diagnostics_channel from 'diagnostics_channel';
*
* const channel = diagnostics_channel.channel('my-channel');
*
* channel.subscribe((message, name) => {
* // Received data
* });
* ```
* @since v15.1.0, v14.17.0
* @param onMessage The handler to receive channel messages
*/
subscribe(onMessage: ChannelListener): void;
/**
* Remove a message handler previously registered to this channel with `channel.subscribe(onMessage)`.
*
* ```js
* import diagnostics_channel from 'diagnostics_channel';
*
* const channel = diagnostics_channel.channel('my-channel');
*
* function onMessage(message, name) {
* // Received data
* }
*
* channel.subscribe(onMessage);
*
* channel.unsubscribe(onMessage);
* ```
* @since v15.1.0, v14.17.0
* @param onMessage The previous subscribed handler to remove
*/
unsubscribe(onMessage: ChannelListener): void;
}
}
declare module 'node:diagnostics_channel' {
export * from 'diagnostics_channel';
}

659
node_modules/@types/node/dns.d.ts generated vendored
View File

@@ -1,659 +0,0 @@
/**
* The `dns` module enables name resolution. For example, use it to look up IP
* addresses of host names.
*
* Although named for the [Domain Name System (DNS)](https://en.wikipedia.org/wiki/Domain_Name_System), it does not always use the
* DNS protocol for lookups. {@link lookup} uses the operating system
* facilities to perform name resolution. It may not need to perform any network
* communication. To perform name resolution the way other applications on the same
* system do, use {@link lookup}.
*
* ```js
* const dns = require('dns');
*
* dns.lookup('example.org', (err, address, family) => {
* console.log('address: %j family: IPv%s', address, family);
* });
* // address: "93.184.216.34" family: IPv4
* ```
*
* All other functions in the `dns` module connect to an actual DNS server to
* perform name resolution. They will always use the network to perform DNS
* queries. These functions do not use the same set of configuration files used by {@link lookup} (e.g. `/etc/hosts`). Use these functions to always perform
* DNS queries, bypassing other name-resolution facilities.
*
* ```js
* const dns = require('dns');
*
* dns.resolve4('archive.org', (err, addresses) => {
* if (err) throw err;
*
* console.log(`addresses: ${JSON.stringify(addresses)}`);
*
* addresses.forEach((a) => {
* dns.reverse(a, (err, hostnames) => {
* if (err) {
* throw err;
* }
* console.log(`reverse for ${a}: ${JSON.stringify(hostnames)}`);
* });
* });
* });
* ```
*
* See the `Implementation considerations section` for more information.
* @see [source](https://github.com/nodejs/node/blob/v17.0.0/lib/dns.js)
*/
declare module 'dns' {
import * as dnsPromises from 'node:dns/promises';
// Supported getaddrinfo flags.
export const ADDRCONFIG: number;
export const V4MAPPED: number;
/**
* If `dns.V4MAPPED` is specified, return resolved IPv6 addresses as
* well as IPv4 mapped IPv6 addresses.
*/
export const ALL: number;
export interface LookupOptions {
family?: number | undefined;
hints?: number | undefined;
all?: boolean | undefined;
/**
* @default true
*/
verbatim?: boolean | undefined;
}
export interface LookupOneOptions extends LookupOptions {
all?: false | undefined;
}
export interface LookupAllOptions extends LookupOptions {
all: true;
}
export interface LookupAddress {
address: string;
family: number;
}
/**
* Resolves a host name (e.g. `'nodejs.org'`) into the first found A (IPv4) or
* AAAA (IPv6) record. All `option` properties are optional. If `options` is an
* integer, then it must be `4` or `6` if `options` is not provided, then IPv4
* and IPv6 addresses are both returned if found.
*
* With the `all` option set to `true`, the arguments for `callback` change to`(err, addresses)`, with `addresses` being an array of objects with the
* properties `address` and `family`.
*
* On error, `err` is an `Error` object, where `err.code` is the error code.
* Keep in mind that `err.code` will be set to `'ENOTFOUND'` not only when
* the host name does not exist but also when the lookup fails in other ways
* such as no available file descriptors.
*
* `dns.lookup()` does not necessarily have anything to do with the DNS protocol.
* The implementation uses an operating system facility that can associate names
* with addresses, and vice versa. This implementation can have subtle but
* important consequences on the behavior of any Node.js program. Please take some
* time to consult the `Implementation considerations section` before using`dns.lookup()`.
*
* Example usage:
*
* ```js
* const dns = require('dns');
* const options = {
* family: 6,
* hints: dns.ADDRCONFIG | dns.V4MAPPED,
* };
* dns.lookup('example.com', options, (err, address, family) =>
* console.log('address: %j family: IPv%s', address, family));
* // address: "2606:2800:220:1:248:1893:25c8:1946" family: IPv6
*
* // When options.all is true, the result will be an Array.
* options.all = true;
* dns.lookup('example.com', options, (err, addresses) =>
* console.log('addresses: %j', addresses));
* // addresses: [{"address":"2606:2800:220:1:248:1893:25c8:1946","family":6}]
* ```
*
* If this method is invoked as its `util.promisify()` ed version, and `all`is not set to `true`, it returns a `Promise` for an `Object` with `address` and`family` properties.
* @since v0.1.90
*/
export function lookup(hostname: string, family: number, callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void): void;
export function lookup(hostname: string, options: LookupOneOptions, callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void): void;
export function lookup(hostname: string, options: LookupAllOptions, callback: (err: NodeJS.ErrnoException | null, addresses: LookupAddress[]) => void): void;
export function lookup(hostname: string, options: LookupOptions, callback: (err: NodeJS.ErrnoException | null, address: string | LookupAddress[], family: number) => void): void;
export function lookup(hostname: string, callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void): void;
export namespace lookup {
function __promisify__(hostname: string, options: LookupAllOptions): Promise<LookupAddress[]>;
function __promisify__(hostname: string, options?: LookupOneOptions | number): Promise<LookupAddress>;
function __promisify__(hostname: string, options: LookupOptions): Promise<LookupAddress | LookupAddress[]>;
}
/**
* Resolves the given `address` and `port` into a host name and service using
* the operating system's underlying `getnameinfo` implementation.
*
* If `address` is not a valid IP address, a `TypeError` will be thrown.
* The `port` will be coerced to a number. If it is not a legal port, a `TypeError`will be thrown.
*
* On an error, `err` is an `Error` object, where `err.code` is the error code.
*
* ```js
* const dns = require('dns');
* dns.lookupService('127.0.0.1', 22, (err, hostname, service) => {
* console.log(hostname, service);
* // Prints: localhost ssh
* });
* ```
*
* If this method is invoked as its `util.promisify()` ed version, it returns a`Promise` for an `Object` with `hostname` and `service` properties.
* @since v0.11.14
*/
export function lookupService(address: string, port: number, callback: (err: NodeJS.ErrnoException | null, hostname: string, service: string) => void): void;
export namespace lookupService {
function __promisify__(
address: string,
port: number
): Promise<{
hostname: string;
service: string;
}>;
}
export interface ResolveOptions {
ttl: boolean;
}
export interface ResolveWithTtlOptions extends ResolveOptions {
ttl: true;
}
export interface RecordWithTtl {
address: string;
ttl: number;
}
/** @deprecated Use `AnyARecord` or `AnyAaaaRecord` instead. */
export type AnyRecordWithTtl = AnyARecord | AnyAaaaRecord;
export interface AnyARecord extends RecordWithTtl {
type: 'A';
}
export interface AnyAaaaRecord extends RecordWithTtl {
type: 'AAAA';
}
export interface CaaRecord {
critial: number;
issue?: string | undefined;
issuewild?: string | undefined;
iodef?: string | undefined;
contactemail?: string | undefined;
contactphone?: string | undefined;
}
export interface MxRecord {
priority: number;
exchange: string;
}
export interface AnyMxRecord extends MxRecord {
type: 'MX';
}
export interface NaptrRecord {
flags: string;
service: string;
regexp: string;
replacement: string;
order: number;
preference: number;
}
export interface AnyNaptrRecord extends NaptrRecord {
type: 'NAPTR';
}
export interface SoaRecord {
nsname: string;
hostmaster: string;
serial: number;
refresh: number;
retry: number;
expire: number;
minttl: number;
}
export interface AnySoaRecord extends SoaRecord {
type: 'SOA';
}
export interface SrvRecord {
priority: number;
weight: number;
port: number;
name: string;
}
export interface AnySrvRecord extends SrvRecord {
type: 'SRV';
}
export interface AnyTxtRecord {
type: 'TXT';
entries: string[];
}
export interface AnyNsRecord {
type: 'NS';
value: string;
}
export interface AnyPtrRecord {
type: 'PTR';
value: string;
}
export interface AnyCnameRecord {
type: 'CNAME';
value: string;
}
export type AnyRecord = AnyARecord | AnyAaaaRecord | AnyCnameRecord | AnyMxRecord | AnyNaptrRecord | AnyNsRecord | AnyPtrRecord | AnySoaRecord | AnySrvRecord | AnyTxtRecord;
/**
* Uses the DNS protocol to resolve a host name (e.g. `'nodejs.org'`) into an array
* of the resource records. The `callback` function has arguments`(err, records)`. When successful, `records` will be an array of resource
* records. The type and structure of individual results varies based on `rrtype`:
*
* <omitted>
*
* On error, `err` is an `Error` object, where `err.code` is one of theDNS error codes.
* @since v0.1.27
* @param hostname Host name to resolve.
* @param [rrtype='A'] Resource record type.
*/
export function resolve(hostname: string, callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void): void;
export function resolve(hostname: string, rrtype: 'A', callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void): void;
export function resolve(hostname: string, rrtype: 'AAAA', callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void): void;
export function resolve(hostname: string, rrtype: 'ANY', callback: (err: NodeJS.ErrnoException | null, addresses: AnyRecord[]) => void): void;
export function resolve(hostname: string, rrtype: 'CNAME', callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void): void;
export function resolve(hostname: string, rrtype: 'MX', callback: (err: NodeJS.ErrnoException | null, addresses: MxRecord[]) => void): void;
export function resolve(hostname: string, rrtype: 'NAPTR', callback: (err: NodeJS.ErrnoException | null, addresses: NaptrRecord[]) => void): void;
export function resolve(hostname: string, rrtype: 'NS', callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void): void;
export function resolve(hostname: string, rrtype: 'PTR', callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void): void;
export function resolve(hostname: string, rrtype: 'SOA', callback: (err: NodeJS.ErrnoException | null, addresses: SoaRecord) => void): void;
export function resolve(hostname: string, rrtype: 'SRV', callback: (err: NodeJS.ErrnoException | null, addresses: SrvRecord[]) => void): void;
export function resolve(hostname: string, rrtype: 'TXT', callback: (err: NodeJS.ErrnoException | null, addresses: string[][]) => void): void;
export function resolve(
hostname: string,
rrtype: string,
callback: (err: NodeJS.ErrnoException | null, addresses: string[] | MxRecord[] | NaptrRecord[] | SoaRecord | SrvRecord[] | string[][] | AnyRecord[]) => void
): void;
export namespace resolve {
function __promisify__(hostname: string, rrtype?: 'A' | 'AAAA' | 'CNAME' | 'NS' | 'PTR'): Promise<string[]>;
function __promisify__(hostname: string, rrtype: 'ANY'): Promise<AnyRecord[]>;
function __promisify__(hostname: string, rrtype: 'MX'): Promise<MxRecord[]>;
function __promisify__(hostname: string, rrtype: 'NAPTR'): Promise<NaptrRecord[]>;
function __promisify__(hostname: string, rrtype: 'SOA'): Promise<SoaRecord>;
function __promisify__(hostname: string, rrtype: 'SRV'): Promise<SrvRecord[]>;
function __promisify__(hostname: string, rrtype: 'TXT'): Promise<string[][]>;
function __promisify__(hostname: string, rrtype: string): Promise<string[] | MxRecord[] | NaptrRecord[] | SoaRecord | SrvRecord[] | string[][] | AnyRecord[]>;
}
/**
* Uses the DNS protocol to resolve a IPv4 addresses (`A` records) for the`hostname`. The `addresses` argument passed to the `callback` function
* will contain an array of IPv4 addresses (e.g.`['74.125.79.104', '74.125.79.105', '74.125.79.106']`).
* @since v0.1.16
* @param hostname Host name to resolve.
*/
export function resolve4(hostname: string, callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void): void;
export function resolve4(hostname: string, options: ResolveWithTtlOptions, callback: (err: NodeJS.ErrnoException | null, addresses: RecordWithTtl[]) => void): void;
export function resolve4(hostname: string, options: ResolveOptions, callback: (err: NodeJS.ErrnoException | null, addresses: string[] | RecordWithTtl[]) => void): void;
export namespace resolve4 {
function __promisify__(hostname: string): Promise<string[]>;
function __promisify__(hostname: string, options: ResolveWithTtlOptions): Promise<RecordWithTtl[]>;
function __promisify__(hostname: string, options?: ResolveOptions): Promise<string[] | RecordWithTtl[]>;
}
/**
* Uses the DNS protocol to resolve a IPv6 addresses (`AAAA` records) for the`hostname`. The `addresses` argument passed to the `callback` function
* will contain an array of IPv6 addresses.
* @since v0.1.16
* @param hostname Host name to resolve.
*/
export function resolve6(hostname: string, callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void): void;
export function resolve6(hostname: string, options: ResolveWithTtlOptions, callback: (err: NodeJS.ErrnoException | null, addresses: RecordWithTtl[]) => void): void;
export function resolve6(hostname: string, options: ResolveOptions, callback: (err: NodeJS.ErrnoException | null, addresses: string[] | RecordWithTtl[]) => void): void;
export namespace resolve6 {
function __promisify__(hostname: string): Promise<string[]>;
function __promisify__(hostname: string, options: ResolveWithTtlOptions): Promise<RecordWithTtl[]>;
function __promisify__(hostname: string, options?: ResolveOptions): Promise<string[] | RecordWithTtl[]>;
}
/**
* Uses the DNS protocol to resolve `CNAME` records for the `hostname`. The`addresses` argument passed to the `callback` function
* will contain an array of canonical name records available for the `hostname`(e.g. `['bar.example.com']`).
* @since v0.3.2
*/
export function resolveCname(hostname: string, callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void): void;
export namespace resolveCname {
function __promisify__(hostname: string): Promise<string[]>;
}
/**
* Uses the DNS protocol to resolve `CAA` records for the `hostname`. The`addresses` argument passed to the `callback` function
* will contain an array of certification authority authorization records
* available for the `hostname` (e.g. `[{critical: 0, iodef: 'mailto:pki@example.com'}, {critical: 128, issue: 'pki.example.com'}]`).
* @since v15.0.0, v14.17.0
*/
export function resolveCaa(hostname: string, callback: (err: NodeJS.ErrnoException | null, records: CaaRecord[]) => void): void;
export namespace resolveCaa {
function __promisify__(hostname: string): Promise<CaaRecord[]>;
}
/**
* Uses the DNS protocol to resolve mail exchange records (`MX` records) for the`hostname`. The `addresses` argument passed to the `callback` function will
* contain an array of objects containing both a `priority` and `exchange`property (e.g. `[{priority: 10, exchange: 'mx.example.com'}, ...]`).
* @since v0.1.27
*/
export function resolveMx(hostname: string, callback: (err: NodeJS.ErrnoException | null, addresses: MxRecord[]) => void): void;
export namespace resolveMx {
function __promisify__(hostname: string): Promise<MxRecord[]>;
}
/**
* Uses the DNS protocol to resolve regular expression based records (`NAPTR`records) for the `hostname`. The `addresses` argument passed to the `callback`function will contain an array of
* objects with the following properties:
*
* * `flags`
* * `service`
* * `regexp`
* * `replacement`
* * `order`
* * `preference`
*
* ```js
* {
* flags: 's',
* service: 'SIP+D2U',
* regexp: '',
* replacement: '_sip._udp.example.com',
* order: 30,
* preference: 100
* }
* ```
* @since v0.9.12
*/
export function resolveNaptr(hostname: string, callback: (err: NodeJS.ErrnoException | null, addresses: NaptrRecord[]) => void): void;
export namespace resolveNaptr {
function __promisify__(hostname: string): Promise<NaptrRecord[]>;
}
/**
* Uses the DNS protocol to resolve name server records (`NS` records) for the`hostname`. The `addresses` argument passed to the `callback` function will
* contain an array of name server records available for `hostname`(e.g. `['ns1.example.com', 'ns2.example.com']`).
* @since v0.1.90
*/
export function resolveNs(hostname: string, callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void): void;
export namespace resolveNs {
function __promisify__(hostname: string): Promise<string[]>;
}
/**
* Uses the DNS protocol to resolve pointer records (`PTR` records) for the`hostname`. The `addresses` argument passed to the `callback` function will
* be an array of strings containing the reply records.
* @since v6.0.0
*/
export function resolvePtr(hostname: string, callback: (err: NodeJS.ErrnoException | null, addresses: string[]) => void): void;
export namespace resolvePtr {
function __promisify__(hostname: string): Promise<string[]>;
}
/**
* Uses the DNS protocol to resolve a start of authority record (`SOA` record) for
* the `hostname`. The `address` argument passed to the `callback` function will
* be an object with the following properties:
*
* * `nsname`
* * `hostmaster`
* * `serial`
* * `refresh`
* * `retry`
* * `expire`
* * `minttl`
*
* ```js
* {
* nsname: 'ns.example.com',
* hostmaster: 'root.example.com',
* serial: 2013101809,
* refresh: 10000,
* retry: 2400,
* expire: 604800,
* minttl: 3600
* }
* ```
* @since v0.11.10
*/
export function resolveSoa(hostname: string, callback: (err: NodeJS.ErrnoException | null, address: SoaRecord) => void): void;
export namespace resolveSoa {
function __promisify__(hostname: string): Promise<SoaRecord>;
}
/**
* Uses the DNS protocol to resolve service records (`SRV` records) for the`hostname`. The `addresses` argument passed to the `callback` function will
* be an array of objects with the following properties:
*
* * `priority`
* * `weight`
* * `port`
* * `name`
*
* ```js
* {
* priority: 10,
* weight: 5,
* port: 21223,
* name: 'service.example.com'
* }
* ```
* @since v0.1.27
*/
export function resolveSrv(hostname: string, callback: (err: NodeJS.ErrnoException | null, addresses: SrvRecord[]) => void): void;
export namespace resolveSrv {
function __promisify__(hostname: string): Promise<SrvRecord[]>;
}
/**
* Uses the DNS protocol to resolve text queries (`TXT` records) for the`hostname`. The `records` argument passed to the `callback` function is a
* two-dimensional array of the text records available for `hostname` (e.g.`[ ['v=spf1 ip4:0.0.0.0 ', '~all' ] ]`). Each sub-array contains TXT chunks of
* one record. Depending on the use case, these could be either joined together or
* treated separately.
* @since v0.1.27
*/
export function resolveTxt(hostname: string, callback: (err: NodeJS.ErrnoException | null, addresses: string[][]) => void): void;
export namespace resolveTxt {
function __promisify__(hostname: string): Promise<string[][]>;
}
/**
* Uses the DNS protocol to resolve all records (also known as `ANY` or `*` query).
* The `ret` argument passed to the `callback` function will be an array containing
* various types of records. Each object has a property `type` that indicates the
* type of the current record. And depending on the `type`, additional properties
* will be present on the object:
*
* <omitted>
*
* Here is an example of the `ret` object passed to the callback:
*
* ```js
* [ { type: 'A', address: '127.0.0.1', ttl: 299 },
* { type: 'CNAME', value: 'example.com' },
* { type: 'MX', exchange: 'alt4.aspmx.l.example.com', priority: 50 },
* { type: 'NS', value: 'ns1.example.com' },
* { type: 'TXT', entries: [ 'v=spf1 include:_spf.example.com ~all' ] },
* { type: 'SOA',
* nsname: 'ns1.example.com',
* hostmaster: 'admin.example.com',
* serial: 156696742,
* refresh: 900,
* retry: 900,
* expire: 1800,
* minttl: 60 } ]
* ```
*
* DNS server operators may choose not to respond to `ANY`queries. It may be better to call individual methods like {@link resolve4},{@link resolveMx}, and so on. For more details, see [RFC
* 8482](https://tools.ietf.org/html/rfc8482).
*/
export function resolveAny(hostname: string, callback: (err: NodeJS.ErrnoException | null, addresses: AnyRecord[]) => void): void;
export namespace resolveAny {
function __promisify__(hostname: string): Promise<AnyRecord[]>;
}
/**
* Performs a reverse DNS query that resolves an IPv4 or IPv6 address to an
* array of host names.
*
* On error, `err` is an `Error` object, where `err.code` is
* one of the `DNS error codes`.
* @since v0.1.16
*/
export function reverse(ip: string, callback: (err: NodeJS.ErrnoException | null, hostnames: string[]) => void): void;
/**
* Sets the IP address and port of servers to be used when performing DNS
* resolution. The `servers` argument is an array of [RFC 5952](https://tools.ietf.org/html/rfc5952#section-6) formatted
* addresses. If the port is the IANA default DNS port (53) it can be omitted.
*
* ```js
* dns.setServers([
* '4.4.4.4',
* '[2001:4860:4860::8888]',
* '4.4.4.4:1053',
* '[2001:4860:4860::8888]:1053',
* ]);
* ```
*
* An error will be thrown if an invalid address is provided.
*
* The `dns.setServers()` method must not be called while a DNS query is in
* progress.
*
* The {@link setServers} method affects only {@link resolve},`dns.resolve*()` and {@link reverse} (and specifically _not_ {@link lookup}).
*
* This method works much like [resolve.conf](https://man7.org/linux/man-pages/man5/resolv.conf.5.html).
* That is, if attempting to resolve with the first server provided results in a`NOTFOUND` error, the `resolve()` method will _not_ attempt to resolve with
* subsequent servers provided. Fallback DNS servers will only be used if the
* earlier ones time out or result in some other error.
* @since v0.11.3
* @param servers array of `RFC 5952` formatted addresses
*/
export function setServers(servers: ReadonlyArray<string>): void;
/**
* Returns an array of IP address strings, formatted according to [RFC 5952](https://tools.ietf.org/html/rfc5952#section-6),
* that are currently configured for DNS resolution. A string will include a port
* section if a custom port is used.
*
* ```js
* [
* '4.4.4.4',
* '2001:4860:4860::8888',
* '4.4.4.4:1053',
* '[2001:4860:4860::8888]:1053',
* ]
* ```
* @since v0.11.3
*/
export function getServers(): string[];
/**
* Set the default value of `verbatim` in {@link lookup} and `dnsPromises.lookup()`. The value could be:
*
* * `ipv4first`: sets default `verbatim` `false`.
* * `verbatim`: sets default `verbatim` `true`.
*
* The default is `ipv4first` and {@link setDefaultResultOrder} have higher
* priority than `--dns-result-order`. When using `worker threads`,{@link setDefaultResultOrder} from the main thread won't affect the default
* dns orders in workers.
* @since v16.4.0, v14.18.0
* @param order must be `'ipv4first'` or `'verbatim'`.
*/
export function setDefaultResultOrder(order: 'ipv4first' | 'verbatim'): void;
// Error codes
export const NODATA: string;
export const FORMERR: string;
export const SERVFAIL: string;
export const NOTFOUND: string;
export const NOTIMP: string;
export const REFUSED: string;
export const BADQUERY: string;
export const BADNAME: string;
export const BADFAMILY: string;
export const BADRESP: string;
export const CONNREFUSED: string;
export const TIMEOUT: string;
export const EOF: string;
export const FILE: string;
export const NOMEM: string;
export const DESTRUCTION: string;
export const BADSTR: string;
export const BADFLAGS: string;
export const NONAME: string;
export const BADHINTS: string;
export const NOTINITIALIZED: string;
export const LOADIPHLPAPI: string;
export const ADDRGETNETWORKPARAMS: string;
export const CANCELLED: string;
export interface ResolverOptions {
timeout?: number | undefined;
/**
* @default 4
*/
tries?: number;
}
/**
* An independent resolver for DNS requests.
*
* Creating a new resolver uses the default server settings. Setting
* the servers used for a resolver using `resolver.setServers()` does not affect
* other resolvers:
*
* ```js
* const { Resolver } = require('dns');
* const resolver = new Resolver();
* resolver.setServers(['4.4.4.4']);
*
* // This request will use the server at 4.4.4.4, independent of global settings.
* resolver.resolve4('example.org', (err, addresses) => {
* // ...
* });
* ```
*
* The following methods from the `dns` module are available:
*
* * `resolver.getServers()`
* * `resolver.resolve()`
* * `resolver.resolve4()`
* * `resolver.resolve6()`
* * `resolver.resolveAny()`
* * `resolver.resolveCaa()`
* * `resolver.resolveCname()`
* * `resolver.resolveMx()`
* * `resolver.resolveNaptr()`
* * `resolver.resolveNs()`
* * `resolver.resolvePtr()`
* * `resolver.resolveSoa()`
* * `resolver.resolveSrv()`
* * `resolver.resolveTxt()`
* * `resolver.reverse()`
* * `resolver.setServers()`
* @since v8.3.0
*/
export class Resolver {
constructor(options?: ResolverOptions);
/**
* Cancel all outstanding DNS queries made by this resolver. The corresponding
* callbacks will be called with an error with code `ECANCELLED`.
* @since v8.3.0
*/
cancel(): void;
getServers: typeof getServers;
resolve: typeof resolve;
resolve4: typeof resolve4;
resolve6: typeof resolve6;
resolveAny: typeof resolveAny;
resolveCname: typeof resolveCname;
resolveMx: typeof resolveMx;
resolveNaptr: typeof resolveNaptr;
resolveNs: typeof resolveNs;
resolvePtr: typeof resolvePtr;
resolveSoa: typeof resolveSoa;
resolveSrv: typeof resolveSrv;
resolveTxt: typeof resolveTxt;
reverse: typeof reverse;
/**
* The resolver instance will send its requests from the specified IP address.
* This allows programs to specify outbound interfaces when used on multi-homed
* systems.
*
* If a v4 or v6 address is not specified, it is set to the default, and the
* operating system will choose a local address automatically.
*
* The resolver will use the v4 local address when making requests to IPv4 DNS
* servers, and the v6 local address when making requests to IPv6 DNS servers.
* The `rrtype` of resolution requests has no impact on the local address used.
* @since v15.1.0, v14.17.0
* @param [ipv4='0.0.0.0'] A string representation of an IPv4 address.
* @param [ipv6='::0'] A string representation of an IPv6 address.
*/
setLocalAddress(ipv4?: string, ipv6?: string): void;
setServers: typeof setServers;
}
export { dnsPromises as promises };
}
declare module 'node:dns' {
export * from 'dns';
}

View File

@@ -1,370 +0,0 @@
/**
* The `dns.promises` API provides an alternative set of asynchronous DNS methods
* that return `Promise` objects rather than using callbacks. The API is accessible
* via `require('dns').promises` or `require('dns/promises')`.
* @since v10.6.0
*/
declare module 'dns/promises' {
import {
LookupAddress,
LookupOneOptions,
LookupAllOptions,
LookupOptions,
AnyRecord,
CaaRecord,
MxRecord,
NaptrRecord,
SoaRecord,
SrvRecord,
ResolveWithTtlOptions,
RecordWithTtl,
ResolveOptions,
ResolverOptions,
} from 'node:dns';
/**
* Returns an array of IP address strings, formatted according to [RFC 5952](https://tools.ietf.org/html/rfc5952#section-6),
* that are currently configured for DNS resolution. A string will include a port
* section if a custom port is used.
*
* ```js
* [
* '4.4.4.4',
* '2001:4860:4860::8888',
* '4.4.4.4:1053',
* '[2001:4860:4860::8888]:1053',
* ]
* ```
* @since v10.6.0
*/
function getServers(): string[];
/**
* Resolves a host name (e.g. `'nodejs.org'`) into the first found A (IPv4) or
* AAAA (IPv6) record. All `option` properties are optional. If `options` is an
* integer, then it must be `4` or `6` if `options` is not provided, then IPv4
* and IPv6 addresses are both returned if found.
*
* With the `all` option set to `true`, the `Promise` is resolved with `addresses`being an array of objects with the properties `address` and `family`.
*
* On error, the `Promise` is rejected with an `Error` object, where `err.code`is the error code.
* Keep in mind that `err.code` will be set to `'ENOTFOUND'` not only when
* the host name does not exist but also when the lookup fails in other ways
* such as no available file descriptors.
*
* `dnsPromises.lookup()` does not necessarily have anything to do with the DNS
* protocol. The implementation uses an operating system facility that can
* associate names with addresses, and vice versa. This implementation can have
* subtle but important consequences on the behavior of any Node.js program. Please
* take some time to consult the `Implementation considerations section` before
* using `dnsPromises.lookup()`.
*
* Example usage:
*
* ```js
* const dns = require('dns');
* const dnsPromises = dns.promises;
* const options = {
* family: 6,
* hints: dns.ADDRCONFIG | dns.V4MAPPED,
* };
*
* dnsPromises.lookup('example.com', options).then((result) => {
* console.log('address: %j family: IPv%s', result.address, result.family);
* // address: "2606:2800:220:1:248:1893:25c8:1946" family: IPv6
* });
*
* // When options.all is true, the result will be an Array.
* options.all = true;
* dnsPromises.lookup('example.com', options).then((result) => {
* console.log('addresses: %j', result);
* // addresses: [{"address":"2606:2800:220:1:248:1893:25c8:1946","family":6}]
* });
* ```
* @since v10.6.0
*/
function lookup(hostname: string, family: number): Promise<LookupAddress>;
function lookup(hostname: string, options: LookupOneOptions): Promise<LookupAddress>;
function lookup(hostname: string, options: LookupAllOptions): Promise<LookupAddress[]>;
function lookup(hostname: string, options: LookupOptions): Promise<LookupAddress | LookupAddress[]>;
function lookup(hostname: string): Promise<LookupAddress>;
/**
* Resolves the given `address` and `port` into a host name and service using
* the operating system's underlying `getnameinfo` implementation.
*
* If `address` is not a valid IP address, a `TypeError` will be thrown.
* The `port` will be coerced to a number. If it is not a legal port, a `TypeError`will be thrown.
*
* On error, the `Promise` is rejected with an `Error` object, where `err.code`is the error code.
*
* ```js
* const dnsPromises = require('dns').promises;
* dnsPromises.lookupService('127.0.0.1', 22).then((result) => {
* console.log(result.hostname, result.service);
* // Prints: localhost ssh
* });
* ```
* @since v10.6.0
*/
function lookupService(
address: string,
port: number
): Promise<{
hostname: string;
service: string;
}>;
/**
* Uses the DNS protocol to resolve a host name (e.g. `'nodejs.org'`) into an array
* of the resource records. When successful, the `Promise` is resolved with an
* array of resource records. The type and structure of individual results vary
* based on `rrtype`:
*
* <omitted>
*
* On error, the `Promise` is rejected with an `Error` object, where `err.code`is one of the DNS error codes.
* @since v10.6.0
* @param hostname Host name to resolve.
* @param [rrtype='A'] Resource record type.
*/
function resolve(hostname: string): Promise<string[]>;
function resolve(hostname: string, rrtype: 'A'): Promise<string[]>;
function resolve(hostname: string, rrtype: 'AAAA'): Promise<string[]>;
function resolve(hostname: string, rrtype: 'ANY'): Promise<AnyRecord[]>;
function resolve(hostname: string, rrtype: 'CAA'): Promise<CaaRecord[]>;
function resolve(hostname: string, rrtype: 'CNAME'): Promise<string[]>;
function resolve(hostname: string, rrtype: 'MX'): Promise<MxRecord[]>;
function resolve(hostname: string, rrtype: 'NAPTR'): Promise<NaptrRecord[]>;
function resolve(hostname: string, rrtype: 'NS'): Promise<string[]>;
function resolve(hostname: string, rrtype: 'PTR'): Promise<string[]>;
function resolve(hostname: string, rrtype: 'SOA'): Promise<SoaRecord>;
function resolve(hostname: string, rrtype: 'SRV'): Promise<SrvRecord[]>;
function resolve(hostname: string, rrtype: 'TXT'): Promise<string[][]>;
function resolve(hostname: string, rrtype: string): Promise<string[] | MxRecord[] | NaptrRecord[] | SoaRecord | SrvRecord[] | string[][] | AnyRecord[]>;
/**
* Uses the DNS protocol to resolve IPv4 addresses (`A` records) for the`hostname`. On success, the `Promise` is resolved with an array of IPv4
* addresses (e.g. `['74.125.79.104', '74.125.79.105', '74.125.79.106']`).
* @since v10.6.0
* @param hostname Host name to resolve.
*/
function resolve4(hostname: string): Promise<string[]>;
function resolve4(hostname: string, options: ResolveWithTtlOptions): Promise<RecordWithTtl[]>;
function resolve4(hostname: string, options: ResolveOptions): Promise<string[] | RecordWithTtl[]>;
/**
* Uses the DNS protocol to resolve IPv6 addresses (`AAAA` records) for the`hostname`. On success, the `Promise` is resolved with an array of IPv6
* addresses.
* @since v10.6.0
* @param hostname Host name to resolve.
*/
function resolve6(hostname: string): Promise<string[]>;
function resolve6(hostname: string, options: ResolveWithTtlOptions): Promise<RecordWithTtl[]>;
function resolve6(hostname: string, options: ResolveOptions): Promise<string[] | RecordWithTtl[]>;
/**
* Uses the DNS protocol to resolve all records (also known as `ANY` or `*` query).
* On success, the `Promise` is resolved with an array containing various types of
* records. Each object has a property `type` that indicates the type of the
* current record. And depending on the `type`, additional properties will be
* present on the object:
*
* <omitted>
*
* Here is an example of the result object:
*
* ```js
* [ { type: 'A', address: '127.0.0.1', ttl: 299 },
* { type: 'CNAME', value: 'example.com' },
* { type: 'MX', exchange: 'alt4.aspmx.l.example.com', priority: 50 },
* { type: 'NS', value: 'ns1.example.com' },
* { type: 'TXT', entries: [ 'v=spf1 include:_spf.example.com ~all' ] },
* { type: 'SOA',
* nsname: 'ns1.example.com',
* hostmaster: 'admin.example.com',
* serial: 156696742,
* refresh: 900,
* retry: 900,
* expire: 1800,
* minttl: 60 } ]
* ```
* @since v10.6.0
*/
function resolveAny(hostname: string): Promise<AnyRecord[]>;
/**
* Uses the DNS protocol to resolve `CAA` records for the `hostname`. On success,
* the `Promise` is resolved with an array of objects containing available
* certification authority authorization records available for the `hostname`(e.g. `[{critical: 0, iodef: 'mailto:pki@example.com'},{critical: 128, issue: 'pki.example.com'}]`).
* @since v15.0.0, v14.17.0
*/
function resolveCaa(hostname: string): Promise<CaaRecord[]>;
/**
* Uses the DNS protocol to resolve `CNAME` records for the `hostname`. On success,
* the `Promise` is resolved with an array of canonical name records available for
* the `hostname` (e.g. `['bar.example.com']`).
* @since v10.6.0
*/
function resolveCname(hostname: string): Promise<string[]>;
/**
* Uses the DNS protocol to resolve mail exchange records (`MX` records) for the`hostname`. On success, the `Promise` is resolved with an array of objects
* containing both a `priority` and `exchange` property (e.g.`[{priority: 10, exchange: 'mx.example.com'}, ...]`).
* @since v10.6.0
*/
function resolveMx(hostname: string): Promise<MxRecord[]>;
/**
* Uses the DNS protocol to resolve regular expression based records (`NAPTR`records) for the `hostname`. On success, the `Promise` is resolved with an array
* of objects with the following properties:
*
* * `flags`
* * `service`
* * `regexp`
* * `replacement`
* * `order`
* * `preference`
*
* ```js
* {
* flags: 's',
* service: 'SIP+D2U',
* regexp: '',
* replacement: '_sip._udp.example.com',
* order: 30,
* preference: 100
* }
* ```
* @since v10.6.0
*/
function resolveNaptr(hostname: string): Promise<NaptrRecord[]>;
/**
* Uses the DNS protocol to resolve name server records (`NS` records) for the`hostname`. On success, the `Promise` is resolved with an array of name server
* records available for `hostname` (e.g.`['ns1.example.com', 'ns2.example.com']`).
* @since v10.6.0
*/
function resolveNs(hostname: string): Promise<string[]>;
/**
* Uses the DNS protocol to resolve pointer records (`PTR` records) for the`hostname`. On success, the `Promise` is resolved with an array of strings
* containing the reply records.
* @since v10.6.0
*/
function resolvePtr(hostname: string): Promise<string[]>;
/**
* Uses the DNS protocol to resolve a start of authority record (`SOA` record) for
* the `hostname`. On success, the `Promise` is resolved with an object with the
* following properties:
*
* * `nsname`
* * `hostmaster`
* * `serial`
* * `refresh`
* * `retry`
* * `expire`
* * `minttl`
*
* ```js
* {
* nsname: 'ns.example.com',
* hostmaster: 'root.example.com',
* serial: 2013101809,
* refresh: 10000,
* retry: 2400,
* expire: 604800,
* minttl: 3600
* }
* ```
* @since v10.6.0
*/
function resolveSoa(hostname: string): Promise<SoaRecord>;
/**
* Uses the DNS protocol to resolve service records (`SRV` records) for the`hostname`. On success, the `Promise` is resolved with an array of objects with
* the following properties:
*
* * `priority`
* * `weight`
* * `port`
* * `name`
*
* ```js
* {
* priority: 10,
* weight: 5,
* port: 21223,
* name: 'service.example.com'
* }
* ```
* @since v10.6.0
*/
function resolveSrv(hostname: string): Promise<SrvRecord[]>;
/**
* Uses the DNS protocol to resolve text queries (`TXT` records) for the`hostname`. On success, the `Promise` is resolved with a two-dimensional array
* of the text records available for `hostname` (e.g.`[ ['v=spf1 ip4:0.0.0.0 ', '~all' ] ]`). Each sub-array contains TXT chunks of
* one record. Depending on the use case, these could be either joined together or
* treated separately.
* @since v10.6.0
*/
function resolveTxt(hostname: string): Promise<string[][]>;
/**
* Performs a reverse DNS query that resolves an IPv4 or IPv6 address to an
* array of host names.
*
* On error, the `Promise` is rejected with an `Error` object, where `err.code`is one of the DNS error codes.
* @since v10.6.0
*/
function reverse(ip: string): Promise<string[]>;
/**
* Sets the IP address and port of servers to be used when performing DNS
* resolution. The `servers` argument is an array of [RFC 5952](https://tools.ietf.org/html/rfc5952#section-6) formatted
* addresses. If the port is the IANA default DNS port (53) it can be omitted.
*
* ```js
* dnsPromises.setServers([
* '4.4.4.4',
* '[2001:4860:4860::8888]',
* '4.4.4.4:1053',
* '[2001:4860:4860::8888]:1053',
* ]);
* ```
*
* An error will be thrown if an invalid address is provided.
*
* The `dnsPromises.setServers()` method must not be called while a DNS query is in
* progress.
*
* This method works much like [resolve.conf](https://man7.org/linux/man-pages/man5/resolv.conf.5.html).
* That is, if attempting to resolve with the first server provided results in a`NOTFOUND` error, the `resolve()` method will _not_ attempt to resolve with
* subsequent servers provided. Fallback DNS servers will only be used if the
* earlier ones time out or result in some other error.
* @since v10.6.0
* @param servers array of `RFC 5952` formatted addresses
*/
function setServers(servers: ReadonlyArray<string>): void;
/**
* Set the default value of `verbatim` in `dns.lookup()` and `dnsPromises.lookup()`. The value could be:
*
* * `ipv4first`: sets default `verbatim` `false`.
* * `verbatim`: sets default `verbatim` `true`.
*
* The default is `ipv4first` and `dnsPromises.setDefaultResultOrder()` have
* higher priority than `--dns-result-order`. When using `worker threads`,`dnsPromises.setDefaultResultOrder()` from the main thread won't affect the
* default dns orders in workers.
* @since v16.4.0, v14.18.0
* @param order must be `'ipv4first'` or `'verbatim'`.
*/
function setDefaultResultOrder(order: 'ipv4first' | 'verbatim'): void;
class Resolver {
constructor(options?: ResolverOptions);
cancel(): void;
getServers: typeof getServers;
resolve: typeof resolve;
resolve4: typeof resolve4;
resolve6: typeof resolve6;
resolveAny: typeof resolveAny;
resolveCname: typeof resolveCname;
resolveMx: typeof resolveMx;
resolveNaptr: typeof resolveNaptr;
resolveNs: typeof resolveNs;
resolvePtr: typeof resolvePtr;
resolveSoa: typeof resolveSoa;
resolveSrv: typeof resolveSrv;
resolveTxt: typeof resolveTxt;
reverse: typeof reverse;
setLocalAddress(ipv4?: string, ipv6?: string): void;
setServers: typeof setServers;
}
}
declare module 'node:dns/promises' {
export * from 'dns/promises';
}

169
node_modules/@types/node/domain.d.ts generated vendored
View File

@@ -1,169 +0,0 @@
/**
* **This module is pending deprecation.** Once a replacement API has been
* finalized, this module will be fully deprecated. Most developers should**not** have cause to use this module. Users who absolutely must have
* the functionality that domains provide may rely on it for the time being
* but should expect to have to migrate to a different solution
* in the future.
*
* Domains provide a way to handle multiple different IO operations as a
* single group. If any of the event emitters or callbacks registered to a
* domain emit an `'error'` event, or throw an error, then the domain object
* will be notified, rather than losing the context of the error in the`process.on('uncaughtException')` handler, or causing the program to
* exit immediately with an error code.
* @deprecated Since v1.4.2 - Deprecated
* @see [source](https://github.com/nodejs/node/blob/v17.0.0/lib/domain.js)
*/
declare module 'domain' {
import EventEmitter = require('node:events');
/**
* The `Domain` class encapsulates the functionality of routing errors and
* uncaught exceptions to the active `Domain` object.
*
* To handle the errors that it catches, listen to its `'error'` event.
*/
class Domain extends EventEmitter {
/**
* An array of timers and event emitters that have been explicitly added
* to the domain.
*/
members: Array<EventEmitter | NodeJS.Timer>;
/**
* The `enter()` method is plumbing used by the `run()`, `bind()`, and`intercept()` methods to set the active domain. It sets `domain.active` and`process.domain` to the domain, and implicitly
* pushes the domain onto the domain
* stack managed by the domain module (see {@link exit} for details on the
* domain stack). The call to `enter()` delimits the beginning of a chain of
* asynchronous calls and I/O operations bound to a domain.
*
* Calling `enter()` changes only the active domain, and does not alter the domain
* itself. `enter()` and `exit()` can be called an arbitrary number of times on a
* single domain.
*/
enter(): void;
/**
* The `exit()` method exits the current domain, popping it off the domain stack.
* Any time execution is going to switch to the context of a different chain of
* asynchronous calls, it's important to ensure that the current domain is exited.
* The call to `exit()` delimits either the end of or an interruption to the chain
* of asynchronous calls and I/O operations bound to a domain.
*
* If there are multiple, nested domains bound to the current execution context,`exit()` will exit any domains nested within this domain.
*
* Calling `exit()` changes only the active domain, and does not alter the domain
* itself. `enter()` and `exit()` can be called an arbitrary number of times on a
* single domain.
*/
exit(): void;
/**
* Run the supplied function in the context of the domain, implicitly
* binding all event emitters, timers, and lowlevel requests that are
* created in that context. Optionally, arguments can be passed to
* the function.
*
* This is the most basic way to use a domain.
*
* ```js
* const domain = require('domain');
* const fs = require('fs');
* const d = domain.create();
* d.on('error', (er) => {
* console.error('Caught error!', er);
* });
* d.run(() => {
* process.nextTick(() => {
* setTimeout(() => { // Simulating some various async stuff
* fs.open('non-existent file', 'r', (er, fd) => {
* if (er) throw er;
* // proceed...
* });
* }, 100);
* });
* });
* ```
*
* In this example, the `d.on('error')` handler will be triggered, rather
* than crashing the program.
*/
run<T>(fn: (...args: any[]) => T, ...args: any[]): T;
/**
* Explicitly adds an emitter to the domain. If any event handlers called by
* the emitter throw an error, or if the emitter emits an `'error'` event, it
* will be routed to the domain's `'error'` event, just like with implicit
* binding.
*
* This also works with timers that are returned from `setInterval()` and `setTimeout()`. If their callback function throws, it will be caught by
* the domain `'error'` handler.
*
* If the Timer or `EventEmitter` was already bound to a domain, it is removed
* from that one, and bound to this one instead.
* @param emitter emitter or timer to be added to the domain
*/
add(emitter: EventEmitter | NodeJS.Timer): void;
/**
* The opposite of {@link add}. Removes domain handling from the
* specified emitter.
* @param emitter emitter or timer to be removed from the domain
*/
remove(emitter: EventEmitter | NodeJS.Timer): void;
/**
* The returned function will be a wrapper around the supplied callback
* function. When the returned function is called, any errors that are
* thrown will be routed to the domain's `'error'` event.
*
* ```js
* const d = domain.create();
*
* function readSomeFile(filename, cb) {
* fs.readFile(filename, 'utf8', d.bind((er, data) => {
* // If this throws, it will also be passed to the domain.
* return cb(er, data ? JSON.parse(data) : null);
* }));
* }
*
* d.on('error', (er) => {
* // An error occurred somewhere. If we throw it now, it will crash the program
* // with the normal line number and stack message.
* });
* ```
* @param callback The callback function
* @return The bound function
*/
bind<T extends Function>(callback: T): T;
/**
* This method is almost identical to {@link bind}. However, in
* addition to catching thrown errors, it will also intercept `Error` objects sent as the first argument to the function.
*
* In this way, the common `if (err) return callback(err);` pattern can be replaced
* with a single error handler in a single place.
*
* ```js
* const d = domain.create();
*
* function readSomeFile(filename, cb) {
* fs.readFile(filename, 'utf8', d.intercept((data) => {
* // Note, the first argument is never passed to the
* // callback since it is assumed to be the 'Error' argument
* // and thus intercepted by the domain.
*
* // If this throws, it will also be passed to the domain
* // so the error-handling logic can be moved to the 'error'
* // event on the domain instead of being repeated throughout
* // the program.
* return cb(null, JSON.parse(data));
* }));
* }
*
* d.on('error', (er) => {
* // An error occurred somewhere. If we throw it now, it will crash the program
* // with the normal line number and stack message.
* });
* ```
* @param callback The callback function
* @return The intercepted function
*/
intercept<T extends Function>(callback: T): T;
}
function create(): Domain;
}
declare module 'node:domain' {
export * from 'domain';
}

623
node_modules/@types/node/events.d.ts generated vendored
View File

@@ -1,623 +0,0 @@
/**
* Much of the Node.js core API is built around an idiomatic asynchronous
* event-driven architecture in which certain kinds of objects (called "emitters")
* emit named events that cause `Function` objects ("listeners") to be called.
*
* For instance: a `net.Server` object emits an event each time a peer
* connects to it; a `fs.ReadStream` emits an event when the file is opened;
* a `stream` emits an event whenever data is available to be read.
*
* All objects that emit events are instances of the `EventEmitter` class. These
* objects expose an `eventEmitter.on()` function that allows one or more
* functions to be attached to named events emitted by the object. Typically,
* event names are camel-cased strings but any valid JavaScript property key
* can be used.
*
* When the `EventEmitter` object emits an event, all of the functions attached
* to that specific event are called _synchronously_. Any values returned by the
* called listeners are _ignored_ and discarded.
*
* The following example shows a simple `EventEmitter` instance with a single
* listener. The `eventEmitter.on()` method is used to register listeners, while
* the `eventEmitter.emit()` method is used to trigger the event.
*
* ```js
* const EventEmitter = require('events');
*
* class MyEmitter extends EventEmitter {}
*
* const myEmitter = new MyEmitter();
* myEmitter.on('event', () => {
* console.log('an event occurred!');
* });
* myEmitter.emit('event');
* ```
* @see [source](https://github.com/nodejs/node/blob/v17.0.0/lib/events.js)
*/
declare module 'events' {
interface EventEmitterOptions {
/**
* Enables automatic capturing of promise rejection.
*/
captureRejections?: boolean | undefined;
}
interface NodeEventTarget {
once(eventName: string | symbol, listener: (...args: any[]) => void): this;
}
interface DOMEventTarget {
addEventListener(
eventName: string,
listener: (...args: any[]) => void,
opts?: {
once: boolean;
}
): any;
}
interface StaticEventEmitterOptions {
signal?: AbortSignal | undefined;
}
interface EventEmitter extends NodeJS.EventEmitter {}
/**
* The `EventEmitter` class is defined and exposed by the `events` module:
*
* ```js
* const EventEmitter = require('events');
* ```
*
* All `EventEmitter`s emit the event `'newListener'` when new listeners are
* added and `'removeListener'` when existing listeners are removed.
*
* It supports the following option:
* @since v0.1.26
*/
class EventEmitter {
constructor(options?: EventEmitterOptions);
/**
* Creates a `Promise` that is fulfilled when the `EventEmitter` emits the given
* event or that is rejected if the `EventEmitter` emits `'error'` while waiting.
* The `Promise` will resolve with an array of all the arguments emitted to the
* given event.
*
* This method is intentionally generic and works with the web platform [EventTarget](https://dom.spec.whatwg.org/#interface-eventtarget) interface, which has no special`'error'` event
* semantics and does not listen to the `'error'` event.
*
* ```js
* const { once, EventEmitter } = require('events');
*
* async function run() {
* const ee = new EventEmitter();
*
* process.nextTick(() => {
* ee.emit('myevent', 42);
* });
*
* const [value] = await once(ee, 'myevent');
* console.log(value);
*
* const err = new Error('kaboom');
* process.nextTick(() => {
* ee.emit('error', err);
* });
*
* try {
* await once(ee, 'myevent');
* } catch (err) {
* console.log('error happened', err);
* }
* }
*
* run();
* ```
*
* The special handling of the `'error'` event is only used when `events.once()`is used to wait for another event. If `events.once()` is used to wait for the
* '`error'` event itself, then it is treated as any other kind of event without
* special handling:
*
* ```js
* const { EventEmitter, once } = require('events');
*
* const ee = new EventEmitter();
*
* once(ee, 'error')
* .then(([err]) => console.log('ok', err.message))
* .catch((err) => console.log('error', err.message));
*
* ee.emit('error', new Error('boom'));
*
* // Prints: ok boom
* ```
*
* An `AbortSignal` can be used to cancel waiting for the event:
*
* ```js
* const { EventEmitter, once } = require('events');
*
* const ee = new EventEmitter();
* const ac = new AbortController();
*
* async function foo(emitter, event, signal) {
* try {
* await once(emitter, event, { signal });
* console.log('event emitted!');
* } catch (error) {
* if (error.name === 'AbortError') {
* console.error('Waiting for the event was canceled!');
* } else {
* console.error('There was an error', error.message);
* }
* }
* }
*
* foo(ee, 'foo', ac.signal);
* ac.abort(); // Abort waiting for the event
* ee.emit('foo'); // Prints: Waiting for the event was canceled!
* ```
* @since v11.13.0, v10.16.0
*/
static once(emitter: NodeEventTarget, eventName: string | symbol, options?: StaticEventEmitterOptions): Promise<any[]>;
static once(emitter: DOMEventTarget, eventName: string, options?: StaticEventEmitterOptions): Promise<any[]>;
/**
* ```js
* const { on, EventEmitter } = require('events');
*
* (async () => {
* const ee = new EventEmitter();
*
* // Emit later on
* process.nextTick(() => {
* ee.emit('foo', 'bar');
* ee.emit('foo', 42);
* });
*
* for await (const event of on(ee, 'foo')) {
* // The execution of this inner block is synchronous and it
* // processes one event at a time (even with await). Do not use
* // if concurrent execution is required.
* console.log(event); // prints ['bar'] [42]
* }
* // Unreachable here
* })();
* ```
*
* Returns an `AsyncIterator` that iterates `eventName` events. It will throw
* if the `EventEmitter` emits `'error'`. It removes all listeners when
* exiting the loop. The `value` returned by each iteration is an array
* composed of the emitted event arguments.
*
* An `AbortSignal` can be used to cancel waiting on events:
*
* ```js
* const { on, EventEmitter } = require('events');
* const ac = new AbortController();
*
* (async () => {
* const ee = new EventEmitter();
*
* // Emit later on
* process.nextTick(() => {
* ee.emit('foo', 'bar');
* ee.emit('foo', 42);
* });
*
* for await (const event of on(ee, 'foo', { signal: ac.signal })) {
* // The execution of this inner block is synchronous and it
* // processes one event at a time (even with await). Do not use
* // if concurrent execution is required.
* console.log(event); // prints ['bar'] [42]
* }
* // Unreachable here
* })();
*
* process.nextTick(() => ac.abort());
* ```
* @since v13.6.0, v12.16.0
* @param eventName The name of the event being listened for
* @return that iterates `eventName` events emitted by the `emitter`
*/
static on(emitter: NodeJS.EventEmitter, eventName: string, options?: StaticEventEmitterOptions): AsyncIterableIterator<any>;
/**
* A class method that returns the number of listeners for the given `eventName`registered on the given `emitter`.
*
* ```js
* const { EventEmitter, listenerCount } = require('events');
* const myEmitter = new EventEmitter();
* myEmitter.on('event', () => {});
* myEmitter.on('event', () => {});
* console.log(listenerCount(myEmitter, 'event'));
* // Prints: 2
* ```
* @since v0.9.12
* @deprecated Since v3.2.0 - Use `listenerCount` instead.
* @param emitter The emitter to query
* @param eventName The event name
*/
static listenerCount(emitter: NodeJS.EventEmitter, eventName: string | symbol): number;
/**
* Returns a copy of the array of listeners for the event named `eventName`.
*
* For `EventEmitter`s this behaves exactly the same as calling `.listeners` on
* the emitter.
*
* For `EventTarget`s this is the only way to get the event listeners for the
* event target. This is useful for debugging and diagnostic purposes.
*
* ```js
* const { getEventListeners, EventEmitter } = require('events');
*
* {
* const ee = new EventEmitter();
* const listener = () => console.log('Events are fun');
* ee.on('foo', listener);
* getEventListeners(ee, 'foo'); // [listener]
* }
* {
* const et = new EventTarget();
* const listener = () => console.log('Events are fun');
* et.addEventListener('foo', listener);
* getEventListeners(et, 'foo'); // [listener]
* }
* ```
* @since v15.2.0, v14.17.0
*/
static getEventListeners(emitter: DOMEventTarget | NodeJS.EventEmitter, name: string | symbol): Function[];
/**
* This symbol shall be used to install a listener for only monitoring `'error'`
* events. Listeners installed using this symbol are called before the regular
* `'error'` listeners are called.
*
* Installing a listener using this symbol does not change the behavior once an
* `'error'` event is emitted, therefore the process will still crash if no
* regular `'error'` listener is installed.
*/
static readonly errorMonitor: unique symbol;
static readonly captureRejectionSymbol: unique symbol;
/**
* Sets or gets the default captureRejection value for all emitters.
*/
// TODO: These should be described using static getter/setter pairs:
static captureRejections: boolean;
static defaultMaxListeners: number;
}
import internal = require('node:events');
namespace EventEmitter {
// Should just be `export { EventEmitter }`, but that doesn't work in TypeScript 3.4
export { internal as EventEmitter };
export interface Abortable {
/**
* When provided the corresponding `AbortController` can be used to cancel an asynchronous action.
*/
signal?: AbortSignal | undefined;
}
}
global {
namespace NodeJS {
interface EventEmitter {
/**
* Alias for `emitter.on(eventName, listener)`.
* @since v0.1.26
*/
addListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
/**
* Adds the `listener` function to the end of the listeners array for the
* event named `eventName`. No checks are made to see if the `listener` has
* already been added. Multiple calls passing the same combination of `eventName`and `listener` will result in the `listener` being added, and called, multiple
* times.
*
* ```js
* server.on('connection', (stream) => {
* console.log('someone connected!');
* });
* ```
*
* Returns a reference to the `EventEmitter`, so that calls can be chained.
*
* By default, event listeners are invoked in the order they are added. The`emitter.prependListener()` method can be used as an alternative to add the
* event listener to the beginning of the listeners array.
*
* ```js
* const myEE = new EventEmitter();
* myEE.on('foo', () => console.log('a'));
* myEE.prependListener('foo', () => console.log('b'));
* myEE.emit('foo');
* // Prints:
* // b
* // a
* ```
* @since v0.1.101
* @param eventName The name of the event.
* @param listener The callback function
*/
on(eventName: string | symbol, listener: (...args: any[]) => void): this;
/**
* Adds a **one-time**`listener` function for the event named `eventName`. The
* next time `eventName` is triggered, this listener is removed and then invoked.
*
* ```js
* server.once('connection', (stream) => {
* console.log('Ah, we have our first user!');
* });
* ```
*
* Returns a reference to the `EventEmitter`, so that calls can be chained.
*
* By default, event listeners are invoked in the order they are added. The`emitter.prependOnceListener()` method can be used as an alternative to add the
* event listener to the beginning of the listeners array.
*
* ```js
* const myEE = new EventEmitter();
* myEE.once('foo', () => console.log('a'));
* myEE.prependOnceListener('foo', () => console.log('b'));
* myEE.emit('foo');
* // Prints:
* // b
* // a
* ```
* @since v0.3.0
* @param eventName The name of the event.
* @param listener The callback function
*/
once(eventName: string | symbol, listener: (...args: any[]) => void): this;
/**
* Removes the specified `listener` from the listener array for the event named`eventName`.
*
* ```js
* const callback = (stream) => {
* console.log('someone connected!');
* };
* server.on('connection', callback);
* // ...
* server.removeListener('connection', callback);
* ```
*
* `removeListener()` will remove, at most, one instance of a listener from the
* listener array. If any single listener has been added multiple times to the
* listener array for the specified `eventName`, then `removeListener()` must be
* called multiple times to remove each instance.
*
* Once an event is emitted, all listeners attached to it at the
* time of emitting are called in order. This implies that any`removeListener()` or `removeAllListeners()` calls _after_ emitting and_before_ the last listener finishes execution will
* not remove them from`emit()` in progress. Subsequent events behave as expected.
*
* ```js
* const myEmitter = new MyEmitter();
*
* const callbackA = () => {
* console.log('A');
* myEmitter.removeListener('event', callbackB);
* };
*
* const callbackB = () => {
* console.log('B');
* };
*
* myEmitter.on('event', callbackA);
*
* myEmitter.on('event', callbackB);
*
* // callbackA removes listener callbackB but it will still be called.
* // Internal listener array at time of emit [callbackA, callbackB]
* myEmitter.emit('event');
* // Prints:
* // A
* // B
*
* // callbackB is now removed.
* // Internal listener array [callbackA]
* myEmitter.emit('event');
* // Prints:
* // A
* ```
*
* Because listeners are managed using an internal array, calling this will
* change the position indices of any listener registered _after_ the listener
* being removed. This will not impact the order in which listeners are called,
* but it means that any copies of the listener array as returned by
* the `emitter.listeners()` method will need to be recreated.
*
* When a single function has been added as a handler multiple times for a single
* event (as in the example below), `removeListener()` will remove the most
* recently added instance. In the example the `once('ping')`listener is removed:
*
* ```js
* const ee = new EventEmitter();
*
* function pong() {
* console.log('pong');
* }
*
* ee.on('ping', pong);
* ee.once('ping', pong);
* ee.removeListener('ping', pong);
*
* ee.emit('ping');
* ee.emit('ping');
* ```
*
* Returns a reference to the `EventEmitter`, so that calls can be chained.
* @since v0.1.26
*/
removeListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
/**
* Alias for `emitter.removeListener()`.
* @since v10.0.0
*/
off(eventName: string | symbol, listener: (...args: any[]) => void): this;
/**
* Removes all listeners, or those of the specified `eventName`.
*
* It is bad practice to remove listeners added elsewhere in the code,
* particularly when the `EventEmitter` instance was created by some other
* component or module (e.g. sockets or file streams).
*
* Returns a reference to the `EventEmitter`, so that calls can be chained.
* @since v0.1.26
*/
removeAllListeners(event?: string | symbol): this;
/**
* By default `EventEmitter`s will print a warning if more than `10` listeners are
* added for a particular event. This is a useful default that helps finding
* memory leaks. The `emitter.setMaxListeners()` method allows the limit to be
* modified for this specific `EventEmitter` instance. The value can be set to`Infinity` (or `0`) to indicate an unlimited number of listeners.
*
* Returns a reference to the `EventEmitter`, so that calls can be chained.
* @since v0.3.5
*/
setMaxListeners(n: number): this;
/**
* Returns the current max listener value for the `EventEmitter` which is either
* set by `emitter.setMaxListeners(n)` or defaults to {@link defaultMaxListeners}.
* @since v1.0.0
*/
getMaxListeners(): number;
/**
* Returns a copy of the array of listeners for the event named `eventName`.
*
* ```js
* server.on('connection', (stream) => {
* console.log('someone connected!');
* });
* console.log(util.inspect(server.listeners('connection')));
* // Prints: [ [Function] ]
* ```
* @since v0.1.26
*/
listeners(eventName: string | symbol): Function[];
/**
* Returns a copy of the array of listeners for the event named `eventName`,
* including any wrappers (such as those created by `.once()`).
*
* ```js
* const emitter = new EventEmitter();
* emitter.once('log', () => console.log('log once'));
*
* // Returns a new Array with a function `onceWrapper` which has a property
* // `listener` which contains the original listener bound above
* const listeners = emitter.rawListeners('log');
* const logFnWrapper = listeners[0];
*
* // Logs "log once" to the console and does not unbind the `once` event
* logFnWrapper.listener();
*
* // Logs "log once" to the console and removes the listener
* logFnWrapper();
*
* emitter.on('log', () => console.log('log persistently'));
* // Will return a new Array with a single function bound by `.on()` above
* const newListeners = emitter.rawListeners('log');
*
* // Logs "log persistently" twice
* newListeners[0]();
* emitter.emit('log');
* ```
* @since v9.4.0
*/
rawListeners(eventName: string | symbol): Function[];
/**
* Synchronously calls each of the listeners registered for the event named`eventName`, in the order they were registered, passing the supplied arguments
* to each.
*
* Returns `true` if the event had listeners, `false` otherwise.
*
* ```js
* const EventEmitter = require('events');
* const myEmitter = new EventEmitter();
*
* // First listener
* myEmitter.on('event', function firstListener() {
* console.log('Helloooo! first listener');
* });
* // Second listener
* myEmitter.on('event', function secondListener(arg1, arg2) {
* console.log(`event with parameters ${arg1}, ${arg2} in second listener`);
* });
* // Third listener
* myEmitter.on('event', function thirdListener(...args) {
* const parameters = args.join(', ');
* console.log(`event with parameters ${parameters} in third listener`);
* });
*
* console.log(myEmitter.listeners('event'));
*
* myEmitter.emit('event', 1, 2, 3, 4, 5);
*
* // Prints:
* // [
* // [Function: firstListener],
* // [Function: secondListener],
* // [Function: thirdListener]
* // ]
* // Helloooo! first listener
* // event with parameters 1, 2 in second listener
* // event with parameters 1, 2, 3, 4, 5 in third listener
* ```
* @since v0.1.26
*/
emit(eventName: string | symbol, ...args: any[]): boolean;
/**
* Returns the number of listeners listening to the event named `eventName`.
* @since v3.2.0
* @param eventName The name of the event being listened for
*/
listenerCount(eventName: string | symbol): number;
/**
* Adds the `listener` function to the _beginning_ of the listeners array for the
* event named `eventName`. No checks are made to see if the `listener` has
* already been added. Multiple calls passing the same combination of `eventName`and `listener` will result in the `listener` being added, and called, multiple
* times.
*
* ```js
* server.prependListener('connection', (stream) => {
* console.log('someone connected!');
* });
* ```
*
* Returns a reference to the `EventEmitter`, so that calls can be chained.
* @since v6.0.0
* @param eventName The name of the event.
* @param listener The callback function
*/
prependListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
/**
* Adds a **one-time**`listener` function for the event named `eventName` to the_beginning_ of the listeners array. The next time `eventName` is triggered, this
* listener is removed, and then invoked.
*
* ```js
* server.prependOnceListener('connection', (stream) => {
* console.log('Ah, we have our first user!');
* });
* ```
*
* Returns a reference to the `EventEmitter`, so that calls can be chained.
* @since v6.0.0
* @param eventName The name of the event.
* @param listener The callback function
*/
prependOnceListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
/**
* Returns an array listing the events for which the emitter has registered
* listeners. The values in the array are strings or `Symbol`s.
*
* ```js
* const EventEmitter = require('events');
* const myEE = new EventEmitter();
* myEE.on('foo', () => {});
* myEE.on('bar', () => {});
*
* const sym = Symbol('symbol');
* myEE.on(sym, () => {});
*
* console.log(myEE.eventNames());
* // Prints: [ 'foo', 'bar', Symbol(symbol) ]
* ```
* @since v6.0.0
*/
eventNames(): Array<string | symbol>;
}
}
}
export = EventEmitter;
}
declare module 'node:events' {
import events = require('events');
export = events;
}

3835
node_modules/@types/node/fs.d.ts generated vendored

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More