Compare commits

...

2176 Commits
v6.8.2 ... main

Author SHA1 Message Date
d24a8e99cc Fix badge 2025-04-09 16:33:19 +02:00
59d2d56091 Fix badge 2025-04-09 16:30:27 +02:00
8515b7060a Merge pull request #833 from keycloakify/all-contributors/add-wnmzzzz
docs: add wnmzzzz as a contributor for test
2025-04-09 16:29:50 +02:00
a076b3f4d0 docs: update .all-contributorsrc [skip ci] 2025-04-09 14:29:32 +00:00
1e3240ef35 docs: update README.md [skip ci] 2025-04-09 14:29:31 +00:00
d767080dfe Merge pull request #831 from wnmzzzz/patch-1
Add Story for AppInitiatedAction to UpdatePassword
2025-04-09 16:29:02 +02:00
b228eda488 Add Story for AppInitiatedAction to UpdatePassword 2025-04-07 08:42:46 +02:00
35f54964ce Bump version 2025-04-04 23:57:42 +02:00
759b834ccc Follow up on #827 2025-04-04 23:57:13 +02:00
137e12cbbb Merge pull request #830 from keycloakify/all-contributors/add-kodebach
docs: add kodebach as a contributor for code
2025-04-04 23:57:08 +02:00
57b08d9dea docs: update .all-contributorsrc [skip ci] 2025-04-04 21:56:54 +00:00
1dd7b673a1 docs: update README.md [skip ci] 2025-04-04 21:56:53 +00:00
b00ffc50c3 Merge pull request #827 from kodebach/fix-keycloak-36012
Fix double submit bug in OTP Form
2025-04-04 23:34:01 +02:00
f9db40d33d fix: https://github.com/keycloak/keycloak/issues/36012
adapted from https://github.com/keycloak/keycloak/pull/36096
2025-04-02 12:08:37 +00:00
4bc6a843d8 Merge pull request #820 from kingjan1999/fix-required-actions-whitespace
fix: add whitespace before required actions in info page
2025-03-21 13:21:02 +01:00
da3e7514f0 fix: add whitespace before required actions in info page 2025-03-21 11:37:08 +01:00
bc396bc41b Update runPrettier so that it will still work if we ever switch to ESM 2025-03-16 02:17:11 +01:00
947efe8d63 Bump version 2025-03-16 00:49:08 +01:00
64189bf8fe #815 2025-03-16 00:48:50 +01:00
400c630418 Bump version 2025-03-13 22:03:16 +01:00
402360b436 #814 https://github.com/keycloak/keycloak/issues/38029 2025-03-13 22:03:02 +01:00
9f001f1521 Bump version 2025-03-13 13:32:23 +01:00
368e3a32c5 #772 2025-03-13 13:32:08 +01:00
002e3d4b3d Bump version 2025-03-12 19:22:58 +01:00
f94f9b51c9 Fix Vitest VSCode extention 2025-03-12 19:22:32 +01:00
055b15bd46 Bump version 2025-03-12 00:53:05 +01:00
0e70b0b0de https://github.com/keycloak/keycloak/issues/38029 2025-03-12 00:52:50 +01:00
8faf9a3eed Bump version 2025-02-27 12:21:32 +01:00
075d9f9de5 #802 2025-02-27 12:21:32 +01:00
840079be32 Merge pull request #797 from keycloakify/all-contributors/add-bacongobbler
docs: add bacongobbler as a contributor for doc
2025-02-24 19:43:18 +01:00
50ae962f09 docs: update .all-contributorsrc [skip ci] 2025-02-24 18:43:07 +00:00
61aa1f9896 docs: update README.md [skip ci] 2025-02-24 18:43:06 +00:00
d88e0e4dd5 Bump version 2025-02-24 18:46:06 +01:00
18c36eb4de Help pepole debug when mvn build fails 2025-02-24 18:06:28 +01:00
80aeabad51 Bump version 2025-02-17 12:59:49 +01:00
419e1f473a Merge pull request #791 from keycloakify/all-contributors/add-EternalSide
docs: add EternalSide as a contributor for code
2025-02-17 12:59:19 +01:00
80988125e8 Merge pull request #789 from EternalSide/fix/add-passwordPolicy-maxLength
fix: add maxLength passwordPolicy in the getUserProfileApi
2025-02-17 12:59:08 +01:00
271ad2da71 docs: update .all-contributorsrc [skip ci] 2025-02-17 11:56:31 +00:00
b2732f2595 docs: update README.md [skip ci] 2025-02-17 11:56:30 +00:00
53820e1e34 fix: add maxLength passwordPolicy in the getUserProfileApi 2025-02-17 13:21:55 +03:00
09dd45e437 Bump version 2025-02-11 15:56:03 +01:00
1f654a7820 #785 2025-02-11 15:55:47 +01:00
0690f40bad Bump version 2025-02-02 18:26:31 +01:00
2285883149 Fix typo #778 2025-02-02 18:26:08 +01:00
af87e41bb8 Bump version 2025-01-25 18:31:05 +01:00
9ba884483d keycloakify-email isn't strictly bound to jsx-email 2025-01-25 18:30:52 +01:00
f5a300953a Bump version 2025-01-24 21:27:03 +01:00
ab9a962f58 #771 2025-01-24 21:26:43 +01:00
484adb607f Bump version 2025-01-23 16:41:49 +01:00
e1f38d4196 Fix 767 2025-01-23 16:41:02 +01:00
5de629acf2 Merge pull request #767 from mislavperi/main
Expanded podman support
2025-01-23 15:36:21 +00:00
8b4b24a036 consisteny 2025-01-23 14:42:53 +01:00
75ab130249 refactor to shorten the code 2025-01-23 14:42:03 +01:00
981ca7e9a4 expanded podman support 2025-01-23 10:55:51 +01:00
acb4e260a7 Bump version 2025-01-11 16:08:38 +01:00
ff20b0a844 Avoid re-building when shared files from SPAs are changed 2025-01-11 16:08:17 +01:00
1b77c69a01 Bump version 2025-01-10 19:07:35 +01:00
158275f5c2 Workaround bug with the versions subcomand of older npm versions 2025-01-10 19:07:11 +01:00
a085c8093e Bump version 2025-01-08 00:07:34 +01:00
cb358bd745 #754: PasswordWrapper fix for React 19 2025-01-08 00:06:45 +01:00
e788c46601 Bump version 2025-01-07 20:51:49 +01:00
d551b4bffb Add missing Patternfly fonts 2025-01-07 20:51:27 +01:00
c168c7b156 Bump version 2025-01-06 02:47:42 +01:00
7a46115042 Enable persisting email change on test user 2025-01-06 02:47:28 +01:00
249a7bde89 Fix adding comment to certain ftl files 2025-01-06 02:31:46 +01:00
813740a002 Bump version 2025-01-05 21:34:34 +01:00
7840c2a6f5 Fix previous release 2025-01-05 21:34:16 +01:00
8f6c0d36d9 Bump version 2025-01-05 21:09:24 +01:00
12690b892b Fix replacing of xKeycloakify.themeName in theme variant 2025-01-05 21:09:06 +01:00
d01b4b71c9 Bump version 2025-01-05 21:00:53 +01:00
c29e600786 Fix generateResource bug 2025-01-05 21:00:34 +01:00
6309b7c45d Bump version 2025-01-05 04:27:11 +01:00
7e7996e40c Fix auto enable themes when using start-keycloak 2025-01-05 04:26:45 +01:00
deaeab0f61 Infer META-INF/keycloak-themes.jar 2025-01-05 03:14:34 +01:00
6bd5451230 Bump version 2025-01-05 02:09:33 +01:00
fb2d651a6f Rely on @keycloakify/email-native for email initialization 2025-01-05 02:08:18 +01:00
4845d7c32d Support incorporating theme native theme, with theme variant support #733 2025-01-05 02:08:13 +01:00
c33c315120 Bump version 2025-01-03 22:58:12 +01:00
99b8f1e789 Update i18n account multi-page boilerplate 2025-01-03 22:57:57 +01:00
6af13e1405 Bump version 2025-01-03 22:39:01 +01:00
f59fa4238c Link to the documentation for implementing non builtin pages 2025-01-03 22:38:45 +01:00
248effc57d Bump version 2025-01-03 02:47:31 +01:00
9e540b2c1f Fix assets import from public in .svelte files 2025-01-03 02:47:31 +01:00
ab7b5ff490 Remove ringerhq 2025-01-03 01:06:29 +01:00
486f944e0f Bump version 2025-01-02 10:25:16 +01:00
6cc3d4c442 Fix conflict in --path shorthand with --project, --path shorthand is now -t (target) 2025-01-02 10:25:16 +01:00
083290c6d4 update broken link 2024-12-30 02:04:09 +01:00
cd1b55b850 Fix test 2024-12-27 01:49:38 +01:00
482ba6c639 Bump version 2024-12-27 01:41:29 +01:00
e2921b7e37 Fix auto add of postinstall script 2024-12-27 01:38:11 +01:00
c87b6153bb Fix some errors implementing the new account SPA feature 2024-12-27 01:36:29 +01:00
488dd2c6b9 Migrate to extention model for Account SPA 2024-12-26 15:55:59 +01:00
1ac678a368 Remove dead code 2024-12-26 15:24:12 +01:00
5866c802e5 Always initialize email with the latest keycloak version 2024-12-26 15:24:03 +01:00
fe892c840b Bump version 2024-12-24 17:40:31 +01:00
9685dfb55a Fix git integration bug 2024-12-24 17:39:54 +01:00
c1dc899bc1 Better naming convention 'uiModules' -> 'extensionModules' 2024-12-24 16:43:42 +01:00
d2da43c617 Implement --revert for own command 2024-12-24 01:10:32 +01:00
6de5fd4f96 Optimization and potentially prevend leaving the repo in a broken state 2024-12-23 18:49:00 +01:00
cc3d0d61dd Consistent naming scheme 'eject' -> 'own' 2024-12-23 18:34:42 +01:00
4403f00274 Reneame the 'eject-file' command to 'own' 2024-12-23 17:55:40 +01:00
eddfb8e634 Bump version 2024-12-22 21:58:07 +01:00
4f2790f6d3 Fixes on the admin initialization cmd 2024-12-22 21:57:52 +01:00
96690e1354 Generate the postinstall script as the first entry of the package.json 2024-12-22 21:25:51 +01:00
982f216a01 Do not take into account react in the resolution of ui modules peer dependencies for supporting React 19 2024-12-22 21:21:20 +01:00
13c21e8910 Implement initialize-admin-theme command 2024-12-22 17:09:15 +01:00
94b7d2b85b Bump version 2024-12-21 19:40:07 +01:00
9a4f89e69d Sort out version of keycloak not supported depending on which theme is implemented (start-keycloak cmd) 2024-12-21 19:39:10 +01:00
a5ba03cca0 Reload when .properties files are updated 2024-12-21 19:02:13 +01:00
5203813e7b Rebuild the theme when properties files changes 2024-12-21 15:22:19 +01:00
0e461fd072 Fix bugs in svg assets commenting for mirror files 2024-12-21 14:25:47 +01:00
326411ca5d Correctly generate i18n messages for admin UI 2024-12-21 12:09:29 +01:00
c39c450e90 Support generating eject comments for .svg files 2024-12-20 13:22:15 +01:00
3191954dda Support generating eject comment for .properties file 2024-12-20 13:03:58 +01:00
20c6d2ea86 Bump version 2024-12-19 19:07:24 +01:00
f43544e134 ensure no diff if config hasn't changed 2024-12-19 19:06:54 +01:00
474a863708 Correctly patch the security-admin-console client so that it can be run with HMR 2024-12-18 20:57:42 +01:00
0bacdca8fe Bump version 2024-12-17 18:04:22 +01:00
f023d6bca7 Fixes windows issues #747 2024-12-17 18:04:06 +01:00
150b01f1f3 Bump version 2024-12-17 10:44:38 +01:00
2b2bb20658 #746 2024-12-17 10:44:24 +01:00
70570faed6 Bump version 2024-12-16 18:04:01 +01:00
5d3b7c9a82 Add necessary token claim to access admin in dev mode 2024-12-16 18:04:01 +01:00
95b9b12a3b Try to fix error on windows 2024-12-16 18:04:01 +01:00
0e027055cb Bump version 2024-12-15 19:48:42 +01:00
e47b002535 #744 2024-12-15 19:48:23 +01:00
8dd6dcd1fc Merge pull request #745 from keycloakify/keycloak_config_persistance
Keycloak config persistance
2024-12-15 19:45:52 +01:00
10cfa1cf41 Update default realm configs 2024-12-15 19:45:05 +01:00
3938584aeb Update default realm configs 2024-12-15 18:43:53 +01:00
163b060dc5 Additional teaks 2024-12-15 18:15:36 +01:00
67f8ae41fc Update prepare realm script 2024-12-15 17:42:45 +01:00
b6e9fe2585 Update default realm config for kc 26 2024-12-15 13:28:05 +01:00
5b83bd8fa9 Update dump realm local script 2024-12-15 13:27:49 +01:00
d0f43b6318 Add logging and debug for backup configuration process 2024-12-15 13:11:01 +01:00
df338ed6a0 Improve ordering to minimize diff 2024-12-15 12:28:09 +01:00
295994d02a Use KC_BOOTSTRAP_ADMIN_ in newer keycloak 2024-12-15 11:57:45 +01:00
f9e15f93c4 Fix spelling mistake 2024-12-15 11:49:33 +01:00
2659cf391c Fix schema validation error 2024-12-15 11:47:59 +01:00
76416ddd5b Put persisted realm configs in .keycloakify 2024-12-15 11:45:00 +01:00
8e8a0ccf54 Store https://my-theme.keycloakify.dev as a constant 2024-12-15 11:38:50 +01:00
db0ec954df Fix zod schema error 2024-12-15 11:34:41 +01:00
dc942aa5de Implement cache for fetching available docker images tags 2024-12-15 08:53:54 +01:00
029cfcb591 Fix fetching of keycloak versions 2024-12-14 18:37:54 +01:00
b1b6919395 Assuming latest supported 2024-12-14 14:44:30 +01:00
9185740d35 Keycloak config persistance implemented (to test) 2024-12-14 14:36:11 +01:00
8d59fe7b67 Change structure 2024-12-13 12:16:41 +01:00
92b505dd56 Load custom extention for logging realm change 2024-12-13 12:07:21 +01:00
c0e6661d3d Add function to dump the realm config 2024-12-13 11:31:01 +01:00
0cae2c68d8 Add utils to edit the realm 2024-12-13 09:07:11 +01:00
1e43343529 Update keycloak 26 realm default config (fmt) 2024-12-12 11:19:06 +01:00
0a74dca7c2 Prettier ignore realm default config 2024-12-12 11:16:01 +01:00
a66a373256 Update dump-keycloak-realm internal script https://github.com/keycloak/keycloak/issues/33800 2024-12-10 04:12:56 +01:00
606cf7ad02 Bump version 2024-12-09 05:08:57 +01:00
5225749c7b React 19 compat #741 2024-12-09 05:06:47 +01:00
819e3833ad Bump version 2024-12-08 19:43:00 +01:00
b0ba37fcc4 Smarter appBuild script 2024-12-08 19:42:43 +01:00
f4829b557f Bump version 2024-12-06 00:38:23 +01:00
60a9b5a693 Improve i18n api typing 2024-12-06 00:38:09 +01:00
c323b94a8c Bump version 2024-12-04 00:09:14 +01:00
4bbc0241ec Do not crash when parser can't be inferred 2024-12-04 00:04:49 +01:00
5a7dacfcdd Bump version 2024-12-02 00:41:30 +01:00
7e05e1bf0c Use random port for dev server 2024-12-02 00:41:12 +01:00
1530ca32c8 Bump version 2024-12-01 00:07:28 +01:00
ed054f131a Merge pull request #736 from keycloakify/hmr_in_start_keycloak
Implement hot module replacement for developing Account SPA and Admin UI
2024-12-01 00:01:57 +01:00
ec74ceef4d Implement hot module replacement for developing Account SPA and Admin UI 2024-11-30 23:55:24 +01:00
fd3261cdf1 Bump version 2024-11-25 11:41:36 +01:00
b4b53d2552 Re export wide type def of the kcContext 2024-11-25 11:41:17 +01:00
0371d9ea7a Bump version 2024-11-23 08:49:17 +01:00
73031e74ec Make it so it's not required to manually call the copy-keycloak-resources-to-public script even in webpack projects 2024-11-23 08:48:06 +01:00
f71ab4635f Bump version 2024-11-22 06:16:42 +01:00
983db6780a #730 2024-11-22 06:16:15 +01:00
ea22107b9b Bump version 2024-11-21 07:12:53 +01:00
8e4a7fed9e Fix invalid dom nesting errors 2024-11-21 07:12:23 +01:00
30efd8fcf4 Bump version 2024-11-21 06:27:09 +01:00
f4c4e92ca1 #726 2024-11-21 06:26:54 +01:00
cfda99f5b0 Bump version 2024-11-19 03:49:32 +01:00
5063b1c7ab Rename LoginDeviceVerifyUserCode to LoginOauth2DeviceVerifyUserCode (as it should have been) 2024-11-19 03:49:01 +01:00
955b6cac45 Remove noisy stories 2024-11-19 03:22:49 +01:00
1fa3d6133c Remove unused template prop 2024-11-19 03:22:35 +01:00
023939a064 Bump version 2024-11-18 21:38:59 +01:00
de4490cf0f Update tsafe 2024-11-18 21:38:27 +01:00
e0cda43724 Change pattern for telling if linked 2024-11-18 21:34:10 +01:00
d4ac67dba8 Bump version 2024-11-18 18:44:57 +01:00
23f4b59559 Merge pull request #725 from keycloakify/all-contributors/add-tripheo0412
docs: add tripheo0412 as a contributor for doc
2024-11-18 17:44:35 +00:00
4a7ba4a1c9 docs: update .all-contributorsrc [skip ci] 2024-11-18 17:27:30 +00:00
a3e765e1fc docs: update README.md [skip ci] 2024-11-18 17:27:29 +00:00
ee3614dbf1 Merge pull request #723 from keycloakify/all-contributors/add-zvn2060
docs: add zvn2060 as a contributor for code
2024-11-18 17:24:28 +00:00
8099ec1ffe docs: update .all-contributorsrc [skip ci] 2024-11-18 17:24:15 +00:00
2e2b0ab3ae docs: update README.md [skip ci] 2024-11-18 17:24:14 +00:00
a1f15f2f6b Merge pull request #722 from zvn2060/main
Fix #721: mismatched LoginPasskeysConditionalAuthenticate
2024-11-18 17:23:15 +00:00
8fe74fe7ee Fix #721: mismatched LoginPasskeysConditionalAuthenticate 2024-11-18 13:59:44 +08:00
232be50225 Bump version 2024-11-18 04:46:58 +01:00
a00bb0c4db Fix logical error 2024-11-18 04:46:29 +01:00
5517d6baf4 Make runPrettier work when project is linked 2024-11-18 04:46:06 +01:00
e2975503a4 Automatically untrack files implemented by ui modules 2024-11-18 03:47:57 +01:00
1231c92198 Update comment 2024-11-18 03:19:13 +01:00
64ca0bc0ca Bump version 2024-11-18 01:09:46 +01:00
2bceb9385c Fix: creating directory 2024-11-18 00:26:48 +01:00
954bc43c22 Bump version 2024-11-17 23:51:02 +01:00
8c99aa4b9d gererate diffrent comment depending on the file type 2024-11-17 23:50:49 +01:00
884195d30d Bump version 2024-11-17 23:21:44 +01:00
17dd726158 Fix passing wrong path to npmInstall 2024-11-17 23:20:24 +01:00
7ee30b6a42 Fix managed gitignore 2024-11-17 23:13:45 +01:00
397f8133bf Fix comment formatting 2024-11-17 23:08:52 +01:00
c9abc6dc5c Fix crawl async 2024-11-17 23:05:41 +01:00
e78eafd1f1 Fix 2024-11-17 20:53:38 +01:00
e50f2bd692 Bump version 2024-11-17 19:35:37 +01:00
ed0428bd55 Remove runFormat 2024-11-17 19:34:57 +01:00
2a126d65c5 Merge pull request #717 from keycloakify/admin_theme_support
Run prettier of a file per file basis (and prepare for extension support)
2024-11-17 18:34:17 +00:00
30149ff1f2 Remove ignored file that where removed 2024-11-17 19:33:44 +01:00
32f471624a Remove ignored file that where removed 2024-11-17 19:31:29 +01:00
7f5eabb639 Format page when ejecting for the account 2024-11-17 19:25:53 +01:00
32fb1e2f71 Fix runPrettier script 2024-11-17 19:22:34 +01:00
7c3c6d3643 Fix misnamed kc.gen 2024-11-17 16:46:25 +01:00
b6d2154d56 Fix usage of deprecated node api 2024-11-17 16:45:14 +01:00
b8d4daf4c1 Temporarely restore runFormat (for merge conflicts) 2024-11-17 03:38:38 +01:00
c03623875a The admin theme does not support traditional eject 2024-11-17 03:35:01 +01:00
c423e4cacc Adapt the npmInstall script so that it works when packages are linked 2024-11-17 03:24:41 +01:00
c593f5cb97 Re-sync version with main 2024-11-16 21:38:31 +01:00
2ad36a8137 Fix runPrettier 2024-11-16 21:38:31 +01:00
bccaddc2de Bump version 2024-11-13 14:38:48 +01:00
97c12c8a12 Merge pull request #720 from sukvvon/fix/add-versatility-in-runFormat
fix(runFormat.ts): improve filtering in scriptNames
2024-11-13 13:38:25 +00:00
b349a819ba fix(runFormat.ts): improve filtering in scriptNames 2024-11-13 21:49:55 +09:00
792d4262c8 Bump version 2024-11-10 14:30:00 +01:00
37a9046a40 Merge pull request #718 from kathari00/fix_storyname
fix story
2024-11-10 13:29:39 +00:00
5ad29d9f43 fix story 2024-11-10 10:33:21 +00:00
645031543e Merge branch 'main' into admin_theme_support 2024-11-10 09:39:26 +00:00
b43c02f279 Release candidate 2024-11-10 10:35:19 +01:00
63877d53be Eject file command implementation 2024-11-09 20:33:53 +01:00
79a580b4a5 Bump version 2024-11-09 19:50:48 +01:00
994f1f8d3d #714 #713 2024-11-09 19:50:29 +01:00
a73281d46d Checkpoint 2024-11-09 14:02:19 +01:00
a60a0d0696 checkpoint 2024-11-09 09:35:41 +01:00
a2ea81b3b8 Bump version 2024-11-06 10:10:55 +01:00
a0461e3ef0 #711 2024-11-06 10:10:27 +01:00
93fcf96cde checkpoint 2024-11-03 01:56:41 +01:00
d7455fd100 Only format kc-gen file 2024-11-03 00:25:28 +01:00
af7a45d125 checkpoint 2024-11-02 22:39:03 +01:00
5357626317 Bump version 2024-10-31 11:05:43 +01:00
552c95c59e https://github.com/keycloakify/keycloakify/pull/705#issuecomment-2448689532 2024-10-31 11:05:25 +01:00
50590697ca Bump version 2024-10-30 15:51:32 +01:00
e261736fa3 Fix: kcContext.scripts can be undefined in error.ftl 2024-10-30 15:51:16 +01:00
db37320280 up 2024-10-27 00:10:39 +02:00
263f55fdd3 Bump version 2024-10-26 22:07:54 +00:00
2b7f8a24a3 Fix import.meta.env.BASE_URL not being corectly replaced when build in windows 2024-10-26 22:07:29 +00:00
b0aa0feab5 up 2024-10-26 22:06:37 +02:00
0e93d4ed09 Implement admin theme support (checkpoint) 2024-10-26 21:23:18 +02:00
dc4eac1a04 Bump version 2024-10-25 04:12:20 +02:00
53a427d190 Delegate add sotry 2024-10-25 04:12:07 +02:00
ae969f91ac Bump version 2024-10-25 02:57:41 +02:00
c83319d6f3 Tell if we should update kcGen based on the hash 2024-10-25 02:57:26 +02:00
329b4cb0fb Enable to link in any keycloakify starter 2024-10-25 02:56:57 +02:00
533f5992d1 coding style fix 2024-10-25 02:44:19 +02:00
cb103cc3e2 Bump version 2024-10-25 00:21:15 +00:00
afdf89fb12 Try to run format on behaf of the user when generating new files with the CLI 2024-10-25 00:20:35 +00:00
26a87b8eaa Remove debug log 2024-10-25 00:01:39 +00:00
25d31463f4 Make script delegation work on windows 2024-10-25 00:01:12 +00:00
2542c38c9b Make linking script work on windows server 2024-10-24 23:47:34 +00:00
7326038424 Fix linking script on windows 2024-10-24 23:21:12 +00:00
12e632d221 More sensible patch for building on windows 2024-10-24 22:38:26 +00:00
3fc2108214 Fix build for windows 2024-10-24 16:56:13 +00:00
babbe39494 Merge pull request #701 from nima70/main
Removed Error Message from Terms and Conditions Page
2024-10-20 21:10:56 +02:00
32b4585e39 I removed the local=en 2024-10-20 13:49:10 -04:00
43469a869c I removed the error message for terms and conditions page 2024-10-20 12:35:14 -04:00
6f823e6478 Bump version 2024-10-20 13:08:59 +02:00
e33693e20e Merge pull request #700 from nima70/main
Storybooks Second Release: Login Page Stories with Improved Coverage
2024-10-20 13:02:27 +02:00
ad3f091d4a Merge pull request #698 from keycloakify/decouple_userprofile_logic
Decouple userprofile logic
2024-10-20 12:52:48 +02:00
3ff01d186d account page test coverage improved 2024-10-19 19:10:32 -04:00
0cf8caa53b storybooks second release 2024-10-19 17:24:29 -04:00
25920c208d Release candidate 2024-10-19 22:33:34 +02:00
19da96113f Don't export internals 2024-10-19 22:33:34 +02:00
6e584e809e Merge branch 'main' into decouple_userprofile_logic 2024-10-19 22:29:48 +02:00
4185188a5b Release candidate 2024-10-19 22:28:10 +02:00
4273322ed5 docs: update .all-contributorsrc [skip ci] 2024-10-19 22:27:30 +02:00
ba0532c95d docs: update README.md [skip ci] 2024-10-19 22:27:30 +02:00
3a2fe597ba Bump version 2024-10-19 22:27:30 +02:00
dda77952a0 #694 Probably some shell handle double quote differently 2024-10-19 22:27:30 +02:00
d2e518d96b #693 #692 2024-10-19 22:27:30 +02:00
f3a97b2538 Bump version 2024-10-19 22:27:29 +02:00
cacd017244 #696 2024-10-19 22:27:29 +02:00
f5b15a5ef6 Fix Phase two links 2024-10-19 22:27:29 +02:00
de620dca56 Fix light mode rendering 2024-10-19 22:27:29 +02:00
8decf4a3c9 Add phaseTwo as sponsor 2024-10-19 22:27:29 +02:00
831326952b Resize zone2 logo 2024-10-19 22:27:29 +02:00
27da578446 Bump version 2024-10-19 22:27:29 +02:00
2c1cca168f Resolve package.json path relative to the package.json 2024-10-19 22:27:29 +02:00
e498fb784b Bump version 2024-10-19 22:27:29 +02:00
2917719315 Add dir=rtl attribut to html when using a RTL language 2024-10-19 22:27:29 +02:00
9ed90995e4 typesafety fix 2024-10-19 22:27:29 +02:00
0f99bb5bdc fix: added parameter type for story context on register page 2024-10-19 22:27:29 +02:00
1f4d4473e4 Bump version 2024-10-19 22:27:29 +02:00
5332001ff4 docs: update .all-contributorsrc [skip ci] 2024-10-19 22:27:29 +02:00
22241fd7ad docs: update README.md [skip ci] 2024-10-19 22:27:29 +02:00
ddeade9775 Changes:
- First draft of test coverage improvement for storybooks
- code's page html rendering issue fixed
2024-10-19 22:27:29 +02:00
f1cb165bdd Bump version 2024-10-19 22:27:29 +02:00
9873353990 Fix initialize-email-theme 2024-10-19 22:27:29 +02:00
b879569b81 Announcement about Keycloak 26 2024-10-19 22:27:29 +02:00
c3e821088b Bump version 2024-10-19 22:27:29 +02:00
dc4f386e7a Fix vite quitting if custom handler implemented 2024-10-19 22:27:29 +02:00
a40810b364 Bump version 2024-10-19 22:27:29 +02:00
1690629717 Fix: check for delegation of the eject-page command 2024-10-19 22:27:29 +02:00
9a6a71c8bc Fix litle inconsistency 2024-10-19 22:27:29 +02:00
d626699f08 Bump version 2024-10-19 22:27:29 +02:00
6aa60e685b Release candidate 2024-10-19 22:27:29 +02:00
9910762abc Add initialize-email-theme, initialize-account-theme and copy-keycloak-resources-to-public to commands that can be delegated to a custom handler 2024-10-19 22:27:29 +02:00
182fb430f1 Fix dead code 2024-10-19 22:27:29 +02:00
bda20e2fbe Release candidate 2024-10-19 22:27:29 +02:00
bc586eceef Make sure the update-kc-gen command is delegated when building with vite 2024-10-19 22:27:29 +02:00
128b27221a Release candidate 2024-10-19 22:27:29 +02:00
2dfb4eda9d No need to handle non react environement with custom handler support 2024-10-19 22:27:29 +02:00
fed6af4dfe Release candidate 2024-10-19 22:27:29 +02:00
c4ee6cd85c Fix not handling correctly exit cause 2024-10-19 22:27:29 +02:00
8fc307bd8d Release candidate 2024-10-19 22:27:29 +02:00
9e9ffcd586 add debug logs 2024-10-19 22:27:29 +02:00
49b064b5f2 Release candidate 2024-10-19 22:27:29 +02:00
ef6f5a4c23 Add other missing declaration files 2024-10-19 22:27:28 +02:00
e92562fd44 Release candidate 2024-10-19 22:27:28 +02:00
fe65ddb5f8 Fix missing exports 2024-10-19 22:27:28 +02:00
ffd405c6db Release candidate 2024-10-19 22:27:28 +02:00
9e41868e0d Implement custom handler cli hook 2024-10-19 22:27:28 +02:00
ca6accc889 Bump version 2024-10-19 22:27:28 +02:00
dfe2e1562a Fix cache issue 2024-10-19 22:27:28 +02:00
ab43bb73d7 Bump version 2024-10-19 22:27:28 +02:00
22b0b95e54 Update readme, support keycloak 26 2024-10-19 22:27:28 +02:00
290ad8b592 Update version ranges for Multi-Page account theme 2024-10-19 22:27:28 +02:00
d5519dbb55 Release candidate 2024-10-19 22:27:28 +02:00
4de9e059e9 Aditional context exclusion 2024-10-19 22:27:28 +02:00
e573aff6ae Release candidate 2024-10-19 22:27:28 +02:00
908e083dee Update version target range 2024-10-19 22:27:28 +02:00
ec29724997 Fix link in CONTRIBUTING.md 2024-10-19 22:27:28 +02:00
88756e9807 Bump version 2024-10-19 22:27:28 +02:00
80d8a0c4e3 ['select-radiobuttons'/'multiselect-checkboxes'] fixed 'inputOptionLabels' 2024-10-19 22:27:28 +02:00
7241f0c741 Bump version 2024-10-19 22:27:28 +02:00
8565eb3fb8 Update tsafe 2024-10-19 22:27:28 +02:00
87198f6e56 docs: update .all-contributorsrc [skip ci] 2024-10-19 22:27:28 +02:00
fa934da442 docs: update README.md [skip ci] 2024-10-19 22:27:28 +02:00
6c4dc711d2 Put Kathi as first contributor 2024-10-19 22:27:28 +02:00
1f2a755a97 docs: update .all-contributorsrc [skip ci] 2024-10-19 22:27:28 +02:00
a0e3dc163a docs: update README.md [skip ci] 2024-10-19 22:27:28 +02:00
810dc6ceb5 Bump version 2024-10-19 22:27:28 +02:00
7203c742be Avoid modifying BASE_URL for App context 2024-10-19 22:27:28 +02:00
2fd04cfb61 Bump version 2024-10-19 22:27:28 +02:00
9c44d13f73 Update tsafe (provide ESM distribution) 2024-10-19 22:27:27 +02:00
d6436a58a2 update ci 2024-10-19 22:27:27 +02:00
613167f3a6 Bump version 2024-10-19 22:27:27 +02:00
ab0c281d98 Fix allegated vulnerability 2024-10-19 22:27:27 +02:00
c84dc281a2 Bump version 2024-10-19 22:27:27 +02:00
835833a61b Remove unessesary reference to react specific construct in KcContext 2024-10-19 22:27:27 +02:00
9af542ec89 Bump version 2024-10-19 22:27:27 +02:00
06e33196bb Refactor: Make ClassKey importable without having react as a dependency 2024-10-19 22:27:27 +02:00
36dd324139 complete decoupling of user profile form validation logic 2024-10-19 10:18:22 +02:00
52d4fe920c Merge pull request #697 from keycloakify/all-contributors/add-marvinruder
docs: add marvinruder as a contributor for bug
2024-10-19 03:21:05 +02:00
0d090d50d4 Bump version 2024-10-19 02:28:32 +02:00
e57232edde #694 Probably some shell handle double quote differently 2024-10-19 02:28:11 +02:00
dfe1e7ddd1 docs: update .all-contributorsrc [skip ci] 2024-10-19 00:24:37 +00:00
5ffc42c9db docs: update README.md [skip ci] 2024-10-19 00:24:36 +00:00
c63648a1b0 #693 #692 2024-10-19 02:19:41 +02:00
80fd4095c4 Bump version 2024-10-17 23:23:52 +02:00
31d7a938f2 #696 2024-10-17 23:23:26 +02:00
ee1b6868f8 Fix Phase two links 2024-10-17 19:54:14 +02:00
7c7e5544e4 Fix light mode rendering 2024-10-16 05:13:52 +02:00
06fe26fbe7 Add phaseTwo as sponsor 2024-10-16 04:10:17 +02:00
c932c7d8f6 Resize zone2 logo 2024-10-16 03:37:00 +02:00
d56c536446 Bump version 2024-10-13 00:55:23 +02:00
f5a9a28124 Resolve package.json path relative to the package.json 2024-10-13 00:55:06 +02:00
b86039536e Bump version 2024-10-12 17:33:44 +02:00
59c4675e8a Add dir=rtl attribut to html when using a RTL language 2024-10-12 17:30:30 +02:00
fbf6a329df typesafety fix 2024-10-11 23:55:07 +02:00
ddec3118a4 Merge pull request #688 from keycloakify/fix/missing-type-register-story
fix: added parameter type for story context on register page
2024-10-08 23:58:06 +02:00
94e2786297 fix: added parameter type for story context on register page 2024-10-08 16:50:11 -05:00
ecfdff5454 Bump version 2024-10-07 23:53:05 +02:00
c598a58ec9 Merge pull request #685 from keycloakify/all-contributors/add-nima70
docs: add nima70 as a contributor for code, and test
2024-10-07 23:47:25 +02:00
3e38beb190 docs: update .all-contributorsrc [skip ci] 2024-10-07 21:47:11 +00:00
61a86e8e82 docs: update README.md [skip ci] 2024-10-07 21:47:10 +00:00
9c8e127fa0 Merge pull request #672 from nima70/main
Changes:
2024-10-07 23:46:13 +02:00
eb23b40e5c Bump version 2024-10-07 21:03:04 +02:00
9e19aafcd0 Fix initialize-email-theme 2024-10-07 21:02:51 +02:00
4cdf26b6f9 Announcement about Keycloak 26 2024-10-07 20:56:03 +02:00
88de58cc22 Bump version 2024-10-06 22:56:42 +02:00
f6b48c88b9 Fix vite quitting if custom handler implemented 2024-10-06 22:55:18 +02:00
12534e57ad Bump version 2024-10-06 22:09:21 +02:00
52e33bba2d Fix: check for delegation of the eject-page command 2024-10-06 22:08:43 +02:00
d63e5f4e54 Fix litle inconsistency 2024-10-06 15:37:32 +02:00
8d31866a0b Bump version 2024-10-06 15:09:53 +02:00
7d818f217a Merge pull request #683 from keycloakify/feat_custom_handler
Feat custom handler
2024-10-06 15:09:26 +02:00
7156665684 Release candidate 2024-10-06 13:19:12 +02:00
5045c5e8bf Add initialize-email-theme, initialize-account-theme and copy-keycloak-resources-to-public to commands that can be delegated to a custom handler 2024-10-06 13:18:30 +02:00
9de2ed9eaf Fix dead code 2024-10-06 12:44:46 +02:00
096cf7a570 Release candidate 2024-10-06 09:07:10 +02:00
a04f07d149 Make sure the update-kc-gen command is delegated when building with vite 2024-10-06 09:06:49 +02:00
63775b2866 Release candidate 2024-10-06 06:45:06 +02:00
e8609de7b4 No need to handle non react environement with custom handler support 2024-10-06 06:44:53 +02:00
e62aa89d72 Release candidate 2024-10-06 06:42:04 +02:00
77f12a940d Fix not handling correctly exit cause 2024-10-06 06:41:51 +02:00
0fe49e3d6e Release candidate 2024-10-05 22:29:13 +02:00
881386a123 add debug logs 2024-10-05 22:28:36 +02:00
7b9aec4ed0 Release candidate 2024-10-05 21:39:32 +02:00
cf18f9d06c Add other missing declaration files 2024-10-05 21:39:14 +02:00
052936f769 Release candidate 2024-10-05 21:23:57 +02:00
590de7a67b Fix missing exports 2024-10-05 21:23:17 +02:00
7f608ad8ad Release candidate 2024-10-05 20:31:41 +02:00
35b012b937 Implement custom handler cli hook 2024-10-05 20:30:09 +02:00
e3bd7f3bc5 Bump version 2024-10-04 16:56:17 +02:00
e14f187fc0 Fix cache issue 2024-10-04 16:56:02 +02:00
da495b90ae Bump version 2024-10-04 13:00:15 +02:00
8d9b80f549 Update readme, support keycloak 26 2024-10-04 12:59:56 +02:00
2e9da33622 Merge pull request #681 from keycloakify/keycloak-26
Update version target range
2024-10-04 12:58:50 +02:00
6f416ad335 Update version ranges for Multi-Page account theme 2024-10-04 12:58:31 +02:00
4e982ee898 Release candidate 2024-10-04 12:44:22 +02:00
bcb514ae9c Aditional context exclusion 2024-10-04 12:44:03 +02:00
cfdad8d71d Release candidate 2024-10-04 12:17:54 +02:00
39ad1eb8d1 Update version target range 2024-10-04 12:17:08 +02:00
3d1d2e316b Merge pull request #680 from pnzrr/pnzrr-patch-1
Fix link in CONTRIBUTING.md
2024-10-04 06:58:52 +02:00
dd217e8a46 Fix link in CONTRIBUTING.md 2024-10-03 21:04:02 -06:00
1339a96ea4 Bump version 2024-10-02 23:36:58 +02:00
616e834c90 Merge pull request #678 from johanjk/main
respect inputOptionLabels
2024-10-02 23:36:23 +02:00
80eaa77acc ['select-radiobuttons'/'multiselect-checkboxes'] fixed 'inputOptionLabels' 2024-10-02 16:16:16 +02:00
ce3135c83b Bump version 2024-10-02 13:44:22 +02:00
09abc73068 Update tsafe 2024-10-02 13:42:38 +02:00
037d623550 Merge pull request #676 from keycloakify/all-contributors/add-luca-peruzzo
docs: add luca-peruzzo as a contributor for code, and test
2024-10-02 11:05:58 +02:00
8c8d2fd6a8 docs: update .all-contributorsrc [skip ci] 2024-10-02 09:05:35 +00:00
153a99d63f docs: update README.md [skip ci] 2024-10-02 09:05:34 +00:00
939e3ca7ea Put Kathi as first contributor 2024-10-02 11:02:25 +02:00
a0dc7eeb7c Merge pull request #675 from keycloakify/all-contributors/add-kathari00
docs: add kathari00 as a contributor for code, test, and doc
2024-10-02 11:00:06 +02:00
c21d072231 docs: update .all-contributorsrc [skip ci] 2024-10-02 08:59:49 +00:00
2e10ec8073 docs: update README.md [skip ci] 2024-10-02 08:59:48 +00:00
1177d6770c Bump version 2024-10-01 11:59:39 +02:00
d492a393fe Merge pull request #674 from keycloakify/dont_touch_base_url
Avoid modifying BASE_URL for App context
2024-10-01 11:59:14 +02:00
77952337c5 Avoid modifying BASE_URL for App context 2024-10-01 11:52:40 +02:00
6716fcb881 Bump version 2024-09-30 18:10:26 +02:00
302fe8d7cd Update tsafe (provide ESM distribution) 2024-09-30 18:10:09 +02:00
2ea5e34e81 update ci 2024-09-30 17:57:41 +02:00
d7103b1ad9 Bump version 2024-09-30 11:49:33 +02:00
9f8a36fe93 Fix allegated vulnerability 2024-09-30 11:48:57 +02:00
47ca811878 Bump version 2024-09-30 01:22:49 +02:00
8cacb21f1b Remove unessesary reference to react specific construct in KcContext 2024-09-30 01:22:37 +02:00
a0c95207cf Bump version 2024-09-30 01:10:45 +02:00
da3023cf5e Refactor: Make ClassKey importable without having react as a dependency 2024-09-30 00:31:27 +02:00
5892cf2ba7 Decouple user profile form logic so it can be consumed in angular 2024-09-30 00:19:37 +02:00
c9d7fc1b6e Changes:
- First draft of test coverage improvement for storybooks
- code's page html rendering issue fixed
2024-09-29 04:35:02 -04:00
94779c3476 Bump version 2024-09-28 00:43:55 +02:00
802a6ab5ec Explicitely prohibit space and special character in theme names 2024-09-28 00:34:24 +02:00
04307c8226 Remove dead code 2024-09-28 00:17:17 +02:00
ff6b91b801 refactor 2024-09-28 00:05:19 +02:00
c8ca598465 Refactor 2024-09-27 23:45:14 +02:00
9444b897ee #669 2024-09-27 23:37:23 +02:00
3d1951b72c Merge together generateResourcesForMainTheme and generateResourcesForThemeVariant 2024-09-27 23:05:51 +02:00
acc27ae448 #668 2024-09-26 20:34:00 +02:00
e6993214ff Bump version 2024-09-25 10:26:46 +02:00
2f02a4379c Enable i18n in Single-Page account theme 2024-09-25 10:26:25 +02:00
b57d014e9a Bump version 2024-09-24 19:47:01 +02:00
f57f311aab Fix async io not awaited and don't crash if .ftl files does not exist for some reason 2024-09-24 19:47:01 +02:00
4f11415107 Merge pull request #667 from keycloakify/all-contributors/add-uchar
docs: add uchar as a contributor for test, and code
2024-09-23 05:00:23 +02:00
346fd7175f Only publish storybook if we are on main 2024-09-23 04:36:00 +02:00
7c02d77057 docs: update .all-contributorsrc [skip ci] 2024-09-23 02:31:26 +00:00
d3fd4b6bbf docs: update README.md [skip ci] 2024-09-23 02:31:25 +00:00
43ef527810 Bump version 2024-09-23 00:29:17 +02:00
a6032a1387 Merge pull request #653 from keycloakify/i18n_extraLanguages_and_perThemeVariantTranslations
Start implementing per theme variant translations and ability to add extra languages
2024-09-23 00:23:55 +02:00
23179cac53 Fix last bug 2024-09-23 00:19:34 +02:00
954c3319bb Fix bug in label resolution 2024-09-22 23:46:45 +02:00
eb6ec0275d Remove debug log 2024-09-22 22:53:31 +02:00
890f8bc2d5 Fix: Forget to create a dir before writing files 2024-09-22 22:53:13 +02:00
26b8dd9cda Improve intentionality 2024-09-22 22:48:31 +02:00
c07af8491c Complete statical parsing of withExtraLanguages 2024-09-22 22:46:56 +02:00
10d4da9fbf No need to escape since we sanitize 2024-09-22 22:18:24 +02:00
95e861099f Integrate kcSanitize 2024-09-22 20:41:18 +02:00
6dc51dfab3 Fix some bugs in the vendoring script 2024-09-22 20:21:07 +02:00
ddb0af1dcb Vendor dompurify, use isomorphic-dompurify only for tests 2024-09-22 20:12:11 +02:00
b6e9043d91 Reorganize kcSanitarize 2024-09-22 18:56:05 +02:00
7c553ee10d Restore package.json and yarn.lock 2024-09-22 18:29:29 +02:00
2a6b14adc6 Merge pull request #666 from uchar/fix/dangerouslySetInnerHTML
Fix/dangerously set inner html
2024-09-22 18:27:25 +02:00
159a5f60d0 Add missing scope in ftl template 2024-09-22 18:22:11 +02:00
08f03b3118 Merge branch 'main' into i18n_extraLanguages_and_perThemeVariantTranslations 2024-09-22 18:15:25 +02:00
f137960f96 Reneame useStylesAndScript to useInitialize 2024-09-22 18:12:46 +02:00
e5ab46727a Make the i18n API more type safe 2024-09-22 17:14:03 +02:00
8d2679b76e Progess in parsing of the extra languages provided by the user 2024-09-22 15:39:32 +02:00
b0b6b994ed Almost done, left to extract the extra language resources 2024-09-22 04:39:24 +02:00
bb163132fe Fix minor inconsistency 2024-09-21 23:42:59 +02:00
439bed2f24 Update account theme i18n.ts boilerplate 2024-09-21 23:41:08 +02:00
5a233d8878 Avoid too many types declaration indirections 2024-09-21 23:35:44 +02:00
20cdbb6185 Rename .create() by .build() for i18nBuilder 2024-09-21 23:21:15 +02:00
b3c4208e44 Rename i18nInitializer by i18nBuilder 2024-09-21 23:09:12 +02:00
8623037224 Various little adjustments relative to the new i18n API 2024-09-21 22:35:30 +02:00
e8d3d3d741 Automatically generate account i18n code 2024-09-21 21:44:14 +02:00
cc700f0ba0 Untrack account i18n, code will be generated automatically 2024-09-21 21:33:57 +02:00
801a5cce17 Enable to add label to extra message not in the default set 2024-09-21 21:33:04 +02:00
2a3ad58c18 Throw an error if providing translation for a language that is already supported 2024-09-21 18:17:43 +02:00
969744f4cb Complete runtime API implementation 2024-09-21 17:59:16 +02:00
40ebbdebeb Fix some type errors 2024-09-21 04:45:00 +02:00
eb64886dcf Generate LanguageTage.ts 2024-09-21 04:36:48 +02:00
81fc9d57bd remove async from sanitize 2024-09-18 18:37:17 +03:30
66b480f837 use textarea on client for decode 2024-09-18 11:13:49 +03:30
7e6a84ce19 Add more tests 2024-09-17 09:39:07 +03:30
68e7642827 Remove extra comment 2024-09-17 01:11:14 +03:30
b37c7ccc8a Merge with master 2024-09-17 01:01:45 +03:30
b7c9ba8ffd Merge branch 'main' of https://github.com/uchar/keycloakify into fix/dangerouslySetInnerHTML 2024-09-17 01:01:02 +03:30
c8a31c4b6a Add KCSantisizer 2024-09-17 00:56:46 +03:30
fb6f450bfe Bump version 2024-09-16 14:30:50 +02:00
9a97d86ff9 #638 #631 Follow up 2024-09-16 14:30:27 +02:00
a5e3ecb38b Bump version 2024-09-16 13:50:08 +02:00
9b22d94600 Remove previous .keycloakify dir 2024-09-16 13:49:54 +02:00
a42ddb959b Bump version 2024-09-16 13:38:05 +02:00
b4e94d3c00 Use keycloakify-dev-resources instead of .keycloakify 2024-09-16 13:37:29 +02:00
aad89a2001 Start implementing per theme variant translations and ability to add extra languages 2024-09-15 16:55:18 +02:00
07e4f99f80 Bump version 2024-09-13 12:47:01 +02:00
715562c750 Merge pull request #646 from BII-GmbH/main 2024-09-13 12:46:16 +02:00
ef4f4d8374 allow docker start script to work with podman 2024-09-13 10:58:52 +02:00
e15f13646c GitHub pages does not serve dotfile, patch storybook build #645 2024-09-10 19:31:03 +02:00
8677c17f29 #645 2024-09-10 19:01:20 +02:00
21ee42b5a4 Bump version 2024-09-10 10:41:35 +02:00
d20964ec94 Merge pull request #632 from keycloakify/passkey-conditional-authenticate
Passkey conditional authenticate
2024-09-10 10:40:57 +02:00
3155f5da66 registrationDisabled is optional 2024-09-10 10:12:33 +02:00
50e38b6a10 Complete rework of WebauthnRegister 2024-09-10 09:57:47 +02:00
72c31776d7 Update WebauthnAuthenticate 2024-09-09 08:49:59 +02:00
7456750828 Support the new recapcha without breaking for older keycloak 2024-09-09 08:25:00 +02:00
b8a08f0789 Social is now optional on the kcContext 2024-09-09 07:51:49 +02:00
28990a12da Fix LoginRecoveryAuthnCodeConfig 2024-09-09 07:39:45 +02:00
7e5abe8589 Fix LoginPasskesConditionalAuthenticate 2024-09-09 06:59:11 +02:00
1d57f4b4dc Include missing dependency file 2024-09-08 18:16:49 +02:00
9f875160ea Make template initialization not ejected by default 2024-09-08 17:31:55 +02:00
785ed095bc Repatriate keycloak v24 scripts 2024-09-08 14:41:45 +02:00
359e93a1ba Fix path error 2024-09-08 13:00:40 +02:00
ee6322aae4 Extract only required files 2024-09-08 12:52:29 +02:00
98d3d1967a Fix import error 2024-09-08 12:09:19 +02:00
01c3b148e6 Group all build time generated resource under a 'res' directory 2024-09-08 12:06:49 +02:00
77d3a5190d Refactor checkpoint 2024-09-08 12:00:07 +02:00
93c1c56279 Refactor checkpoint 2024-09-08 00:06:47 +02:00
8340608045 Merge branch 'main' into passkey-conditional-authenticate 2024-09-07 15:27:51 +02:00
5502a74994 Bump version 2024-09-05 01:23:13 +02:00
c5ef4c973b #598 2024-09-05 01:19:50 +02:00
dbae909903 Pulling the message resources of the account theme from the installed keycloak-account-ui version 2024-09-05 00:32:21 +02:00
74317a1f3c Remove extra "v" when initializing single page account theme 2024-09-05 00:31:33 +02:00
569e933f02 Release candidate 2024-09-02 03:37:36 +02:00
46c40d713a Fix broken path 2024-09-02 03:37:16 +02:00
f3602219f3 Release candidate 2024-09-02 03:27:09 +02:00
c6b52acf2f #631 2024-09-02 03:26:39 +02:00
7260589136 Always generates the pages for legacy keycloak no matter what. 2024-08-30 19:27:56 +02:00
b2e9ddaa4f Bump version 2024-08-30 15:35:45 +02:00
4338b3ecb7 #627 2024-08-30 15:35:21 +02:00
0f81d9f146 Update readme 2024-08-29 01:22:16 +02:00
9980b10a83 Bump version 2024-08-28 17:09:21 +02:00
6bfd388827 Fix bug package.json not properly updated when initializing the single page account theme with webpack 2024-08-28 17:08:11 +02:00
8203ed687b Bump version 2024-08-28 14:43:40 +02:00
f394e06e4d Fix bug in type validation for webpack when initializing the account theme 2024-08-28 14:43:25 +02:00
8db35a81da Bump version 2024-08-27 17:41:04 +02:00
2e0ebfcf58 https://github.com/keycloakify/keycloakify-starter/issues/25 2024-08-27 17:40:48 +02:00
51d2ff85e0 Bump version 2024-08-26 18:20:32 +02:00
8b54426b89 Fix invalid dom nesting 2024-08-26 18:20:32 +02:00
fa346c5b1f Merge pull request #621 from keycloakify/all-contributors/add-liamlows
docs: add liamlows as a contributor for code, and doc
2024-08-26 04:27:06 +02:00
d87788980d docs: update .all-contributorsrc [skip ci] 2024-08-26 02:26:54 +00:00
1e4319498c docs: update README.md [skip ci] 2024-08-26 02:26:53 +00:00
48501407fc Release v10 🎉 2024-08-26 04:05:40 +02:00
01cbdee2ca Release candidate 2024-08-25 19:02:33 +02:00
b70c0af0a9 Add users to provided realm configuration if none exists 2024-08-25 19:02:00 +02:00
dcaee9cb7f Release candidate 2024-08-25 03:19:58 +02:00
1d8b6c7792 Fix logical error in multivalued attributes 2024-08-25 03:19:34 +02:00
c98dbe84c6 Add missing space before the * 2024-08-25 02:54:46 +02:00
1785916d32 download and extract actually just for downloading and extracting 2024-08-24 23:15:54 +02:00
c6cf564842 Release candidate 2024-08-23 19:01:56 +02:00
380b739017 Don't pin the patch version in the docker tag 2024-08-23 19:01:37 +02:00
c3f3c55303 Release candidate 2024-08-23 18:45:56 +02:00
2c01018529 #618 2024-08-23 18:36:40 +02:00
dd2edf3013 Merge pull request #616 from keycloakify/all-contributors/add-oliviergoulet5
docs: add oliviergoulet5 as a contributor for code
2024-08-22 00:56:15 +02:00
7f3cdf9fac Release candidate 2024-08-22 00:55:39 +02:00
f75a91fbc1 docs: update .all-contributorsrc [skip ci] 2024-08-21 22:55:00 +00:00
f151086bb1 docs: update README.md [skip ci] 2024-08-21 22:54:59 +00:00
7c833e6f10 Merge pull request #615 from oliviergoulet5/fix-array-operations
Fix array comparison and improve type check
2024-08-22 00:53:48 +02:00
885e8314e8 Fix array comparison and type check 2024-08-21 17:13:06 -04:00
3bdd955ab6 Release candidate 2024-08-19 02:11:31 +02:00
9499587bad Fix formating bug of Docker command being run 2024-08-19 02:10:59 +02:00
0879ddba7c Release candidate 2024-08-19 00:25:54 +02:00
106a1dd4c7 Support parsing of the KC_HTTP_RELATIVE_PATH option 2024-08-19 00:25:41 +02:00
5580248bcd Release candidate 2024-08-19 00:00:22 +02:00
c9c10b8fba Fix issue with the port in the start-keycloak command 2024-08-19 00:00:08 +02:00
ed254922e9 Relase candidate 2024-08-18 23:46:12 +02:00
4b7d1e2cec Fix bug in docker command 2024-08-18 23:45:58 +02:00
775ae57258 Release candidate 2024-08-18 21:10:37 +02:00
96e4cd79ee Enable to configure the port via the build options 2024-08-18 21:10:18 +02:00
bb70f7df4f Release candidate 2024-08-18 20:56:34 +02:00
602de2e407 Fix bug with spaces in docker run command 2024-08-18 20:56:25 +02:00
225ced989c Release candidate 2024-08-18 19:20:57 +02:00
ab53698f34 Merge pull request #612 from keycloakify/extensions
keycloak start command options support in config
2024-08-18 19:20:31 +02:00
02f2124126 keycloak start command options support in config 2024-08-18 19:19:35 +02:00
66623e3324 Release candidate 2024-08-16 08:48:21 +02:00
4cc886fd04 Update misleading note in the readme 2024-08-16 08:48:06 +02:00
a10b490245 Release candidate 2024-08-15 22:40:34 +02:00
b947b8a00d Display name and displayNameHtml are always provided 2024-08-15 22:40:08 +02:00
60fa240a4d #611 2024-08-15 22:38:45 +02:00
e05cd87b7c Release candidate 2024-08-14 18:32:21 +02:00
8e41c905ed Add the icons to the social provider in the story 2024-08-14 18:31:55 +02:00
e21f607ab0 Merge pull request #609 from keycloakify/all-contributors/add-madmadson
docs: add madmadson as a contributor for code
2024-08-14 16:36:59 +02:00
34af5abb82 docs: update .all-contributorsrc [skip ci] 2024-08-14 14:36:45 +00:00
fc1cdb5dc9 docs: update README.md [skip ci] 2024-08-14 14:36:44 +00:00
069a0cc980 Release candidate 2024-08-14 16:34:45 +02:00
78363727e1 Add correct fetch options to octokit 2024-08-14 16:34:22 +02:00
23b16746f6 Release candidate 2024-08-14 15:48:37 +02:00
6edf9c3d15 Fix div duplication 2024-08-14 15:48:16 +02:00
2e371d2078 Fix linking script for windows 2024-08-14 07:11:16 +02:00
b70b478e25 Pin cheerio to a given version 2024-08-13 14:58:46 +02:00
97ad132086 Update to latest typescript v4 release 2024-08-13 09:31:23 +02:00
2c5c54bf46 Don't use default import for cheerio (prepare for v1) 2024-08-13 09:25:06 +02:00
c0ca078b43 Release candidate 2024-08-13 00:20:54 +02:00
53e94d04f6 Improve message related to pnpm dlx 2024-08-13 00:17:26 +02:00
dd198f9f06 Tell pepole they can explicitely provide the keycloak version 2024-08-13 00:17:26 +02:00
43f455f4d0 Provide the proxy options to oktokit 2024-08-13 00:17:26 +02:00
d9132ea5a5 Merge pull request #603 from keycloakify/debug_fetch_proxy
Debug fetch proxy
2024-08-07 19:28:04 +02:00
d5c7e2547b Release candidate 2024-08-07 19:01:15 +02:00
13b87de06c Remove debug log 2024-08-07 19:00:57 +02:00
83bdbb7a7e Release candidate 2024-08-07 16:07:25 +02:00
89320b8d51 Fix get proxy option 2024-08-07 16:07:07 +02:00
5fa9c3879c Release candidate 2024-08-07 11:48:02 +02:00
c0cd76d40e Debug log for proxy config 2024-08-07 11:46:05 +02:00
01f60f8013 Release candidate 2024-08-07 07:51:07 +02:00
91ad0712af Make defaultuser english in keycloak 25 2024-08-07 07:33:48 +02:00
2cb1b36725 Release candidate 2024-08-07 06:22:22 +02:00
67ce66765f Enable delete account in default Keycloak realm configuration 2024-08-07 06:21:59 +02:00
c8cc453942 Release candidate 2024-08-06 06:42:02 +02:00
3f835f152f #602 2024-08-06 06:41:25 +02:00
35e8a853e0 Release candidate 2024-08-02 14:09:29 +02:00
d084a4bf4a Fix bug spaces in path keycloak-start 2024-08-02 14:09:09 +02:00
2a6b79e097 Release candidate 2024-07-31 18:46:10 +02:00
5d786c922f Enable the errors to be displayed immediately and not after focus is lost 2024-07-31 18:45:48 +02:00
26bd5dd534 Release candidate 2024-07-31 11:57:38 +02:00
b4df0ce52c Set the default user locale to english 2024-07-31 11:57:13 +02:00
386a8d7cd7 Rework the storybook 2024-07-29 05:12:31 +02:00
5221fb3479 Prevent reload loop in storybook 2024-07-29 02:48:57 +02:00
2871f63f25 Mention account Single Page in the storybook 2024-07-29 00:29:36 +02:00
4c282d0559 Release candidate 2024-07-28 20:01:27 +02:00
4ac14dc074 Prevent exposing too much information in the kcContext.realm of the single page account UI 2024-07-28 20:01:11 +02:00
fcdbb04ea6 Do not make select theme type when there's only one option 2024-07-28 19:37:15 +02:00
14f283cf49 Do not enable to add story when single page account theme 2024-07-28 19:33:27 +02:00
efc459663a Adapt eject-page for Single-Page account ui 2024-07-28 19:24:00 +02:00
d459aaf943 Add hint on how to enable 2024-07-28 18:32:22 +02:00
921c7d5441 Restore the CI setup for main 2024-07-27 18:03:15 +02:00
7d7e648968 Merge pull request #538 from keycloakify/keycloak_24
Keycloakify v10 (Keycloak v24 & 25 support and much more)
2024-07-27 17:56:06 +02:00
96fc779ec8 Release candidate 2024-07-27 17:50:58 +02:00
9605e17e96 Fix generaion of entrypoint 2024-07-27 17:50:31 +02:00
111c1675f9 Release candidate 2024-07-27 01:14:03 +02:00
d547ec3126 #596 2024-07-27 01:13:47 +02:00
0ce6a7be7f #597 2024-07-27 01:07:04 +02:00
1e5eae69e9 Update README.md 2024-07-27 00:52:57 +02:00
89d9208f44 Fix storybook build 2024-07-27 00:44:59 +02:00
3e80aaf242 Fix vitest setup 2024-07-25 20:03:03 +02:00
86c3159ded Release candidate 2024-07-25 19:58:24 +02:00
230e05abc0 Fix tsconfig exclusion 2024-07-25 19:53:36 +02:00
ff2e6e6432 Fix spelling in directory structure 2024-07-25 19:53:36 +02:00
dc00be9be6 Don't run npm install when linked 2024-07-25 19:53:36 +02:00
77249d8a58 Feat: Initialize account theme (before debug) 2024-07-25 19:53:36 +02:00
b9ee0afe7f Fix bug in download and extract archive 2024-07-25 19:53:36 +02:00
db23ab0bc2 Introduce build option: accountThemeImplementation 2024-07-25 19:53:36 +02:00
13dc47533c Release candidate 2024-07-24 17:00:11 +02:00
0091a888bc #594 2024-07-24 16:59:54 +02:00
724b585004 Release candidate 2024-07-23 14:58:54 +02:00
c0d127e4f4 #593 2024-07-23 14:58:39 +02:00
1638577d98 Update sponsors section 2024-07-20 12:37:05 +02:00
951c202fd0 Merge pull request #589 from lokmeinmatz/keycloak_24
fix: Typo InputFiledByType to InputFieldByType
2024-07-17 14:30:17 +02:00
a578b86715 Release candidate 2024-07-17 13:58:55 +02:00
b6b384854e Remove tsafe usage from ejectable page 2024-07-17 13:58:39 +02:00
dac937060d fix: Typo InputFiledByType to InputFieldByType 2024-07-17 09:35:59 +02:00
c628183773 Release candidate 2024-07-14 17:55:02 +02:00
eaacaa6966 (BREAKING CHANGE) When classes are overloaded disable default paterlyfly classes 2024-07-14 17:54:44 +02:00
9a09e280c9 Release candidate 2024-07-14 17:45:54 +02:00
70ac07d861 css replace: Don't choke on parenthesis in urls 2024-07-14 17:45:34 +02:00
dabe372360 Release candidate 2024-07-14 16:58:51 +02:00
d8e3fdeb14 Always use quotes in CSS urls 2024-07-14 16:58:35 +02:00
a147084458 Release candidate 2024-07-14 08:39:36 +02:00
b25e171412 Add the CLEAR special class to remove Paterlyfly classes 2024-07-14 08:39:19 +02:00
60aaa03202 Annotate i18n nodes 2024-07-14 08:11:17 +02:00
3392ab8385 Release candidate 2024-07-13 19:34:23 +02:00
f172b94467 Use uppercase for constants 2024-07-13 19:33:59 +02:00
ca549fe8d8 There's no need to decodeHtmlEntities on everything 2024-07-13 19:02:13 +02:00
0b6f56a774 Naming convention consistency 2024-07-13 18:42:27 +02:00
e5bcff12cb Exclude ream.attributes since it's never used 2024-07-13 18:29:30 +02:00
2754900f7a Refactor of the FreeMarker template 2024-07-13 18:17:21 +02:00
54f43d3331 Remove debug message 2024-07-13 11:02:12 +02:00
4900200c06 Release candidate 2024-07-13 09:27:10 +02:00
6d82a74db4 Improve incremental build time 2024-07-13 09:26:48 +02:00
2d7f21b021 Release candidate 2024-07-13 09:07:36 +02:00
4292c0c642 Rework i18n 2024-07-13 09:07:11 +02:00
9dca515a42 Add group annotations to Attribute 2024-07-13 08:00:16 +02:00
b577cd9829 Update storybook story SelectAuthenticator.stories.ts 2024-07-11 18:23:08 +02:00
704682cbbe Release candidate 2024-07-11 17:58:41 +02:00
858f0d77c0 #585 2024-07-11 17:58:26 +02:00
31ef6063f2 Add missing font and optimize keycloak theme resources extraction 2024-07-11 17:49:58 +02:00
f3bd81c55b Release candidate 2024-07-10 22:18:46 +02:00
24bb4902c2 Includes in the kcContext missing realm defined translations #582 2024-07-10 22:18:24 +02:00
ca7821cfad Remove unused import 2024-07-10 01:26:55 +02:00
a73b25580e Release candidate 2024-07-09 15:00:21 +02:00
82e179730e Fix error related to npm config get 2024-07-09 14:59:59 +02:00
b5d5002061 Mock kcContext.url.resourcePath for account v3 2024-07-09 14:04:25 +02:00
2ab2c9e05e Release candidate 2024-07-08 15:21:23 +02:00
b1e9ba3ac6 Fix buil when paths with spaces 2024-07-08 15:21:03 +02:00
5822ed0185 Relase candidate 2024-07-08 14:54:37 +02:00
17b295788d Generate i18n messages for account v3 2024-07-08 14:54:09 +02:00
6cd5b958c7 Fix typo 2024-07-08 00:21:40 +02:00
df92cc5f73 Fix 2024-07-08 00:21:31 +02:00
03106cdee3 Release candidate 2024-07-07 18:45:38 +02:00
c4638daf1b Support building account v3 2024-07-07 18:45:14 +02:00
e2f5eb79ad Release candidate 2024-07-05 19:55:14 +02:00
b6c8e9bca0 Remove debug console log 2024-07-05 19:55:02 +02:00
573839019e Release candidate 2024-07-04 20:00:03 +02:00
815bf10ae0 Add line break 2024-07-04 19:59:28 +02:00
7c257d97a7 #577 2024-07-04 19:53:57 +02:00
59f8814660 Add missing patternfly image 2024-07-04 19:26:48 +02:00
1a6993099f Release candidate 2024-07-01 19:15:01 +02:00
f62ded3c8e Fix saml-post-form.ftl storybook 2024-07-01 19:15:01 +02:00
4eca6366cc Merge branch 'main' into keycloak_24 2024-06-30 07:13:20 +00:00
51a45b355d Remove script only used in CI 2024-06-29 19:41:36 +02:00
e5765cb902 Release candidate 2024-06-29 19:38:57 +02:00
6e922d2033 Merge pull request #575 from keycloakify/fix/keycloak_24-added-applications-story
Added & Fixed Applications Page Under Account
2024-06-29 17:36:55 +00:00
5d1695ada8 fix: added in applications.ftl story and fixed issue with double comma when realmRolesAvailable and resourceRolesAvailable both present 2024-06-28 16:36:56 -05:00
6e3ce29067 Relase candidate 2024-06-28 19:03:37 +02:00
2b9bbc4cef Ensure pnpm dlx isn't used 2024-06-28 19:03:19 +02:00
9557145f72 Bump version 2024-06-28 19:01:13 +02:00
249877b9c5 Better wording for assertNoPnpmDlx 2024-06-28 19:01:00 +02:00
ff2321fde5 Bump version 2024-06-28 18:51:06 +02:00
1edd6e4193 No pnpm dlx 2024-06-28 18:50:45 +02:00
c7d47f128e Release candidate 2024-06-28 07:16:39 +02:00
14cb07efb2 Make terms acceptance a required field on the Register page 2024-06-28 07:16:17 +02:00
a51724208c Release candidate 2024-06-28 06:47:02 +02:00
050e2b2b99 Improve Register page default stories 2024-06-28 06:46:26 +02:00
3706f15f7e Fix bug resolving user profile translations 2024-06-28 06:46:12 +02:00
bdde9162d9 Merge pull request #572 from keycloakify/fix/readme-update-discord
Fix/readme update discord
2024-06-25 16:38:31 -05:00
99b4933536 Merge branch 'main' into fix/readme-update-discord 2024-06-25 16:32:59 -05:00
c5caf7e0da fix: fix for previous discord readme addition, forgot the <a> tag 2024-06-25 16:32:21 -05:00
bcc5308cfb Merge pull request #571 from keycloakify/fix/readme-update-discord
Modified Readme.md to have a more visible discord invitation link
2024-06-25 21:29:50 +00:00
9fb902db5c fix: modified the readme using a slightly more visible discord invitation link 2024-06-25 16:27:22 -05:00
7461e38034 Release candidate 2024-06-25 22:51:21 +02:00
dccd85a151 Fix readExtraPage 2024-06-25 22:51:07 +02:00
910604fdad Use vite template by default 2024-06-25 22:50:51 +02:00
508cb9158e Release candidate 2024-06-24 03:58:55 +02:00
915c500d32 Feedback when running keycloakfy build 2024-06-24 03:58:42 +02:00
60bd6621c8 Release candidate 2024-06-24 02:43:03 +02:00
b5f6262763 Support cd in running build script in webpack 2024-06-24 02:42:44 +02:00
2b8c4422de Release candidate 2024-06-23 22:48:01 +02:00
a686432c65 Shell: true for windows 2024-06-23 22:47:45 +02:00
449e625877 Fix storybook build 2024-06-23 22:39:29 +02:00
1ac07dafde Release candidate 2024-06-23 21:23:44 +02:00
3878e28b56 Improve monorepo project support, work if there only a package.json at the root (like NX) 2024-06-23 21:23:06 +02:00
cf6bc8666b Include fsevents.node in npm bundle 2024-06-23 21:10:11 +02:00
f76063eb40 Make it easier to link to another starter 2024-06-23 20:54:08 +02:00
ed52c5824d Give immediate feedback if projectDirPath is wrong 2024-06-23 16:56:24 +02:00
9333400322 Remove unused buildContext prop 2024-06-23 02:07:34 +02:00
3689cfcc0d Consistency 2024-06-23 02:06:45 +02:00
b73eceb535 Release candidate 2024-06-23 00:46:01 +02:00
5dc3453fc9 Enable user profile in default keycloak 23 configuration 2024-06-23 00:45:26 +02:00
cef1139a4b Release candidate 2024-06-23 00:37:26 +02:00
ac96959947 Add missing fieldNames from synthetic user attributes 2024-06-23 00:37:06 +02:00
4d73d877ba move used defined exclusions down 2024-06-23 00:18:03 +02:00
9f1186302e Release candidate 2024-06-22 20:12:22 +02:00
319dcc0d15 Stable i18n messages across Keycloak versions 2024-06-22 20:12:02 +02:00
e99fdb8561 Log what file have changed when linking dynamically in starter 2024-06-22 20:11:34 +02:00
f37a342a63 Release candidate 2024-06-22 17:18:52 +02:00
09a039894d Remove React as peer dpendency so that Keycloakify can be more easily used in Vue and Angular projects 2024-06-22 17:18:08 +02:00
3efbb1a9fd Release candidate 2024-06-22 17:05:37 +02:00
920ee62ee3 Implement fallback to english for messages bundle provided via Keycloakify 2024-06-22 17:05:14 +02:00
1ace44fe31 Rename extraMessages -> messageBundle 2024-06-22 17:03:59 +02:00
a60f05415b Export fallback language tag ("en") as a constant 2024-06-22 17:03:44 +02:00
42c9d39e02 Release candidate 2024-06-22 17:01:48 +02:00
a8186f1ed9 Don't use tsafe directly in ejectable components 2024-06-22 17:01:45 +02:00
c2ff515a17 Enable termsText to be extended via local message bundle 2024-06-22 14:09:11 +02:00
960c3ba558 Release candidate 2024-06-22 02:53:51 +02:00
454a9cd01c Remove useDownloadTerms see: https://docs.keycloakify.dev/terms-and-conditions, remove react-markdown 2024-06-22 02:53:30 +02:00
7d42ce1c87 Release candidate 2024-06-21 22:07:50 +02:00
57f6f980cf Update terms storybook 2024-06-21 22:07:36 +02:00
8cba3aae2c Release candidate 2024-06-21 21:25:41 +02:00
01b32f78ed Allow to override termsText 2024-06-21 21:24:04 +02:00
b6066dfd5f Release candidate 2024-06-21 20:28:32 +02:00
3ad554ed59 #569 2024-06-21 20:28:14 +02:00
6aacc6361b Release candidate 2024-06-21 02:13:48 +02:00
638e4e6410 Set the terms to empty string when building 2024-06-21 02:13:31 +02:00
aa9b7cccc7 Rework Terms 2024-06-21 02:01:55 +02:00
41739c8528 Bump version 2024-06-20 04:28:33 +02:00
89b32dc7fc Fix wrong code snippet 2024-06-20 04:28:12 +02:00
44aec23251 Release candidate 2024-06-19 22:41:42 +02:00
12fd6160c5 Fix inline CSS in html 2024-06-19 22:41:25 +02:00
8819abc418 Release candidate 2024-06-19 03:56:13 +02:00
96b627095c https://github.com/adbayb/termost/pull/31 2024-06-19 03:52:57 +02:00
239f98aa9c fmt 2024-06-19 01:49:13 +00:00
f5d0511662 Update README.md 2024-06-19 03:36:00 +02:00
75582d2a26 Update README.md 2024-06-19 03:33:44 +02:00
dba004f924 Release candidate 2024-06-19 01:41:45 +02:00
5423a07c47 Patch CSS for Keycloak by using relative paths instead of css variables 2024-06-19 01:41:22 +02:00
aba725372e Release candidate 2024-06-18 22:41:08 +02:00
a61aa9dd5d Add missing fonts from the account theme's default assets 2024-06-18 16:41:09 +02:00
74349b20ce Adding missing font from default theme resources 2024-06-17 13:26:32 +02:00
09ab9a1c8f Fix storybook build 2024-06-17 13:03:39 +02:00
abfe5789a3 Publish new storybook 2024-06-17 12:53:06 +02:00
67ebac496d Release candidate 2024-06-17 00:07:53 +02:00
60a2bf173b Add missing base font face 2024-06-17 00:07:38 +02:00
4e03f07864 Do not includes all shared source, it's bundled already 2024-06-17 00:00:41 +02:00
aef1709d7f Release candidate 2024-06-16 18:27:37 +02:00
2f590f7be2 Add missing file in npm bundle 2024-06-16 18:27:18 +02:00
d5fa6ca89a Fix unit tests 2024-06-16 17:55:06 +02:00
8eaaffb25a Release candidate 2024-06-16 15:19:44 +02:00
28c5e2bab2 Rename use 'dist' instead of 'build' for basenameOfTheKeycloakifyResourcesDir 2024-06-16 15:19:27 +02:00
e212039f2c Release cadidate 2024-06-16 14:59:11 +02:00
99b0b67f77 Add publicDirpath option for webpack 2024-06-16 14:58:51 +02:00
6ec9ba3c01 Add version in build options 2024-06-16 14:53:18 +02:00
d7960a7dcf Release candidate 2024-06-16 14:05:38 +02:00
2a6e9af9c9 Enable to use an other directory than build/assets in webpack 2024-06-16 14:05:23 +02:00
327e4d1f90 Add doc link 2024-06-16 11:48:39 +02:00
fffadd7b9e Release candidate 2024-06-16 11:11:53 +02:00
aaaf0d2e77 Add missing declaration files 2024-06-16 11:11:35 +02:00
9f9a9b8c90 Release candidate 2024-06-16 02:30:09 +02:00
1f6edb3c0c Use the configured jar file basename if any 2024-06-16 02:19:56 +02:00
142efb4f99 Do leave artifact in the build directory when using start-keycloak 2024-06-16 01:41:47 +02:00
532655d2d5 Rename jarTargets -> keycloakVersionTargets 2024-06-16 01:34:06 +02:00
287edabd90 Enable to build only for specific keycloak version 2024-06-16 01:29:15 +02:00
7aaedbe2ce Release candidate 2024-06-15 17:40:51 +02:00
4cae1c673c Use getAlgorithmKey in account 2024-06-15 17:33:27 +02:00
8e01d836a9 Cherrypick what resource from the default theme we keep 2024-06-15 17:32:58 +02:00
f6dc8f0741 Memoize getImplementThemeTypes 2024-06-15 14:45:22 +02:00
3a976d08d2 Release candidate 2024-06-15 14:40:56 +02:00
50e83b1eb5 Only build for specific keycloak version in start-keycloak 2024-06-15 14:30:18 +02:00
61fbbb0b09 Refactor how we update META-INF and how we read what theme types are implemented 2024-06-15 14:23:35 +02:00
9e70e5c12e Suggest 'npm run' instead of 'yarn' to be more generic 2024-06-15 11:27:03 +02:00
69d9b64468 Use tsx instead of ts-node 2024-06-15 11:23:53 +02:00
0620d29880 spawn in shell in local scripts 2024-06-15 01:06:06 +02:00
b52dc74d9b Release candidate 2024-06-14 23:59:16 +02:00
a46aef2e7e Use shell for Window resolution of envs 2024-06-14 23:58:54 +02:00
736806a53d Relase candidate 2024-06-14 22:25:23 +02:00
f1475e5cdf Settle on calling the global 'kcContext' and reduce levels of indirections 2024-06-14 22:24:51 +02:00
d04724c70a fetchProxyOptions compatibility Window OS 2024-06-14 21:53:17 +02:00
bacaadc16d Remove dead file 2024-06-14 21:52:46 +02:00
c51dd235f0 Release candidate 2024-06-14 21:31:26 +02:00
92f2c9857e Fix the linking script 2024-06-14 21:31:03 +02:00
3998cc7f8b Fix for the linking script on windows OS 2024-06-14 20:45:52 +02:00
c126d080bc Make tests pass on windows OS 2024-06-14 19:06:48 +02:00
bc05f1714d Fix windows OS compatibility issue 2024-06-14 18:59:25 +02:00
e98becb94b Release candidate 2024-06-13 22:58:50 +02:00
250b94c8b5 Fix missing build option for webpack 2024-06-13 22:58:32 +02:00
47f03f6833 Improve stories 2024-06-13 00:47:18 +02:00
6e7ae48f78 Update sotry 2024-06-13 00:30:07 +02:00
526dbcc0e7 Improve stories 2024-06-12 23:22:21 +02:00
1abc5a5643 Release candidate 2024-06-12 23:11:46 +02:00
c81c350136 Improve mock and stories 2024-06-12 23:11:06 +02:00
f90dc8bc7e fix syntax error 2024-06-12 22:52:53 +02:00
072e22d072 Exclude kcContext.execution 2024-06-12 22:18:55 +02:00
59807c1bb0 Patch only required on the login page 2024-06-12 22:17:58 +02:00
7c19e1f1f7 Fix wrong condition for displaying error in the template 2024-06-12 21:38:48 +02:00
3b9f915f57 Fix logical error in generating pom file 2024-06-12 20:39:03 +02:00
d85cc530d4 remove debug log 2024-06-12 20:25:44 +02:00
2bb27c7642 More compact ftl output 2024-06-12 20:13:44 +02:00
e90e003204 Fully remove comments #542 2024-06-12 20:12:11 +02:00
b1e58e1add Refactor how userFromField is passed down to the client 2024-06-12 19:41:05 +02:00
0fd836314a Release candidate 2024-06-12 14:48:26 +02:00
0bc3f08cc1 Rename generateSrcMainResources -> generateResources 2024-06-12 14:48:08 +02:00
a78af5080a Fix environement variables all on the same line 2024-06-12 14:43:53 +02:00
074e465284 Release candidate 2024-06-12 12:02:13 +02:00
bc8165d0ae Fix usage of dirname instead of basename 2024-06-12 12:01:55 +02:00
ba8561d75a Release candidate 2024-06-12 10:50:13 +02:00
b2d381ba4b Apply the name of the theme in the preconfigured realm 2024-06-12 10:50:00 +02:00
d39353d332 Release candidate 2024-06-12 09:20:25 +02:00
ee916af48e Provide default message for the info page 2024-06-12 09:20:10 +02:00
da1dc0309b Release candidate 2024-06-12 08:57:59 +02:00
30f4e7d833 Add PasswordPolicies on every page where there's user profile 2024-06-12 08:57:40 +02:00
cf3a86fb9b Release candidate 2024-06-11 21:22:34 +02:00
e1633f43f4 Apply same strategy for UserProfileFormField than for TempateProps for extendability 2024-06-11 21:21:58 +02:00
5b64cfc23c Release candidate 2024-06-11 20:50:31 +02:00
19709cf085 Only types are capitalized 2024-06-11 20:50:11 +02:00
b8bb6c4f02 Fix build 2024-06-11 20:40:00 +02:00
b7a543f8cb Do not export PageProps in the index 2024-06-11 20:30:39 +02:00
04b4e19563 Release candidate 2024-06-11 20:27:53 +02:00
ffb27fc66d Extract Props from UserProfileFormFields so it's ejectable 2024-06-11 20:27:03 +02:00
8b5f7eefda Release candidate 2024-06-11 19:14:19 +02:00
c750bf4ee8 Export PageProps 2024-06-11 19:14:04 +02:00
aa74019ef6 Fix build 2024-06-11 19:08:36 +02:00
9be6d9f75f Release candidate 2024-06-11 17:27:40 +02:00
81ebb9b552 Prevent the jar to be corrupted when rebuild 2024-06-11 17:19:36 +02:00
5e13b8c41f Exclude Keycloak 22 from test panel 2024-06-11 17:12:12 +02:00
dd1ed948ec Update Keycloak 25 default realm config 2024-06-11 16:26:03 +02:00
8b93f701cf Add realms configurations for Keycloak majors 2024-06-11 16:19:54 +02:00
2f0084de5b Pass the input options translation to the kcContext 2024-06-11 16:10:54 +02:00
2ef9828625 Start with keycloak 18 for local container 2024-06-11 11:39:03 +02:00
89db8983a7 Fix exception in terms.ftl 2024-06-11 11:37:45 +02:00
287dd9bd31 Refactor + attributes with options rendered by default as select inputs 2024-06-11 09:22:50 +02:00
9a92054c1a Remove unused dependency 2024-06-10 21:06:02 +02:00
4189036213 Fix storybook 2024-06-10 21:05:17 +02:00
2c0a427ba5 Fix the script to export realm 2024-06-10 20:51:00 +02:00
77b488d624 Fix the formatNumber function 2024-06-10 20:14:14 +02:00
5249e05746 Release candidate 2024-06-10 19:36:11 +02:00
1e7a0dd7a6 Enable to add files to the jar with the post build options 2024-06-10 19:35:56 +02:00
fd67f2402a Release candidate 2024-06-10 17:30:20 +02:00
60a65ede2f Preserve ordering on user attributes 2024-06-10 17:30:00 +02:00
1fa659ce61 Release candidate 2024-06-10 16:01:56 +02:00
0ab903dbc7 Add new build target for Kc 25 https://github.com/p2-inc/keycloak-account-v1/pull/13 2024-06-10 15:29:08 +02:00
70b0a04793 Release candidate 2024-06-10 15:08:46 +02:00
c0df9aa939 Remove logs 2024-06-10 09:32:07 +02:00
60a1886942 Fix path error 2024-06-10 09:28:31 +02:00
1ebf97871b Fix logical error 2024-06-10 09:26:47 +02:00
72e321aa32 Fix update of the build process checkpoint 2024-06-10 09:24:16 +02:00
b0f602b565 Fix post build script 2024-06-10 09:12:24 +02:00
84c774503d Build rework checkpoint 2024-06-10 07:57:12 +02:00
9bbc7cc651 Release candidate 2024-06-09 15:04:47 +02:00
458083fb6d Prettier stable generated code 2024-06-09 15:04:31 +02:00
8dcfc840b4 Remove useless 'as const' 2024-06-09 14:34:41 +02:00
9d06a3a6ad Release candidate 2024-06-09 14:33:42 +02:00
86cd08b954 Add missing file to the NPM bundle 2024-06-09 14:33:29 +02:00
144c3cc082 Release candidate 2024-06-09 11:53:41 +02:00
802cef41a6 Rename KcApp to KcPage 2024-06-09 11:53:25 +02:00
e128e8f0a9 Release candidate 2024-06-09 11:25:05 +02:00
8a25b93ab2 Rename Fallback to DefaultPage 2024-06-09 11:24:50 +02:00
7a040935e9 i18n need to be passed as props if we want to be able to ovewrite 2024-06-09 11:20:45 +02:00
2015882688 Avoid loop rebuild in watch mode 2024-06-09 10:28:06 +02:00
379301eb9d Release candidate 2024-06-09 09:50:27 +02:00
5d86b05cdb Fix eject-page script 2024-06-09 09:50:02 +02:00
73c99d3157 Fix scripts 2024-06-09 09:39:16 +02:00
acba197c94 Release candidate 2024-06-09 09:36:31 +02:00
2441d8ed8a Fix tests 2024-06-09 09:36:16 +02:00
9c123f37c8 Make of doMakeUserConfirmPassword a prop of UserProfileFormFields 2024-06-09 09:34:39 +02:00
b48dbd99cf Enable to pass a path to a file for exclusions #525 2024-06-09 09:20:55 +02:00
25c8599d8f Rename BuildOptions -> BuildContext 2024-06-09 09:15:45 +02:00
3453a17c15 Rename reactAppRootDirPath -> projectDirPath and reactAppBuildDirPath -> projectBuildDirPath 2024-06-09 09:03:43 +02:00
6e95dacd3a Remove todo 2024-06-09 08:52:52 +02:00
a286e252e9 Enable to pass environement variables to test docker container 2024-06-09 08:50:59 +02:00
a8997e92c3 Improve cache strategy for getKcClsx 2024-06-09 08:37:23 +02:00
89137153a0 Remove isStorybook util 2024-06-09 08:37:02 +02:00
e3382de8e0 Fix boolean logic error 2024-06-09 08:30:57 +02:00
1a48681591 getClassName -> kcClsx 2024-06-09 08:27:07 +02:00
8f006f0009 Apply the new way i18n is implemented to every pages 2024-06-09 04:43:18 +02:00
77e32aad2a Make useGetClassName not a hook 2024-06-08 18:54:27 +02:00
8d365dae53 Refactor i18n, make component use the hook directly 2024-06-08 17:55:05 +02:00
01fb89674c Refactor i18n so that we don't have to wait for translations to be downloaded to render the page 2024-06-08 15:50:04 +02:00
e3144adc61 Release candidate 2024-06-08 14:21:20 +02:00
c9fb0ca6ae Rename extention types 2024-06-08 14:20:56 +02:00
82d7e1371e Add code gen for environement variables an theme name 2024-06-08 14:02:07 +02:00
e1341dfdba Release candidate 2024-06-08 07:17:29 +02:00
7f917311d8 Export ClassKey in the index instead of PageProps 2024-06-08 07:17:11 +02:00
2bfb856f07 Inconsistency fix 2024-06-08 04:10:04 +02:00
702f52f1c9 Only add the lang annotation if the lang is different from the current 2024-06-07 08:05:35 +02:00
7ba8649940 Release candidate 2024-06-07 08:03:28 +02:00
485ca28a29 Enable the lang of the term to be undefined 2024-06-07 08:03:13 +02:00
33460afaf2 Release candidate 2024-06-07 02:20:27 +02:00
2421ac2c11 Make the user return the actual language of the terms for accesibility 2024-06-07 02:20:12 +02:00
f0cdb0b80b Fix build 2024-06-06 09:31:27 +02:00
2af953927e Release candidate 2024-06-06 09:14:27 +02:00
dcb9fbd0f7 fixes for the add-story command 2024-06-06 09:13:58 +02:00
5bc1f6479d fmt 2024-06-06 09:13:13 +02:00
f3e4bca468 Add script to copy over the stories 2024-06-06 07:41:01 +02:00
54645f5cff Update storybook setup for portability 2024-06-06 07:28:34 +02:00
a7f3e00821 Remove createKcContextMock from the index 2024-06-06 06:27:28 +02:00
108c281b0c Enable to eject Template.tsx and UserProfileFormFields.tsx 2024-06-06 06:12:05 +02:00
58892cbb56 Change first level build target of bin 2024-06-06 06:11:34 +02:00
dae1053ca8 Consistency with the starter 2024-06-06 06:10:41 +02:00
83a9778c30 Release candidate 2024-06-06 04:36:16 +02:00
c52157bfb9 Remove dependency to powerhooks 2024-06-06 04:35:57 +02:00
62bf846d5f Release candidate 2024-06-06 02:29:25 +02:00
148f7fa316 Rollback unarrowing of the getKcContextMock return type 2024-06-06 02:29:09 +02:00
f488327885 Release candidate 2024-06-06 01:31:17 +02:00
593b929254 #525 2024-06-06 01:31:00 +02:00
9218e97315 Relase candidate 2024-06-06 00:33:51 +02:00
beb0e8bd77 Add missing translations 2024-06-06 00:33:34 +02:00
cace66e9f8 No longer need for a eslint rule ignore for with default vite starter 2024-06-05 23:30:28 +02:00
ef850c71fd Release candidate 2024-06-05 23:24:44 +02:00
aa8dc1919f Make getKcContext mock return type less narrow 2024-06-05 23:24:21 +02:00
c7c9b19853 Fix case error 2024-06-05 23:08:40 +02:00
68c26e0f5b Fix case issue 2024-06-05 22:55:09 +02:00
6bcdf286ef Add missing export 2024-06-05 22:53:14 +02:00
d9345396e8 Fix build 2024-06-05 22:48:13 +02:00
4c423900d4 Release candidate 2024-06-05 21:44:54 +02:00
504419b26d Fix storybook 2024-06-05 21:44:26 +02:00
6e058eafed It's less confusing to use "#" as urls in mock kcContext 2024-06-05 21:44:14 +02:00
08fc9d8631 Fix tests by updating vitest 2024-06-05 21:23:34 +02:00
e8a11991a0 Rename kcContext -> KcContext and improve consistency 2024-06-05 21:13:58 +02:00
3e6d679838 Release candidate 2024-06-05 18:50:26 +02:00
4dad859c4d Fix storybook 2024-06-05 18:50:00 +02:00
ef9c933ca8 Relase candidate 2024-06-05 18:42:50 +02:00
0461190a67 Do not export default the Fallback component 2024-06-05 18:42:32 +02:00
06b3211b08 Ease up the instentiation of i18n 2024-06-05 18:41:53 +02:00
2033a9ce0c Release candidate 2024-06-05 18:15:25 +02:00
fca18d9209 Add missing file to the NPM bundle 2024-06-05 18:15:13 +02:00
4f99088449 Release candidate 2024-06-05 06:11:18 +02:00
b1da684008 Re implement asset fetching 2024-06-05 06:10:11 +02:00
89fb6de2d5 Full ordering of stories 2024-06-05 06:09:42 +02:00
b665bae3bb Another improvement on storybook switching from one page to another 2024-06-05 01:32:31 +02:00
0b5a7544ca Address white falshes in storybook 2024-06-05 01:02:17 +02:00
183826ca0d Improve terms story 2024-06-04 04:06:29 +02:00
e507aace6b Change ordering of stories 2024-06-04 04:06:09 +02:00
43c93ef0b4 Update the intro story 2024-06-04 02:05:09 +02:00
093e51e092 Fix escaping error 2024-06-04 01:49:26 +02:00
17e1655eaf Fix recaptcha in storybook 2024-06-04 01:39:54 +02:00
6b570f2b9a Update register story 2024-06-03 23:54:08 +02:00
f239d105a7 Fix missing key 2024-06-03 23:53:53 +02:00
776d8378e3 Shorter white flash when changing stories 2024-06-03 23:40:33 +02:00
dd770cd7c6 Remove unessesary stories 2024-06-03 23:40:21 +02:00
4b3de54e18 Make it more conveignent to run storybook 2024-06-03 23:26:04 +02:00
5741cd1b2b Lower the priority of the without password story. 2024-06-03 23:25:37 +02:00
b780d7136e Fix mistake after using attributesByName instead of attributes 2024-06-03 23:25:02 +02:00
3c28a05746 Fix copy-keycloak-resources-to-public 2024-06-03 22:45:09 +02:00
57ac5badba Update the euristic for getting the NPM workspace root. 2024-06-03 22:37:22 +02:00
e873eb5123 Rollback typescript because updating storybook would add a one month delay to the release 2024-06-03 22:36:54 +02:00
c1a63edd71 Refactor kcContext, avoid having mocks in the dist https://github.com/keycloakify/keycloakify/discussions/299#discussioncomment-9616747 2024-06-03 18:28:34 +02:00
37a060c4db Change ordering of pages 2024-06-03 01:23:41 +02:00
157e4ac485 Add missing storybook pages 2024-06-03 01:23:28 +02:00
ba4d9675a8 More homogeneous storybook setup 2024-06-03 00:11:19 +02:00
e011fb094c Factorize parameters in storybook 2024-06-02 22:37:04 +02:00
f55a934939 Complete migration of storybook from @lordvlad #274 2024-06-02 22:29:53 +02:00
96a88fe865 Fix add remove button for multifield attributes 2024-06-02 00:31:08 +02:00
6cdb83d730 Fix the way we handle multivalued single fileld (multiselct, multiselect-checkboxes) 2024-06-02 00:24:07 +02:00
95f06df45d Extenalize some core logic from the ejectable component 2024-06-01 22:54:17 +02:00
ec52b357d5 Fix logical error with radibuttons 2024-05-30 23:23:16 +02:00
d84546cd7d Correct error validation password policy 2024-05-30 22:50:06 +02:00
4eee4156da Release candidate 2024-05-28 01:27:01 +02:00
70f475d13e Remove some more noise in the kcContext 2024-05-28 01:26:33 +02:00
3a50a61b12 First test against the key for faster ftl rendering 2024-05-28 01:08:02 +02:00
a217f617d8 Remove profile.attributesByName from the kcContext 2024-05-28 01:05:35 +02:00
fdfcd78f02 Watches more files that are relevent to the keycloak theme 2024-05-28 00:55:46 +02:00
56d6d8001a Fix #549 after test 2024-05-28 00:23:48 +02:00
c3ee8e10e6 Release candidate 2024-05-27 23:45:07 +02:00
2f42732deb #549 Done 2024-05-27 23:44:41 +02:00
956b8260e7 Release candidate 2024-05-27 18:33:48 +02:00
b7954f87e0 Fix error creating dir that might exist already 2024-05-27 18:33:16 +02:00
540ce55dc2 Release candidate 2024-05-27 17:23:08 +02:00
d71a2c98d1 Force initial build on keycloakify start 2024-05-27 17:22:55 +02:00
cb9cec676d Accomodate for Angular 2024-05-27 17:21:06 +02:00
9f2755bc7f Most of the work done for #549 2024-05-27 17:18:06 +02:00
fbe5a1f477 Remove dependency to react-markdown in the main bundle. 2024-05-27 01:09:49 +02:00
338642094d Remove dependency to evt in the component library 2024-05-27 00:12:51 +02:00
a3270d10f0 Release candidate 2024-05-26 22:34:01 +02:00
4c5924556a Re arange the output of start-keycloak 2024-05-26 19:55:59 +02:00
99a9b62c6c Fix logical error 2024-05-26 19:50:25 +02:00
1497672a4e Fix logical error 2024-05-26 19:40:13 +02:00
01161fd8ef up 2024-05-26 19:31:09 +02:00
68f5ee42e6 Make hot reloading when testing account theme with older Keycloak version work 2024-05-26 19:29:12 +02:00
53955a0713 Build the app when running npx keycloak start-keycloak 2024-05-26 17:58:41 +02:00
2271fd43b8 First step toward implementing #549 2024-05-26 16:21:04 +02:00
6a44cfb876 Release candidate 2024-05-26 12:42:28 +02:00
37c90d53e0 Fix tests 2024-05-26 12:42:13 +02:00
9a5ac5f13f Readability improvement 2024-05-26 12:38:00 +02:00
6603852355 Remove premature optimization 2024-05-26 12:24:35 +02:00
5670a71e6b Fix error with Set initialization 2024-05-26 12:19:47 +02:00
332b1f74d9 Enable to safely build jars with maven in parallel 2024-05-26 11:00:02 +02:00
c28caaa495 Fix hot reloading when testing in keycloak 2024-05-25 22:36:03 +02:00
74fed835e8 Remove web module from the account resources 2024-05-25 20:01:59 +02:00
6bb7f7dc16 Using POO do not increace performances 2024-05-25 12:30:21 +02:00
84bb2338d1 Filter out many of the unused keycloak resources 2024-05-25 12:19:03 +02:00
caa42538a1 Make sure the cache dont get corrupted if any operation is canceled mid way 2024-05-25 11:41:46 +02:00
f5b9a8de55 Node types def are lying 2024-05-25 11:02:22 +02:00
dd33f554da Resource fetching optimization 2024-05-24 17:26:38 +02:00
7e84d0b108 Just make sure compilations run don't overlap 2024-05-23 20:53:42 +02:00
9e1217fbf0 Use the default debounce of chokidar-cli 2024-05-20 21:58:47 +02:00
26d3c7f9e0 Better understanding of what's running 2024-05-20 21:47:06 +02:00
76542e6859 up 2024-05-20 21:10:58 +02:00
3c4bbf8aa7 Fix build and watch (hopefully for good this time) 2024-05-20 21:07:59 +02:00
ccc5ac6a1f Update prettier configuration 2024-05-20 19:30:15 +02:00
b34f86d2f0 Fix some bugs in start-keycloak 2024-05-20 19:30:04 +02:00
ee5f73519a Fix build script 2024-05-20 19:29:38 +02:00
22e7ff1424 Update prettier configuration 2024-05-20 15:50:58 +02:00
7a89888d11 Restore realm configuration 2024-05-20 15:34:07 +02:00
64fe15cf8c #554 2024-05-20 15:24:35 +02:00
336813646f Also watch the source files of Keycloakify for improving the experience of the maintainers 2024-05-20 10:16:38 +02:00
53a18c462a Move a wrong assertion down two lines 2024-05-20 10:08:00 +02:00
b893eee086 Don't trust the type system to much, we don't know for sure what the kcContext is actually like 2024-05-20 10:07:34 +02:00
792020dd18 Release candidate 2024-05-20 02:43:17 +02:00
0c11ba05af Fix auto re-build in start-keycloak 2024-05-20 02:42:57 +02:00
2cf82f510e Improve start-keycloak command 2024-05-20 02:27:40 +02:00
7c0cbe3a31 Rename function for consistency 2024-05-20 02:27:26 +02:00
08e659cf01 Better cli insight with download-keycloak-default-theme 2024-05-20 02:26:14 +02:00
97c3f4fa5f Fix build jar script 2024-05-20 02:25:45 +02:00
06a24d35cb https://github.com/keycloakify/keycloakify/issues/550 2024-05-19 23:17:45 +02:00
9b6d1a957f Release candidate 2024-05-19 10:46:42 +02:00
189bd4697a Rename scripts 2024-05-19 10:46:26 +02:00
d52252cd55 Fix typo 2024-05-19 10:35:02 +02:00
303bbc8431 Do not upload if admin token empty 2024-05-19 10:33:01 +02:00
727dc471c2 Better formatting for download-builtin-keycloak-theme 2024-05-19 10:32:38 +02:00
45b5c21ab5 Update eject-keycloak-page formatting 2024-05-19 10:23:38 +02:00
adddce7764 Release candidate 2024-05-19 09:52:44 +02:00
b35a9f8f61 Update keywords 2024-05-19 09:52:26 +02:00
34b46a9280 Add missing file in the NPM bundle 2024-05-19 09:51:41 +02:00
383a9953e2 Update enable_short_import_path script version 2024-05-19 09:51:16 +02:00
94bc7127fa Update action-gh-release to v2 2024-05-19 09:38:02 +02:00
289f0efd24 Release candidate 2024-05-19 09:33:20 +02:00
3f15586dae Update ci workflow 2024-05-19 09:33:02 +02:00
b8a33dabd4 Release candidate 2024-05-19 09:02:20 +02:00
3a4b44a83c Avoid breaking node retrocompatibility with dist patch 2024-05-19 09:02:04 +02:00
8c6303f016 Update action version 2024-05-19 08:57:45 +02:00
b71f3f559a Update ts-ci actions 2024-05-19 08:55:47 +02:00
6541460821 Release candidate 2024-05-19 08:51:36 +02:00
e63cd68a3e Patch deprecated Buffer API usage in vite-plugin 2024-05-19 08:51:13 +02:00
5602dc58ff Release candidate 2024-05-19 08:36:57 +02:00
8ed3561a55 Keep the bin executable when it's not bundled 2024-05-19 08:36:33 +02:00
4e72bc3a72 Update enable_short_import_path script 2024-05-19 08:35:54 +02:00
45c41ad321 Update ts-ci 2024-05-19 08:28:11 +02:00
14eccc761b Release candidate 2024-05-19 05:30:17 +02:00
6b24c4284f Update most dependencies 2024-05-19 05:29:58 +02:00
ba44de87d6 Ease testing local build in the starter 2024-05-19 05:29:20 +02:00
846181cfc7 Release candidate 2024-05-19 04:54:38 +02:00
2a8849dd11 We can now use ESM only packages in src/bin 2024-05-19 04:54:15 +02:00
5a879ece3c Release candidate 2024-05-19 04:45:43 +02:00
ffd734cc2d Patch ncc https://github.com/vercel/ncc/issues/484 2024-05-19 04:45:11 +02:00
ac414489ff Fix logical error 2024-05-19 04:44:38 +02:00
10da0cab47 https://github.com/adbayb/termost/issues/30 2024-05-19 04:26:01 +02:00
59f8119047 Do not manually bundle 2024-05-19 04:21:35 +02:00
a28a1531d9 https://github.com/adbayb/termost/pull/31 2024-05-19 04:03:03 +02:00
977d0dc761 Bundle bin 2024-05-19 04:02:36 +02:00
a0de1b38ce Release candidate 2024-05-18 11:40:26 +02:00
401f390e5b Improve loggin 2024-05-18 11:40:09 +02:00
e69febe0c0 Check if docker installed and running 2024-05-18 11:09:04 +02:00
eba4ddbbd8 Improve help formatting 2024-05-18 10:53:14 +02:00
49bb276c78 Rename start-keycloak-container -> start-keycloak 2024-05-18 10:48:47 +02:00
b48bccb706 Print a message if Maven not installed 2024-05-18 10:46:48 +02:00
a2160fc8ce Remove the --silent cli option 2024-05-18 10:26:56 +02:00
af829e9ac9 Improve login 2024-05-18 10:24:55 +02:00
2c5473da27 Colors in the terminal 2024-05-18 10:02:14 +02:00
e248a58201 Resolve XDG_CACHE_HOME relative to CWD 2024-05-18 09:18:40 +02:00
d85ab889b3 Support "~" in getAbsoluteAndInOsFormat 2024-05-18 09:15:39 +02:00
49eae307cd Create the cache dir path if not exist 2024-05-18 09:00:57 +02:00
a2563f0b7d Safely load command scripts 2024-05-18 08:56:11 +02:00
c8d2866ada Remove unessesary log 2024-05-18 08:12:34 +02:00
2f8d89012b Implement caching for promptKeycloakVersion 2024-05-18 08:11:20 +02:00
a3d9016cfe Remove debug log 2024-05-18 07:55:17 +02:00
24c14ea8f6 Improve log formatting 2024-05-18 07:53:41 +02:00
c93e787393 Fix docker command 2024-05-18 07:53:06 +02:00
b6cc3ee022 Create the .gitignore first 2024-05-18 04:48:04 +02:00
30e4112f79 Add missing period 2024-05-18 04:39:02 +02:00
067e148897 Improve helper text when inititializing email theme 2024-05-18 04:37:19 +02:00
7fc6f7a7ae Add context when prompting for the keycloak version number 2024-05-18 04:33:31 +02:00
60fcb5fe10 Update octokit 2024-05-18 04:24:02 +02:00
627ccd024c Release candidate 2024-05-18 04:18:15 +02:00
9fa1460400 Make it clear that the copy-keycloak-resources-to-public command is for CRA 2024-05-18 04:17:45 +02:00
c780e9b9f5 Greatly improve the DX with eject-keycloak-page 2024-05-18 04:14:36 +02:00
c2f15a569f Don't fail if the pages directory do not exist when ejecting 2024-05-18 03:41:12 +02:00
8311eaba1d Log cli errors https://github.com/adbayb/termost/issues/30 2024-05-18 03:35:55 +02:00
22aa48e343 Fail when wrong options https://github.com/adbayb/termost/issues/29 2024-05-18 03:12:07 +02:00
87cd37c467 Update watch script 2024-05-18 01:49:51 +02:00
a78a884c6e Update typescript version 2024-05-18 01:28:54 +02:00
0bfd73bcc6 Release candidate 2024-05-18 01:17:31 +02:00
113bb35a2b Remove legacy command 2024-05-18 01:17:31 +02:00
9b27f25f6c Implement stary-keycloak-container 2024-05-18 01:17:31 +02:00
e09acedf67 factorize access to META-INF/keycloak-themes.json 2024-05-18 01:17:31 +02:00
a80449333c Rename generateTheme to generateSrcMainResources 2024-05-18 01:17:31 +02:00
64f71d4265 Remove legacy branch from renovate 2024-05-18 01:17:31 +02:00
fb44700dd5 Don't over factorize 2024-05-18 01:17:31 +02:00
7d61be231e use cacheDirPath for temporary directories 2024-05-18 01:17:31 +02:00
f935922241 Stop passing as parameter the non variadic srcMainResource path 2024-05-18 01:17:31 +02:00
d5bb7679ca Make it clearer what's going on when calling post build script 2024-05-18 01:17:31 +02:00
b2a00737d3 Do not trickle down parameters as much 2024-05-18 01:17:31 +02:00
3cd3e08ede Introduce start-keycloak-container command 2024-05-18 01:17:31 +02:00
14fe55e5c4 Factorize getBuildOptions 2024-05-18 01:17:31 +02:00
8cf0f96401 improve error message 2024-05-18 01:17:31 +02:00
7da612c083 fix import error 2024-05-18 01:17:31 +02:00
afcc3fd0ce Move getNpmWorkspaceRootDirPath to tools 2024-05-18 01:17:31 +02:00
ab9e163105 Remove files that are no longer nessesary 2024-05-18 01:17:31 +02:00
63f9c815e0 Remove the need for creating a temporary vite.json file 2024-05-18 01:17:31 +02:00
6b7e5b6bc3 Only propose latest major in cli select 2024-05-18 01:17:31 +02:00
2ec22f07d5 Fallback to the build command https://github.com/adbayb/termost/issues/28 2024-05-18 01:17:31 +02:00
d4f03b6b9e bin general reresh, introducing termost 2024-05-18 01:17:31 +02:00
931e002b12 Release candidate 2024-05-18 01:17:31 +02:00
247f9b0c9d Fix error rending 2024-05-18 01:17:31 +02:00
0a72791022 Fix useUserProfileForm reducer 2024-05-18 01:17:31 +02:00
4fbb5f2023 Release candidate 2024-05-18 01:17:31 +02:00
7589b204fe Less verbose js coments (for start) #542 2024-05-18 01:17:31 +02:00
aa43abb544 Fix vim motion typo 2024-05-18 01:17:31 +02:00
85df0c2c6d Improve a little bit the readability of the rendered template 2024-05-18 01:17:31 +02:00
9bcfa58ec0 Fix error in ftl templat 2024-05-18 01:17:31 +02:00
11b2c6651d #545 2024-05-18 01:17:31 +02:00
6d57872e85 Update build scripts 2024-05-18 01:17:31 +02:00
8f98eff9b5 Remove debug file 2024-05-18 01:17:31 +02:00
e18a34c987 Update build script 2024-05-18 01:17:31 +02:00
19f1b4b14e Converts all functions without arguments at the same place in the ftl template 2024-05-18 01:17:31 +02:00
497f747d69 Forget to add displayRequiredFields on some pages 2024-05-18 01:17:31 +02:00
5e2debc695 Fix language menu select in templates 2024-05-18 01:17:31 +02:00
3ce571daab Remove --feature=declarative-user-profile from testing container launch script 2024-05-18 01:17:31 +02:00
1165477360 Fix inputs using value instead of defaultValue 2024-05-18 01:17:31 +02:00
05a223d5f0 Add missing mock value 2024-05-18 01:17:31 +02:00
0b21c04d82 Relase candidate 2024-05-18 01:17:31 +02:00
21454b9168 Pass totp.policy.getAlgorithmKey() to the freemarker template 2024-05-18 01:17:31 +02:00
699a3c8bf6 Fix path error in generate theme variant 2024-05-18 01:17:31 +02:00
f9fc0cda95 Release candidate 2024-05-18 01:17:31 +02:00
9249932a25 Fix build jar script 2024-05-18 01:17:31 +02:00
4f6e60683b Fix non closed tag 2024-05-18 01:17:31 +02:00
ad3cf3fab8 Fix several logical errors 2024-05-18 01:17:31 +02:00
a1b4ef10db Remove debug log 2024-05-18 01:17:31 +02:00
10fd863408 Update the exceptions 2024-05-18 01:17:31 +02:00
908ca9feda Better portability 2024-05-18 01:17:31 +02:00
4c4987ee7c Fix storybook build 2024-05-18 01:17:31 +02:00
bbe23b911a Release candidate 2024-05-18 01:17:26 +02:00
8d21425ae0 route the pages removed in kc 24 at low level 2024-05-18 01:15:58 +02:00
78517164d4 Done with multi target build 2024-05-18 01:15:58 +02:00
3ad694d4de Only buildJar function left to implement 2024-05-18 01:15:58 +02:00
824076f730 Implement generateThemeVariants 2024-05-18 01:15:58 +02:00
5a7d452429 Refactor 2024-05-18 01:15:58 +02:00
88fa63d848 Checkpoint 2024-05-18 01:15:58 +02:00
a26a813ad5 Checkpoint 2024-05-18 01:15:58 +02:00
385cb85309 Multi target build (checkpoint before futher refactor) 2024-05-18 01:15:58 +02:00
d4f5a1fff4 Remove retrocompatiblity before re-introducing it 2024-05-18 01:15:58 +02:00
771322aa97 Use Keycloak 24.0.4 assets (and use kc 24 in testing container) 2024-05-18 01:15:58 +02:00
f1177469c2 Update the getKcContext function 2024-05-18 01:15:58 +02:00
b122957ec0 Update account theme template 2024-05-18 01:15:58 +02:00
ec421e62ff More sensible mock date for UpdateEmail 2024-05-18 01:15:58 +02:00
e9f7f9d091 Provide mocks data for the new pages 2024-05-18 01:15:58 +02:00
cf39095351 Forgot to actually insert the script 2024-05-18 01:15:58 +02:00
94b618626d Add webauthn-error.ftl page 2024-05-18 01:15:58 +02:00
1e50427d62 Update saml-post-form.ftl page 2024-05-18 01:15:58 +02:00
73dfb7b91b Update the logout-confirm.ftl page 2024-05-18 01:15:58 +02:00
88aaa18a24 Add the login-x509-info.ftl page 2024-05-18 01:15:58 +02:00
3909e50d49 Update login-verify-email.ftl page 2024-05-18 01:15:58 +02:00
8683cf88fe Update login-username.ftl page 2024-05-18 01:15:58 +02:00
d5376b80c2 Update login-update-password.ftl page 2024-05-18 01:15:58 +02:00
3ec5aa84ad Update login-reset-password.ftl page 2024-05-18 01:15:58 +02:00
c80c399e6b Add login-reset-otp.ftl page 2024-05-18 01:15:58 +02:00
9001e254cc Add login-recovery-authn-code-input.ftl page 2024-05-18 01:15:58 +02:00
754b5be768 Fix type confusion 2024-05-18 01:15:58 +02:00
064f3b2041 Add login-recovery-authn-code-config.ftl page 2024-05-18 01:15:58 +02:00
c6874194a0 fmt 2024-05-18 01:15:58 +02:00
43092ce81a Fix ftl template not correctly parsing numbers 2024-05-18 01:15:58 +02:00
1abf0bb0d7 Update page login-password.ftl 2024-05-18 01:15:58 +02:00
1eb01ca9ff Update the login-page-expired.ftl page 2024-05-18 01:15:58 +02:00
324b1fed5d Update the login-otp.ftl page 2024-05-18 01:15:58 +02:00
ac238ff2b0 Rename src/login/pages/LoginDeviceVerifyUserCode.tsx to src/login/pages/LoginOauth2DeviceVerifyUserCode.tsx to respect naming convention 2024-05-18 01:15:58 +02:00
1d4cf2a446 Update login-oauth-grant.ftl page 2024-05-18 01:15:58 +02:00
89ddfa18b7 Update login-config-totp.ftl 2024-05-18 01:15:58 +02:00
7b60ab50b1 Update info.ftl 2024-05-18 01:15:58 +02:00
250d1d66dd Update idp-review-user-profile.ftl page 2024-05-18 01:15:58 +02:00
08cd62d924 add frontchannel-logout.ftl page 2024-05-18 01:15:58 +02:00
4f7a1c784f Remove the usePrepareTemplate hook 2024-05-18 01:15:58 +02:00
8d0d17910c Update error.ftl 2024-05-18 01:15:58 +02:00
34e1621b84 Add delete-account-confirm.ftl page 2024-05-18 01:15:58 +02:00
93d90d0ba6 Add code.ftl page 2024-05-18 01:15:58 +02:00
d5f3c789df Prevent multiple loading of the same script 2024-05-18 01:15:58 +02:00
652643f189 Add webauthn-register.ftl page 2024-05-18 01:15:58 +02:00
5cfb289736 update webauthn-autenticate.ftl 2024-05-18 01:15:58 +02:00
570fbd5632 Effort toward reconsiliating the server templating and the react world 2024-05-18 01:15:58 +02:00
88efe4a523 New mechanism for dynamically loading css and js (checkpoint) 2024-05-18 01:15:58 +02:00
a1db79ff47 Do not restrict to any perticular version of React 2024-05-18 01:15:58 +02:00
bac159a42c update evt 2024-05-18 01:15:58 +02:00
54b129630e Refactor terms 2024-05-18 01:15:58 +02:00
fdead071e7 Remove Register_legacy 2024-05-18 01:15:58 +02:00
0cfa8de0ad Update update-email.ftl page 2024-05-18 01:15:58 +02:00
9adfa2200a Update SelectAuthenticator.tsx 2024-05-18 01:15:58 +02:00
f5ab145906 Remove misleading comment 2024-05-18 01:15:58 +02:00
3eeba99152 Add delete-credential.ftl page 2024-05-18 01:15:58 +02:00
58dfd3c25c Factorise LoginUserProfile and LoginUpdateProfile 2024-05-18 01:15:58 +02:00
f97d33ffc1 Refactor and handle legacy login-update-profile.ftl 2024-05-18 01:15:58 +02:00
75212e643c Remove comment 2024-05-18 01:15:58 +02:00
22a0c9f401 Remove unused variable 2024-05-18 01:15:58 +02:00
7772550438 Login page overhaul 2024-05-18 01:15:58 +02:00
a887844a37 Load scripts after component rendered #470 2024-05-18 01:15:58 +02:00
b61f442a15 Update readFieldNameUsage for new messagePerField methods 2024-05-18 01:15:58 +02:00
0e20a26d6c Handle password field hide/reveal 2024-05-18 01:15:58 +02:00
b629af8dee Remove dead file 2024-05-18 01:15:58 +02:00
f0ffb3fc10 Fully retrocompatible, factorized Register page 🚀 2024-05-18 01:15:58 +02:00
96f0e6df2a File structure update 2024-05-18 01:15:58 +02:00
fb4a7d2ba3 Done with the new Register page (not yet retrocompatible) 2024-05-18 01:15:58 +02:00
0b0321474d Download terms when kcContext.termsAcceptanceRequired is set to true 2024-05-18 01:15:58 +02:00
a633423b72 Do not inject password field when password isn't required 2024-05-18 01:15:58 +02:00
f5781e8ee7 Add TermsAcceptance component 2024-05-18 01:15:58 +02:00
2c318cf64f Actually use the doUseDefaultCss param in useClassName 2024-05-18 01:15:58 +02:00
be330886da Complete UserProfileFormFields 2024-05-18 01:15:58 +02:00
73a39bedf5 Apply number unformat during validation if any 2024-05-18 01:15:58 +02:00
d04950cbc9 Load number unformat for pre form submission 2024-05-18 01:15:58 +02:00
b4d924adfa Almost done with UserProfileFormField.tsx 2024-05-18 01:15:58 +02:00
3f1316183d Good progress on UserProfileFormFields component 2024-05-18 01:15:58 +02:00
b17724fdda Done with select tag 2024-05-18 01:15:58 +02:00
41c2685dc4 Multivalued attributes that uses a single field have an inputType that starts with "multiselect" 2024-05-18 01:15:58 +02:00
b450e3db65 If required multivalued single file must have at least one value 2024-05-18 01:15:58 +02:00
352d2a7bc8 use valueOrValues to simplify type definitions 2024-05-18 01:15:58 +02:00
47f2bc9cd7 We have a polyfill for Array.every 2024-05-18 01:15:58 +02:00
2db0e8f68a Register form hook finally completed 2024-05-18 01:15:58 +02:00
f7d733b407 Checkpoint validation supporting various multi valued fields 2024-05-18 01:15:58 +02:00
4b78ef52e0 Progress checkpoint on useUserProfileForm 2024-05-18 01:15:58 +02:00
f42e6764b7 Checkpoint before refactor again 2024-05-18 01:15:58 +02:00
8f627aa382 Feature TextArea 2024-05-18 01:15:58 +02:00
9c6e3da304 Extract field errors into a separate component 2024-05-18 01:15:58 +02:00
319927e1dc Extract form group label into a separate component 2024-05-18 01:15:58 +02:00
4909928d3a Progress on form reactivity 2024-05-18 01:15:58 +02:00
423d031210 Simplify the API of useUserProfileForm 2024-05-18 01:15:58 +02:00
ab5269ddaf Start refactor of UserProfilesFormFields 2024-05-18 01:15:58 +02:00
96a6e81235 Implement password policy validation 2024-05-18 01:15:58 +02:00
6d8b0e0539 do not use custom validator to check if password confirmation matches password 2024-05-18 01:15:58 +02:00
f09ea971cf Dot not create fake attribute field, hide password confirm at an higher level 2024-05-18 01:15:58 +02:00
8030bf42ff Big refactor of useFormValidator into useUserProfileForm 2024-05-18 01:15:58 +02:00
008fa2b0c4 Add multivalued field validator 2024-05-18 01:15:58 +02:00
9040704659 Update KcContext type def, use an ext to get password policies. 2024-05-18 01:15:58 +02:00
7e793cabe8 Refactor useFormValidation 2024-05-18 01:15:58 +02:00
f1a0887e9b checkpoint update on useFormValidation 2024-05-18 01:15:58 +02:00
f6bdd92f9e Update Login template for Keycloak 24 2024-05-18 01:15:58 +02:00
a0367066b4 Feat polifill for getFirstError and make existsError accept more than one field (kcContext.messagePerField) 2024-05-18 01:15:58 +02:00
00651c0c3c Drop compat with Keycloak prior to v12 #359 2024-05-18 01:15:58 +02:00
a7a3ec711b Fully sync login template with Keycloak 24 2024-05-18 01:15:58 +02:00
de5bc82382 Update css classes keys to reflect Keycloak 24 2024-05-18 01:15:58 +02:00
138208bf82 Update prepare template for Keycloak 24 2024-05-18 01:15:58 +02:00
0796b3dedf Bump version 2024-05-17 17:54:23 +02:00
662a76bbb6 Merge pull request #553 from giorgoslytos/feat/federated-identity-account-page
Addition of Federated Identity Account page
2024-05-17 13:56:27 +00:00
a664195625 fix: Use of 'Meta' from storybook instead of 'ComponentMeta' 2024-05-17 12:17:17 +03:00
e533e127bf feat: Addition of stories for account pages that were missing them 2024-05-17 12:16:36 +03:00
346e3df009 feat: Addition of FederatedIdentity Account Page 2024-05-17 12:14:47 +03:00
19ba0873f5 fix: Prune 2024-05-16 17:11:45 +03:00
fb4acc62c4 fix: Addition of prop logoutUrl 2024-05-16 17:09:03 +03:00
fd538e95ca fix: Correction of import of useGetClassName 2024-05-16 14:05:58 +03:00
def2d8b75b fix: Correction of Application page 2024-05-16 14:05:28 +03:00
586b28af1c fix: Correction of types on KcContext for account pages 2024-05-16 12:53:31 +03:00
585c279d10 Bump version 2024-04-17 15:57:39 +02:00
51bc65e671 Avoid logging expected error messages to the console 2024-04-17 15:57:13 +02:00
ff1758cdce Update README.md 2024-04-13 04:50:34 +02:00
72a3c37e84 Update the downlaod-builtin-keycloak-theme for v24 2024-04-13 01:12:36 +02:00
c99cdf5566 Bump version 2024-04-10 03:33:46 +02:00
ad339710f1 Add corect document title and html lang property in account theme 2024-04-10 03:33:33 +02:00
00d2d12056 Bump version 2024-04-10 03:20:02 +02:00
6a7b472c0e #535 2024-04-10 03:19:46 +02:00
6991d868be Set html lang property and page title in usePrepareTemplate 2024-04-10 01:38:59 +02:00
85cc665d17 Bump version 2024-04-09 17:17:27 +02:00
5bb22fc345 #534 2024-04-09 17:17:12 +02:00
5417dc1bed Add notice about Keycloak 24 2024-04-09 14:42:43 +02:00
ee20d33724 Bump version 2024-03-19 14:57:47 +01:00
7887bd2b67 Filter out some Ftl context 2024-03-19 14:57:32 +01:00
168582efea Bump version 2024-03-16 05:42:50 +01:00
2c55d13f91 Add some more options for Keycloak version when downloading builtin theme (in the prompt) 2024-03-16 05:42:27 +01:00
23e5f553d4 #522 2024-03-16 05:34:55 +01:00
bc44eadcec Bump version 2024-03-05 19:43:06 +01:00
a3e3136600 #519 2024-03-05 19:42:49 +01:00
c7bfcee8d2 Update README.md 2024-03-04 21:46:00 +01:00
12ebd19716 Bump version 2024-03-02 18:45:21 +01:00
aec9ffa5db Merge pull request #515 from giorgoslytos/feat/additional-account-pages
feat: addition of extra account pages
2024-03-02 17:54:45 +01:00
2e6321342e Merge branch 'main' into feat/additional-account-pages 2024-03-02 13:41:48 +02:00
6e71da62f0 Bump version 2024-03-02 09:29:50 +01:00
5bf33aae75 Better support for environnement variables 2024-03-02 09:29:36 +01:00
06b2dc63ff Bump version 2024-03-02 09:02:17 +01:00
1bb0c9dfc2 Add generic type for kcContext.properties 2024-03-02 09:01:57 +01:00
a2b167e120 Bump version 2024-02-29 21:57:54 +01:00
0909a4b7cc Remove dead code, (I didn't fully understand how it landed here in the first place) 2024-02-29 21:57:37 +01:00
fd7d2bb9bf Bump version 2024-02-27 23:55:40 +01:00
63c40fd816 Add type definition for the user property in the kcContext of the terms.ftl page 2024-02-27 23:55:06 +01:00
0569fa5e58 Bump version 2024-02-27 23:32:07 +01:00
ba74952e0b #513 2024-02-27 23:19:55 +01:00
20c28f785a Bump version 2024-02-27 06:52:17 +01:00
e9b249ddc7 Fix ftl script bug and definitively address #512 and #432 2024-02-27 06:52:17 +01:00
604bb484a3 Update README.md 2024-02-25 21:02:31 +01:00
010c93793a Bump version 2024-02-24 05:38:59 +01:00
dc1d4a66f4 keycloakifyBuildDirPath was non absolute 2024-02-24 05:38:42 +01:00
8ef633d7ef Bump version 2024-02-24 05:09:08 +01:00
2176d33da1 Support generating source map 2024-02-24 05:08:39 +01:00
5b794e2d22 Bump version 2024-02-23 20:02:41 +01:00
ccd75d56c5 Run the post build script in the react app directory 2024-02-23 19:38:01 +01:00
b700066833 Release candidate 2024-02-23 19:22:28 +01:00
546ee006d3 Rename postBuildScript to postBuild, make the params of the Vite plugin optional 2024-02-23 19:22:14 +01:00
7f333a6a36 Release candidate 2024-02-23 19:16:57 +01:00
ae757ee371 Enable to provide the configuration to the Vite plugin, enable user to provide a post build script #148 2024-02-23 19:16:53 +01:00
69936750d5 Bump version 2024-02-21 20:43:34 +01:00
442bfa4ed6 Update types to reflect what is actually there on the kcContext 2024-02-21 20:43:18 +01:00
79e25e69bb feat: addition of Log account page 2024-02-21 13:44:33 +02:00
b95c12772d style: Add spaces between words 2024-02-21 13:36:55 +02:00
de47525d7c feat: Addition of Application Account page 2024-02-19 17:29:46 +02:00
f49d20e47c feat: Totp account page fixed and completed 2024-02-19 11:57:03 +02:00
33b9917229 fix: locales in account totp page 2024-02-19 08:58:27 +02:00
2a88e6802f Bump version 2024-02-18 11:28:29 +01:00
bcc8b12e13 Enable to statically build storybook in Vite project 2024-02-18 11:28:17 +01:00
9b974505eb Update ci 2024-02-17 04:15:31 +01:00
29b1c26771 Bump version 2024-02-17 03:47:03 +01:00
02db20d98b Enable to release on v8 branch 2024-02-17 03:47:03 +01:00
757354df7d Follow up on #406 2024-02-17 03:47:03 +01:00
319d7dbe94 feat: Addition of Totp account page 2024-02-16 17:40:12 +02:00
feb8eaf95a Merge branch 'keycloakify:main' into additional-account-pages 2024-02-13 22:27:25 +02:00
563518cf46 Remove poll 2024-02-13 15:07:14 +01:00
7c42d9082a Remove broken badge 2024-02-13 04:47:14 +01:00
040284af71 Reference Vite doc 2024-02-13 04:46:36 +01:00
34f64184d9 Bump version 2024-02-13 01:33:31 +01:00
b9abd74156 Create a .gitignore that matches all in the build_keycloak directory 2024-02-13 01:33:15 +01:00
a1c0bfda6c Bump version 2024-02-13 01:13:26 +01:00
617dcef09d Merge pull request #499 from keycloakify/vite
Vite
2024-02-13 01:04:54 +01:00
d9c406800a Fix tests 2024-02-12 23:57:21 +01:00
54b869def1 Release candidate 2024-02-12 23:43:21 +01:00
d80a583979 Better test for isStorybook 2024-02-12 23:42:31 +01:00
99bfd7379b Release candidate 2024-02-12 13:43:34 +01:00
5f257382fa Prevent accumulation of assets in build dir 2024-02-12 13:43:12 +01:00
e3e6847c82 Prevent users from having to use any 2024-02-12 13:27:07 +01:00
4ee0823acb Bump version 2024-02-12 01:47:21 +01:00
d466123b1c Use Keycloak 23.0.6 2024-02-12 01:41:08 +01:00
21cbc14a48 Release candidate 2024-02-12 01:34:54 +01:00
b2f2c3e386 Disalow releative basename in vite config 2024-02-12 01:34:34 +01:00
b03340ed10 Do not dynamically import "en" to make vite happy 2024-02-12 00:33:12 +01:00
5b563d8e9b Improve privacy on the buildinfo file that might be served 2024-02-12 00:32:18 +01:00
2790487fc7 Release candidate 2024-02-11 23:59:23 +01:00
ad5a368065 Run vite configResolved in dev mode as well 2024-02-11 23:59:10 +01:00
7c0a631a9a Fix crash in vite-plugin 2024-02-11 23:54:28 +01:00
4a8920749a release candidate 2024-02-11 23:48:09 +01:00
8ab118dd06 Remove path-browserify dependency 2024-02-11 23:47:58 +01:00
e6661cb898 Fix build 2024-02-11 23:22:37 +01:00
1671850714 Release candidate 2024-02-11 20:21:05 +01:00
d568bafe04 Remove unessesary file 2024-02-11 20:20:52 +01:00
43dcce8478 Add cache to getNpmWorkspaceRootDirPath 2024-02-11 20:20:38 +01:00
ad70a4cffd Make Vite run copy-keaycloak-resources-to-public 2024-02-11 20:15:18 +01:00
6d4a948dd8 minor refactor 2024-02-11 19:25:52 +01:00
839ba6a964 Improve monorepo support 2024-02-11 18:28:58 +01:00
b5cfdb9d0a Store vite plugin output in cache dir path 2024-02-11 16:17:58 +01:00
9706338182 Release candidate 2024-02-11 12:05:05 +01:00
05f52c3d23 Remove unessesary log 2024-02-11 12:04:50 +01:00
df3acb6932 Release candidate 2024-02-11 10:38:12 +01:00
b3c242595e Fix PUBLIC_URL typing 2024-02-11 10:37:22 +01:00
26985f8d81 Release candidate 2024-02-11 00:21:21 +01:00
05e5e4efec When is storybook, don't print mock related warning in console 2024-02-11 00:21:04 +01:00
e88be30fc8 Merge branch 'keycloakify:main' into additional-account-pages 2024-02-10 22:50:42 +02:00
4d67f16e94 Release candidate 2024-02-10 20:40:59 +01:00
334ec1870a Throw if unrecognized bundler when getting BASE_URL for mocks 2024-02-10 20:40:41 +01:00
ef5e4fccd3 Replace the common asset path url in the ftl (upstream) to help pepole figure out what's going on 2024-02-10 20:04:08 +01:00
8535edcfd4 Relase candidate 2024-02-10 19:51:39 +01:00
bda76200d7 Base url that works everywhere in mocks 2024-02-10 19:51:24 +01:00
db0dc96cc7 Release candidate 2024-02-09 17:56:57 +01:00
6d62b5a150 Don't replace process.env.PUBLIC_URL 2024-02-09 17:56:28 +01:00
217439d673 Export polyfill of process.env.PUBLIC_URL 2024-02-09 17:52:26 +01:00
1f79a8f7dc Deprecate keycloakJsAdapter 2024-02-09 17:52:03 +01:00
7596786b18 Release candidate 2024-02-08 23:37:14 +01:00
2540b06c94 Tweak the resources of the default theme that are kept 2024-02-08 23:36:52 +01:00
43eeaf3002 Remove misleading comment in start-keycloak-container script 2024-02-08 14:41:07 +01:00
037cd150de Release candidate 2024-02-08 14:10:10 +01:00
ae0b059217 Fix a couple of bug in donwnload-builtin-keycloak-theme 2024-02-08 14:09:55 +01:00
8255ce1158 Release candidate 2024-02-08 00:56:55 +01:00
5bf905723c Feature -p or --project option for ease of use in monorepo setups #449 2024-02-08 00:56:33 +01:00
3e336f4937 Release candidate 2024-02-08 00:12:50 +01:00
cd1cc37916 Explicitely exclude a number of node deps from Keycloak commons for bundle size 2024-02-08 00:12:10 +01:00
4ad7183d7e Prevent crashing when github is not up to date yet 2024-02-07 21:22:58 +01:00
e1b52e7439 Implement remote caching mechanism to prevent full download of the Keycloak sources 2024-02-07 21:17:09 +01:00
dca8c9f9d7 Merge main, fix runtime error in scripts, fix clean build 2024-02-07 20:01:26 +01:00
22496e36eb feat: Addition of Sessions page 2024-02-07 15:18:27 +02:00
7e4eba6376 Update README, add poll 2024-02-07 10:29:46 +01:00
f642a56eaa Release candidate 2024-02-06 08:00:07 +01:00
c091089830 Apply #502 from main 2024-02-06 07:59:21 +01:00
18900d20e1 Update All contributors 2024-02-06 07:56:06 +01:00
6c622b1580 Remove TODO comment 2024-02-06 07:55:32 +01:00
4290cd23b2 Remove what seems to be dead code (not used in the starter nor in onyxia, besite, looks fishy) '//TODO: Write a test case for this' for ref and .chunk.css", 2024-02-06 07:29:48 +01:00
5076c1e93f Unit test passing 2024-02-06 07:28:03 +01:00
884b701fc6 Removing keycloak-resources dir from dist dir after build 2024-02-05 09:26:54 +01:00
73a8ec0295 Building version 2024-02-05 08:52:58 +01:00
a29b6097a4 Reintroduce doBuildRetrocompatAccountTheme (for now) 2024-02-04 10:25:48 +01:00
a9231e2ed8 Bump version 2024-02-04 06:31:10 +01:00
5f4669a7a6 Merge pull request #502 from giorgoslytos/fix/login-otp-radio-inputs 2024-02-03 10:53:58 +01:00
75c54df109 Fix some errors in base account v1 theme 2024-02-03 08:30:06 +01:00
2a07f7151d Merge pull request #503 from keycloakify/all-contributors/add-giorgoslytos
docs: add giorgoslytos as a contributor for code
2024-02-03 08:13:16 +01:00
b6ecff2dd3 docs: update .all-contributorsrc [skip ci] 2024-02-03 07:11:46 +00:00
83df27ec99 docs: update README.md [skip ci] 2024-02-03 07:11:45 +00:00
ca255985c0 fix: radio inputs on login-otp page 2024-02-02 14:35:14 +02:00
82f34c38f6 Bump version 2024-02-01 06:22:54 +01:00
694b4c8027 Reintroduce doBuildRetrocompatAccountTheme (for now) and fix multiple things in the account default theme 2024-02-01 06:22:33 +01:00
bd25621b2c Remove dead code 2024-01-31 21:56:46 +01:00
fde34be270 Compiling build 2024-01-30 07:10:53 +01:00
7c7ce159fe Complete build option 2024-01-30 06:55:26 +01:00
5a57bb59e5 Refactor 2024-01-30 06:38:26 +01:00
cd278f4ab5 Refactor 2024-01-30 06:37:49 +01:00
8b24e23721 refactor 2024-01-30 06:04:05 +01:00
22fa1411bf Moving on 2024-01-30 05:54:36 +01:00
2799a52d0c Implement router for js remplacer depending of the bundle (vite or webpack) 2024-01-30 01:24:44 +01:00
4c2e01a7a8 Done with implementation of webpack js replacer test case for support of non root base 2024-01-30 00:56:47 +01:00
7267d2ef38 Add a test case for the code that enable the import to work in webpack if base isn't / 2024-01-30 00:37:30 +01:00
1eb6b154f7 Rearenge test case 2024-01-30 00:15:08 +01:00
f55d61bf0b Rename test case file 2024-01-30 00:10:59 +01:00
5b350274bd Fundation 2024-01-30 00:06:17 +01:00
b6d2f9f691 Disable tests for now 2024-01-27 19:00:45 +01:00
81106b5deb Release candidate 2024-01-27 18:51:05 +01:00
66e595e649 Vite investigations 2024-01-27 18:49:29 +01:00
33b7bb6184 Bump version 2024-01-26 04:09:54 +01:00
7d9130b2af Remove the doBuildRetrocompatAccountTheme and the retrocompat_ option in dropdown 2024-01-26 03:39:48 +01:00
482d71743b Bump version 2024-01-26 02:03:06 +01:00
1db37a4727 Do not include retrocompat_ in META-INF when using doBuildRetrocompatAccountTheme false 2024-01-26 02:02:45 +01:00
194d16ff91 Bump version 2024-01-26 00:56:45 +01:00
b1e2284c0e Fix condition for displaying info in login page 2024-01-26 00:55:50 +01:00
70d1aa70a3 Bump version 2024-01-18 18:09:59 +01:00
3b17d6e0ab Merge pull request #496 from keycloakify/all-contributors/add-Moulyy
docs: add Moulyy as a contributor for code
2024-01-18 18:09:10 +01:00
9a5819b93b docs: update .all-contributorsrc [skip ci] 2024-01-18 16:45:36 +00:00
a260cd67b0 docs: update README.md [skip ci] 2024-01-18 16:45:35 +00:00
64111fb0ec Merge pull request #495 from Moulyy/fix/add_password_visibility_classkey
Add "kcInputGroup" in ClassKey type definition and useGetClassName
2024-01-18 17:43:48 +01:00
faf2be23d9 Add "kcInputGroup" in ClassKey type definition and the patternfly class associated in useGetClassName 2024-01-18 17:31:16 +01:00
0eb4a6a315 Merge pull request #494 from alexted/patch-1
Update README.md
2024-01-18 16:59:17 +01:00
85673250ed Update README.md
extra "v" deleted  in several places of changelog highlights
2024-01-18 17:48:10 +02:00
09daa741ce Install pnpm if needed 2024-01-18 01:02:14 +01:00
55e2379aab Bump version 2024-01-18 00:57:02 +01:00
9937977203 Merge branch 'main' of https://github.com/keycloakify/keycloakify 2024-01-18 00:56:26 +01:00
c897e7491a Use message of Keycloak version 23.0.4 #493 2024-01-18 00:54:09 +01:00
0a74a95283 Bump version 2024-01-15 11:02:00 +01:00
74ef2c3dff Merge pull request #489 from law108000/main
refactor: point the import statement to keycloakify instead of internal
2024-01-15 11:01:14 +01:00
9976dfacc0 Merge pull request #491 from keycloakify/all-contributors/add-law108000
docs: add law108000 as a contributor for code
2024-01-15 10:59:17 +01:00
659f8ddc7a docs: update .all-contributorsrc [skip ci] 2024-01-15 09:56:30 +00:00
9e4cc2ae57 docs: update README.md [skip ci] 2024-01-15 09:56:29 +00:00
a27d78fcdf refactor: point the import statement to keycloakify instead of internal
align import statement with other page templates
2024-01-15 14:03:01 +08:00
e507435bcb Bump version 2024-01-10 13:01:23 +01:00
d5f234909f Merge branch 'main' of https://github.com/keycloakify/keycloakify 2024-01-10 12:43:11 +01:00
c17f721625 Bump version 2024-01-10 12:43:04 +01:00
600705130f #483: Enable the Template prop not to be lazy 2024-01-10 12:42:48 +01:00
5c5dce1422 Bump version 2023-12-18 13:19:54 +01:00
53585bf2f0 Merge pull request #476 from BlackVoid/feature/document-title
Fixes #473: Set document title using the "loginTitle" translation
2023-12-18 13:18:51 +01:00
116f88a503 Fixes #473: Set document title using the "loginTitle" translation 2023-12-18 11:56:37 +01:00
aaba8cd2c7 Bump version 2023-12-14 15:34:13 +01:00
b67aeb0d3a Fix tests #471 2023-12-14 15:33:57 +01:00
f620562d68 docs: update .all-contributorsrc [skip ci] 2023-12-14 15:09:10 +01:00
5231d0eaa1 docs: update README.md [skip ci] 2023-12-14 15:09:09 +01:00
cb470e3573 Handle CSS with multiple urls 2023-12-12 14:51:09 +00:00
0a0f90aa2e Bump version 2023-12-04 14:10:25 +01:00
635207d12c Generate a README alongside the jars to indicate why there's two jar file 2023-12-04 14:10:10 +01:00
5e4a829413 Bump version 2023-12-01 00:54:36 +01:00
b13b3fd92e Mention the nessesity to use retrocompat- with older Keycloak versions 2023-12-01 00:54:19 +01:00
564dc8e6f1 Bump version 2023-12-01 00:03:58 +01:00
6e4cced8c6 Rename original- to retrocompat- 2023-12-01 00:03:44 +01:00
29a4a5027c Bump version 2023-11-30 22:37:13 +01:00
ee327448b4 Delete original- jar 2023-11-30 22:36:58 +01:00
d078960c5c Bump version 2023-11-30 18:53:17 +01:00
2e8cd375fc Merge pull request #462 from BlackVoid/fix/client-attributes
Fixes #460: Fixes KcContext to contain attributes for client object
2023-11-30 18:52:26 +01:00
1f6751cb01 Fixes #460: Fixes KcContext to contain attributes for client object 2023-11-30 17:17:58 +01:00
3cca4e31cd Merge pull request #463 from keycloakify/all-contributors/add-BlackVoid
docs: add BlackVoid as a contributor for code
2023-11-30 17:10:29 +01:00
b93902800c docs: update .all-contributorsrc [skip ci] 2023-11-30 16:06:55 +00:00
70f6bb3fda docs: update README.md [skip ci] 2023-11-30 16:06:54 +00:00
c075cb6311 Merge pull request #461 from BlackVoid/fix/template
Fixes #459: Use Template from props
2023-11-30 17:03:36 +01:00
d7db85b062 Fixes #459: Use Template from props 2023-11-30 16:29:53 +01:00
b442e7d958 Merge pull request #457 from keycloakify/all-contributors/add-xgp
docs: add xgp as a contributor for code
2023-11-28 13:06:04 +01:00
a495ae637f docs: update .all-contributorsrc [skip ci] 2023-11-28 12:05:49 +00:00
94748a96a9 docs: update README.md [skip ci] 2023-11-28 12:05:48 +00:00
7657429054 Release v9 2023-11-28 12:53:37 +01:00
2ff6dbf975 Update README 2023-11-28 12:53:10 +01:00
4f34628c14 Merge pull request #414 from keycloakify/v9
v9: Support Keycloak 22 and decoupling account / login builtin resources
2023-11-28 12:18:07 +01:00
6ff2111cee #389 https://github.com/p2-inc/keycloak-account-v1/issues/3 2023-11-28 12:17:16 +01:00
85957980f6 update CI 2023-11-26 16:24:57 +01:00
a6dcfe2c87 Release candidate 2023-11-26 16:14:25 +01:00
c32d590fbb #389 https://github.com/xgp/keycloak-account-v1/issues/3 2023-11-26 16:10:34 +01:00
ab41462f71 Actually resolve conflict with main 2023-11-26 15:07:27 +01:00
951f16b1a5 Merge pull request #456 from keycloakify/v9_tmp
Rebase v9 to main
2023-11-26 14:46:28 +01:00
b5818888bb Merge branch 'v9' into v9_tmp 2023-11-26 14:45:23 +01:00
1a326bf7e4 Bump version 2023-11-22 18:58:04 +01:00
e1afc1cf7a Add themeVersion in KcContext type 2023-11-22 18:57:43 +01:00
bb007ddce5 fmt 2023-11-22 11:44:58 +01:00
b5dd0317c7 Update README.md 2023-11-22 11:39:10 +01:00
3c54541a73 Bump version 2023-11-19 03:27:54 +01:00
2657f01135 Enable to ignore part of the HTML 2023-11-19 03:27:40 +01:00
7223409eb1 Bump version 2023-11-07 16:33:33 +01:00
c41eae63e7 Fix info.ftl page rendering in storybook 2023-11-07 16:33:19 +01:00
c8b85c43aa Bump version 2023-11-04 16:47:58 +01:00
e918788c3f Reverse previous change, it breaks cra build 2023-11-04 16:47:13 +01:00
b53f4f997c Bump version 2023-11-04 16:29:36 +01:00
481d93ebc4 Create symlink to build in keycloak-resource for test env that better reflect prod 2023-11-04 16:29:09 +01:00
1ce666f136 Bump version 2023-11-04 15:50:51 +01:00
49a8e702bc Merge pull request #447 from celinepelletier/add-oauth2-device-flow-pages
feat: add login-oauth2-device-verify-user-code and login-oauth-grant pages
2023-11-04 00:14:52 +01:00
5d59e652d7 feat: add login-oauth2-device-verify-user-code and login-oauth-grant in storybook 2023-11-03 16:48:40 -04:00
02af8c7311 Merge pull request #448 from keycloakify/all-contributors/add-celinepelletier
docs: add celinepelletier as a contributor for code
2023-11-03 21:31:55 +01:00
fadf4e867c docs: update .all-contributorsrc [skip ci] 2023-11-03 20:29:27 +00:00
0839859fef docs: update README.md [skip ci] 2023-11-03 20:29:26 +00:00
c122b48e35 feat: add login-oauth2-device-verify-user-code and login-oauth-grant pages 2023-11-03 16:06:46 -04:00
cebb297bbf Merge branch 'main' of https://github.com/keycloakify/keycloakify 2023-10-26 12:56:47 +02:00
2e31b796f7 Bump version 2023-10-26 12:52:06 +02:00
e0a61b51cb Little fix on LoginConfigTotp.tsx 2023-10-26 12:51:43 +02:00
46e50e622b Bump version 2023-10-24 16:23:28 +02:00
7cfa1df0b2 Merge pull request #440 from keycloakify/discussion_432
Discussion 432
2023-10-24 16:22:57 +02:00
8a63648339 Release candidate 2023-10-22 16:39:34 +02:00
bb6b026720 https://github.com/keycloakify/keycloakify/discussions/432#discussioncomment-7332729 2023-10-22 16:39:15 +02:00
2a13b314dc Bump version 2023-10-22 16:05:09 +02:00
4506b3f6d4 Remove some dead code 2023-10-22 16:04:47 +02:00
804abef0de #433 2023-10-22 15:58:11 +02:00
7e932b920e Bump version 2023-10-20 15:06:29 +02:00
46fdfbc507 Merge pull request #438 from keycloakify/all-contributors/add-rome-user
docs: add rome-user as a contributor for code
2023-10-20 15:05:48 +02:00
a4ff8607c5 docs: update .all-contributorsrc [skip ci] 2023-10-20 13:05:34 +00:00
7fe4eeda57 docs: update README.md [skip ci] 2023-10-20 13:05:33 +00:00
9f25cddaa4 Allow "keycloak_theme" as a theme source directory
Fixes #429.
2023-10-08 18:12:57 -07:00
eb64fe60d0 Bump version 2023-10-09 00:49:50 +02:00
36f404e17d Preserve css order 2023-10-09 00:49:35 +02:00
5398590939 https://github.com/keycloakify/keycloakify/discussions/371#discussioncomment-7223711 2023-10-09 00:49:18 +02:00
96d5cfea14 Bump version 2023-10-02 22:49:31 +02:00
79007ebd55 #427 2023-10-02 22:49:04 +02:00
fcb519dac3 Bump version 2023-09-27 04:08:13 +02:00
2b487aa959 Merge pull request #423 from zavoloklom/fix/js-code-path-replacer
fix: change JS path transformation for static resources
2023-09-27 04:07:41 +02:00
733feadcb2 Merge pull request #425 from keycloakify/all-contributors/add-zavoloklom
docs: add zavoloklom as a contributor for test, and code
2023-09-27 03:59:00 +02:00
5ae568f19c docs: update .all-contributorsrc [skip ci] 2023-09-27 01:58:05 +00:00
0e51807856 docs: update README.md [skip ci] 2023-09-27 01:58:04 +00:00
b6eb165207 https://github.com/keycloakify/keycloakify/discussions/422 2023-09-25 13:41:51 +02:00
d26dbf4b3d fix: change JS path transformation for static resources
- Handle both arrow functions and traditional function expressions
- Add tests to ensure correctness of transformations
2023-09-24 23:42:10 +02:00
e06ef01f72 Merge branch 'main' into v9 2023-09-22 15:52:23 +02:00
a722582709 Bump version 2023-09-22 15:51:52 +02:00
de64deb5c5 #421 2023-09-22 15:51:18 +02:00
7de54a2cc4 Fix tests 2023-09-04 03:26:30 +02:00
c788b8cc82 Release candidate 2023-09-04 02:49:58 +02:00
cb8db1a541 Fix build 2023-09-04 02:49:32 +02:00
8a7a551c3b Fix mock path error in account 2023-09-04 02:38:19 +02:00
84d180b810 Fix bug with asset paths 2023-09-04 02:34:10 +02:00
de261a27ca Do not display that the jar have been created if we don't create it. 2023-09-04 02:29:16 +02:00
28288a8f7b Build retrocompatible account theme 2023-09-04 02:16:55 +02:00
cd8548fc32 Remove extraThemeNames option in favor of extending themeName to accept array 2023-09-04 01:19:21 +02:00
37dbd49589 Rename extraThemeNames to themeVariantNames 2023-09-04 00:53:57 +02:00
5af8d67b62 Refactor and update docker script 2023-09-04 00:25:36 +02:00
72e6309c4a Fix warning 2023-09-03 23:32:21 +02:00
18f0f3cce1 Refactor build option managment 2023-09-03 23:26:34 +02:00
8c3e9ff192 Remove inhouse bundler, we actually need Maven to build now 2023-09-03 21:10:20 +02:00
21d6d27435 Rename build option, update readme 2023-09-03 21:02:51 +02:00
39ff7913d6 https://github.com/xgp/keycloak-account-v1/issues/3 2023-09-03 07:14:57 +02:00
402c6fc64a Fix log message when prompting which version to download 2023-09-03 01:38:38 +02:00
a1f934466c Update resolution with Keycloak 22 and up 2023-09-01 17:45:33 +02:00
15aa114579 Release v8 2023-08-28 20:11:32 +02:00
b9cc82e37d Show how to persist cache between builds 2023-08-28 20:09:48 +02:00
8af9c8b150 Release candidate 2023-08-28 19:25:31 +02:00
7dcc985222 Remove debug log 2023-08-28 19:25:15 +02:00
9c2bc19897 Give futher instruction for migrating to v8 2023-08-28 19:17:32 +02:00
801b08359a Release candidate 2023-08-28 18:35:55 +02:00
c469dee158 Accomodate https://github.com/keycloakify/keycloakify/pull/65#issuecomment-991896344 and #406 2023-08-28 18:35:37 +02:00
2aa7eda1e9 Fix typo and formatting 2023-08-25 08:42:00 +02:00
f1246c9e00 Add version note 2023-08-24 09:44:00 +02:00
2749cbe4d1 Disable test with starter for now 2023-08-24 09:17:12 +02:00
d2a9280ab3 Update CI 2023-08-24 09:07:09 +02:00
8e25ee0fc9 Release candidate for v8 2023-08-24 09:06:42 +02:00
55026f913b Remove debug console.log 2023-08-24 09:05:58 +02:00
7cc40e2453 Merge pull request #404 from keycloakify/smaller_jar_size
Smaller jar size
2023-08-24 09:02:03 +02:00
cb6b19952d Merge pull request #396 from ddubrava/remove-message-from-kc-context-mock
feat: remove message from kcContextCommonMock
2023-08-24 09:00:16 +02:00
983af57842 Actually remove non used resources 2023-08-24 08:58:00 +02:00
3c2820dc31 Meta progaming for detecting static assets usage a build time 2023-08-23 08:13:09 +02:00
1c25b69160 Remove --external-assets option 2023-08-21 05:54:17 +02:00
641cc38ae4 Remove unused parameter 2023-08-21 04:29:32 +02:00
cd68b07e19 Build keycloak static assets and improve cache mechanism to keep build time in check https://github.com/xgp/keycloak-account-v1/issues/3 2023-08-21 04:26:58 +02:00
2b252c9abb npm install missing resources 2023-08-20 03:00:45 +02:00
e2e8370bb9 Bump version 2023-08-20 02:58:29 +02:00
e9e31394c4 #380 2023-08-20 02:58:10 +02:00
2825ccbcd5 Update README.md 2023-08-19 19:44:49 +02:00
377a14ff72 Update README.md 2023-08-19 19:44:13 +02:00
a83997b9b4 Update README.md 2023-08-19 08:53:29 +02:00
3e155d8e80 Restore starter test build step 2023-08-15 20:27:09 +02:00
6953b72ee6 Temporarely comment the test with the starter App in order to be able to release 2023-08-15 19:50:10 +02:00
ab370a1dda Merge pull request #400 from keycloakify/fix/usernameEditDisabled-usernameHidden
fix: update WithImmutablePresetUsername story parameters
2023-08-15 19:20:05 +02:00
20845e5860 fix: update WithImmutablePresetUsername story parameters 2023-08-15 08:59:00 +02:00
9ed3257006 Bump version 2023-08-14 21:57:37 +02:00
2221e30c0a fmt 2023-08-14 21:57:19 +02:00
ce43dca23b Merge pull request #399 from keycloakify/fix/usernameEditDisabled-usernameHidden
fix: usernameEditDisabled renamed to usernameHidden
2023-08-14 21:56:03 +02:00
4acf5d0931 fix: usernameEditDisabled renamed to usernameHidden
- update KcContext definition for login.ftl
- update the username field rendering conditions in Login.tsx

Closes #397
2023-08-14 15:53:05 +02:00
b742ed73aa feat: remove message from kcContextCommonMock 2023-08-11 12:54:09 +02:00
5156b2e0cc Bump version 2023-08-07 15:21:43 +02:00
6b81cf4a24 #391 2023-08-07 15:21:22 +02:00
cca3a68fe4 Merge branch 'main' of https://github.com/keycloakify/keycloakify 2023-08-07 15:10:48 +02:00
adb2904872 Bump version 2023-08-07 15:10:43 +02:00
d68b8d03dd #392 2023-08-07 15:10:25 +02:00
e7afb88f22 Update README.md 2023-08-02 12:55:08 +02:00
48cbfc64c0 Add warning about Keycloak 22 2023-08-02 09:15:10 +02:00
0b067858bc Bump version 2023-08-02 09:04:05 +02:00
2d44d98f17 Test with Keycloak 21.1.2 by default, use --features=declarative-user-profile for enabling User Profile features 2023-08-02 08:56:47 +02:00
74ef3096ae Can't support Account theme in Keycloak 22 2023-08-02 08:48:38 +02:00
8f1163fd75 Release candidate 2023-08-01 19:10:25 +02:00
a240d503c5 Hotfix for #388 2023-08-01 19:09:18 +02:00
e331a641b2 Bump version 2023-07-29 03:45:50 +02:00
85db4b8e0a Export the default PageId type 2023-07-29 03:45:33 +02:00
0aa139cf4a Bump version 2023-07-26 19:33:55 +02:00
4140ca6fbd #385 2023-07-26 19:33:38 +02:00
a8ce9da9ee Bump version 2023-07-24 23:22:51 +02:00
476a33c0ab #380 2023-07-24 23:22:16 +02:00
8e868c9fda Bump version 2023-07-24 22:39:16 +02:00
17c8b1a172 When no path PUBLIC_URL is empty string 2023-07-24 22:38:53 +02:00
b374c04d73 Bump version 2023-07-24 20:45:31 +02:00
e750d824ad #47 #384 2023-07-24 20:45:02 +02:00
dd4c50c3eb Bump version 2023-07-24 01:11:40 +02:00
20cc869299 #375 2023-07-24 01:11:34 +02:00
7214dbccdb Bump version 2023-07-07 17:03:03 +02:00
e6cebdd546 #377 2023-07-07 17:02:18 +02:00
0301003ccf Bump version 2023-06-27 17:52:18 +02:00
de2efe0c01 #369 2023-06-27 17:51:59 +02:00
90d765d7f6 #366: fix tests 2023-06-21 18:23:12 +02:00
3e0a1721ce Merge pull request #366 from Gravity-Software-srl/main
prevent crawlRec from crashing when dir_path does not exist
2023-06-21 18:17:11 +02:00
7214fbcd4c Bump version 2023-06-21 18:08:02 +02:00
4b8aecfe91 #364 2023-06-21 18:06:12 +02:00
387c71c0aa prevent crawlRec from crashing when dir_path does not exist 2023-06-21 16:24:58 +02:00
8d5ce21df4 Note about compatibility 2023-06-21 14:19:28 +02:00
f6dfcfbae9 Fix scripts build 2023-06-21 04:01:11 +02:00
69e9595db9 Bump version 2023-06-21 03:56:30 +02:00
de390678fd Deprecate the extraPages options, analyze the code to detect extra pages 2023-06-21 03:54:43 +02:00
cf9a7b8c60 Update json-schema 2023-06-21 02:56:55 +02:00
73e9c16a8d Fix some types approximations 2023-06-21 02:55:44 +02:00
9775623981 Bump version 2023-06-19 03:17:26 +02:00
20b7bb3c99 Fix error with inital select state 2023-06-19 03:17:12 +02:00
3defc16658 Bump version 2023-06-19 02:00:13 +02:00
0dbe592182 Match even if there's a cariage return after pritIfExists... 2023-06-19 02:00:02 +02:00
a7c0e5bdaa Bump version 2023-06-19 01:37:20 +02:00
3b051cbbea Fix build 2023-06-19 01:37:03 +02:00
f7edfd1c29 Add release note 2023-06-19 01:37:03 +02:00
b182c43965 Be more lax on the detection of field name. 2023-06-19 01:37:03 +02:00
4639e7ad2e Better exception message 2023-06-19 01:37:03 +02:00
56241203a0 Merge branch 'main' of https://github.com/keycloakify/keycloakify 2023-06-19 00:09:33 +02:00
8c8540de5d Analyze the code to see what field names are acutally used. Deprecates the customUserAttributes option, it's no longer needed 2023-06-19 00:09:21 +02:00
b45af78322 Bump version 2023-06-18 16:13:36 +02:00
98bcf3bf7e #362: otpCredentials is an array! 2023-06-18 16:13:18 +02:00
e28bcfced3 Bump version 2023-06-18 00:27:36 +02:00
a5bd990245 #362 2023-06-18 00:27:20 +02:00
58301e0844 Bump version 2023-06-17 00:52:35 +02:00
c9213fb6cd Bump version 2023-06-16 23:44:21 +02:00
641819a364 #359: fix: 'Local variable assigned outside a macro.' 2023-06-16 23:44:04 +02:00
3ee3a8b41d #359: Remove comment 2023-06-16 23:39:49 +02:00
5600403088 Bump version 2023-06-16 23:30:58 +02:00
3b00bace23 #359: Wrongely named FTL variable name fix 2023-06-16 23:30:36 +02:00
fcba470aad Release candidate 2023-06-16 11:31:40 +02:00
206e602d73 Accomodate #218 and #359 2023-06-16 11:29:04 +02:00
f98d1aaade Bump version 2023-06-12 21:34:42 +02:00
310f857257 #357 2023-06-12 21:34:24 +02:00
a2b1055094 Merge branch 'main' of https://github.com/keycloakify/keycloakify 2023-06-10 10:40:19 +02:00
f23ddecef3 Bump version 2023-06-10 10:40:10 +02:00
54687ec3c0 #355 2023-06-10 10:39:47 +02:00
545f0fcea5 Update discord link 2023-06-09 12:17:17 +02:00
5db8ce3043 Bump version 2023-06-08 23:25:30 +02:00
ed48669ae1 #354: Feature theme variant 2023-06-08 23:09:14 +02:00
69c3befb2d Wording 2023-06-05 06:01:47 +02:00
fc39e837ea Bump version 2023-05-25 07:40:41 +02:00
6df9f28c02 #277 fix storybook 2023-05-25 07:40:20 +02:00
f3d0947427 Bump version 2023-05-23 13:25:46 +02:00
3326a4cf2a Merge pull request #350 from abdurrahmanekr/patch-1
Change node.js 16.6.0 dependency that Array.prototype.at
2023-05-23 13:24:19 +02:00
9a6ea87b0c Change node.js 16.6.0 dependency that Array.prototype.at 2023-05-23 13:15:02 +03:00
12179d0ec0 Merge pull request #349 from keycloakify/all-contributors/add-kpoelhekke
docs: add kpoelhekke as a contributor for code
2023-05-15 16:56:38 +02:00
d4141fc51e Bump version 2023-05-15 16:43:16 +02:00
c32ab6181c docs: update .all-contributorsrc [skip ci] 2023-05-15 14:42:31 +00:00
3847882599 Merge pull request #348 from kpoelhekke/main
Parse datetime objects as iso strings
2023-05-15 16:42:31 +02:00
4db157f663 docs: update README.md [skip ci] 2023-05-15 14:42:30 +00:00
351b4e84c9 Parse datetime objects as iso strings 2023-05-15 16:09:15 +02:00
0c65561bcb Merge branch 'main' of https://github.com/keycloakify/keycloakify 2023-05-02 18:14:52 +02:00
00200f75a0 Fix cloud iam link 2023-05-02 18:14:41 +02:00
58614a74f5 Merge pull request #343 from keycloakify/all-contributors/add-satanshiro
docs: add satanshiro as a contributor for code
2023-05-02 16:22:00 +02:00
f3d64663a0 docs: update .all-contributorsrc [skip ci] 2023-05-02 14:21:20 +00:00
8be8c270f8 docs: update README.md [skip ci] 2023-05-02 14:21:19 +00:00
a56037f1c9 Bump version 2023-05-02 16:18:21 +02:00
2ff7955ec3 fmt 2023-05-02 16:17:53 +02:00
f2044c4d26 change name 2023-05-02 16:53:43 +03:00
4113f0faea fix-saml-post-form 2023-05-02 16:50:44 +03:00
bacd09484a Bump version 2023-05-02 04:51:36 +02:00
8253eb62bd Fix typo 2023-05-02 04:51:23 +02:00
70b659a0a0 Brag less 2023-05-02 04:36:20 +02:00
79ed74ab17 Somewhat dissociate from Keycloakify from React 2023-05-02 04:22:06 +02:00
93bb3ebd69 Bump version 2023-04-28 18:47:55 +02:00
e8e516159c Merge branch 'main' of https://github.com/keycloakify/keycloakify 2023-04-28 18:47:30 +02:00
1431c031a0 #340 2023-04-28 18:47:25 +02:00
209c2183e1 Bump version 2023-04-28 17:58:09 +02:00
0c98c282a0 Merge pull request #339 from keycloakify/fix/fix-broken-jar
fix: fix broken jar
2023-04-28 17:57:41 +02:00
58c10796a1 fix: fix broken jar
Many tools will handle zipfiles which lack directory entries
just fine, others will not. Looks like the JDKs JAR libs are
not  handling  it well. This commit will make sure to create
folder entries.
2023-04-28 16:59:06 +02:00
603e6a99f3 Update package.json 2023-04-27 18:00:14 +02:00
6622ebc04e Merge pull request #337 from keycloakify/fix/restore-missing-pom-xml-in-jar
Fix/restore missing pom xml in jar
2023-04-27 17:59:53 +02:00
465dbb4a8d fix formatting 2023-04-27 14:47:15 +02:00
08ae908453 fix: adjust test after adjusting jar.ts 2023-04-27 14:44:45 +02:00
c35a1e7c50 fix: fix paths after changing root path param meaning 2023-04-27 14:33:21 +02:00
ecb22c3829 fix: restore missing pom.xml in jar archive 2023-04-27 14:33:19 +02:00
eebf969f7e Bump version 2023-04-27 11:52:28 +02:00
5816f25c3e #334 2023-04-27 11:52:02 +02:00
b2a81d880d fmt 2023-04-25 01:42:42 +02:00
b10c1476a6 Bump version 2023-04-25 01:40:07 +02:00
e11cd09a12 Bump version 2023-04-25 01:40:07 +02:00
27575eda68 fix: collapse empty ca list to undefined 2023-04-25 01:40:07 +02:00
f33b9a1ec6 feat: honor npmrc settings for ssl ca, cert and strict ssh handling 2023-04-25 01:40:07 +02:00
7c45fff7ba Update README.md 2023-04-25 01:39:45 +02:00
ecdb0775cd Update CloudIAM logo 2023-04-25 01:39:21 +02:00
6ef90a56ed Bump version 2023-04-21 01:00:39 +02:00
71b86ff43b Realtime validation for account/password.ftl 2023-04-21 01:00:18 +02:00
0535e06ae1 Update Cloud IAM wording 2023-04-20 22:15:02 +02:00
6261f5e7cc Update CloudIAM logo and referal link 2023-04-20 22:11:52 +02:00
f256b74929 Bump version 2023-04-20 20:52:23 +02:00
4f1182a230 Feat sml-post-form.ftl #277 2023-04-20 20:51:46 +02:00
e7c20547f8 Bump version 2023-04-20 20:09:29 +02:00
9ab4c510fe #209 2023-04-20 20:08:47 +02:00
7d78c52064 Bump version 2023-04-20 18:15:18 +02:00
6223d91291 Update post build instructions 2023-04-20 18:15:03 +02:00
840b5e1312 Bump version 2023-04-20 13:18:17 +02:00
e69813f6e3 Fix small logical error 2023-04-20 13:17:57 +02:00
3c0c057e06 Bump version 2023-04-20 13:10:46 +02:00
984d12b3f2 Fix bad types in Account kcContext 2023-04-20 13:10:33 +02:00
61dc54f115 Bump version 2023-04-20 13:05:33 +02:00
34e47cccc1 Enable to redirect back to the application from the account pages 2023-04-20 13:05:01 +02:00
c170345550 Update README 2023-04-20 05:52:04 +02:00
1e40706f72 Update CNAME 2023-04-20 05:44:16 +02:00
ea1a747ebf Add all missing pages to the storybook 2023-04-20 05:41:34 +02:00
a14e967020 Add IdpReviewUserProfile.tsx story 2023-04-20 05:19:38 +02:00
0fff10d2c6 Bump version 2023-04-20 04:18:36 +02:00
7c2123614d Remove margin for canvas container 2023-04-20 04:18:17 +02:00
d149866703 Add story for password.ftl 2023-04-20 04:17:37 +02:00
18039140db Important fix, assets common where broken 2023-04-20 04:17:12 +02:00
4de9599018 Bump version 2023-04-20 03:37:31 +02:00
bb85829d71 Add referrerURI to the base type and make it optional 2023-04-20 03:37:11 +02:00
ff077943ec Fixe syntax error in DockContainer, remove story padding 2023-04-20 02:55:05 +02:00
f057114bcc Add account/password.ftl to storybook 2023-04-20 02:54:24 +02:00
e7bfe7f80d Add account/account.ftl to storybook 2023-04-20 02:52:49 +02:00
18112a97ab Deal with story ordering 2023-04-20 02:41:06 +02:00
8ee6fb58ac Reproduce directory layout #274 2023-04-20 01:55:13 +02:00
08831fc31d Add storybook Error page #274 2023-04-20 01:53:36 +02:00
c5c25394fb Hide addon pannel by default 2023-04-20 01:28:02 +02:00
2f649c9866 Remove forgoten console.log 2023-04-20 00:14:00 +02:00
91c5dd40fa Enable the storybook to default to canevas 2023-04-20 00:13:25 +02:00
e95e688cf0 Add rotating logo for the intro 2023-04-19 23:41:25 +02:00
9845f1de08 Update prettierignore 2023-04-19 23:09:30 +02:00
07032d312d Correctly load up the fonts 2023-04-19 22:29:46 +02:00
ccb5d32763 update yarn.lock 2023-04-19 19:08:56 +02:00
bf83e4b03b docs: update .all-contributorsrc [skip ci] 2023-04-19 19:08:56 +02:00
03b491763f docs: update README.md [skip ci] 2023-04-19 19:08:56 +02:00
3abc9edf0e Rename missnamed components 2023-04-19 19:04:48 +02:00
f9accc51d3 Bump version #303 2023-04-19 17:57:21 +02:00
c3bade81b4 Restore copy assets to public in the keycloakify script for backward compatibility 2023-04-19 17:50:27 +02:00
6edd1f00dd Update prettier ignore 2023-04-19 17:36:18 +02:00
02be899629 Merge pull request #325 from Gravity-Software-srl/main
fix typing of algToKeyUriAlg + totp interface + cleanup build dir
2023-04-19 17:34:24 +02:00
8e043f289a sync with upstream main 2023-04-19 17:23:52 +02:00
30fecf8578 Revert "add build option keepBuildDir"
This reverts commit 86884607ef.
2023-04-19 17:04:46 +02:00
1112da33e3 Fix build 2023-04-19 05:32:19 +02:00
ffa65e871e Fix build 2023-04-19 05:10:25 +02:00
f49c7b465b Release beta 2023-04-19 05:05:21 +02:00
e6f75156ec New script only for copying default assets to public 2023-04-19 05:04:11 +02:00
ebafeb19ad Bump version 2023-04-19 03:21:25 +02:00
5166c719c4 Better scripts 2023-04-19 03:21:04 +02:00
bf92ea8340 Update yarn.lock 2023-04-19 03:20:47 +02:00
cf1e595ba2 Clean up dynamically inserted assets when template is unmounted #274 2023-04-19 03:20:22 +02:00
2bf3296c0f Attempt to fix ci 2023-04-18 04:35:16 +02:00
11513f73b7 Add discord 2023-04-18 04:29:02 +02:00
b6f60c6835 Update prettierignore 2023-04-18 04:16:49 +02:00
e9d276010f Merge branch 'main' of https://github.com/keycloakify/keycloakify 2023-04-18 04:12:07 +02:00
b08c4b0b29 Update yarn.lock 2023-04-18 04:11:53 +02:00
d684807d96 Copy keycloak assets into storybook static #274 2023-04-18 04:04:55 +02:00
9a60ef7c47 Update README.md 2023-04-17 11:13:24 +02:00
cc446059de Moving on with setup of the reference storybook #274 2023-04-17 04:02:34 +02:00
d75b809c13 Bump version 2023-04-17 04:02:33 +02:00
9fc3998cf7 Avoid deprecating getKcContext #274 2023-04-17 04:02:18 +02:00
238baa72cf Bump version 2023-04-17 01:33:01 +02:00
089f0f7a87 More explicit naming 2023-04-17 01:32:06 +02:00
aa9d3d1931 Bump version 2023-04-17 00:46:45 +02:00
2fc6aed4f1 Correct the account password page 2023-04-17 00:46:30 +02:00
c2fdea7886 Bump version 2023-04-17 00:28:34 +02:00
c8f71946d4 We where copying login theme assets into accont theme 2023-04-17 00:27:49 +02:00
d1cc6ed88d Smarter getKcContext typing 2023-04-16 03:00:03 +02:00
f6e6cf3750 Better typing for createGetKcContext 2023-04-16 02:36:15 +02:00
d1c7491704 Create a storybook friendly getKcContext 2023-04-16 02:09:26 +02:00
fd49c2fd23 Add step to build storybook 2023-04-15 22:23:09 +02:00
f7fb2efcdd Setup Storybook v6 (I spent 2hours trying to use v7 instead but it isn't worth it 2023-04-15 22:18:11 +02:00
ff0608c202 Update Contributor list 2023-04-15 01:59:01 +02:00
e63e20eade update dontributor list 2023-04-15 01:56:06 +02:00
335292cf4c Merge pull request #323 from keycloakify/all-contributors/add-asashay
docs: add asashay as a contributor for test, and code
2023-04-15 01:55:22 +02:00
7cb927c8b8 docs: update .all-contributorsrc [skip ci] 2023-04-14 23:55:13 +00:00
802d6b3dad docs: update README.md [skip ci] 2023-04-14 23:55:12 +00:00
c16bf28369 update dontributor list 2023-04-15 01:54:05 +02:00
0de76ae613 Merge pull request #322 from keycloakify/all-contributors/add-marcmrf
docs: add marcmrf as a contributor for test, and code
2023-04-15 01:52:32 +02:00
880396e3a6 docs: update .all-contributorsrc [skip ci] 2023-04-14 23:52:23 +00:00
879fc2812d docs: update README.md [skip ci] 2023-04-14 23:52:22 +00:00
6ac6209bd0 Merge pull request #321 from keycloakify/all-contributors/add-lazToum
docs: add lazToum as a contributor for test, and code
2023-04-15 01:50:15 +02:00
d7fd76c568 docs: update .all-contributorsrc [skip ci] 2023-04-14 23:50:04 +00:00
543e08276f docs: update README.md [skip ci] 2023-04-14 23:50:03 +00:00
2e647b9196 Merge pull request #320 from keycloakify/all-contributors/add-juffe
docs: add juffe as a contributor for test, and code
2023-04-15 01:47:54 +02:00
69cf556582 docs: update .all-contributorsrc [skip ci] 2023-04-14 23:47:46 +00:00
e168ee2ae6 docs: update README.md [skip ci] 2023-04-14 23:47:45 +00:00
274d758ba8 Merge pull request #319 from keycloakify/all-contributors/add-0x-Void
docs: add 0x-Void as a contributor for test, and code
2023-04-15 01:46:03 +02:00
b52e35be7d docs: update .all-contributorsrc [skip ci] 2023-04-14 23:45:51 +00:00
7f1ba8f166 docs: update README.md [skip ci] 2023-04-14 23:45:50 +00:00
72a5b9bac5 Merge pull request #318 from keycloakify/all-contributors/add-aidangilmore
docs: add aidangilmore as a contributor for test, and code
2023-04-15 01:44:54 +02:00
34fb0c2753 docs: update .all-contributorsrc [skip ci] 2023-04-14 23:44:45 +00:00
e5f0885cb0 docs: update README.md [skip ci] 2023-04-14 23:44:44 +00:00
4f93190162 Reorder all contributor 2023-04-15 01:42:35 +02:00
9d1dcd278a Merge pull request #317 from keycloakify/all-contributors/add-revolunet
docs: add revolunet as a contributor for test, and code
2023-04-15 01:37:12 +02:00
45d4bce0e7 docs: update .all-contributorsrc [skip ci] 2023-04-14 23:36:46 +00:00
680a7206d3 docs: update README.md [skip ci] 2023-04-14 23:36:44 +00:00
8a08e9fd64 Add contributors 2023-04-15 01:35:52 +02:00
0080dabe09 Include Cloud IAM in the README 2023-04-15 00:44:48 +02:00
556ce60b27 Bump version 2023-04-13 15:10:27 +02:00
12857e3027 Update Account.tsx #306 2023-04-13 15:09:48 +02:00
10965b82a9 Merge remote-tracking branch 'upstream/main' 2023-04-12 11:45:11 +02:00
86884607ef add build option keepBuildDir
if set to true, will not cleanup build_keycloak directory
2023-04-12 11:44:37 +02:00
1ff0449332 removed "$" typo in LoginConfigTotp.tsx 2023-04-11 15:44:54 +02:00
57b056b388 Bump version 2023-04-07 02:04:17 +02:00
9058e9ac9d Merge branch 'main' of https://github.com/keycloakify/keycloakify 2023-04-07 01:39:46 +02:00
ad3de8bff5 Bump version 2023-04-07 01:39:30 +02:00
476b100b04 Remove warning about last release not working 2023-04-07 01:39:15 +02:00
b2c7c86609 Fix the ftl script 2023-04-07 01:38:45 +02:00
f8a8ec2e4d Bump version 2023-04-06 22:41:52 +02:00
393a5ba125 Merge pull request #304 from keycloakify/fix-broken-jar
Fix-broken-jar
2023-04-06 22:41:20 +02:00
466c2d3eb4 chore: reenable test cleanup 2023-04-06 22:06:42 +02:00
b325b3537f style: fix formatting 2023-04-06 22:06:14 +02:00
e429127313 chore(jar): add jar test 2023-04-06 22:02:45 +02:00
2d05521789 fix(jar): fix empty jar 2023-04-06 21:34:20 +02:00
564ffc2be9 infer type of algToKeyUriAlg from type of kcConfig + fix totp interface
- add more yarn dirs to .gitignore
2023-04-06 17:50:26 +02:00
feaf34c124 Include the theme version in kcContext 2023-04-06 16:38:13 +02:00
c1e0563eba latest release broken 2023-04-06 11:10:44 +02:00
1c66f35337 Merge pull request #302 from keycloakify/json-schema
feat: add keycloakify json schema
2023-04-06 08:19:54 +02:00
4a7dd64982 feat: add keycloakify json schema 2023-04-05 20:04:54 +00:00
a84f984a07 Bump version 2023-04-05 18:14:07 +02:00
1f31a228d7 Merge pull request #301 from keycloakify/lordvlad-patch-1
Update jar.ts
2023-04-05 18:00:06 +02:00
309308db15 Update jar.ts
Yazl requires explicit handling of directories
2023-04-05 17:41:23 +02:00
a02c38ac45 Fix eject-keycloak-page 2023-04-04 02:53:56 +02:00
49e495dbbe Include keycloakfiy version number in kcContext (for debug purpose) 2023-04-04 01:40:55 +02:00
f6c2ccb0d6 Merge branch 'main' of https://github.com/keycloakify/keycloakify 2023-04-03 22:21:19 +02:00
dcd30f2cad Simplification on FTL script 2023-04-03 22:13:29 +02:00
e5d540ebd2 Fix CI 2023-04-03 22:03:05 +02:00
1073a610d6 Update CI 2023-04-03 22:03:04 +02:00
034f6f8b0e Bump version 2023-04-03 22:03:04 +02:00
3edb23be97 #297 2023-04-03 22:03:04 +02:00
d308c04465 Fix type error in useDownloadTerms 2023-04-03 22:03:04 +02:00
c5899eba94 Refactor Terms.tsx 2023-04-03 22:03:04 +02:00
97cb20b731 Fix CI 2023-04-03 21:56:48 +02:00
f58a5ad524 Merge branch 'main' of github.com:InseeFrLab/keycloakify
* 'main' of github.com:InseeFrLab/keycloakify:
  Update CI
  Bump version
  #297
  Fix type error in useDownloadTerms
  Refactor Terms.tsx
2023-04-03 20:59:59 +02:00
e00692956c refactor: don't catch top-level promises
the ts compiler will handle it for us
2023-04-03 20:59:34 +02:00
b2c1b41981 Update CI 2023-04-03 20:31:19 +02:00
ffa8440d1b Bump version 2023-04-03 20:19:43 +02:00
32f66b3eaa #297 2023-04-03 20:16:38 +02:00
42b196bd0b Fix type error in useDownloadTerms 2023-04-03 20:10:40 +02:00
68dab45931 Refactor Terms.tsx 2023-04-03 20:10:06 +02:00
af2dbb0389 Merge pull request #296 from keycloakify/introduce-yazl
Introduce-yazl
2023-04-02 23:02:16 +02:00
5abbc7f9a7 style: run prettier 2023-04-02 22:49:12 +02:00
dcfefad17f refactor(jar): introduce yazl for creating jars
* introduce yazl
* remove old zip code
* refactor jar code to make it better testable
* introduce unit test for jar creation
2023-04-02 22:47:42 +02:00
4ece6457fd Bump version 2023-04-02 03:10:38 +02:00
53e38336fb #287 Refacror 2023-04-02 03:10:16 +02:00
0b16df7731 Bump version 2023-04-01 23:00:19 +02:00
900125d92e fmt 2023-04-01 23:00:03 +02:00
6aaaf5a9d3 Merge pull request #289 from keycloakify/some-minor-fixes
Some-minor-fixes
2023-04-01 22:59:31 +02:00
bd2f6d8fee style: move loose test into test suite 2023-04-01 22:52:09 +02:00
baae22657e style: fix formatting 2023-04-01 22:44:13 +02:00
46264c85f4 Add unit test and fix some more use cases 2023-04-01 22:36:54 +02:00
2811eb6024 fix: fix typing 2023-04-01 22:08:00 +02:00
218c1a5a50 refactor: use path.sep to be cross-platform 2023-04-01 22:08:00 +02:00
ab5287a3d4 refactor: type-safe trimIndent 2023-04-01 22:07:59 +02:00
d55c62c073 Bump version 2023-04-01 16:32:10 +02:00
4833c34800 Merge pull request #293 from 0x-Void/add-select-authenticator-page
Add support for the select-authenticator.ftl page
2023-04-01 16:31:45 +02:00
fc70e657f0 Bump version 2023-04-01 14:02:48 +02:00
ee23f629f6 Add themeName option 2023-04-01 14:02:32 +02:00
44402c9571 Bump version 2023-04-01 13:31:56 +02:00
ffefb38161 #40 2023-04-01 13:31:35 +02:00
6d667f653e Bump version 2023-03-31 17:46:01 +02:00
1c75fed727 Merge pull request #290 from keycloakify/fix/unzip
refactor: use yauzl for unzipping
2023-03-31 17:45:13 +02:00
e7837aea88 feat: add select-authenticator page 2023-03-31 17:38:22 +02:00
9c133be779 fix: create cache dir if it doesn't already exist 2023-03-31 09:36:59 -06:00
71eb953fd3 Minor changes 2023-03-31 13:25:48 +02:00
f49ef21fed Merge branch 'main' into fix/unzip 2023-03-31 12:29:10 +02:00
6a6fa04ba0 Merge pull request #287 from keycloakify/vitest-integration
Vitest integration
2023-03-31 12:00:25 +02:00
83b0838c94 Minor fixes to the Vitest setup 2023-03-31 11:56:54 +02:00
4ebc1e671f feat(config): add ability to customize input/output directory 2023-03-30 21:24:11 -06:00
08c7e38587 refactor: use yauzl for unzipping 2023-03-30 22:56:58 +02:00
b863d9feb3 chore: add .devcontainer file 2023-03-30 02:47:54 -06:00
e527f043b0 test: add test for valid jar artifacts 2023-03-30 02:46:44 -06:00
58bb403787 test: refactor existing tests to vitest 2023-03-30 02:46:25 -06:00
e4725c23eb feat: add vitest testing 2023-03-30 02:45:43 -06:00
b0db8caf65 Bump version 2023-03-30 08:01:08 +02:00
3bcc6bdf93 Merge pull request #286 from willwill96/KEYCLOAKIFY-285
fix: pass only strings to trimIndent
2023-03-30 07:38:21 +02:00
eafb75a958 fix: do not swallow errors 2023-03-29 18:48:10 -06:00
31ca0939aa fix: pass only strings to trimIndent 2023-03-29 18:07:43 -06:00
7784fdcd6a Bump version #284 2023-03-29 21:36:27 +02:00
8247eef735 Merge pull request #269 from keycloakify/fix-download-cache
fix(download): fix download cache not behaving as expected
2023-03-29 21:35:44 +02:00
cb6629f301 fix(test): fix test after changes to downloadAndUnzip 2023-03-29 09:59:54 +02:00
3a6fe1b374 fix(cache): fix download caches
* also fix npm config running 4 times in the worst case
* factor out unzip methods
* factor and enhance trimindent
* factor out more utils
* restore windows build, which failed cause generate-i18n-messages did not write any files
2023-03-29 09:54:29 +02:00
0ba2f37004 Merge branch 'main' into fix-download-cache 2023-03-29 09:22:55 +02:00
e052dee753 Bump version 2023-03-28 10:01:15 +02:00
9c2ec32d12 Merge pull request #282 from juffe/add-update-email-page
feat: add update-email.ftl page
2023-03-28 10:00:23 +02:00
1669c38bc9 feat: add update-email.ftl page 2023-03-28 10:26:24 +03:00
c6ce6d1b49 #281: Add location and occupation to user attribute (as a patch until https://github.com/keycloakify/keycloakify/issues/40%23issuecomment-1202102662) 2023-03-28 05:19:35 +02:00
bc242b0aa7 fmt 2023-03-27 21:03:11 +02:00
41b67f6af4 Merge pull request #279 from bralandealmeida/fix/add-url-to-login-reset-password
Fix: add  to login reset password page
2023-03-27 21:01:43 +02:00
bef21e1cb9 fix: add url to login reset password page
fix: add  to login reset password page

fix: add urls to kc context mocks
2023-03-27 15:16:19 -03:00
8c73630f5a Update reamde 2023-03-27 17:47:26 +02:00
724953d5b7 Bump version 2023-03-25 05:11:47 +01:00
a22b231982 Mute max listener warning 2023-03-25 05:11:25 +01:00
910bfe2318 Fix previous release 2023-03-25 05:09:28 +01:00
70a524da46 Bump version 2023-03-25 04:56:28 +01:00
bf6c846fac Use locate theme dir in eject script 2023-03-25 04:56:17 +01:00
b83e4bef3f Bump version 2023-03-25 04:43:40 +01:00
9f7fe0d8f7 Fix error initialization email 2023-03-25 04:42:44 +01:00
741dee57e4 Bump version 2023-03-25 04:28:47 +01:00
fff4dba708 #276: Add build option to select keycloak default assets version 2023-03-25 04:28:10 +01:00
f4f7ab3e49 Make email theme initialization work with theme-only projects 2023-03-25 04:20:10 +01:00
88fe99b1b8 Bump version 2023-03-24 06:25:45 +01:00
92c1486f6a Fix previous release 2023-03-24 06:25:25 +01:00
caea64cef3 Fix build 2023-03-24 06:01:07 +01:00
90783d8ee8 Bump version 2023-03-24 05:51:01 +01:00
be57801e21 Fix email theme path 2023-03-24 05:50:40 +01:00
ff84786b4e Bump version 2023-03-24 05:44:00 +01:00
1e863672cb Find the email theme in src 2023-03-24 05:43:34 +01:00
fb98a9c383 Bump version 2023-03-24 04:14:57 +01:00
05163f22cb Rename InseeFrLab to Keycloakify 2023-03-24 04:14:41 +01:00
160f12d7d3 Rename UserProfileCommon to UserProfileFormFields 2023-03-24 04:12:52 +01:00
49e4e36184 Update README.md 2023-03-22 21:33:41 +01:00
c4f8879cda Bump version 2023-03-22 04:49:30 +01:00
8f54166653 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-03-22 04:48:08 +01:00
b9f020c447 Merge pull request #272 from willwill96/update-login-types
feat(login context): improve login typings
2023-03-22 04:22:13 +01:00
c357f3eb4d Mention storybook in the changelog 2023-03-22 03:48:21 +01:00
7ebbb0417a feat(login context): improve login typings 2023-03-21 19:47:05 -07:00
6e4b4173b5 Add link to storybook #274 2023-03-22 03:46:30 +01:00
87ebad7efb Bump version 2023-03-22 03:34:59 +01:00
3294aaed3b Pefrorm Keycloak theme download in paralel 2023-03-22 03:34:44 +01:00
0e21f3eab6 Add missing mock 2023-03-22 03:07:33 +01:00
9fcf692cb8 Bump version 2023-03-22 03:03:24 +01:00
da577ea3cc Add stateChecker to password context 2023-03-22 03:02:44 +01:00
6ae1d8938a Fix eslint 2023-03-22 03:00:44 +01:00
3e18a7390c Bump version: Release v7 🚀 2023-03-22 01:55:49 +01:00
5f43f1afc6 Shot a new post build tutorial video 2023-03-22 01:46:05 +01:00
2fc9c03430 Relase candidate 2023-03-21 23:39:40 +01:00
d951a9ba02 Improve post build instructions 2023-03-21 23:21:30 +01:00
93385af675 Release candidate 2023-03-21 19:44:41 +01:00
dd75d0ece7 Account theme specific instructions 2023-03-21 19:44:01 +01:00
dcd37ed916 Relase candidate 2023-03-21 18:32:21 +01:00
2e4d722d7f Return undefined if the context dosen't match the theme 2023-03-21 18:32:00 +01:00
09543400ca Relase candidate 2023-03-21 16:47:58 +01:00
8b101e5043 Fix error in log related to getLogoUrl 2023-03-21 16:47:30 +01:00
b31fff9c2b Release candidate 2023-03-21 15:16:37 +01:00
0c5b100dd9 Update post build instructions 2023-03-21 15:16:23 +01:00
253825a35e fix(download): fix download cache not behaving as expected 2023-03-21 14:48:16 +01:00
8937d19891 Release candidate 2023-03-21 14:21:27 +01:00
0fdd9e75a6 Fix the helper type that enable to extend the KcContext 2023-03-21 14:21:09 +01:00
77da00c2c5 Release candidate 2023-03-21 06:05:30 +01:00
3744080d11 Relase candidate 2023-03-21 05:27:53 +01:00
c9e546a8fd Fix numerous bugs, improve structure 2023-03-21 05:27:31 +01:00
6691992a79 Bump version 2023-03-21 03:02:05 +01:00
1ea0f4c339 Fix usePrepareTemplate 2023-03-21 03:01:49 +01:00
8bfa117be2 Release candidate 2023-03-21 02:36:30 +01:00
b3acecdcea Use children prop for Template 2023-03-21 02:36:13 +01:00
ec479c7e91 Update initialize-email-theme script 2023-03-21 01:50:21 +01:00
fd7760d9ed Update eject-keycloak-page 2023-03-21 01:18:02 +01:00
c9fcec6889 Replace postinstall by prepare 2023-03-20 05:39:34 +01:00
fd901ef2cf Run build only on latest ubuntu and node 16 2023-03-20 05:39:12 +01:00
8afdaa8f0e Relase candidate 2023-03-20 05:29:14 +01:00
254bfccc62 Feature Account theme customization 2023-03-20 05:28:53 +01:00
5b4aeca63c Done implementing the templates and the two first pages 2023-03-20 05:14:25 +01:00
17871daf0c Build account theme 2023-03-20 01:48:03 +01:00
cdd4460968 Rename iitialize-email-theme 2023-03-20 01:30:42 +01:00
fa6a37880b Fix script for account and email 2023-03-20 01:30:16 +01:00
d4e1dabe12 Fix build error 2023-03-20 00:59:53 +01:00
a3fd376b24 Refactor of the i18n download mechanism 2023-03-20 00:58:35 +01:00
aaac1f54e8 Untrack generated messages 2023-03-20 00:03:15 +01:00
41c0329822 Move everything under the login dir 2023-03-19 23:12:45 +01:00
74d48fd7e1 relase candidate 2023-03-19 16:59:57 +01:00
9c3c953129 Fix download i18n script 2023-03-19 16:59:27 +01:00
f5cae18da7 Relase candidate 2023-03-19 16:58:42 +01:00
59d47592d9 Fix other scripts 2023-03-19 16:58:26 +01:00
2b6c991190 Fmt 2023-03-19 16:47:00 +01:00
26020ba8bb Relase candidate 2023-03-19 16:37:48 +01:00
b573bc20b5 Fix 2023-03-19 16:37:35 +01:00
210dbfa265 Bump version 2023-03-19 16:31:25 +01:00
b37cac93ff Fix 2023-03-19 16:31:13 +01:00
eea953efb6 Relase candidate 2023-03-19 16:28:51 +01:00
7ad9d7b291 Remove all unessesary relative import 2023-03-19 16:28:38 +01:00
20937c4f72 Relase candidate 2023-03-19 15:53:13 +01:00
dbbfa07639 Feature new script: Eject-keycloak-page 2023-03-19 15:52:41 +01:00
9e1a4cad5c Update homepage 2023-03-19 15:49:27 +01:00
02bbedcfca Remove no longer used tools 2023-03-19 14:56:30 +01:00
cd70d90914 Refactor completed 2023-03-19 14:48:01 +01:00
819f297de8 Better i18n API 2023-03-19 14:03:06 +01:00
0608adde89 Better naming convention for i18n API 2023-03-19 13:54:39 +01:00
ad7bcf4669 Fix lining script 2023-03-18 19:05:27 +01:00
2eccc86e83 Remove eventEmitter warning 2023-03-18 19:02:17 +01:00
16d18f23a1 Refactor of the main component and i18n 2023-03-18 18:54:33 +01:00
5631ae1b6c Better naming convention 2023-03-18 18:27:50 +01:00
5fb29992f6 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-03-18 16:58:21 +01:00
910d633ac2 Fix build test app 2023-03-18 16:57:58 +01:00
32f8380e56 Fix build:test 2023-03-18 16:20:21 +01:00
43e4dd6bb6 Fix build for real 2023-03-18 16:17:33 +01:00
4f0b1688db Thank you @justkey007 for tsc-alias 2023-03-18 15:49:45 +01:00
9e75ee09bb fix(deps): update garronej_modules_update 2023-03-18 09:11:03 +00:00
9ae8822e00 Fix build 2023-03-18 06:25:19 +01:00
babffd1fe6 Make the project compile 2023-03-18 06:14:05 +01:00
5615d62032 Scripts dir outside of the src dir 2023-03-18 02:12:12 +01:00
4b89d15c1e progressing 2023-03-17 20:40:29 +01:00
815f510d5f Exclude tsbuild info of the bin dir from the bundle 2023-03-16 23:03:18 +01:00
199ba193be Rename getKcContext dir to kcContext 2023-03-16 23:02:06 +01:00
4ae9bd3f9a Fix repo url in package.json 2023-03-16 22:57:24 +01:00
1c9cf639ea Remove console log 2023-03-16 22:44:44 +01:00
0040464ca1 Move lib up one level 2023-03-16 22:43:09 +01:00
79997efbb6 First commit towars supporting account theme 2023-03-16 22:13:46 +01:00
0e42009798 Fix bin test script 2023-03-16 14:39:40 +01:00
93fdcb8739 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-03-16 13:33:01 +01:00
aca926e202 Fix link script command 2023-03-16 13:31:56 +01:00
9941027b10 Update package.json 2023-03-15 12:45:58 +01:00
9104de4290 Merge pull request #263 from mkreuzmayr/main
Fix start container script paths for windows
2023-03-15 12:45:43 +01:00
5dc692809c Fix start container script paths for windows 2023-03-15 10:07:01 +01:00
8dc1d1bd21 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-03-13 10:47:10 +01:00
fe588485a9 Update changelog 2023-03-13 10:47:02 +01:00
19ef1d7025 Bump version 2023-03-13 10:44:46 +01:00
62523a8662 Merge pull request #260 from InseeFrLab/lordvlad/issue257
Run keycloakify behind corporate proxy
2023-03-13 10:44:08 +01:00
6e97665e2e Merge branch 'main' into lordvlad/issue257 2023-03-09 18:33:18 +01:00
4988680353 Update description 2023-03-09 18:30:58 +01:00
c5de5c20c7 Bump version 2023-03-08 23:21:44 +01:00
1a0fee1aa2 Make things cleaner 2023-03-08 23:21:32 +01:00
06a44603cd Release candidate 2023-03-08 22:32:45 +01:00
e48459762e Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-03-08 22:28:46 +01:00
235ebeae97 Bump version 2023-03-08 22:25:10 +01:00
dfe909606e Deprecate useFormValidationSlice 2023-03-08 22:24:51 +01:00
6fd0c7726c Merge branch 'lordvlad/issue257' of github.com:InseeFrLab/keycloakify into lordvlad/issue257
* 'lordvlad/issue257' of github.com:InseeFrLab/keycloakify:
  Only test build on LTS node version
  Update dependency evt to ^2.4.15
  Update README.md
  Bump version
  Avoid passing unessesary realm values in the error.ftl page
2023-03-08 10:25:10 +01:00
819e045811 feat(proxy): respect XDG_CACHE_HOME if set 2023-03-08 10:24:52 +01:00
1ba780598d Relase candidate 2023-03-07 18:18:37 +01:00
aeb0cb3110 Only test build on LTS node version 2023-03-07 18:18:04 +01:00
88923838c5 Bump version 2023-03-07 17:29:30 +01:00
df9f6fd7fd Merge branch 'main' into lordvlad/issue257 2023-03-07 17:16:24 +01:00
98e46d6ac9 Update dependency evt to ^2.4.15 2023-03-07 17:07:49 +01:00
daff614fb4 Update README.md 2023-03-07 17:07:48 +01:00
5ea324c7f2 Bump version 2023-03-07 17:07:48 +01:00
23fedbf94a Avoid passing unessesary realm values in the error.ftl page 2023-03-07 17:07:45 +01:00
593d66d8d6 style: remove unused dependency 2023-03-07 16:45:17 +01:00
851dcd5bf7 Run keycloakify behind corporate proxy
Fixes #257

Use make-fetch-happen for the download step. This lib will use `PROXY`
and `HTTPS_PROXY` and `NO_PROXY` env vars out of the box.

Additionally we'll try and get proxy config from npm. Unfortunately,
the most straightforward options is to call npm config to do this, since
npm  config is not easily extracted as a lib and we don't want to
replicate the resolution mechanisms.
2023-03-07 16:43:12 +01:00
2e919681ae Update dependency evt to ^2.4.15 2023-03-07 10:14:46 +00:00
5da68cd48c Update README.md 2023-03-05 23:37:37 +01:00
27fdaeff46 Bump version 2023-02-27 11:55:39 +01:00
53c0079656 Use extract instead of subtype to ease copy paste into theme repo 2023-02-27 11:55:25 +01:00
93780b77e0 Bump version 2023-02-27 11:32:14 +01:00
b712ed0421 Avoid using tsafe utils to avoid forcing user to install tsafe 2023-02-27 11:32:00 +01:00
ee96f1b345 Bump version 2023-02-27 11:29:23 +01:00
d13464df3d Get rid of the ReactComponent type, classes based component are no longer used 2023-02-27 11:29:05 +01:00
6bde2e4d96 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-02-27 10:39:44 +01:00
0a4953c020 Bump version 2023-02-27 10:39:37 +01:00
96c488880c Abstract away Template logic 2023-02-27 10:39:22 +01:00
7e0adf3f66 Update README.md 2023-02-26 17:32:35 +01:00
09f716440a Bump version 2023-02-26 16:41:47 +01:00
2251c84171 Use the new syntax for importing as type 2023-02-26 16:37:06 +01:00
5cfe78dcd1 Update prettier config 2023-02-26 15:39:03 +01:00
6a48325132 Be more relax on the type safety to avoir headache 2023-02-26 15:37:52 +01:00
294be0a79a see prev commit 2023-02-26 15:36:52 +01:00
c94b264b44 Don't need a dir for a single file 2023-02-26 15:36:35 +01:00
7220c4e3e3 Fix deepAssign 2023-02-26 15:35:57 +01:00
5aadeba2ec fix clsx 2023-02-26 15:35:30 +01:00
0f47a5b6ba Small Template refactor 2023-02-25 20:11:55 +01:00
36f32d28f2 Stop auto updating powerhooks 2023-02-25 19:21:55 +01:00
6d69ccf229 Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-02-25 19:21:17 +01:00
37073b42be Avoir introducing breaking changes for CSS only setup 2023-02-25 19:19:46 +01:00
837501c948 Refactor 2023-02-25 18:26:39 +01:00
b300966fa8 Refactor and get rid of unessesary dependencies 2023-02-25 18:11:23 +01:00
730eb06c84 fix(deps): update dependency powerhooks to ^0.26.2 2023-02-14 12:08:41 +00:00
aca8d3f4b7 fix(deps): update dependency powerhooks to ^0.26.1 2023-02-08 16:38:28 +00:00
b5b3af4659 Bump version 2023-02-07 01:32:36 +01:00
6cd231426d Import Blob from node builtins 2023-02-07 01:32:20 +01:00
0c7cd1cd75 Bump version 2023-02-07 01:21:17 +01:00
2425704ead Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-02-07 01:20:33 +01:00
4e22159206 Bump version 2023-02-07 01:20:26 +01:00
52cf1ba02c Fix tsafe related warnings 2023-02-07 01:20:12 +01:00
516e84182f fix(deps): update dependency powerhooks to ^0.26.0 2023-02-05 15:10:42 +00:00
a3a9853e18 bump version 2023-02-05 14:58:53 +01:00
08e26600fd Use keycloakify as bundler by default 2023-02-05 14:58:38 +01:00
7793c2c6ba Update package.json 2023-02-05 14:41:32 +01:00
9e826d16dd Merge pull request #241 from lordvlad/mvn-begone
Mvn begone addendum
2023-02-05 14:41:13 +01:00
80618bbd9c Merge branch 'main' into mvn-begone 2023-02-05 13:36:52 +01:00
38ad47ea75 use hand-crafted promise, pipeline does not resolve properly 2023-02-05 13:32:24 +01:00
45ed359bef fix keycloak theme source path for internal bundler 2023-02-05 13:31:34 +01:00
fcc26c3e7a now that main is a promise, we shuold catch errors 2023-02-05 13:31:03 +01:00
d4ff6b1f40 fix: bundler fix missing directory 2023-02-05 12:59:05 +01:00
557de34eea fix: bundler fix missing change 2023-02-05 12:56:01 +01:00
e034dc4d90 Merge branch 'mvn-begone' of github.com:lordvlad/keycloakify into mvn-begone
* 'mvn-begone' of github.com:lordvlad/keycloakify:
  fix(deps): update garronej_modules_update
  Update README.md
  Rollback via update
  Bump version
  keycloak test script: use env to launch bash
  fix(deps): update dependency powerhooks to ^0.22.0
  Update dependency powerhooks to ^0.21.0
  Relase candidate
  fmt
  Update README.md
  Bump version
  Update src/bin/tools/downloadAndUnzip.ts
  Bump version
  #232
  Bump version
  keycloak test script: use env to launch bash
  fix(deps): update dependency powerhooks to ^0.22.0
  Update dependency powerhooks to ^0.21.0
2023-02-05 12:35:15 +01:00
cfbd1e5e4b fix(bundler): fix type mismatch introduced in last-minute 'fixes' 2023-02-05 12:34:48 +01:00
0df661819f Bump version 2023-02-04 20:51:06 +01:00
1a9f6d10d4 Actually run the top level await 2023-02-04 20:50:53 +01:00
a787215c95 Bump version 2023-02-04 20:39:38 +01:00
64ab400af5 Temporarly restore mvn as default bundler 2023-02-04 20:38:50 +01:00
a463878bf2 Bump version 2023-02-04 20:22:45 +01:00
9f72024c61 Merge pull request #240 from InseeFrLab/mvn-begone
Mvn begone
2023-02-04 19:47:36 +01:00
243fbd4dc9 Set the artifactId name in the build option 2023-02-04 19:36:42 +01:00
4e6a290693 Make new node based bundler the default 2023-02-04 18:02:39 +01:00
ac05d529ca Minor fixes 2023-02-04 17:44:02 +01:00
b38d79004a Merge branch 'main' into mvn-begone 2023-02-03 14:42:05 +01:00
f4a547df11 introduce options to choose a bundle strategy
Pick from 'none', 'keycloakify' or 'mvn', default to 'mvn'. 'none' will
not create a jar, 'keycloakify' will create a jar file using only tools
available to native nodejs, no additional  system library required.
Choosing 'mvn' will behave as before, starting maven in a subprocess.

The bundler can be chosen in `package.json` or via `KEYCLOAKIFY_BUNDLER`
env var.

This commit also adds `KEYCLOAKIFY_GROUP_ID` and
`KEYCLOAKIFY_ARTIFACT_ID` env vars, which will be used to
define group id and artifact id in pom.xml and pom.properties, if given.
2023-02-03 14:28:06 +01:00
2b87c35058 introduce utils for creating a jar archive 2023-02-03 14:06:24 +01:00
b11833e450 fix typo 2023-02-03 13:48:32 +01:00
fa8e119514 fix(deps): update garronej_modules_update 2023-02-01 15:16:03 +00:00
677cb5c330 Update README.md 2023-01-27 15:46:30 +01:00
6e74c79bfe Merge pull request #233 from InseeFrLab/windows_compat
Windows compat
2023-01-27 15:45:31 +01:00
54474f5908 Merge branch 'main' into windows_compat 2023-01-27 15:45:25 +01:00
99cc0f519b Rollback via update 2023-01-27 01:13:16 +01:00
92a01f89ef Bump version 2023-01-27 01:12:46 +01:00
fd83a0c743 keycloak test script: use env to launch bash
This minor change allows users to use the latest version of `bash` on Mac OS.

`/bin/bash` is very old on Mac OS, and many users will have `bash` from MacPorts or Homebrew. This change allows such users to be able to use the newer `bash` installation.
2023-01-27 01:12:46 +01:00
988e46c875 fix(deps): update dependency powerhooks to ^0.22.0 2023-01-27 01:12:46 +01:00
f081c2fc20 Update dependency powerhooks to ^0.21.0 2023-01-27 01:12:46 +01:00
b4b376a1a5 Relase candidate 2023-01-27 01:09:11 +01:00
0db4179d47 fmt 2023-01-27 00:32:41 +01:00
795b7c6234 Update README.md 2023-01-27 00:27:46 +01:00
091b9a57f5 Bump version 2023-01-27 00:22:28 +01:00
564e1422ac Merge pull request #226 from lordvlad/fix-win-build
windows compatibility
2023-01-27 00:20:12 +01:00
8ed4ed3fc4 Update src/bin/tools/downloadAndUnzip.ts 2023-01-26 22:29:51 +01:00
29fe4566a7 style: remove unused variable 2023-01-26 22:20:16 +01:00
ae3bfb28ed refactor: follow suggesion to make methods read a bit more sync 2023-01-26 22:00:31 +01:00
14aab97d8a Merge branch 'main' of https://github.com/InseeFrLab/keycloakify 2023-01-26 10:50:01 +01:00
52d7a47cd7 Bump version 2023-01-26 10:48:57 +01:00
f338dcbeed #232 2023-01-26 10:48:24 +01:00
dcec058a22 Bump version 2023-01-23 02:57:12 +01:00
2bdc6b156b Merge pull request #231 from rome-user/patch-1
keycloak test script: use env to launch bash
2023-01-23 02:56:32 +01:00
84ca9e6b81 keycloak test script: use env to launch bash
This minor change allows users to use the latest version of `bash` on Mac OS.

`/bin/bash` is very old on Mac OS, and many users will have `bash` from MacPorts or Homebrew. This change allows such users to be able to use the newer `bash` installation.
2023-01-22 17:28:27 -08:00
11cb0fd2db Merge pull request #230 from InseeFrLab/renovate_main-garronej_modules_update
Update dependency powerhooks to ^0.22.0 (main)
2023-01-18 00:58:38 +01:00
3f620ffb6f fix(deps): update dependency powerhooks to ^0.22.0 2023-01-16 18:23:50 +00:00
1a0e05d073 rewrite download and unzip to use nodejs native methods 2023-01-16 14:42:20 +01:00
a4d2de23a1 Update dependency powerhooks to ^0.21.0 2023-01-16 02:38:07 +00:00
85cecc9811 fix(build): _properly_ fix bin paths 2023-01-12 22:56:02 +01:00
9899f742a8 fix(build): also rewrite bin paths in dist/package.json 2023-01-12 22:51:48 +01:00
b5484740b7 fix(build): remove dependency on *nix chown 2023-01-12 22:27:22 +01:00
016b15b437 Bump version 2023-01-10 09:05:33 +01:00
6fb936798e https://github.com/InseeFrLab/keycloakify/issues/217#issuecomment-1376849781 2023-01-10 09:05:19 +01:00
a692b87843 Bump version 2023-01-08 15:01:12 +01:00
19663885a4 Merge pull request #222 from InseeFrLab/pr/lordvlad/221
Pr/lordvlad/221
2023-01-08 14:59:22 +01:00
49b87777f9 Fmt 2023-01-08 14:57:18 +01:00
d4523bb1e6 Merge branch 'main' into patch-1 2023-01-08 12:22:14 +01:00
e3200899e2 fix: replace rm system calls with nodejs native functions; closes #219 2023-01-08 12:19:41 +01:00
36c7a1ab9e Bump version 2023-01-08 00:14:01 +01:00
c54fbd5eca Merge pull request #218 from pedrodsRocha1/improve-secure-login-flow
fix - handle username and password login errors the same way
2023-01-08 00:03:16 +01:00
bbe828071e fix - handle username and password login errors the same 2023-01-06 15:41:01 +01:00
23f6c7db00 Update garronej_modules_update 2022-11-29 18:31:14 +00:00
b1ea9e7a71 Update dependency powerhooks to ^0.20.27 2022-11-16 05:21:45 +00:00
fb71d0e272 Update dependency powerhooks to ^0.20.26 2022-11-13 22:11:59 +00:00
fa72a29999 Bump version 2022-11-11 15:49:05 +01:00
af77b31d54 Update default test container to Keycloak 20.0.1 2022-11-11 15:48:32 +01:00
8280dace26 Update garronej_modules_update 2022-10-25 03:18:01 +00:00
ecaf1c7b7c Bump version 2022-10-16 00:51:11 +02:00
8702ec29a8 Drop dependency to @emotion/react 2022-10-16 00:49:49 +02:00
d8206434bc Bump version 2022-10-15 15:39:56 +02:00
c71c2a8710 Fix breaking change of 6.8 2022-10-15 15:39:32 +02:00
650 changed files with 67193 additions and 42025 deletions

375
.all-contributorsrc Normal file
View File

@ -0,0 +1,375 @@
{
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"commitConvention": "angular",
"contributors": [
{
"login": "lordvlad",
"name": "Waldemar Reusch",
"avatar_url": "https://avatars.githubusercontent.com/u/1217769?v=4",
"profile": "https://github.com/lordvlad",
"contributions": [
"code"
]
},
{
"login": "willwill96",
"name": "William Will",
"avatar_url": "https://avatars.githubusercontent.com/u/10997562?v=4",
"profile": "https://willwill96.github.io/the-ui-dawg-static-site/en/introduction/",
"contributions": [
"code"
]
},
{
"login": "Ann2827",
"name": "Bystrova Ann",
"avatar_url": "https://avatars.githubusercontent.com/u/32645809?v=4",
"profile": "https://github.com/Ann2827",
"contributions": [
"code"
]
},
{
"login": "mkreuzmayr",
"name": "Michael Kreuzmayr",
"avatar_url": "https://avatars.githubusercontent.com/u/20108212?v=4",
"profile": "https://github.com/mkreuzmayr",
"contributions": [
"code"
]
},
{
"login": "Mstrodl",
"name": "Mary ",
"avatar_url": "https://avatars.githubusercontent.com/u/6877780?v=4",
"profile": "https://coolmathgames.tech",
"contributions": [
"code"
]
},
{
"login": "Tasyp",
"name": "German Öö",
"avatar_url": "https://avatars.githubusercontent.com/u/6623212?v=4",
"profile": "https://tasyp.xyz/",
"contributions": [
"code"
]
},
{
"login": "revolunet",
"name": "Julien Bouquillon",
"avatar_url": "https://avatars.githubusercontent.com/u/124937?v=4",
"profile": "https://revolunet.com",
"contributions": [
"code"
]
},
{
"login": "aidangilmore",
"name": "Aidan Gilmore",
"avatar_url": "https://avatars.githubusercontent.com/u/32880357?v=4",
"profile": "https://github.com/aidangilmore",
"contributions": [
"code"
]
},
{
"login": "0x-Void",
"name": "Void",
"avatar_url": "https://avatars.githubusercontent.com/u/32745739?v=4",
"profile": "https://github.com/0x-Void",
"contributions": [
"code"
]
},
{
"login": "juffe",
"name": "juffe",
"avatar_url": "https://avatars.githubusercontent.com/u/5393231?v=4",
"profile": "https://github.com/juffe",
"contributions": [
"code"
]
},
{
"login": "lazToum",
"name": "Lazaros Toumanidis",
"avatar_url": "https://avatars.githubusercontent.com/u/4764837?v=4",
"profile": "https://github.com/lazToum",
"contributions": [
"code"
]
},
{
"login": "marcmrf",
"name": "Marc",
"avatar_url": "https://avatars.githubusercontent.com/u/9928519?v=4",
"profile": "https://github.com/marcmrf",
"contributions": [
"code"
]
},
{
"login": "kasir-barati",
"name": "Kasir Barati",
"avatar_url": "https://avatars.githubusercontent.com/u/73785723?v=4",
"profile": "http://kasir-barati.github.io",
"contributions": [
"doc"
]
},
{
"login": "asashay",
"name": "Alex Oliynyk",
"avatar_url": "https://avatars.githubusercontent.com/u/10714670?v=4",
"profile": "https://github.com/asashay",
"contributions": [
"code"
]
},
{
"login": "thosil",
"name": "Thomas Silvestre",
"avatar_url": "https://avatars.githubusercontent.com/u/1140574?v=4",
"profile": "https://www.gravitysoftware.be",
"contributions": [
"code"
]
},
{
"login": "satanshiro",
"name": "satanshiro",
"avatar_url": "https://avatars.githubusercontent.com/u/38865738?v=4",
"profile": "https://github.com/satanshiro",
"contributions": [
"code"
]
},
{
"login": "kpoelhekke",
"name": "Koen Poelhekke",
"avatar_url": "https://avatars.githubusercontent.com/u/1632377?v=4",
"profile": "https://poelhekke.dev",
"contributions": [
"code"
]
},
{
"login": "zavoloklom",
"name": "Sergey Kupletsky",
"avatar_url": "https://avatars.githubusercontent.com/u/4151869?v=4",
"profile": "https://github.com/zavoloklom",
"contributions": [
"test",
"code"
]
},
{
"login": "rome-user",
"name": "rome-user",
"avatar_url": "https://avatars.githubusercontent.com/u/114131048?v=4",
"profile": "https://github.com/rome-user",
"contributions": [
"code"
]
},
{
"login": "celinepelletier",
"name": "Céline Pelletier",
"avatar_url": "https://avatars.githubusercontent.com/u/82821620?v=4",
"profile": "https://github.com/celinepelletier",
"contributions": [
"code"
]
},
{
"login": "xgp",
"name": "Garth",
"avatar_url": "https://avatars.githubusercontent.com/u/244253?v=4",
"profile": "https://github.com/xgp",
"contributions": [
"code"
]
},
{
"login": "BlackVoid",
"name": "Felix Gustavsson",
"avatar_url": "https://avatars.githubusercontent.com/u/673720?v=4",
"profile": "https://github.com/BlackVoid",
"contributions": [
"code"
]
},
{
"login": "msiemens",
"name": "Markus Siemens",
"avatar_url": "https://avatars.githubusercontent.com/u/1873922?v=4",
"profile": "https://m-siemens.de/",
"contributions": [
"code"
]
},
{
"login": "law108000",
"name": "Rlok",
"avatar_url": "https://avatars.githubusercontent.com/u/8112024?v=4",
"profile": "https://github.com/law108000",
"contributions": [
"code"
]
},
{
"login": "Moulyy",
"name": "Moulyy",
"avatar_url": "https://avatars.githubusercontent.com/u/115405804?v=4",
"profile": "https://github.com/Moulyy",
"contributions": [
"code"
]
},
{
"login": "madmadson",
"name": "Tobias Matt",
"avatar_url": "https://avatars.githubusercontent.com/u/798831?v=4",
"profile": "https://github.com/madmadson",
"contributions": [
"code"
]
},
{
"login": "oliviergoulet5",
"name": "Olivier Goulet",
"avatar_url": "https://avatars.githubusercontent.com/u/17685861?v=4",
"profile": "https://github.com/oliviergoulet5",
"contributions": [
"code"
]
},
{
"login": "liamlows",
"name": "Liam Lowsley-Williams",
"avatar_url": "https://avatars.githubusercontent.com/u/1365914?v=4",
"profile": "https://github.com/liamlows",
"contributions": [
"code",
"doc"
]
},
{
"login": "uchar",
"name": "Omid",
"avatar_url": "https://avatars.githubusercontent.com/u/5172296?v=4",
"profile": "https://www.linkedin.com/in/oes-rioniz/",
"contributions": [
"test",
"code"
]
},
{
"login": "kathari00",
"name": "Katharina Eiserfey",
"avatar_url": "https://avatars.githubusercontent.com/u/42547712?v=4",
"profile": "https://github.com/kathari00",
"contributions": [
"code",
"test",
"doc"
]
},
{
"login": "luca-peruzzo",
"name": "Luca Peruzzo",
"avatar_url": "https://avatars.githubusercontent.com/u/69015314?v=4",
"profile": "https://github.com/luca-peruzzo",
"contributions": [
"code",
"test"
]
},
{
"login": "nima70",
"name": "Nima Shokouhfar",
"avatar_url": "https://avatars.githubusercontent.com/u/5094767?v=4",
"profile": "https://github.com/nima70",
"contributions": [
"code",
"test"
]
},
{
"login": "marvinruder",
"name": "Marvin A. Ruder",
"avatar_url": "https://avatars.githubusercontent.com/u/18495294?v=4",
"profile": "https://mruder.dev",
"contributions": [
"bug"
]
},
{
"login": "zvn2060",
"name": "HI_OuO",
"avatar_url": "https://avatars.githubusercontent.com/u/45450852?v=4",
"profile": "https://github.com/zvn2060",
"contributions": [
"code"
]
},
{
"login": "tripheo0412",
"name": "Tri Hoang",
"avatar_url": "https://avatars.githubusercontent.com/u/25382052?v=4",
"profile": "https://github.com/tripheo0412",
"contributions": [
"doc"
]
},
{
"login": "EternalSide",
"name": "Lesha",
"avatar_url": "https://avatars.githubusercontent.com/u/118743608?v=4",
"profile": "http://t.me/AAT_L",
"contributions": [
"code"
]
},
{
"login": "bacongobbler",
"name": "Matthew Fisher",
"avatar_url": "https://avatars.githubusercontent.com/u/1360539?v=4",
"profile": "https://blog.bacongobbler.com",
"contributions": [
"doc"
]
},
{
"login": "kodebach",
"name": "Klemens Böswirth",
"avatar_url": "https://avatars.githubusercontent.com/u/23529132?v=4",
"profile": "https://github.com/kodebach",
"contributions": [
"code"
]
},
{
"login": "wnmzzzz",
"name": "wnmzzzz",
"avatar_url": "https://avatars.githubusercontent.com/u/117174301?v=4",
"profile": "https://github.com/wnmzzzz",
"contributions": [
"test"
]
}
],
"contributorsPerLine": 7,
"skipCi": true,
"repoType": "github",
"repoHost": "https://github.com",
"projectName": "keycloakify",
"projectOwner": "keycloakify",
"commitType": "docs"
}

View File

@ -1,4 +1,3 @@
# These are supported funding model platforms
github: [garronej]
custom: ['https://www.ringerhq.com/experts/garronej']

25
.github/release.yaml vendored
View File

@ -1,25 +0,0 @@
changelog:
exclude:
labels:
- ignore-for-release
authors:
- octocat
categories:
- title: Breaking Changes 🛠
labels:
- breaking
- title: Exciting New Features 🎉
labels:
- feature
- title: Fixes 🔧
labels:
- fix
- title: Documentation 🔧
labels:
- docs
- title: CI 👷
labels:
- ci
- title: Other Changes
labels:
- '*'

View File

@ -13,48 +13,51 @@ jobs:
runs-on: ubuntu-latest
if: ${{ !github.event.created && github.repository != 'garronej/ts-ci' }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: bahmutov/npm-install@v1
- name: If this step fails run 'yarn format' then commit again.
run: |
PACKAGE_MANAGER=npm
if [ -f "./yarn.lock" ]; then
PACKAGE_MANAGER=yarn
fi
$PACKAGE_MANAGER run format:check
- name: If this step fails run 'npm run format' then commit again.
run: npm run _format --list-different
test:
runs-on: ${{ matrix.os }}
needs: test_lint
strategy:
matrix:
node: [ '14', '15' ,'16', '17' ]
os: [ windows-latest, ubuntu-latest ]
node: [ '18' ]
os: [ ubuntu-latest ]
name: Test with Node v${{ matrix.node }} on ${{ matrix.os }}
steps:
- name: Tell if project is using npm or yarn
id: step1
uses: garronej/ts-ci@v2.0.2
with:
action_name: tell_if_project_uses_npm_or_yarn
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- uses: bahmutov/npm-install@v1
- if: steps.step1.outputs.npm_or_yarn == 'yarn'
run: |
yarn build
yarn test
- if: steps.step1.outputs.npm_or_yarn == 'npm'
run: |
npm run build
npm test
- run: npm run build
- run: npm run test
storybook:
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18'
- uses: bahmutov/npm-install@v1
- run: npm run build-storybook
- run: git remote set-url origin https://git:${GITHUB_TOKEN}@github.com/${{github.repository}}.git
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: npx -y -p gh-pages@3.1.0 gh-pages -d ./storybook-static -u "github-actions-bot <actions@github.com>"
check_if_version_upgraded:
name: Check if version upgrade
# We run this only if it's a push on the default branch or if it's a PR from a
# branch (meaning not a PR from a fork). It would be more straightforward to test if secrets.NPM_TOKEN is
# defined but GitHub Action don't allow it yet.
# When someone forks the repo and opens a PR we want to enables the tests to be run (the previous jobs)
# but obviously only us should be allowed to release.
# In the following check we make sure that we own the branch this CI workflow is running on before continuing.
# Without this check, trying to release would fail anyway because only us have the correct secret.NPM_TOKEN but
# it's cleaner to stop the execution instead of letting the CI crash.
if: |
github.event_name == 'push' ||
github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login
@ -66,16 +69,15 @@ jobs:
is_upgraded_version: ${{ steps.step1.outputs.is_upgraded_version }}
is_pre_release: ${{steps.step1.outputs.is_pre_release }}
steps:
- uses: garronej/ts-ci@v2.0.2
- uses: garronej/ts-ci@v2.1.2
id: step1
with:
action_name: is_package_json_version_upgraded
branch: ${{ github.head_ref || github.ref }}
create_github_release:
runs-on: ubuntu-latest
# We create a release only if the version have been upgraded and we are on the main branch
# or if we are on a branch of the repo that has an PR open on main.
# We create release only if the version in the package.json have been upgraded and this CI is running against the main branch.
# We allow branches with a PR open on main to publish pre-release (x.y.z-rc.u) but not actual releases.
if: |
needs.check_if_version_upgraded.outputs.is_upgraded_version == 'true' &&
(
@ -85,7 +87,7 @@ jobs:
needs:
- check_if_version_upgraded
steps:
- uses: softprops/action-gh-release@v1
- uses: softprops/action-gh-release@v2
with:
name: Release v${{ needs.check_if_version_upgraded.outputs.to_version }}
tag_name: v${{ needs.check_if_version_upgraded.outputs.to_version }}
@ -102,22 +104,20 @@ jobs:
- create_github_release
- check_if_version_upgraded
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
registry-url: https://registry.npmjs.org/
- uses: bahmutov/npm-install@v1
- run: |
PACKAGE_MANAGER=npm
if [ -f "./yarn.lock" ]; then
PACKAGE_MANAGER=yarn
fi
$PACKAGE_MANAGER run build
- run: npx -y -p denoify@1.2.2 enable_short_npm_import_path
- run: npm run build
- run: npx -y -p denoify@1.6.13 enable_short_npm_import_path
env:
DRY_RUN: "0"
- uses: garronej/ts-ci@v2.1.2
with:
action_name: remove_dark_mode_specific_images_from_readme
- name: Publishing on NPM
run: |
if [ "$(npm show . version)" = "$VERSION" ]; then
@ -136,4 +136,4 @@ jobs:
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }}
IS_PRE_RELEASE: ${{ needs.check_if_version_upgraded.outputs.is_pre_release }}
IS_PRE_RELEASE: ${{ needs.check_if_version_upgraded.outputs.is_pre_release }}

17
.gitignore vendored
View File

@ -41,12 +41,21 @@ jspm_packages
.DS_Store
/dist
/dist_test
/keycloakify_starter_test/
/sample_custom_react_project/
/sample_react_project/
/.yarn_home/
.idea
/keycloak_email
/build_keycloak
/src/login/i18n/messages_defaultSet/
/src/account/i18n/
# VS Code devcontainers
.devcontainer
/.yarn
/.yarnrc.yml
/stories/assets/fonts/
/build_storybook/
/storybook-static/

View File

@ -1,9 +1,16 @@
node_modules/
/dist/
/dist_test/
/CHANGELOG.md
/.yarn_home/
/src/test/apps/
/src/tools/types/
/sample_react_project
/build_keycloak/
/build_keycloak/
/.vscode/
/src/login/i18n/messages_defaultSet/
/src/account/i18n/messages_defaultSet/
/dist_test
/sample_react_project/
/sample_custom_react_project/
/keycloakify_starter_test/
/.storybook/static/keycloak-resources/
/src/bin/start-keycloak/*.json

View File

@ -1,11 +1,24 @@
{
"printWidth": 150,
"printWidth": 90,
"tabWidth": 4,
"useTabs": false,
"semi": true,
"singleQuote": false,
"quoteProps": "preserve",
"trailingComma": "none",
"bracketSpacing": true,
"arrowParens": "avoid"
"arrowParens": "avoid",
"overrides": [
{
"files": "*.tsx",
"options": {
"printWidth": 150
}
},
{
"files": "useUserProfileForm.tsx",
"options": {
"printWidth": 150
}
}
]
}

33
.storybook/customTheme.ts Normal file
View File

@ -0,0 +1,33 @@
const brandImage = "logo.png";
const brandTitle = "Keycloakify";
const brandUrl = "https://github.com/keycloakify/keycloakify";
const fontBase = '"Work Sans", sans-serif';
const fontCode = "monospace";
export const darkTheme = {
base: "dark",
appBg: "#1E1E1E",
appContentBg: "#161616",
barBg: "#161616",
colorSecondary: "#8585F6",
textColor: "#FFFFFF",
brandImage,
brandTitle,
brandUrl,
fontBase,
fontCode
};
export const lightTheme: typeof darkTheme = {
base: "light",
appBg: "#F6F6F6",
appContentBg: "#FFFFFF",
barBg: "#FFFFFF",
colorSecondary: "#000091",
textColor: "#212121",
brandImage,
brandTitle,
brandUrl,
fontBase,
fontCode
};

13
.storybook/main.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = {
stories: [
"../stories/**/*.stories.tsx"
],
addons: [
"storybook-dark-mode",
"@storybook/addon-a11y"
],
core: {
builder: "webpack5"
},
staticDirs: ["./static", "../dist/res/public"]
};

View File

@ -0,0 +1,32 @@
<!-- start favicon -->
<link rel="apple-touch-icon" sizes="180x180" href="/favicon_package/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon_package/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon_package/favicon-16x16.png">
<link rel="manifest" href="/favicon_package/site.webmanifest">
<link rel="mask-icon" href="/favicon_package/safari-pinned-tab.svg" color="#5bbad5">
<!-- end favicon -->
<!-- Meta tags generated by metatags.io -->
<!-- Primary Meta Tags -->
<title>Keycloakify Storybook</title>
<meta name="title" content="Keycloakify Storybook">
<meta name="description" content="Storybook of default components to use as a reference when building a custom Keycloak theme">
<!-- Facebook Meta Tags -->
<meta property="og:url" content="https://www.keycloakify.dev">
<meta property="og:type" content="website">
<meta property="og:title" content="Keycloakify Storybook">
<meta property="og:description" content="Storybook of default components to use as a reference when building a custom Keycloak theme">
<meta property="og:image" content="https://storybook.keycloakify.dev/preview.png">
<!-- Twitter Meta Tags -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Keycloakify Storybook">
<meta name="twitter:description" content="Storybook of default components to use as a reference when building a custom Keycloak theme">
<meta name="twitter:image" content="https://storybook.keycloakify.dev/preview.png">
<link rel="preload" href="/fonts/WorkSans/worksans-bold-webfont.woff2" as="font" crossorigin="anonymous">
<link rel="preload" href="/fonts/WorkSans/worksans-medium-webfont.woff2" as="font" crossorigin="anonymous">
<link rel="preload" href="/fonts/WorkSans/worksans-regular-webfont.woff2" as="font" crossorigin="anonymous">
<link rel="preload" href="/fonts/WorkSans/worksans-semibold-webfont.woff2" as="font" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="/fonts/WorkSans/font.css">

6
.storybook/manager.js Normal file
View File

@ -0,0 +1,6 @@
import { addons } from '@storybook/addons';
addons.setConfig({
selectedPanel: 'storybook/a11y/panel',
showPanel: false
});

View File

@ -0,0 +1,23 @@
<link rel="preload" href="/fonts/WorkSans/worksans-bold-webfont.woff2" as="font" crossorigin="anonymous">
<link rel="preload" href="/fonts/WorkSans/worksans-medium-webfont.woff2" as="font" crossorigin="anonymous">
<link rel="preload" href="/fonts/WorkSans/worksans-regular-webfont.woff2" as="font" crossorigin="anonymous">
<link rel="preload" href="/fonts/WorkSans/worksans-semibold-webfont.woff2" as="font" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="/fonts/WorkSans/font.css">
<style>
body.sb-show-main.sb-main-padded {
padding: 0;
}
body:not(.kcBodyClass) {
background-color: #393939;
}
body.sb-show-preparing-docs > .sb-wrapper {
visibility: hidden;
}
body .sb-preparing-story {
visibility: hidden;
}
</style>

161
.storybook/preview.js Normal file
View File

@ -0,0 +1,161 @@
import { darkTheme, lightTheme } from "./customTheme";
import { create as createTheme } from "@storybook/theming";
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
backgrounds: { disable: true },
darkMode: {
light: createTheme(lightTheme),
dark: createTheme(darkTheme),
},
controls: {
disable: true,
},
actions: {
disable: true
},
viewport: {
viewports: {
"1440p": {
name: "1440p",
styles: {
width: "2560px",
height: "1440px",
},
},
fullHD: {
name: "Full HD",
styles: {
width: "1920px",
height: "1080px",
},
},
macBookProBig: {
name: "MacBook Pro Big",
styles: {
width: "1024px",
height: "640px",
},
},
macBookProMedium: {
name: "MacBook Pro Medium",
styles: {
width: "1440px",
height: "900px",
},
},
macBookProSmall: {
name: "MacBook Pro Small",
styles: {
width: "1680px",
height: "1050px",
},
},
pcAgent: {
name: "PC Agent",
styles: {
width: "960px",
height: "540px",
},
},
iphone12Pro: {
name: "Iphone 12 pro",
styles: {
width: "390px",
height: "844px",
},
},
iphone5se: {
name: "Iphone 5/SE",
styles: {
width: "320px",
height: "568px",
},
},
ipadPro: {
name: "Ipad pro",
styles: {
width: "1240px",
height: "1366px",
},
},
"Galaxy s9+": {
name: "Galaxy S9+",
styles: {
width: "320px",
height: "658px",
},
}
},
},
options: {
storySort: (a, b) =>
getHardCodedWeight(b[1].kind) - getHardCodedWeight(a[1].kind),
},
};
const { getHardCodedWeight } = (() => {
const orderedPagesPrefix = [
"Introduction",
"login/login.ftl",
"login/register.ftl",
"login/terms.ftl",
"login/error.ftl",
"login/code.ftl",
"login/delete-account-confirm.ftl",
"login/delete-credential.ftl",
"login/frontchannel-logout.ftl",
"login/idp-review-user-profile.ftl",
"login/info.ftl",
"login/login-config-totp.ftl",
"login/login-idp-link-confirm.ftl",
"login/login-idp-link-email.ftl",
"login/login-oauth-grant.ftl",
"login/login-otp.ftl",
"login/login-page-expired.ftl",
"login/login-password.ftl",
"login/login-reset-otp.ftl",
"login/login-reset-password.ftl",
"login/login-update-password.ftl",
"login/login-update-profile.ftl",
"login/login-username.ftl",
"login/login-verify-email.ftl",
"login/login-x509-info.ftl",
"login/logout-confirm.ftl",
"login/saml-post-form.ftl",
"login/select-authenticator.ftl",
"login/update-email.ftl",
"login/webauthn-authenticate.ftl",
"login/webauthn-error.ftl",
"login/webauthn-register.ftl",
"login/login-oauth2-device-verify-user-code.ftl",
"login/login-recovery-authn-code-config.ftl",
"login/login-recovery-authn-code-input.ftl",
"account/account.ftl",
"account/password.ftl",
"account/federatedIdentity.ftl",
"account/log.ftl",
"account/sessions.ftl",
"account/totp.ftl",
];
function getHardCodedWeight(kind) {
for (let i = 0; i < orderedPagesPrefix.length; i++) {
if (kind.toLowerCase().startsWith(orderedPagesPrefix[i].toLowerCase())) {
return orderedPagesPrefix.length - i;
}
}
return 0;
}
return { getHardCodedWeight };
})();

1
.storybook/static/CNAME Normal file
View File

@ -0,0 +1 @@
storybook.keycloakify.dev

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,193 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="447.000000pt" height="447.000000pt" viewBox="0 0 447.000000 447.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,447.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2177 4413 c-3 -2 -17 -6 -33 -9 -85 -15 -204 -109 -286 -225 -95
-133 -229 -437 -263 -597 -4 -18 -10 -30 -13 -28 -4 2 -7 -11 -8 -30 0 -19 -3
-36 -6 -39 -3 -4 -23 1 -44 10 -51 22 -213 73 -289 92 -301 73 -516 74 -670 3
-124 -57 -186 -153 -188 -295 -1 -67 5 -128 18 -180 3 -13 15 -45 26 -70 43
-99 57 -135 53 -135 -3 0 4 -10 16 -22 11 -12 20 -26 20 -31 0 -5 9 -22 21
-38 11 -16 18 -29 14 -29 -3 0 4 -10 15 -22 11 -12 18 -28 15 -36 -2 -7 -1
-11 3 -8 4 2 19 -12 32 -32 13 -20 33 -47 44 -59 11 -13 17 -23 13 -23 -3 0
12 -19 33 -42 22 -24 38 -49 35 -55 -2 -7 -1 -12 4 -10 4 1 32 -25 62 -58 30
-33 88 -94 131 -135 l76 -75 -71 -70 c-112 -110 -174 -181 -262 -300 -106
-144 -142 -202 -203 -325 -9 -19 -21 -38 -27 -42 -5 -4 -7 -8 -3 -8 4 0 -4
-27 -18 -60 -25 -63 -58 -199 -50 -208 3 -2 1 -12 -4 -22 -5 -10 -6 -20 -3
-24 4 -4 8 -23 10 -44 9 -107 77 -201 183 -251 33 -16 56 -32 52 -36 -4 -5 -2
-5 4 -2 6 4 44 1 85 -6 41 -7 102 -12 136 -12 41 1 60 -2 55 -9 -3 -6 2 -5 11
3 11 8 44 14 86 15 38 0 67 4 64 9 -4 6 10 8 79 11 14 0 24 3 22 5 -4 4 33 14
145 37 13 3 33 10 43 15 11 6 26 8 34 5 10 -4 12 -2 8 6 -5 8 -2 9 9 5 10 -3
17 -2 17 3 0 6 8 10 18 10 9 0 36 9 58 19 32 15 45 16 54 8 9 -9 11 -9 8 2 -2
7 2 15 8 18 10 3 23 -34 38 -108 2 -8 6 -21 9 -29 15 -36 70 -206 70 -217 0
-7 4 -13 8 -13 11 0 30 -61 22 -74 -3 -6 -3 -8 2 -4 8 7 86 -135 88 -159 0 -7
4 -13 8 -13 13 0 39 -56 31 -66 -4 -5 -3 -6 2 -2 5 4 22 -12 39 -35 17 -23 49
-59 71 -80 23 -21 40 -42 39 -47 -2 -6 0 -9 5 -8 15 3 63 -22 68 -37 3 -8 10
-12 15 -10 5 3 22 -1 39 -10 17 -9 35 -13 40 -10 6 3 10 2 10 -2 0 -5 12 -8
28 -8 15 1 33 -4 41 -9 11 -8 13 -8 8 0 -4 7 7 13 33 17 21 3 53 12 71 21 17
9 36 13 42 9 5 -3 7 -1 3 5 -3 6 3 13 14 17 11 3 20 11 20 16 0 5 5 9 11 9 5
0 7 -6 3 -13 -4 -7 -3 -9 2 -4 5 5 9 12 9 17 0 4 14 21 30 37 69 67 162 196
179 251 4 12 12 20 17 16 5 -3 8 -2 7 3 -1 4 6 29 17 56 14 33 24 45 35 41 11
-4 12 -2 4 6 -14 14 -6 52 9 43 5 -3 7 -2 4 4 -3 5 8 45 24 88 17 44 33 86 35
94 7 34 56 206 59 209 1 1 15 -3 31 -9 16 -7 34 -13 39 -15 61 -18 144 -51
139 -56 -3 -4 3 -4 14 -1 11 2 23 0 26 -6 4 -5 14 -7 23 -4 9 4 14 2 10 -3 -3
-5 7 -9 21 -8 34 1 63 -6 58 -15 -2 -3 19 -7 47 -9 28 -2 55 -6 61 -10 13 -8
18 -9 79 -10 27 -1 46 -5 44 -9 -3 -5 20 -6 50 -5 44 3 54 1 48 -10 -6 -10 -5
-11 6 0 16 15 33 16 23 0 -5 -7 -3 -8 7 -3 7 5 29 10 49 12 74 5 116 11 135
18 11 4 37 13 57 21 28 10 38 11 41 1 4 -8 6 -7 6 3 1 8 21 28 46 44 43 26 92
86 109 131 28 73 27 217 -3 313 -5 18 -10 33 -9 35 2 23 -118 257 -184 356
-44 67 -124 177 -138 191 -3 3 -34 39 -70 80 -36 41 -97 107 -137 146 l-72 71
25 22 c14 12 30 19 36 15 6 -4 8 -3 4 4 -3 6 24 42 61 80 38 38 86 91 108 117
22 27 46 54 53 61 6 7 12 17 12 23 0 6 3 11 8 11 4 0 22 22 41 50 19 27 37 47
41 45 4 -3 7 3 6 13 0 9 5 16 12 14 8 -1 11 2 7 7 -5 9 36 83 55 101 5 5 101
200 121 245 7 18 26 84 36 125 2 8 4 27 5 42 0 16 5 25 11 21 6 -4 7 -1 2 7
-11 18 -11 62 0 80 5 8 4 11 -2 7 -6 -4 -12 10 -16 36 -6 51 -10 70 -18 77 -3
3 -15 21 -27 41 -42 70 -184 145 -292 155 -205 20 -451 -18 -709 -108 -30 -10
-60 -17 -68 -14 -8 3 -12 2 -9 -3 5 -7 -42 -31 -60 -31 -1 0 -5 15 -9 33 -18
86 -108 342 -156 444 -17 35 -29 66 -29 69 1 4 -2 10 -7 13 -5 3 -24 32 -42
64 -108 190 -245 296 -403 311 -20 2 -39 2 -41 -1z m53 -124 c0 -5 5 -7 10 -4
14 9 52 -4 45 -16 -3 -5 0 -6 8 -4 17 7 69 -33 61 -47 -5 -7 -2 -8 5 -4 13 8
43 -23 35 -36 -3 -4 1 -6 8 -3 7 2 24 -11 38 -31 14 -19 30 -41 35 -47 50 -56
148 -233 139 -249 -4 -6 -3 -9 2 -5 12 7 97 -192 90 -210 -3 -8 -2 -12 3 -9
11 7 45 -120 35 -135 -4 -8 -3 -9 4 -5 7 4 12 3 12 -3 0 -5 3 -16 6 -25 4 -11
-23 -30 -113 -75 -138 -71 -276 -145 -350 -189 -29 -17 -53 -29 -53 -26 0 3
-7 -1 -15 -10 -14 -14 -19 -13 -53 6 -20 11 -62 34 -92 51 -30 16 -59 33 -65
37 -5 4 -86 47 -180 95 -93 48 -173 91 -177 94 -11 10 3 52 15 44 6 -3 7 -1 3
6 -9 14 12 113 22 107 4 -2 8 7 8 20 2 24 42 134 53 144 3 3 7 12 9 20 2 8 17
44 33 80 24 50 32 61 40 50 8 -11 9 -10 4 7 -5 17 10 47 60 123 36 55 71 98
76 94 5 -3 9 -2 8 3 -4 23 2 35 17 29 8 -3 12 -2 9 3 -7 12 60 78 100 99 25
13 70 27 98 31 4 1 7 -4 7 -10z m-1411 -783 c9 -6 12 -5 8 1 -4 6 10 10 34 11
29 1 38 -1 33 -11 -5 -9 -4 -9 7 1 7 6 19 12 27 12 8 0 10 -5 6 -12 -6 -10 -5
-10 7 -1 9 7 26 10 40 8 13 -3 47 -8 74 -11 63 -7 155 -23 175 -31 15 -6 35
-10 71 -12 12 -1 17 -5 13 -13 -4 -7 -3 -8 4 -4 6 4 31 1 54 -5 24 -7 49 -13
55 -15 25 -5 73 -29 73 -37 0 -5 4 -6 9 -3 12 8 41 -4 41 -16 0 -5 -4 -6 -10
-3 -6 3 -7 -1 -4 -9 3 -9 1 -45 -5 -81 -6 -36 -14 -91 -17 -122 -4 -32 -11
-61 -18 -65 -8 -6 -7 -8 2 -8 7 0 11 -4 8 -8 -3 -4 -8 -42 -11 -83 -4 -40 -9
-81 -12 -89 -3 -8 -6 -44 -8 -80 -1 -36 -3 -65 -4 -65 -1 0 -3 -27 -4 -61 -3
-66 0 -62 -92 -139 -33 -27 -62 -53 -63 -58 -2 -4 -8 -5 -13 -1 -5 3 -9 1 -9
-3 0 -5 -41 -44 -91 -88 -50 -43 -98 -85 -106 -93 -13 -14 -23 -6 -91 64 -86
90 -172 188 -186 213 -5 9 -12 18 -16 21 -12 9 -106 154 -139 215 -18 33 -37
66 -43 72 -6 7 -8 20 -4 28 3 9 2 14 -3 11 -19 -12 -102 225 -105 296 0 27 -4
48 -7 48 -16 0 9 108 32 142 22 31 125 81 151 74 10 -2 18 0 18 5 0 5 14 7 30
5 17 -2 30 0 30 4 0 9 42 7 59 -4z m2823 1 c6 -9 8 -9 8 1 0 7 4 10 10 7 5 -3
29 -8 52 -11 113 -13 201 -66 197 -118 0 -5 4 -12 10 -15 9 -6 11 -27 11 -124
0 -16 -4 -26 -9 -23 -5 3 -7 -2 -4 -13 11 -41 -89 -307 -110 -294 -6 3 -7 1
-3 -6 13 -20 -142 -281 -166 -281 -6 0 -8 -3 -5 -7 11 -10 -51 -84 -197 -238
l-86 -89 -47 44 c-26 25 -52 50 -58 55 -7 6 -39 33 -71 61 -33 29 -86 73 -119
100 -86 69 -87 71 -90 112 -5 69 -16 207 -21 242 -4 33 -10 92 -18 170 -4 37
-14 114 -21 165 -2 17 -9 52 -14 80 -5 27 -9 50 -8 51 50 22 109 44 129 48 15
2 36 10 48 16 12 6 28 9 35 6 8 -3 15 -1 17 5 2 5 19 11 39 13 20 2 40 6 45 9
5 3 29 8 54 12 25 4 48 9 52 11 5 3 8 -1 8 -8 0 -9 2 -10 8 -2 9 16 43 21 61
10 10 -7 12 -6 6 4 -6 10 -3 12 12 8 12 -3 25 -3 31 1 20 12 206 11 214 -2z
m-1950 -189 c23 -13 46 -27 49 -31 3 -5 9 -8 13 -7 15 3 64 -25 59 -34 -3 -5
-1 -6 5 -2 14 9 63 -14 55 -27 -3 -6 -2 -7 4 -4 5 3 56 -21 113 -53 57 -33
107 -60 111 -60 4 0 11 -5 15 -12 5 -8 2 -9 -9 -5 -9 3 -16 2 -14 -2 1 -5 -48
-42 -110 -83 -61 -40 -144 -95 -183 -123 -40 -27 -80 -54 -89 -60 -9 -5 -18
-12 -21 -15 -17 -17 -80 -60 -80 -54 0 3 -9 -4 -20 -17 l-20 -24 6 105 c3 58
7 121 9 140 2 19 5 52 6 72 1 20 5 36 8 34 3 -2 7 19 7 47 2 79 15 149 31 176
8 13 10 20 4 17 -6 -4 -11 -2 -11 3 0 6 4 11 10 11 5 0 7 7 4 15 -8 20 -4 19
48 -7z m1114 -66 c3 -26 8 -56 10 -67 10 -50 19 -145 19 -192 0 -28 3 -49 7
-47 4 3 6 -16 5 -41 -1 -25 0 -45 3 -45 3 0 6 -32 7 -70 2 -39 -1 -70 -5 -70
-5 0 -18 9 -30 20 -12 11 -26 20 -31 20 -5 0 -17 10 -25 22 -9 12 -16 19 -16
15 0 -6 -92 56 -135 90 -25 20 -253 169 -278 182 -15 8 -26 15 -25 16 9 7 158
95 184 108 17 9 34 13 38 10 3 -4 6 -1 6 5 0 11 175 102 197 102 7 0 13 4 13
9 0 5 8 11 18 13 20 4 29 -14 38 -80z m-531 -268 c30 -20 55 -41 55 -45 0 -5
6 -8 13 -6 6 1 11 -4 9 -11 -1 -8 2 -11 7 -7 14 8 82 -38 76 -52 -2 -7 2 -10
11 -6 8 3 25 -3 37 -14 12 -11 35 -27 50 -37 16 -9 25 -22 21 -28 -4 -7 -3 -8
4 -4 10 6 242 -148 250 -166 2 -5 9 -8 16 -8 7 0 21 -9 31 -20 17 -19 14 -24
-10 -21 -5 1 -3 -4 5 -11 12 -10 15 -40 16 -138 0 -77 -4 -128 -10 -132 -7 -5
-7 -8 -1 -8 12 0 14 -259 2 -277 -5 -7 -4 -13 1 -13 12 0 7 -79 -5 -90 -7 -7
-244 -174 -287 -203 -10 -6 -24 -16 -30 -22 -12 -10 -34 -26 -211 -146 -96
-65 -108 -71 -131 -60 -14 6 -22 16 -19 21 3 5 0 7 -7 5 -8 -3 -23 4 -35 15
-12 11 -24 20 -27 20 -5 0 -246 166 -271 187 -5 4 -30 22 -55 38 -25 17 -49
33 -55 38 -5 4 -44 31 -85 61 l-75 54 0 337 c1 317 2 338 19 351 11 7 23 11
29 7 6 -3 7 -1 3 5 -4 7 3 17 18 24 14 6 26 15 26 20 0 4 7 8 15 8 8 0 15 5
15 11 0 6 7 8 16 5 8 -3 13 -2 9 3 -3 5 12 20 32 32 21 13 41 27 44 32 4 5 12
6 19 2 8 -5 11 -4 7 2 -9 15 38 44 60 37 12 -4 14 -3 6 3 -9 6 1 19 37 46 27
20 54 37 59 37 5 0 11 3 13 8 5 12 131 95 144 95 7 0 12 4 11 8 -2 8 51 47 66
48 4 1 32 -15 62 -35z m-822 -752 c2 -270 4 -262 -48 -215 -12 10 -41 34 -65
53 -43 33 -83 67 -157 136 l-34 31 57 49 c177 153 219 185 228 176 4 -4 7 -2
6 3 -4 17 0 29 6 23 3 -4 6 -119 7 -256z m1539 245 c7 -7 26 -20 41 -30 15
-10 25 -23 21 -29 -4 -7 -3 -8 4 -4 7 4 12 2 12 -3 0 -6 6 -10 13 -8 6 1 11
-4 9 -11 -1 -8 2 -11 7 -8 5 4 14 -2 20 -11 5 -10 13 -18 18 -18 4 0 7 -3 6
-7 -2 -5 1 -7 5 -5 5 1 37 -22 71 -52 l62 -54 -53 -49 c-29 -27 -66 -59 -81
-71 -16 -12 -34 -27 -41 -33 -43 -40 -129 -105 -133 -101 -2 3 0 14 6 25 7 13
7 23 -2 32 -9 10 -9 14 1 17 6 3 9 9 6 14 -5 9 -8 289 -4 314 1 6 2 14 1 19
-4 22 -8 86 -5 86 1 0 9 -6 16 -13z m-1862 -353 c48 -45 72 -65 109 -93 17
-13 31 -30 31 -37 0 -8 3 -13 8 -13 21 4 32 -3 32 -18 0 -9 3 -14 6 -10 4 3
13 -1 21 -9 8 -8 30 -26 49 -40 18 -15 31 -31 27 -37 -3 -5 -2 -7 4 -4 23 14
43 -19 48 -78 10 -127 16 -186 40 -410 4 -33 10 -79 14 -102 5 -27 4 -46 -3
-54 -8 -9 -8 -10 0 -6 7 4 14 -2 17 -13 3 -11 0 -20 -5 -20 -6 0 -5 -6 2 -15
7 -8 10 -26 8 -40 -3 -13 -1 -22 3 -19 5 3 9 1 9 -5 0 -8 -35 -25 -50 -23 -3
0 -14 -5 -25 -11 -11 -6 -22 -12 -25 -12 -3 -1 -36 -11 -75 -23 -61 -18 -120
-33 -190 -48 -11 -2 -40 -7 -65 -10 -25 -3 -49 -8 -53 -11 -5 -3 -70 -6 -145
-8 -122 -3 -168 0 -263 18 -72 14 -145 78 -155 136 -3 20 -8 44 -11 54 -2 9
-1 17 4 17 4 0 8 17 7 37 -1 50 6 74 20 66 8 -4 8 -3 0 6 -12 14 33 164 47
155 5 -3 6 2 3 10 -3 9 6 36 20 62 14 26 26 53 26 60 0 8 5 14 10 14 6 0 10 5
10 11 0 22 93 168 103 162 6 -3 7 -2 4 4 -10 17 64 118 77 105 4 -3 5 0 2 8
-4 9 12 35 39 65 25 28 73 80 106 117 34 38 65 65 70 62 5 -3 8 -2 7 3 -5 13
26 42 37 35 6 -3 26 -21 45 -38z m2364 -103 c-1 -3 7 -12 18 -19 10 -7 15 -18
12 -24 -4 -6 -2 -8 3 -5 6 4 21 -8 34 -25 13 -17 29 -36 34 -42 30 -33 63 -84
58 -90 -4 -3 -1 -6 5 -6 17 0 85 -108 76 -121 -4 -7 -3 -9 3 -6 13 8 106 -169
97 -185 -4 -6 -3 -8 3 -5 12 7 36 -50 28 -63 -3 -4 1 -10 9 -13 7 -3 17 -23
21 -44 5 -21 10 -45 12 -53 2 -8 4 -24 6 -35 1 -11 6 -23 10 -26 5 -3 8 -36 8
-73 0 -178 -108 -238 -417 -231 -78 2 -150 5 -161 8 -10 3 -34 8 -53 11 -19 3
-48 8 -65 11 -114 22 -337 94 -329 106 3 5 -2 6 -12 2 -12 -5 -15 -2 -11 13 6
20 16 78 22 129 2 17 6 46 9 65 3 19 8 60 11 90 3 30 8 69 11 87 2 17 7 62 10
100 3 37 10 73 15 80 7 7 6 14 -1 18 -6 3 -7 12 -3 18 4 6 6 26 5 45 -2 25 1
32 10 26 10 -6 10 -5 2 7 -16 21 -6 71 18 88 11 8 30 25 41 38 11 12 24 23 29
23 4 0 25 16 45 36 63 60 110 94 118 86 5 -4 5 -2 2 4 -4 7 9 24 29 39 20 15
49 40 66 57 l30 29 71 -72 c40 -39 71 -74 71 -78z m-1901 -294 c-3 -5 -2 -7 4
-4 12 8 83 -39 83 -55 0 -6 3 -8 6 -5 7 7 136 -80 142 -95 2 -4 10 -8 18 -8 8
0 14 -3 14 -8 0 -4 20 -18 45 -32 25 -14 45 -28 45 -32 0 -5 5 -8 10 -8 12 0
124 -72 128 -82 2 -5 11 -8 20 -8 10 0 -36 -32 -101 -70 -65 -39 -124 -67
-130 -64 -7 4 -8 3 -4 -2 5 -5 -41 -33 -110 -66 -80 -38 -120 -52 -125 -45 -3
7 -9 32 -12 57 -3 25 -8 58 -11 74 -3 15 -8 49 -11 75 -3 25 -7 62 -9 81 -2
19 -7 62 -10 94 -3 33 -8 64 -11 68 -3 4 0 8 6 8 6 0 8 5 4 11 -3 6 -8 43 -11
82 -5 65 -4 70 11 58 9 -7 13 -18 9 -24z m1261 -64 c-4 -94 -9 -162 -18 -228
-2 -16 -7 -55 -10 -85 -4 -30 -8 -62 -10 -70 -1 -8 -7 -37 -11 -65 -5 -27 -10
-53 -11 -57 -1 -5 -2 -13 -3 -20 -1 -9 -4 -9 -13 0 -7 7 -20 12 -30 12 -10 0
-18 5 -18 12 0 6 -3 9 -6 5 -4 -3 -54 19 -113 50 -58 31 -123 64 -143 75 -21
10 -38 23 -38 29 0 6 -4 8 -9 5 -5 -3 -19 2 -32 12 -13 10 -37 24 -52 32 -25
12 -26 15 -10 24 10 6 22 11 26 11 4 0 11 5 15 12 4 6 13 13 21 15 8 2 24 12
36 23 13 12 36 27 52 35 15 8 35 21 43 28 8 7 18 12 23 12 4 0 16 8 26 18 42
37 52 43 64 36 6 -4 9 -3 4 1 -9 11 43 46 56 38 6 -3 7 -2 4 4 -3 5 27 33 68
62 41 28 77 56 80 61 12 20 13 8 9 -87z m-524 -403 c8 -5 30 -17 48 -26 17 -9
32 -21 32 -26 0 -5 4 -7 9 -3 5 3 36 -11 68 -30 32 -19 63 -35 69 -35 5 0 21
-10 34 -22 14 -13 25 -21 25 -18 1 8 154 -64 155 -73 0 -5 -4 -5 -10 -2 -6 4
-7 -1 -3 -10 3 -10 0 -21 -8 -26 -11 -6 -11 -9 -1 -9 9 0 10 -5 4 -17 -5 -10
-15 -40 -21 -68 -7 -27 -18 -63 -26 -80 -7 -16 -24 -58 -36 -92 -13 -34 -28
-59 -33 -56 -5 3 -6 1 -3 -4 9 -14 -22 -75 -34 -68 -5 4 -6 -1 -3 -10 4 -9 -5
-33 -20 -55 -14 -22 -26 -42 -26 -46 0 -3 -19 -33 -42 -67 -185 -267 -312
-305 -471 -142 -56 56 -164 205 -150 205 4 0 3 4 -3 8 -14 8 -97 164 -109 202
-4 14 -16 43 -26 65 -25 55 -76 216 -81 251 -2 23 1 29 14 28 10 -1 16 1 14 5
-3 4 19 18 47 31 29 13 55 29 59 35 4 5 8 7 8 3 0 -4 23 6 50 22 28 16 50 26
50 22 0 -4 4 -2 8 3 4 6 25 19 47 30 22 11 47 25 55 31 38 28 169 92 176 86 4
-4 4 -2 1 5 -19 32 42 11 133 -47z"/>
<path d="M2556 3192 c-3 -5 1 -9 9 -9 8 0 12 4 9 9 -3 4 -7 8 -9 8 -2 0 -6 -4
-9 -8z"/>
<path d="M2455 2831 c-3 -5 -2 -12 3 -15 5 -3 9 1 9 9 0 17 -3 19 -12 6z"/>
<path d="M2500 2790 c-9 -6 -10 -10 -3 -10 6 0 15 5 18 10 8 12 4 12 -15 0z"/>
<path d="M2144 2592 c-70 -35 -108 -103 -100 -179 3 -38 32 -93 62 -118 9 -7
1 -43 -31 -140 -65 -196 -66 -176 5 -173 33 2 60 -1 60 -5 0 -5 4 -6 8 -3 13
8 227 10 239 3 18 -11 23 7 13 39 -6 16 -28 83 -49 149 l-39 120 29 27 c16 16
29 30 29 33 0 22 1 26 8 22 4 -3 8 20 8 51 3 88 -33 145 -108 176 -44 19 -96
18 -134 -2z"/>
<path d="M1113 2105 c0 -8 4 -12 9 -9 5 3 6 10 3 15 -9 13 -12 11 -12 -6z"/>
<path d="M859 1903 c-13 -16 -12 -17 4 -4 9 7 17 15 17 17 0 8 -8 3 -21 -13z"/>
<path d="M1436 1803 c-6 -14 -5 -15 5 -6 7 7 10 15 7 18 -3 3 -9 -2 -12 -12z"/>
<path d="M3760 1596 c0 -2 8 -10 18 -17 15 -13 16 -12 3 4 -13 16 -21 21 -21
13z"/>
<path d="M1616 1691 c-3 -5 2 -15 12 -22 15 -12 16 -12 5 2 -7 9 -10 19 -6 22
3 4 4 7 0 7 -3 0 -8 -4 -11 -9z"/>
<path d="M2710 1590 c0 -5 5 -10 11 -10 5 0 7 5 4 10 -3 6 -8 10 -11 10 -2 0
-4 -4 -4 -10z"/>
<path d="M1090 831 c0 -6 4 -13 10 -16 6 -3 7 1 4 9 -7 18 -14 21 -14 7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,19 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-384x384.png",
"sizes": "384x384",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

View File

@ -0,0 +1,37 @@
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: normal;
/*400*/
font-display: swap;
src: url("./worksans-regular-webfont.woff2") format("woff2");
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("./worksans-medium-webfont.woff2") format("woff2");
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("./worksans-semibold-webfont.woff2") format("woff2");
}
/* latin */
@font-face {
font-family: 'Work Sans';
font-style: normal;
font-weight: bold;
/*700*/
font-display: swap;
src: url("./worksans-bold-webfont.woff2") format("woff2");
}

BIN
.storybook/static/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

View File

@ -1,3 +1,3 @@
Looking to contribute? Thank you! PR are more than welcome.
Please refers to [this documentation page](https://docs.keycloakify.dev/contributing) that will help you get started.
Please refers to [this documentation page](https://docs.keycloakify.dev/faq-and-help/contributing) that will help you get started.

234
README.md
View File

@ -2,14 +2,11 @@
<img src="https://user-images.githubusercontent.com/6702424/109387840-eba11f80-7903-11eb-9050-db1dad883f78.png">
</p>
<p align="center">
<i>🔏 Create Keycloak themes using React 🔏</i>
<i>🔏 Keycloak Theming for the Modern Web 🔏</i>
<br>
<br>
<a href="https://github.com/garronej/keycloakify/actions">
<img src="https://github.com/garronej/keycloakify/workflows/ci/badge.svg?branch=main">
</a>
<a href="https://bundlephobia.com/package/keycloakify">
<img src="https://img.shields.io/bundlephobia/minzip/keycloakify">
<img src="https://github.com/keycloakify/keycloakify/actions/workflows/ci.yaml/badge.svg">
</a>
<a href="https://www.npmjs.com/package/keycloakify">
<img src="https://img.shields.io/npm/dm/keycloakify">
@ -17,157 +14,170 @@
<a href="https://github.com/garronej/keycloakify/blob/main/LICENSE">
<img src="https://img.shields.io/npm/l/keycloakify">
</a>
<a href="https://github.com/InseeFrLab/keycloakify/blob/729503fe31a155a823f46dd66ad4ff34ca274e0a/tsconfig.json#L14">
<img src="https://camo.githubusercontent.com/0f9fcc0ac1b8617ad4989364f60f78b2d6b32985ad6a508f215f14d8f897b8d3/68747470733a2f2f62616467656e2e6e65742f62616467652f547970655363726970742f7374726963742532302546302539462539322541412f626c7565">
</a>
<a href="https://github.com/thomasdarimont/awesome-keycloak">
<img src="https://awesome.re/mentioned-badge.svg"/>
</a>
<p align="center">
Check out our discord server!<br/>
<a href="https://discord.gg/mJdYJSdcm4">
<img src="https://dcbadge.limes.pink/api/server/kYFZG7fQmn"/>
</a>
</p>
<p align="center">
<a href="https://www.keycloakify.dev">Home</a>
-
<a href="https://docs.keycloakify.dev">Documentation</a>
</p>
<p align="center"> ---- Project starter / Demo setup ---- </p>
<p align="center">
<a href="https://github.com/garronej/keycloakify-starter">CSS Level customization</a>
-
<a href="https://github.com/garronej/keycloakify-advanced-starter">Component Level customization</a>
<a href="https://storybook.keycloakify.dev">Storybook</a>
-
<a href="https://github.com/codegouvfr/keycloakify-starter">Starter project</a>
</p>
<p align="center"> ---- </p>
</p>
<p align="center">
<i>Ultimately this build tool generates a Keycloak theme <a href="https://www.keycloakify.dev">Learn more</a></i>
<img src="https://user-images.githubusercontent.com/6702424/110260457-a1c3d380-7fac-11eb-853a-80459b65626b.png">
<i>This build tool generates a Keycloak theme <a href="https://www.keycloakify.dev">Learn more</a></i>
<br/>
<br/>
<img width="400" src="https://github.com/user-attachments/assets/6bf3bef9-00b0-4460-97b9-0d2da8500798">
</p>
> 🗣 V6 have been released 🎉
> [It features major improvements](https://github.com/InseeFrLab/keycloakify#600).
> Checkout [the migration guide](https://docs.keycloakify.dev/v5-to-v6).
Keycloakify is fully compatible with Keycloak from version 11 to 26...[and beyond](https://github.com/keycloakify/keycloakify/discussions/346#discussioncomment-5889791)
# Changelog highlights
> 📣 **Keycloakify 26 Released**
> Themes built with Keycloakify versions **prior** to Keycloak 26 are **incompatible** with Keycloak 26.
> To ensure compatibility, simply upgrade to the latest Keycloakify version for your major release (v10 or v11) and rebuild your theme.
> No breaking changes have been introduced, but the target version ranges have been updated. For more details, see [this guide](https://docs.keycloakify.dev/features/compiler-options/keycloakversiontargets).
## 6.8.0
## Sponsors
- It is now possible to pass a custom `<Template />` component as a prop to `<KcApp />` and every
individual page (`<Login />`, `<RegisterUserProfile />`, ...) it enables to customize only the header and footer for
example without having to switch to a full-component level customization. [See issue](https://github.com/InseeFrLab/keycloakify/issues/191).
Project backers, we trust and recommend their services.
## 6.7.0
<br/>
- Add support for `webauthn-authenticate.ftl` thanks to [@mstrodl](https://github.com/Mstrodl)'s hacktoberfest [PR](https://github.com/InseeFrLab/keycloakify/pull/185).
<div align="center">
## 6.6.0
![Logo Dark](https://github.com/user-attachments/assets/d8f6b6f5-3de4-4adc-ba15-cb4074e8309b#gh-dark-mode-only)
- Add support for `login-password.ftl` thanks to [@mstrodl](https://github.com/Mstrodl)'s hacktoberfest [PR](https://github.com/InseeFrLab/keycloakify/pull/184).
</div>
## 6.5.0
<div align="center">
- Add support for `login-username.ftl` thanks to [@mstrodl](https://github.com/Mstrodl)'s hacktoberfest [PR](https://github.com/InseeFrLab/keycloakify/pull/183).
![Logo Light](https://github.com/user-attachments/assets/20736d6f-f22d-4a9d-9dfe-93be209a8191#gh-light-mode-only)
## 6.4.0
</div>
- You can now optionally pass a `doFetchDefaultThemeResources: boolean` prop to every page component and the default `<KcApp />`
This enables you to prevent the default CSS and JS that comes with the builtin Keycloak theme to be downloaded.
You'll get [a black slate](https://user-images.githubusercontent.com/6702424/192619083-4baa5df4-4a21-4ec7-8e28-d200d1208299.png).
<br/>
## 6.0.0
<p align="center">
<i><a href="https://phasetwo.io/?utm_source=keycloakify"><strong>Keycloak as a Service</strong></a> - Keycloak community contributors of popular <a href="https://github.com/p2-inc#our-extensions-?utm_source=keycloakify">extensions</a> providing free and dedicated <a href="https://phasetwo.io/hosting/?utm_source=keycloakify">Keycloak hosting</a> and enterprise <a href="https://phasetwo.io/support/?utm_source=keycloakify">Keycloak support</a> to businesses of all sizes.</i>
</p>
- Bundle size drastically reduced, locals and component dynamically loaded.
- First print much quicker, use of React.lazy() everywhere.
- Real i18n API.
- Actual documentation for build options.
<br/>
<br/>
<br/>
Checkout [the migration guide](https://docs.keycloakify.dev/v5-to-v6)
<div align="center">
## 5.8.0
![Logo Dark](https://github.com/user-attachments/assets/dd3925fb-a58a-4e91-b360-69c2fa1f1087#gh-dark-mode-only)
- [React.lazy()](https://reactjs.org/docs/code-splitting.html#reactlazy) support 🎉. [#141](https://github.com/InseeFrLab/keycloakify/issues/141)
</div>
## 5.7.0
<div align="center">
- Feat `logout-confirm.ftl`. [PR](https://github.com/InseeFrLab/keycloakify/pull/120)
![Logo Light](https://github.com/user-attachments/assets/6c00c201-eed7-485a-a887-70891559d69b#gh-light-mode-only)
## 5.6.4
</div>
Fix `login-verify-email.ftl` page. [Before](https://user-images.githubusercontent.com/6702424/177436014-0bad22c4-5bfb-45bb-8fc9-dad65143cd0c.png) - [After](https://user-images.githubusercontent.com/6702424/177435797-ec5d7db3-84cf-49cb-8efc-3427a81f744e.png)
<br/>
## v5.6.0
<p align="center">
<a href="https://www.zone2.tech/services/keycloak-consulting">
<i><strong>Keycloak Consulting Services</strong> - Your partner in Keycloak deployment, configuration, and extension development for optimized identity management solutions.</i>
</a>
</p>
Add support for `login-config-totp.ftl` page [#127](https://github.com/InseeFrLab/keycloakify/pull/127).
<div align="center">
## v5.3.0
![Logo Dark](https://user-images.githubusercontent.com/6702424/234135797-c84d0a90-0526-43e5-a186-70cbebdeb278.png#gh-dark-mode-only)
Rename `keycloak_theme_email` to `keycloak_email`.
If you already had a `keycloak_theme_email` you should rename it `keycloak_email`.
</div>
## v5.0.0
<div align="center">
[Migration guide](https://github.com/garronej/keycloakify-demo-app/blob/a5b6a50f24bc25e082931f5ad9ebf47492acd12a/src/index.tsx#L46-L63)
New i18n system.
Import of terms and services have changed. [See example](https://github.com/garronej/keycloakify-demo-app/blob/a5b6a50f24bc25e082931f5ad9ebf47492acd12a/src/index.tsx#L46-L63).
![Logo Light](https://user-images.githubusercontent.com/6702424/234135799-68684c33-4ec5-48d4-8763-0f3922c86643.png#gh-light-mode-only)
## v4.10.0
</div>
Add `login-idp-link-email.ftl` page [See PR](https://github.com/InseeFrLab/keycloakify/pull/92).
<p align="center">
<a href="https://cloud-iam.com/?mtm_campaign=keycloakify-deal&mtm_source=keycloakify-github"><strong>Managed Keycloak Provider</strong> - With Cloud-IAM powering your Keycloak clusters, you can sleep easy knowing you've got the software and the experts you need for operational excellence. Cloud IAM is a french company. </a>
<br/>
Use code <code>keycloakify5</code> at checkout for a 5% discount.
</p>
## v4.8.0
## Contributors ✨
[Email template customization.](#email-template-customization)
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
## v4.7.4
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tbody>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lordvlad"><img src="https://avatars.githubusercontent.com/u/1217769?v=4?s=100" width="100px;" alt="Waldemar Reusch"/><br /><sub><b>Waldemar Reusch</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=lordvlad" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://willwill96.github.io/the-ui-dawg-static-site/en/introduction/"><img src="https://avatars.githubusercontent.com/u/10997562?v=4?s=100" width="100px;" alt="William Will"/><br /><sub><b>William Will</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=willwill96" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Ann2827"><img src="https://avatars.githubusercontent.com/u/32645809?v=4?s=100" width="100px;" alt="Bystrova Ann"/><br /><sub><b>Bystrova Ann</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=Ann2827" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mkreuzmayr"><img src="https://avatars.githubusercontent.com/u/20108212?v=4?s=100" width="100px;" alt="Michael Kreuzmayr"/><br /><sub><b>Michael Kreuzmayr</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=mkreuzmayr" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://coolmathgames.tech"><img src="https://avatars.githubusercontent.com/u/6877780?v=4?s=100" width="100px;" alt="Mary "/><br /><sub><b>Mary </b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=Mstrodl" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://tasyp.xyz/"><img src="https://avatars.githubusercontent.com/u/6623212?v=4?s=100" width="100px;" alt="German Öö"/><br /><sub><b>German Öö</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=Tasyp" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://revolunet.com"><img src="https://avatars.githubusercontent.com/u/124937?v=4?s=100" width="100px;" alt="Julien Bouquillon"/><br /><sub><b>Julien Bouquillon</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=revolunet" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aidangilmore"><img src="https://avatars.githubusercontent.com/u/32880357?v=4?s=100" width="100px;" alt="Aidan Gilmore"/><br /><sub><b>Aidan Gilmore</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=aidangilmore" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/0x-Void"><img src="https://avatars.githubusercontent.com/u/32745739?v=4?s=100" width="100px;" alt="Void"/><br /><sub><b>Void</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=0x-Void" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/juffe"><img src="https://avatars.githubusercontent.com/u/5393231?v=4?s=100" width="100px;" alt="juffe"/><br /><sub><b>juffe</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=juffe" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lazToum"><img src="https://avatars.githubusercontent.com/u/4764837?v=4?s=100" width="100px;" alt="Lazaros Toumanidis"/><br /><sub><b>Lazaros Toumanidis</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=lazToum" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/marcmrf"><img src="https://avatars.githubusercontent.com/u/9928519?v=4?s=100" width="100px;" alt="Marc"/><br /><sub><b>Marc</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=marcmrf" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://kasir-barati.github.io"><img src="https://avatars.githubusercontent.com/u/73785723?v=4?s=100" width="100px;" alt="Kasir Barati"/><br /><sub><b>Kasir Barati</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=kasir-barati" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/asashay"><img src="https://avatars.githubusercontent.com/u/10714670?v=4?s=100" width="100px;" alt="Alex Oliynyk"/><br /><sub><b>Alex Oliynyk</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=asashay" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://www.gravitysoftware.be"><img src="https://avatars.githubusercontent.com/u/1140574?v=4?s=100" width="100px;" alt="Thomas Silvestre"/><br /><sub><b>Thomas Silvestre</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=thosil" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/satanshiro"><img src="https://avatars.githubusercontent.com/u/38865738?v=4?s=100" width="100px;" alt="satanshiro"/><br /><sub><b>satanshiro</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=satanshiro" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://poelhekke.dev"><img src="https://avatars.githubusercontent.com/u/1632377?v=4?s=100" width="100px;" alt="Koen Poelhekke"/><br /><sub><b>Koen Poelhekke</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=kpoelhekke" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zavoloklom"><img src="https://avatars.githubusercontent.com/u/4151869?v=4?s=100" width="100px;" alt="Sergey Kupletsky"/><br /><sub><b>Sergey Kupletsky</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=zavoloklom" title="Tests">⚠️</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=zavoloklom" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rome-user"><img src="https://avatars.githubusercontent.com/u/114131048?v=4?s=100" width="100px;" alt="rome-user"/><br /><sub><b>rome-user</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=rome-user" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/celinepelletier"><img src="https://avatars.githubusercontent.com/u/82821620?v=4?s=100" width="100px;" alt="Céline Pelletier"/><br /><sub><b>Céline Pelletier</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=celinepelletier" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/xgp"><img src="https://avatars.githubusercontent.com/u/244253?v=4?s=100" width="100px;" alt="Garth"/><br /><sub><b>Garth</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=xgp" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/BlackVoid"><img src="https://avatars.githubusercontent.com/u/673720?v=4?s=100" width="100px;" alt="Felix Gustavsson"/><br /><sub><b>Felix Gustavsson</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=BlackVoid" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://m-siemens.de/"><img src="https://avatars.githubusercontent.com/u/1873922?v=4?s=100" width="100px;" alt="Markus Siemens"/><br /><sub><b>Markus Siemens</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=msiemens" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/law108000"><img src="https://avatars.githubusercontent.com/u/8112024?v=4?s=100" width="100px;" alt="Rlok"/><br /><sub><b>Rlok</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=law108000" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Moulyy"><img src="https://avatars.githubusercontent.com/u/115405804?v=4?s=100" width="100px;" alt="Moulyy"/><br /><sub><b>Moulyy</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=Moulyy" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/madmadson"><img src="https://avatars.githubusercontent.com/u/798831?v=4?s=100" width="100px;" alt="Tobias Matt"/><br /><sub><b>Tobias Matt</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=madmadson" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/oliviergoulet5"><img src="https://avatars.githubusercontent.com/u/17685861?v=4?s=100" width="100px;" alt="Olivier Goulet"/><br /><sub><b>Olivier Goulet</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=oliviergoulet5" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/liamlows"><img src="https://avatars.githubusercontent.com/u/1365914?v=4?s=100" width="100px;" alt="Liam Lowsley-Williams"/><br /><sub><b>Liam Lowsley-Williams</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=liamlows" title="Code">💻</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=liamlows" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/oes-rioniz/"><img src="https://avatars.githubusercontent.com/u/5172296?v=4?s=100" width="100px;" alt="Omid"/><br /><sub><b>Omid</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=uchar" title="Tests">⚠️</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=uchar" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kathari00"><img src="https://avatars.githubusercontent.com/u/42547712?v=4?s=100" width="100px;" alt="Katharina Eiserfey"/><br /><sub><b>Katharina Eiserfey</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=kathari00" title="Code">💻</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=kathari00" title="Tests">⚠️</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=kathari00" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/luca-peruzzo"><img src="https://avatars.githubusercontent.com/u/69015314?v=4?s=100" width="100px;" alt="Luca Peruzzo"/><br /><sub><b>Luca Peruzzo</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=luca-peruzzo" title="Code">💻</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=luca-peruzzo" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nima70"><img src="https://avatars.githubusercontent.com/u/5094767?v=4?s=100" width="100px;" alt="Nima Shokouhfar"/><br /><sub><b>Nima Shokouhfar</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=nima70" title="Code">💻</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=nima70" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://mruder.dev"><img src="https://avatars.githubusercontent.com/u/18495294?v=4?s=100" width="100px;" alt="Marvin A. Ruder"/><br /><sub><b>Marvin A. Ruder</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/issues?q=author%3Amarvinruder" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/zvn2060"><img src="https://avatars.githubusercontent.com/u/45450852?v=4?s=100" width="100px;" alt="HI_OuO"/><br /><sub><b>HI_OuO</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=zvn2060" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tripheo0412"><img src="https://avatars.githubusercontent.com/u/25382052?v=4?s=100" width="100px;" alt="Tri Hoang"/><br /><sub><b>Tri Hoang</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=tripheo0412" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://t.me/AAT_L"><img src="https://avatars.githubusercontent.com/u/118743608?v=4?s=100" width="100px;" alt="Lesha"/><br /><sub><b>Lesha</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=EternalSide" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://blog.bacongobbler.com"><img src="https://avatars.githubusercontent.com/u/1360539?v=4?s=100" width="100px;" alt="Matthew Fisher"/><br /><sub><b>Matthew Fisher</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=bacongobbler" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kodebach"><img src="https://avatars.githubusercontent.com/u/23529132?v=4?s=100" width="100px;" alt="Klemens Böswirth"/><br /><sub><b>Klemens Böswirth</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=kodebach" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wnmzzzz"><img src="https://avatars.githubusercontent.com/u/117174301?v=4?s=100" width="100px;" alt="wnmzzzz"/><br /><sub><b>wnmzzzz</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=wnmzzzz" title="Tests">⚠️</a></td>
</tr>
</tbody>
</table>
**M1 Mac** support (for testing locally with a dockerized Keycloak).
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
## v4.7.2
> WARNING: This is broken.
> Testing with local Keycloak container working with M1 Mac. Thanks to [@eduardosanzb](https://github.com/InseeFrLab/keycloakify/issues/43#issuecomment-975699658).
> Be aware: When running M1s you are testing with Keycloak v15 else the local container spun will be a Keycloak v16.1.0.
## v4.7.0
Register with user profile enabled: Out of the box `options` validator support.
[Example](https://user-images.githubusercontent.com/6702424/158911163-81e6bbe8-feb0-4dc8-abff-de199d7a678e.mov)
## v4.6.0
`tss-react` and `powerhooks` are no longer peer dependencies of `keycloakify`.
After updating Keycloakify you can remove `tss-react` and `powerhooks` from your dependencies if you don't use them explicitly.
## v4.5.3
There is a new recommended way to setup highly customized theme. See [here](https://github.com/garronej/keycloakify-demo-app/blob/look_and_feel/src/KcApp/KcApp.tsx).
Unlike with [the previous recommended method](https://github.com/garronej/keycloakify-demo-app/blob/a51660578bea15fb3e506b8a2b78e1056c6d68bb/src/KcApp/KcApp.tsx),
with this new method your theme wont break on minor Keycloakify update.
## v4.3.0
Feature [`login-update-password.ftl`](https://user-images.githubusercontent.com/6702424/147517600-6191cf72-93dd-437b-a35c-47180142063e.png).
Every time a page is added it's a breaking change for non CSS-only theme.
Change [this](https://github.com/garronej/keycloakify-demo-app/blob/df664c13c77ce3c53ac7df0622d94d04e76d3f9f/src/KcApp/KcApp.tsx#L17) and [this](https://github.com/garronej/keycloakify-demo-app/blob/df664c13c77ce3c53ac7df0622d94d04e76d3f9f/src/KcApp/KcApp.tsx#L37) to update.
## v4
- Out of the box [frontend form validation](#user-profile-and-frontend-form-validation) 🥳
- Improvements (and breaking changes in `import { useKcMessage } from "keycloakify"`.
## v3
No breaking changes except that `@emotion/react`, [`tss-react`](https://www.npmjs.com/package/tss-react) and [`powerhooks`](https://www.npmjs.com/package/powerhooks) are now `peerDependencies` instead of being just dependencies.
It's important to avoid problem when using `keycloakify` alongside [`mui`](https://mui.com) and
[when passing params from the app to the login page](https://github.com/InseeFrLab/keycloakify#implement-context-persistence-optional).
## v2.5
- Feature [Use advanced message](https://github.com/InseeFrLab/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/lib/i18n/useKcMessage.tsx#L53-L66)
and [`messagesPerFields`](https://github.com/InseeFrLab/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/lib/getKcContext/KcContextBase.ts#L70-L75) (implementation [here](https://github.com/InseeFrLab/keycloakify/blob/59f106bf9e210b63b190826da2bf5f75fc8b7644/src/bin/build-keycloak-theme/generateFtl/common.ftl#L130-L189))
- Test container now uses Keycloak version `15.0.2`.
## v2
- It's now possible to implement custom `.ftl` pages.
- Support for Keycloak plugins that introduce non standard ftl values.
(Like for example [this plugin](https://github.com/micedre/keycloak-mail-whitelisting) that define `authorizedMailDomains` in `register.ftl`).
<!-- ALL-CONTRIBUTORS-LIST:END -->

137
package.json Executable file → Normal file
View File

@ -1,30 +1,26 @@
{
"name": "keycloakify",
"version": "6.8.2",
"description": "Keycloak theme generator for Reacts app",
"version": "11.8.23",
"description": "Framework to create custom Keycloak UIs",
"repository": {
"type": "git",
"url": "git://github.com/garronej/keycloakify.git"
"url": "git://github.com/keycloakify/keycloakify.git"
},
"main": "dist/lib/index.js",
"types": "dist/lib/index.d.ts",
"scripts": {
"build": "rimraf dist/ && tsc -p src/bin && tsc -p src/lib && yarn grant-exec-perms && yarn copy-files dist/",
"build:test": "rimraf dist_test/ && tsc -p src/test && yarn copy-files dist_test/",
"grant-exec-perms": "node dist/bin/tools/grant-exec-perms.js",
"copy-files": "copyfiles -u 1 src/**/*.ftl",
"pretest": "yarn build:test",
"test": "node dist_test/test/bin && node dist_test/test/lib",
"generate-messages": "node dist/bin/generate-i18n-messages.js",
"link_in_test_app": "node dist/bin/link_in_test_app.js",
"prepare": "tsx scripts/generate-i18n-messages.ts",
"build": "tsx scripts/build/main.ts",
"storybook": "tsx scripts/start-storybook.ts",
"link-in-starter": "tsx scripts/link-in-starter.ts",
"test": "yarn test:types && vitest run",
"test:types": "tsc -p test/tsconfig.json --noEmit",
"_format": "prettier '**/*.{ts,tsx,json,md}'",
"format": "yarn _format --write",
"format:check": "yarn _format --list-different"
"link-in-app": "tsx scripts/link-in-app.ts",
"build-storybook": "tsx scripts/build-storybook.ts",
"dump-keycloak-realm": "tsx scripts/dump-keycloak-realm.ts"
},
"bin": {
"keycloakify": "dist/bin/keycloakify/index.js",
"create-keycloak-email-directory": "dist/bin/create-keycloak-email-directory.js",
"download-builtin-keycloak-theme": "dist/bin/download-builtin-keycloak-theme.js"
"keycloakify": "dist/bin/main.js"
},
"lint-staged": {
"*.{ts,tsx,json,md}": [
@ -39,56 +35,91 @@
"author": "u/garronej",
"license": "MIT",
"files": [
"src/",
"dist/",
"!dist/tsconfig.tsbuildinfo"
"!dist/tsconfig.tsbuildinfo",
"!dist/bin/",
"dist/bin/**/*.d.ts",
"dist/bin/main.js",
"dist/bin/*.index.js",
"dist/bin/*.node",
"dist/bin/shared/constants.js",
"dist/bin/shared/constants.js.map",
"dist/bin/shared/customHandler.js",
"dist/bin/shared/customHandler.js.map",
"!dist/vite-plugin/",
"dist/vite-plugin/index.js",
"dist/vite-plugin/index.d.ts",
"dist/vite-plugin/vite-plugin.d.ts"
],
"keywords": [
"bluehats",
"keycloak",
"react",
"theme",
"FreeMarker",
"ftl",
"login",
"register"
"register",
"account",
"bluehats"
],
"homepage": "https://github.com/garronej/keycloakify",
"peerDependencies": {
"@emotion/react": "^11.4.1",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
"homepage": "https://www.keycloakify.dev",
"dependencies": {
"tsafe": "^1.8.5"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@emotion/react": "^11.4.1",
"@types/memoizee": "^0.4.7",
"@babel/core": "^7.24.5",
"@babel/generator": "^7.24.5",
"@babel/parser": "^7.24.5",
"@babel/preset-env": "7.24.8",
"@babel/types": "^7.24.5",
"@emotion/react": "^11.11.4",
"@octokit/rest": "^20.1.1",
"@storybook/addon-a11y": "^6.5.16",
"@storybook/builder-webpack5": "^6.5.13",
"@storybook/manager-webpack5": "^6.5.13",
"@storybook/react": "^6.5.13",
"@types/babel__generator": "^7.6.4",
"@types/dompurify": "^2.0.0",
"@types/make-fetch-happen": "^10.0.1",
"@types/minimist": "^1.2.2",
"@types/node": "^17.0.25",
"@types/react": "18.0.9",
"copyfiles": "^2.4.1",
"husky": "^4.3.8",
"lint-staged": "^11.0.0",
"prettier": "^2.3.0",
"properties-parser": "^0.3.1",
"react": "18.1.0",
"rimraf": "^3.0.2",
"typescript": "^4.2.3"
},
"dependencies": {
"@octokit/rest": "^18.12.0",
"cheerio": "^1.0.0-rc.5",
"@types/node": "^18.15.3",
"@types/properties-parser": "^0.3.3",
"@types/react": "^18.0.35",
"@types/react-dom": "^18.0.11",
"@types/yauzl": "^2.10.3",
"@vercel/ncc": "^0.38.1",
"babel-loader": "9.1.3",
"chalk": "^4.1.2",
"cheerio": "1.0.0-rc.12",
"chokidar-cli": "^3.0.0",
"cli-select": "^1.1.2",
"evt": "^2.4.6",
"memoizee": "^0.4.15",
"minimal-polyfills": "^2.2.2",
"minimist": "^1.2.6",
"path-browserify": "^1.0.1",
"powerhooks": "^0.20.23",
"react-markdown": "^5.0.3",
"rfc4648": "^1.5.2",
"scripting-tools": "^0.19.13",
"tsafe": "^1.1.3",
"tss-react": "^4.3.4",
"dompurify": "^3.1.6",
"eslint-plugin-storybook": "^0.6.7",
"evt": "^2.5.8",
"html-entities": "^2.5.2",
"husky": "^4.3.8",
"isomorphic-dompurify": "^2.15.0",
"lint-staged": "^11.0.0",
"magic-string": "^0.30.7",
"make-fetch-happen": "^11.0.3",
"powerhooks": "^1.0.19",
"prettier": "^3.2.5",
"properties-parser": "^0.3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"recast": "^0.23.3",
"run-exclusive": "^2.2.19",
"storybook-dark-mode": "^1.1.2",
"termost": "^v0.12.1",
"tsc-alias": "^1.8.10",
"tss-react": "^4.9.10",
"tsx": "^4.15.5",
"typescript": "^4.9.4",
"vite": "^5.2.11",
"vitest": "^1.6.0",
"webpack": "5.93.0",
"webpack-cli": "5.1.4",
"yauzl": "^2.10.0",
"zod": "^3.17.10"
}
}

View File

@ -1,6 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"baseBranches": ["main", "landingpage"],
"baseBranches": ["main"],
"extends": ["config:base"],
"dependencyDashboard": false,
"bumpVersion": "patch",
@ -13,11 +13,11 @@
"packageRules": [
{
"packagePatterns": ["*"],
"excludePackagePatterns": ["tss-react", "powerhooks", "tsafe", "evt"],
"excludePackagePatterns": ["tsafe", "evt"],
"enabled": false
},
{
"packagePatterns": ["tss-react", "powerhooks", "tsafe", "evt"],
"packagePatterns": ["tsafe", "evt"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true,
"automergeType": "branch",

View File

@ -0,0 +1,4 @@
import { run } from "./shared/run";
run("yarn build");
run("npx build-storybook");

View File

@ -0,0 +1,79 @@
import * as fs from "fs";
import { join as pathJoin } from "path";
import { transformCodebase } from "../../src/bin/tools/transformCodebase";
import { downloadKeycloakDefaultTheme } from "../shared/downloadKeycloakDefaultTheme";
import { WELL_KNOWN_DIRECTORY_BASE_NAME } from "../../src/bin/shared/constants";
import { getThisCodebaseRootDirPath } from "../../src/bin/tools/getThisCodebaseRootDirPath";
import { accountMultiPageSupportedLanguages } from "../generate-i18n-messages";
import * as fsPr from "fs/promises";
export async function createAccountV1Dir() {
const { extractedDirPath } = await downloadKeycloakDefaultTheme({
keycloakVersionId: "FOR_ACCOUNT_MULTI_PAGE"
});
const destDirPath = pathJoin(
getThisCodebaseRootDirPath(),
"dist",
"res",
"account-v1"
);
await fsPr.rm(destDirPath, { recursive: true, force: true });
transformCodebase({
srcDirPath: pathJoin(extractedDirPath, "base", "account"),
destDirPath
});
transformCodebase({
srcDirPath: pathJoin(extractedDirPath, "keycloak", "account", "resources"),
destDirPath: pathJoin(destDirPath, "resources")
});
transformCodebase({
srcDirPath: pathJoin(extractedDirPath, "keycloak", "common", "resources"),
destDirPath: pathJoin(
destDirPath,
"resources",
WELL_KNOWN_DIRECTORY_BASE_NAME.RESOURCES_COMMON
)
});
fs.writeFileSync(
pathJoin(destDirPath, "theme.properties"),
Buffer.from(
[
"accountResourceProvider=account-v1",
"",
`locales=${accountMultiPageSupportedLanguages.join(",")}`,
"",
"styles=" +
[
"css/account.css",
"img/icon-sidebar-active.png",
"img/logo.png",
...[
"patternfly.min.css",
"patternfly-additions.min.css",
"patternfly-additions.min.css"
].map(
fileBasename =>
`${WELL_KNOWN_DIRECTORY_BASE_NAME.RESOURCES_COMMON}/node_modules/patternfly/dist/css/${fileBasename}`
)
].join(" "),
"",
"##### css classes for form buttons",
"# main class used for all buttons",
"kcButtonClass=btn",
"# classes defining priority of the button - primary or default (there is typically only one priority button for the form)",
"kcButtonPrimaryClass=btn-primary",
"kcButtonDefaultClass=btn-default",
"# classes defining size of the button",
"kcButtonLargeClass=btn-lg",
""
].join("\n"),
"utf8"
)
);
}

View File

@ -0,0 +1,73 @@
import { join as pathJoin } from "path";
import { downloadKeycloakDefaultTheme } from "../shared/downloadKeycloakDefaultTheme";
import { transformCodebase } from "../../src/bin/tools/transformCodebase";
import { existsAsync } from "../../src/bin/tools/fs.existsAsync";
import { getThisCodebaseRootDirPath } from "../../src/bin/tools/getThisCodebaseRootDirPath";
import { WELL_KNOWN_DIRECTORY_BASE_NAME } from "../../src/bin/shared/constants";
import { assert, type Equals } from "tsafe/assert";
import * as fsPr from "fs/promises";
export async function createPublicKeycloakifyDevResourcesDir() {
await Promise.all(
(["login", "account"] as const).map(async themeType => {
const { extractedDirPath } = await downloadKeycloakDefaultTheme({
keycloakVersionId: (() => {
switch (themeType) {
case "login":
return "FOR_LOGIN_THEME";
case "account":
return "FOR_ACCOUNT_MULTI_PAGE";
}
assert<Equals<typeof themeType, never>>();
})()
});
const destDirPath = pathJoin(
getThisCodebaseRootDirPath(),
"dist",
"res",
"public",
WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES,
themeType
);
await fsPr.rm(destDirPath, { recursive: true, force: true });
base_resources: {
const srcDirPath = pathJoin(
extractedDirPath,
"base",
themeType,
"resources"
);
if (!(await existsAsync(srcDirPath))) {
break base_resources;
}
transformCodebase({
srcDirPath,
destDirPath
});
}
transformCodebase({
srcDirPath: pathJoin(
extractedDirPath,
"keycloak",
themeType,
"resources"
),
destDirPath
});
transformCodebase({
srcDirPath: pathJoin(extractedDirPath, "keycloak", "common", "resources"),
destDirPath: pathJoin(
destDirPath,
WELL_KNOWN_DIRECTORY_BASE_NAME.RESOURCES_COMMON
)
});
})
);
}

View File

@ -0,0 +1,39 @@
import { downloadAndExtractArchive } from "../../src/bin/tools/downloadAndExtractArchive";
import { cacheDirPath } from "../shared/cacheDirPath";
import { getProxyFetchOptions } from "../../src/bin/tools/fetchProxyOptions";
import { getThisCodebaseRootDirPath } from "../../src/bin/tools/getThisCodebaseRootDirPath";
import { existsAsync } from "../../src/bin/tools/fs.existsAsync";
import * as fs from "fs/promises";
import {
KEYCLOAKIFY_LOGGING_VERSION,
KEYCLOAKIFY_LOGIN_JAR_BASENAME
} from "../../src/bin/shared/constants";
import { join as pathJoin } from "path";
export async function downloadKeycloakifyLogging(params: { distDirPath: string }) {
const { distDirPath } = params;
const jarFilePath = pathJoin(
distDirPath,
"src",
"bin",
"start-keycloak",
KEYCLOAKIFY_LOGIN_JAR_BASENAME
);
if (await existsAsync(jarFilePath)) {
return;
}
const { archiveFilePath } = await downloadAndExtractArchive({
cacheDirPath,
fetchOptions: getProxyFetchOptions({
npmConfigGetCwd: getThisCodebaseRootDirPath()
}),
url: `https://github.com/keycloakify/keycloakify-logging/releases/download/${KEYCLOAKIFY_LOGGING_VERSION}/keycloakify-logging-${KEYCLOAKIFY_LOGGING_VERSION}.jar`,
uniqueIdOfOnArchiveFile: "no extraction",
onArchiveFile: async () => {}
});
await fs.cp(archiveFilePath, jarFilePath);
}

188
scripts/build/main.ts Normal file
View File

@ -0,0 +1,188 @@
import * as fs from "fs";
import { join } from "path";
import { assert } from "tsafe/assert";
import { transformCodebase } from "../../src/bin/tools/transformCodebase";
import { createPublicKeycloakifyDevResourcesDir } from "./createPublicKeycloakifyDevResourcesDir";
import { createAccountV1Dir } from "./createAccountV1Dir";
import chalk from "chalk";
import { run } from "../shared/run";
import { vendorFrontendDependencies } from "./vendorFrontendDependencies";
import { downloadKeycloakifyLogging } from "./downloadKeycloakifyLogging";
(async () => {
console.log(chalk.cyan("Building Keycloakify..."));
const startTime = Date.now();
if (fs.existsSync(join("dist", "bin", "main.original.js"))) {
fs.renameSync(
join("dist", "bin", "main.original.js"),
join("dist", "bin", "main.js")
);
fs.readdirSync(join("dist", "bin")).forEach(fileBasename => {
if (/[0-9]\.index.js/.test(fileBasename) || fileBasename.endsWith(".node")) {
fs.rmSync(join("dist", "bin", fileBasename));
}
});
}
run(`npx tsc -p ${join("src", "bin", "tsconfig.json")}`);
if (
!fs
.readFileSync(join("dist", "bin", "main.js"))
.toString("utf8")
.includes("__nccwpck_require__")
) {
fs.cpSync(
join("dist", "bin", "main.js"),
join("dist", "bin", "main.original.js")
);
}
run(
`npx ncc build ${join("dist", "bin", "main.js")} --external prettier -o ${join("dist", "ncc_out")}`
);
transformCodebase({
srcDirPath: join("dist", "ncc_out"),
destDirPath: join("dist", "bin"),
transformSourceCode: ({ fileRelativePath, sourceCode }) => {
if (fileRelativePath === "index.js") {
return {
newFileName: "main.js",
modifiedSourceCode: sourceCode
};
}
return { modifiedSourceCode: sourceCode };
}
});
fs.rmSync(join("dist", "ncc_out"), { recursive: true });
{
let hasBeenPatched = false;
fs.readdirSync(join("dist", "bin")).forEach(fileBasename => {
if (fileBasename !== "main.js" && !fileBasename.endsWith(".index.js")) {
return;
}
const { hasBeenPatched: hasBeenPatched_i } = patchDeprecatedBufferApiUsage(
join("dist", "bin", fileBasename)
);
if (hasBeenPatched_i) {
hasBeenPatched = true;
}
});
assert(hasBeenPatched);
}
fs.chmodSync(
join("dist", "bin", "main.js"),
fs.statSync(join("dist", "bin", "main.js")).mode |
fs.constants.S_IXUSR |
fs.constants.S_IXGRP |
fs.constants.S_IXOTH
);
run(`npx tsc -p ${join("src", "tsconfig.json")}`);
run(`npx tsc-alias -p ${join("src", "tsconfig.json")}`);
vendorFrontendDependencies({ distDirPath: join(process.cwd(), "dist") });
if (fs.existsSync(join("dist", "vite-plugin", "index.original.js"))) {
fs.renameSync(
join("dist", "vite-plugin", "index.original.js"),
join("dist", "vite-plugin", "index.js")
);
}
run(`npx tsc -p ${join("src", "vite-plugin", "tsconfig.json")}`);
if (
!fs
.readFileSync(join("dist", "vite-plugin", "index.js"))
.toString("utf8")
.includes("__nccwpck_require__")
) {
fs.cpSync(
join("dist", "vite-plugin", "index.js"),
join("dist", "vite-plugin", "index.original.js")
);
}
run(
`npx ncc build ${join("dist", "vite-plugin", "index.js")} --external prettier -o ${join(
"dist",
"ncc_out"
)}`
);
fs.readdirSync(join("dist", "ncc_out")).forEach(fileBasename => {
assert(!fileBasename.endsWith(".index.js"));
assert(!fileBasename.endsWith(".node"));
});
transformCodebase({
srcDirPath: join("dist", "ncc_out"),
destDirPath: join("dist", "vite-plugin"),
transformSourceCode: ({ fileRelativePath, sourceCode }) => {
assert(fileRelativePath === "index.js");
return { modifiedSourceCode: sourceCode };
}
});
fs.rmSync(join("dist", "ncc_out"), { recursive: true });
{
const dirBasename = "src";
const destDirPath = join("dist", dirBasename);
fs.rmSync(destDirPath, { recursive: true, force: true });
fs.cpSync(dirBasename, destDirPath, { recursive: true });
}
transformCodebase({
srcDirPath: join("stories"),
destDirPath: join("dist", "stories"),
transformSourceCode: ({ fileRelativePath, sourceCode }) => {
if (!fileRelativePath.endsWith(".stories.tsx")) {
return undefined;
}
return { modifiedSourceCode: sourceCode };
}
});
await createPublicKeycloakifyDevResourcesDir();
await createAccountV1Dir();
await downloadKeycloakifyLogging({
distDirPath: join(process.cwd(), "dist")
});
console.log(
chalk.green(`✓ built in ${((Date.now() - startTime) / 1000).toFixed(2)}s`)
);
})();
function patchDeprecatedBufferApiUsage(filePath: string) {
const before = fs.readFileSync(filePath).toString("utf8");
const after = before.replace(
`var buffer = new Buffer(toRead);`,
`var buffer = Buffer.allocUnsafe ? Buffer.allocUnsafe(toRead) : new Buffer(toRead);`
);
fs.writeFileSync(filePath, Buffer.from(after, "utf8"));
const hasBeenPatched = after !== before;
return { hasBeenPatched };
}

View File

@ -0,0 +1,97 @@
import * as fs from "fs";
import { join as pathJoin, basename as pathBasename, dirname as pathDirname } from "path";
import { assert } from "tsafe/assert";
import { run } from "../shared/run";
import { cacheDirPath as cacheDirPath_base } from "../shared/cacheDirPath";
export function vendorFrontendDependencies(params: { distDirPath: string }) {
const { distDirPath } = params;
const vendorDirPath = pathJoin(distDirPath, "tools", "vendor");
const cacheDirPath = pathJoin(cacheDirPath_base, "vendorFrontendDependencies");
const extraBundleFileBasenames = new Set<string>();
fs.readdirSync(vendorDirPath)
.filter(fileBasename => fileBasename.endsWith(".js"))
.map(fileBasename => pathJoin(vendorDirPath, fileBasename))
.forEach(filePath => {
{
const mapFilePath = `${filePath}.map`;
if (fs.existsSync(mapFilePath)) {
fs.unlinkSync(mapFilePath);
}
}
if (!fs.existsSync(cacheDirPath)) {
fs.mkdirSync(cacheDirPath, { recursive: true });
}
const webpackConfigJsFilePath = pathJoin(cacheDirPath, "webpack.config.js");
const webpackOutputDirPath = pathJoin(cacheDirPath, "webpack_output");
const webpackOutputFilePath = pathJoin(webpackOutputDirPath, "index.js");
fs.writeFileSync(
webpackConfigJsFilePath,
Buffer.from(
[
``,
`module.exports = {`,
` mode: 'production',`,
` entry: Buffer.from("${Buffer.from(filePath, "utf8").toString("base64")}", "base64").toString("utf8"),`,
` output: {`,
` path: Buffer.from("${Buffer.from(webpackOutputDirPath, "utf8").toString("base64")}", "base64").toString("utf8"),`,
` filename: '${pathBasename(webpackOutputFilePath)}',`,
` libraryTarget: 'module',`,
` },`,
` target: "web",`,
` module: {`,
` rules: [`,
` {`,
` test: /\.js$/,`,
` use: {`,
` loader: 'babel-loader',`,
` options: {`,
` presets: ['@babel/preset-env'],`,
` }`,
` }`,
` }`,
` ]`,
` },`,
` experiments: {`,
` outputModule: true`,
` }`,
`};`
].join("\n")
)
);
run(`npx webpack --config ${pathBasename(webpackConfigJsFilePath)}`, {
cwd: pathDirname(webpackConfigJsFilePath)
});
fs.readdirSync(webpackOutputDirPath)
.filter(fileBasename => !fileBasename.endsWith(".txt"))
.map(fileBasename => pathJoin(webpackOutputDirPath, fileBasename))
.forEach(bundleFilePath => {
assert(bundleFilePath.endsWith(".js"));
if (pathBasename(bundleFilePath) === "index.js") {
fs.renameSync(webpackOutputFilePath, filePath);
} else {
const bundleFileBasename = pathBasename(bundleFilePath);
assert(!extraBundleFileBasenames.has(bundleFileBasename));
extraBundleFileBasenames.add(bundleFileBasename);
fs.renameSync(
bundleFilePath,
pathJoin(pathDirname(filePath), bundleFileBasename)
);
}
});
fs.rmSync(webpackOutputDirPath, { recursive: true });
});
}

View File

@ -0,0 +1,45 @@
import { CONTAINER_NAME } from "../src/bin/shared/constants";
import child_process from "child_process";
import { SemVer } from "../src/bin/tools/SemVer";
import { dumpContainerConfig } from "../src/bin/start-keycloak/realmConfig/dumpContainerConfig";
import { cacheDirPath } from "./shared/cacheDirPath";
import { getThisCodebaseRootDirPath } from "../src/bin/tools/getThisCodebaseRootDirPath";
import { writeRealmJsonFile } from "../src/bin/start-keycloak/realmConfig/ParsedRealmJson";
import { join as pathJoin } from "path";
import chalk from "chalk";
(async () => {
const keycloakMajorVersionNumber = SemVer.parse(
child_process
.execSync(`docker inspect --format '{{.Config.Image}}' ${CONTAINER_NAME}`)
.toString("utf8")
.trim()
.split(":")[1]
).major;
const parsedRealmJson = await dumpContainerConfig({
buildContext: {
cacheDirPath
},
keycloakMajorVersionNumber,
realmName: "myrealm"
});
const realmJsonFilePath = pathJoin(
getThisCodebaseRootDirPath(),
"src",
"bin",
"start-keycloak",
"realmConfig",
"defaultConfig",
`realm-kc-${keycloakMajorVersionNumber}.json`
);
await writeRealmJsonFile({
parsedRealmJson,
realmJsonFilePath,
keycloakMajorVersionNumber
});
console.log(chalk.green(`Realm config dumped to ${realmJsonFilePath}`));
})();

View File

@ -0,0 +1,753 @@
import * as fs from "fs";
import {
join as pathJoin,
relative as pathRelative,
dirname as pathDirname,
sep as pathSep
} from "path";
import { assert, type Equals } from "tsafe/assert";
import { same } from "evt/tools/inDepth";
import { crawl } from "../src/bin/tools/crawl";
import { downloadKeycloakDefaultTheme } from "./shared/downloadKeycloakDefaultTheme";
import { getThisCodebaseRootDirPath } from "../src/bin/tools/getThisCodebaseRootDirPath";
import { deepAssign } from "../src/tools/deepAssign";
import { THEME_TYPES } from "../src/bin/shared/constants";
import { transformCodebase } from "../src/bin/tools/transformCodebase";
import propertiesParser from "properties-parser";
if (require.main === module) {
generateI18nMessages();
}
async function generateI18nMessages() {
const thisCodebaseRootDirPath = getThisCodebaseRootDirPath();
const accountI18nDirPath = pathJoin(
thisCodebaseRootDirPath,
"src",
"account",
"i18n"
);
if (fs.existsSync(accountI18nDirPath)) {
fs.rmSync(accountI18nDirPath, { recursive: true });
}
type Dictionary = { [idiomId: string]: string };
const record: { [themeType: string]: { [language: string]: Dictionary } } = {};
for (const themeType of THEME_TYPES.filter(themeType => themeType !== "admin")) {
const { extractedDirPath } = await downloadKeycloakDefaultTheme({
keycloakVersionId: (() => {
switch (themeType) {
case "login":
return "FOR_LOGIN_THEME";
case "account":
return "FOR_ACCOUNT_MULTI_PAGE";
}
assert<Equals<typeof themeType, never>>();
})()
});
{
const baseThemeDirPath = pathJoin(extractedDirPath, "base");
const re = new RegExp(
`^([^\\${pathSep}]+)\\${pathSep}messages\\${pathSep}messages_([^.]+).properties$`
);
crawl({
dirPath: baseThemeDirPath,
returnedPathsType: "relative to dirPath"
}).forEach(filePath => {
const match = filePath.match(re);
if (match === null) {
return;
}
const [, themeType_here, language] = match;
if (themeType_here !== themeType) {
return;
}
(record[themeType] ??= {})[language.replace(/_/g, "-")] =
Object.fromEntries(
Object.entries(
propertiesParser.parse(
fs
.readFileSync(pathJoin(baseThemeDirPath, filePath))
.toString("utf8")
) as Record<string, string>
)
.map(([key, value]) => [key, value.replace(/''/g, "'")])
.map(([key, value]) => [
key === "locale_pt_BR" ? "locale_pt-BR" : key,
value
])
.map(([key, value]) => [
key,
key === "termsText" ? "" : value
])
);
});
}
const recordForThemeType = record[themeType];
const languages = Object.keys(recordForThemeType);
const keycloakifyExtraMessages = (() => {
switch (themeType) {
case "login":
return keycloakifyExtraMessages_login;
case "account":
return keycloakifyExtraMessages_account;
}
assert(false);
})();
/* Migration helper
console.log({ themeType });
{
const all = new Set<string>();
languages.forEach(languages => all.add(languages));
const currentlySupportedLanguages = Object.keys(keycloakifyExtraMessages);
currentlySupportedLanguages.forEach(languages => all.add(languages));
all.forEach(language => {
console.log([
`"${language}": `,
`isInLanguages: ${languages.includes(language)}`,
`isInKeycloakifyExtraMessages: ${currentlySupportedLanguages.includes(language)}`
].join(" "))
});
}
*/
assert(
same(languages, Object.keys(keycloakifyExtraMessages), {
takeIntoAccountArraysOrdering: false
})
);
deepAssign({
target: recordForThemeType,
source: keycloakifyExtraMessages
});
const messagesDirPath = pathJoin(
thisCodebaseRootDirPath,
"src",
themeType,
"i18n",
"messages_defaultSet"
);
if (!fs.existsSync(messagesDirPath)) {
fs.mkdirSync(messagesDirPath, { recursive: true });
}
fs.writeFileSync(
pathJoin(messagesDirPath, "types.ts"),
Buffer.from(
[
``,
`export const languageTags = ${JSON.stringify(languages, null, 2)} as const;`,
``,
`export type LanguageTag = typeof languageTags[number];`,
``,
`export type MessageKey = keyof typeof import("./en")["default"];`,
``
].join("\n"),
"utf8"
)
);
const generatedFileHeader = [
`//This code was automatically generated by running ${pathRelative(
thisCodebaseRootDirPath,
__filename
)}`,
"//PLEASE DO NOT EDIT MANUALLY"
].join("\n");
languages.forEach(language => {
const filePath = pathJoin(messagesDirPath, `${language}.ts`);
fs.mkdirSync(pathDirname(filePath), { recursive: true });
fs.writeFileSync(
filePath,
Buffer.from(
[
generatedFileHeader,
"",
"/* spell-checker: disable */",
`const messages= ${JSON.stringify(
recordForThemeType[language],
null,
2
)};`,
"",
"export default messages;",
"/* spell-checker: enable */"
].join("\n"),
"utf8"
)
);
//console.log(`${filePath} wrote`);
});
fs.writeFileSync(
pathJoin(messagesDirPath, "index.ts"),
Buffer.from(
[
generatedFileHeader,
`import * as en from "./en";`,
"",
"export async function fetchMessages_defaultSet(currentLanguageTag: string) {",
" const { default: messages_defaultSet } = await (() => {",
" switch (currentLanguageTag) {",
` case "en": return en;`,
...languages
.filter(language => language !== "en")
.map(
language =>
` case "${language}": return import("./${language}");`
),
' default: return { "default": {} };',
" }",
" })();",
" return messages_defaultSet;",
"}"
].join("\n"),
"utf8"
)
);
}
transformCodebase({
srcDirPath: pathJoin(thisCodebaseRootDirPath, "src", "login", "i18n"),
destDirPath: accountI18nDirPath,
transformSourceCode: ({ fileRelativePath, sourceCode }) => {
if (fileRelativePath.startsWith("messages_defaultSet")) {
return undefined;
}
return { modifiedSourceCode: sourceCode };
}
});
}
const keycloakifyExtraMessages_login: Record<
| "en"
| "ar"
| "ca"
| "cs"
| "da"
| "de"
| "el"
| "es"
| "fa"
| "fi"
| "fr"
| "hu"
| "it"
| "ja"
| "lt"
| "lv"
| "nl"
| "no"
| "pl"
| "pt"
| "pt-BR"
| "ru"
| "sk"
| "sv"
| "th"
| "tr"
| "uk"
| "ka"
| "zh-CN"
| "zh-TW",
Record<
| "shouldBeEqual"
| "shouldBeDifferent"
| "shouldMatchPattern"
| "mustBeAnInteger"
| "notAValidOption"
| "selectAnOption"
| "remove"
| "addValue"
| "languages",
string
>
> = {
en: {
shouldBeEqual: "{0} should be equal to {1}",
shouldBeDifferent: "{0} should be different to {1}",
shouldMatchPattern: "Pattern should match: `/{0}/`",
mustBeAnInteger: "Must be an integer",
notAValidOption: "Not a valid option",
selectAnOption: "Select an option",
remove: "Remove",
addValue: "Add value",
languages: "Languages"
},
/* spell-checker: disable */
ar: {
shouldBeEqual: "{0} يجب أن يكون مساويًا لـ {1}",
shouldBeDifferent: "{0} يجب أن يكون مختلفًا عن {1}",
shouldMatchPattern: "`/يجب أن يطابق النمط: `/{0}/",
mustBeAnInteger: "يجب أن يكون عددًا صحيحًا",
notAValidOption: "ليس خيارًا صالحًا",
selectAnOption: "اختر خيارًا",
remove: "إزالة",
addValue: "أضف قيمة",
languages: "اللغات"
},
ca: {
shouldBeEqual: "{0} hauria de ser igual a {1}",
shouldBeDifferent: "{0} hauria de ser diferent de {1}",
shouldMatchPattern: "El patró hauria de coincidir: `/{0}/`",
mustBeAnInteger: "Ha de ser un enter",
notAValidOption: "No és una opció vàlida",
selectAnOption: "Selecciona una opció",
remove: "Elimina",
addValue: "Afegeix valor",
languages: "Idiomes"
},
cs: {
shouldBeEqual: "{0} by měl být roven {1}",
shouldBeDifferent: "{0} by měl být odlišný od {1}",
shouldMatchPattern: "Vzor by měl odpovídat: `/{0}/`",
mustBeAnInteger: "Musí být celé číslo",
notAValidOption: "Není platná možnost",
selectAnOption: "Vyberte možnost",
remove: "Odstranit",
addValue: "Přidat hodnotu",
languages: "Jazyky"
},
da: {
shouldBeEqual: "{0} bør være lig med {1}",
shouldBeDifferent: "{0} bør være forskellig fra {1}",
shouldMatchPattern: "Mønsteret bør matche: `/{0}/`",
mustBeAnInteger: "Skal være et heltal",
notAValidOption: "Ikke en gyldig mulighed",
selectAnOption: "Vælg en mulighed",
remove: "Fjern",
addValue: "Tilføj værdi",
languages: "Sprog"
},
de: {
shouldBeEqual: "{0} sollte gleich {1} sein",
shouldBeDifferent: "{0} sollte sich von {1} unterscheiden",
shouldMatchPattern: "Muster sollte übereinstimmen: `/{0}/`",
mustBeAnInteger: "Muss eine ganze Zahl sein",
notAValidOption: "Keine gültige Option",
selectAnOption: "Wählen Sie eine Option",
remove: "Entfernen",
addValue: "Wert hinzufügen",
languages: "Sprachen"
},
el: {
shouldBeEqual: "Το {0} πρέπει να είναι ίσο με {1}",
shouldBeDifferent: "Το {0} πρέπει να διαφέρει από το {1}",
shouldMatchPattern: "Το πρότυπο πρέπει να ταιριάζει: `/{0}/`",
mustBeAnInteger: "Πρέπει να είναι ακέραιος",
notAValidOption: "Δεν είναι μια έγκυρη επιλογή",
selectAnOption: "Επιλέξτε μια επιλογή",
remove: "Αφαίρεση",
addValue: "Προσθήκη τιμής",
languages: "Γλώσσες"
},
es: {
shouldBeEqual: "{0} debería ser igual a {1}",
shouldBeDifferent: "{0} debería ser diferente a {1}",
shouldMatchPattern: "El patrón debería coincidir: `/{0}/`",
mustBeAnInteger: "Debe ser un número entero",
notAValidOption: "No es una opción válida",
selectAnOption: "Selecciona una opción",
remove: "Eliminar",
addValue: "Añadir valor",
languages: "Idiomas"
},
fa: {
shouldBeEqual: "{0} باید برابر باشد با {1}",
shouldBeDifferent: "{0} باید متفاوت باشد از {1}",
shouldMatchPattern: "الگو باید مطابقت داشته باشد: `/{0}/`",
mustBeAnInteger: "باید یک عدد صحیح باشد",
notAValidOption: "یک گزینه معتبر نیست",
selectAnOption: "یک گزینه انتخاب کنید",
remove: "حذف",
addValue: "افزودن مقدار",
languages: "زبان‌ها"
},
fi: {
shouldBeEqual: "{0} pitäisi olla yhtä suuri kuin {1}",
shouldBeDifferent: "{0} pitäisi olla erilainen kuin {1}",
shouldMatchPattern: "Mallin tulisi vastata: `/{0}/`",
mustBeAnInteger: "On oltava kokonaisluku",
notAValidOption: "Ei ole kelvollinen vaihtoehto",
selectAnOption: "Valitse vaihtoehto",
remove: "Poista",
addValue: "Lisää arvo",
languages: "Kielet"
},
fr: {
shouldBeEqual: "{0} devrait être égal à {1}",
shouldBeDifferent: "{0} devrait être différent de {1}",
shouldMatchPattern: "Le motif devrait correspondre: `/{0}/`",
mustBeAnInteger: "Doit être un entier",
notAValidOption: "Pas une option valide",
selectAnOption: "Sélectionnez une option",
remove: "Supprimer",
addValue: "Ajouter une valeur",
languages: "Langues"
},
hu: {
shouldBeEqual: "{0} egyenlő kell legyen {1}-vel",
shouldBeDifferent: "{0} különbözőnek kell lennie, mint {1}",
shouldMatchPattern: "A mintának egyeznie kell: `/{0}/`",
mustBeAnInteger: "Egész számnak kell lennie",
notAValidOption: "Nem érvényes opció",
selectAnOption: "Válasszon egy lehetőséget",
remove: "Eltávolítás",
addValue: "Érték hozzáadása",
languages: "Nyelvek"
},
it: {
shouldBeEqual: "{0} dovrebbe essere uguale a {1}",
shouldBeDifferent: "{0} dovrebbe essere diverso da {1}",
shouldMatchPattern: "Il modello dovrebbe corrispondere: `/{0}/`",
mustBeAnInteger: "Deve essere un numero intero",
notAValidOption: "Non è un'opzione valida",
selectAnOption: "Seleziona un'opzione",
remove: "Rimuovi",
addValue: "Aggiungi valore",
languages: "Lingue"
},
ja: {
shouldBeEqual: "{0} は {1} と等しい必要があります",
shouldBeDifferent: "{0} は {1} と異なる必要があります",
shouldMatchPattern: "パターンは一致する必要があります: `/{0}/`",
mustBeAnInteger: "整数である必要があります",
notAValidOption: "有効なオプションではありません",
selectAnOption: "オプションを選択",
remove: "削除",
addValue: "値を追加",
languages: "言語"
},
lt: {
shouldBeEqual: "{0} turėtų būti lygus {1}",
shouldBeDifferent: "{0} turėtų skirtis nuo {1}",
shouldMatchPattern: "Šablonas turėtų atitikti: `/{0}/`",
mustBeAnInteger: "Turi būti sveikasis skaičius",
notAValidOption: "Netinkama parinktis",
selectAnOption: "Pasirinkite parinktį",
remove: "Pašalinti",
addValue: "Pridėti reikšmę",
languages: "Kalbos"
},
lv: {
shouldBeEqual: "{0} jābūt vienādam ar {1}",
shouldBeDifferent: "{0} jābūt atšķirīgam no {1}",
shouldMatchPattern: "Mustrim jāsakrīt: `/{0}/`",
mustBeAnInteger: "Jābūt veselam skaitlim",
notAValidOption: "Nav derīga opcija",
selectAnOption: "Izvēlieties opciju",
remove: "Noņemt",
addValue: "Pievienot vērtību",
languages: "Valodas"
},
nl: {
shouldBeEqual: "{0} moet gelijk zijn aan {1}",
shouldBeDifferent: "{0} moet verschillen van {1}",
shouldMatchPattern: "Patroon moet overeenkomen: `/{0}/`",
mustBeAnInteger: "Moet een geheel getal zijn",
notAValidOption: "Geen geldige optie",
selectAnOption: "Selecteer een optie",
remove: "Verwijderen",
addValue: "Waarde toevoegen",
languages: "Talen"
},
no: {
shouldBeEqual: "{0} skal være lik {1}",
shouldBeDifferent: "{0} skal være forskjellig fra {1}",
shouldMatchPattern: "Mønsteret skal matche: `/{0}/`",
mustBeAnInteger: "Må være et heltall",
notAValidOption: "Ikke et gyldig alternativ",
selectAnOption: "Velg et alternativ",
remove: "Fjern",
addValue: "Legg til verdi",
languages: "Språk"
},
pl: {
shouldBeEqual: "{0} powinno być równe {1}",
shouldBeDifferent: "{0} powinno być różne od {1}",
shouldMatchPattern: "Wzór pow inien pasować: `/{0}/`",
mustBeAnInteger: "Musi być liczbą całkowitą",
notAValidOption: "Nieprawidłowa opcja",
selectAnOption: "Wybierz opcję",
remove: "Usuń",
addValue: "Dodaj wartość",
languages: "Języki"
},
pt: {
shouldBeEqual: "{0} deve ser igual a {1}",
shouldBeDifferent: "{0} deve ser diferente de {1}",
shouldMatchPattern: "O padrão deve corresponder: `/{0}/`",
mustBeAnInteger: "Deve ser um número inteiro",
notAValidOption: "Não é uma opção válida",
selectAnOption: "Selecione uma opção",
remove: "Remover",
addValue: "Adicionar valor",
languages: "Idiomas"
},
"pt-BR": {
shouldBeEqual: "{0} deve ser igual a {1}",
shouldBeDifferent: "{0} deve ser diferente de {1}",
shouldMatchPattern: "O padrão deve corresponder: `/{0}/`",
mustBeAnInteger: "Deve ser um número inteiro",
notAValidOption: "Não é uma opção válida",
selectAnOption: "Selecione uma opção",
remove: "Remover",
addValue: "Adicionar valor",
languages: "Idiomas"
},
ru: {
shouldBeEqual: "{0} должно быть равно {1}",
shouldBeDifferent: "{0} должно отличаться от {1}",
shouldMatchPattern: "Шаблон должен соответствовать: `/{0}/`",
mustBeAnInteger: "Должно быть целым числом",
notAValidOption: "Недопустимый вариант",
selectAnOption: "Выберите вариант",
remove: "Удалить",
addValue: "Добавить значение",
languages: "Языки"
},
sk: {
shouldBeEqual: "{0} by mal byť rovnaký ako {1}",
shouldBeDifferent: "{0} by mal byť odlišný od {1}",
shouldMatchPattern: "Vzor by mal zodpovedať: `/{0}/`",
mustBeAnInteger: "Musí byť celé číslo",
notAValidOption: "Nie je platná možnosť",
selectAnOption: "Vyberte možnosť",
remove: "Odstrániť",
addValue: "Pridať hodnotu",
languages: "Jazyky"
},
sv: {
shouldBeEqual: "{0} bör vara lika med {1}",
shouldBeDifferent: "{0} bör vara annorlunda än {1}",
shouldMatchPattern: "Mönstret bör matcha: `/{0}/`",
mustBeAnInteger: "Måste vara ett heltal",
notAValidOption: "Inte ett giltigt alternativ",
selectAnOption: "Välj ett alternativ",
remove: "Ta bort",
addValue: "Lägg till värde",
languages: "Språk"
},
th: {
shouldBeEqual: "{0} ควรเท่ากับ {1}",
shouldBeDifferent: "{0} ควรแตกต่างจาก {1}",
shouldMatchPattern: "รูปแบบควรตรงกับ: `/{0}/`",
mustBeAnInteger: "ต้องเป็นจำนวนเต็ม",
notAValidOption: "ไม่ใช่ตัวเลือกที่ถูกต้อง",
selectAnOption: "เลือกตัวเลือก",
remove: "ลบ",
addValue: "เพิ่มค่า",
languages: "ภาษา"
},
tr: {
shouldBeEqual: "{0} {1} eşit olmalıdır",
shouldBeDifferent: "{0} {1} farklı olmalıdır",
shouldMatchPattern: "Desen eşleşmelidir: `/{0}/`",
mustBeAnInteger: "Tam sayı olmalıdır",
notAValidOption: "Geçerli bir seçenek değil",
selectAnOption: "Bir seçenek seçin",
remove: "Kaldır",
addValue: "Değer ekle",
languages: "Diller"
},
uk: {
shouldBeEqual: "{0} повинно бути рівним {1}",
shouldBeDifferent: "{0} повинно відрізнятися від {1}",
shouldMatchPattern: "Шаблон повинен відповідати: `/{0}/`",
mustBeAnInteger: "Повинно бути цілим числом",
notAValidOption: "Не є дійсною опцією",
selectAnOption: "Виберіть опцію",
remove: "Видалити",
addValue: "Додати значення",
languages: "Мови"
},
ka: {
shouldBeEqual: "{0} უნდა იყოს ტოლი {1}-სთვის",
shouldBeDifferent: "{0} უნდა იყოს სხვა {1}-სთვის",
shouldMatchPattern: "შაბლონს უნდა ემთხვევა: `/{0}/`",
mustBeAnInteger: "უნდა იყოს მთელი რიცხვი",
notAValidOption: "არასწორი ვარიანტი",
selectAnOption: "აირჩიეთ ვარიანტი",
remove: "წაშალეთ",
addValue: "დაამატეთ მნიშვნელობა",
languages: "ენები"
},
"zh-CN": {
shouldBeEqual: "{0} 应该等于 {1}",
shouldBeDifferent: "{0} 应该不同于 {1}",
shouldMatchPattern: "模式应匹配: `/{0}/`",
mustBeAnInteger: "必须是整数",
notAValidOption: "不是有效选项",
selectAnOption: "选择一个选项",
remove: "移除",
addValue: "添加值",
languages: "语言"
},
"zh-TW": {
shouldBeEqual: "{0} 應該等於 {1}",
shouldBeDifferent: "{0} 應該不同於 {1}",
shouldMatchPattern: "模式應匹配: `/{0}/`",
mustBeAnInteger: "必須是整數",
notAValidOption: "不是有效選項",
selectAnOption: "選擇一個選項",
remove: "移除",
addValue: "添加值",
languages: "語言"
}
/* spell-checker: enable */
};
export const accountMultiPageSupportedLanguages = [
"en",
"ar",
"ca",
"cs",
"da",
"de",
"es",
"fi",
"fr",
"hu",
"it",
"ja",
"lt",
"lv",
"nl",
"no",
"pl",
"pt-BR",
"ru",
"sk",
"sv",
"tr",
"zh-CN"
] as const;
const keycloakifyExtraMessages_account: Record<
(typeof accountMultiPageSupportedLanguages)[number],
Record<"newPasswordSameAsOld" | "passwordConfirmNotMatch", string>
> = {
en: {
newPasswordSameAsOld: "New password must be different from the old one",
passwordConfirmNotMatch: "Password confirmation does not match"
},
/* spell-checker: disable */
ar: {
newPasswordSameAsOld: "يجب أن تكون كلمة المرور الجديدة مختلفة عن القديمة",
passwordConfirmNotMatch: "تأكيد كلمة المرور لا يتطابق"
},
ca: {
newPasswordSameAsOld: "La nova contrasenya ha de ser diferent de l'anterior",
passwordConfirmNotMatch: "La confirmació de la contrasenya no coincideix"
},
cs: {
newPasswordSameAsOld: "Nové heslo musí být odlišné od starého",
passwordConfirmNotMatch: "Potvrzení hesla se neshoduje"
},
da: {
newPasswordSameAsOld: "Det nye kodeord skal være forskelligt fra det gamle",
passwordConfirmNotMatch: "Adgangskodebekræftelse matcher ikke"
},
de: {
newPasswordSameAsOld: "Das neue Passwort muss sich vom alten unterscheiden",
passwordConfirmNotMatch: "Passwortbestätigung stimmt nicht überein"
},
es: {
newPasswordSameAsOld: "La nueva contraseña debe ser diferente de la anterior",
passwordConfirmNotMatch: "La confirmación de la contraseña no coincide"
},
fi: {
newPasswordSameAsOld: "Uusi salasana on oltava erilainen kuin vanha",
passwordConfirmNotMatch: "Salasanan vahvistus ei täsmää"
},
fr: {
newPasswordSameAsOld: "Le nouveau mot de passe doit être différent de l'ancien",
passwordConfirmNotMatch: "La confirmation du mot de passe ne correspond pas"
},
hu: {
newPasswordSameAsOld: "Az új jelszónak különböznie kell az előzőtől",
passwordConfirmNotMatch: "A jelszó megerősítése nem egyezik"
},
it: {
newPasswordSameAsOld:
"La nuova password deve essere diversa da quella precedente",
passwordConfirmNotMatch: "La conferma della password non corrisponde"
},
ja: {
newPasswordSameAsOld: "新しいパスワードは古いパスワードと異なる必要があります",
passwordConfirmNotMatch: "パスワード確認が一致しません"
},
lt: {
newPasswordSameAsOld: "Naujas slaptažodis turi skirtis nuo seno",
passwordConfirmNotMatch: "Slaptažodžio patvirtinimas neatitinka"
},
lv: {
newPasswordSameAsOld: "Jaunajam parolam jābūt atšķirīgam no vecā",
passwordConfirmNotMatch: "Paroles apstiprināšana neatbilst"
},
nl: {
newPasswordSameAsOld: "Het nieuwe wachtwoord moet verschillend zijn van het oude",
passwordConfirmNotMatch: "Wachtwoordbevestiging komt niet overeen"
},
no: {
newPasswordSameAsOld: "Det nye passordet må være forskjellig fra det gamle",
passwordConfirmNotMatch: "Passordbekreftelsen stemmer ikke"
},
pl: {
newPasswordSameAsOld: "Nowe hasło musi być inne niż stare",
passwordConfirmNotMatch: "Potwierdzenie hasła nie pasuje"
},
"pt-BR": {
newPasswordSameAsOld: "A nova senha deve ser diferente da antiga",
passwordConfirmNotMatch: "A confirmação da senha não corresponde"
},
ru: {
newPasswordSameAsOld: "Новый пароль должен отличаться от старого",
passwordConfirmNotMatch: "Подтверждение пароля не совпадает"
},
sk: {
newPasswordSameAsOld: "Nové heslo musí byť odlišné od starého",
passwordConfirmNotMatch: "Potvrdenie hesla sa nezhoduje"
},
sv: {
newPasswordSameAsOld: "Det nya lösenordet måste skilja sig från det gamla",
passwordConfirmNotMatch: "Lösenordsbekräftelsen matchar inte"
},
tr: {
newPasswordSameAsOld: "Yeni şifre eskisinden farklı olmalıdır",
passwordConfirmNotMatch: "Şifre doğrulama eşleşmiyor"
},
"zh-CN": {
newPasswordSameAsOld: "新密码必须与旧密码不同",
passwordConfirmNotMatch: "密码确认不匹配"
}
/* spell-checker: enable */
};

241
scripts/link-in-app.ts Normal file
View File

@ -0,0 +1,241 @@
import { execSync } from "child_process";
import { join as pathJoin, relative as pathRelative } from "path";
import { getThisCodebaseRootDirPath } from "../src/bin/tools/getThisCodebaseRootDirPath";
import * as fs from "fs";
import * as os from "os";
const singletonDependencies: string[] = ["react", "@types/react"];
// For example [ "@emotion" ] it's more convenient than
// having to list every sub emotion packages (@emotion/css @emotion/utils ...)
// in singletonDependencies
const namespaceSingletonDependencies: string[] = [];
const rootDirPath = getThisCodebaseRootDirPath();
const commonThirdPartyDeps = [
...namespaceSingletonDependencies
.map(namespaceModuleName =>
fs
.readdirSync(pathJoin(rootDirPath, "node_modules", namespaceModuleName))
.map(submoduleName => `${namespaceModuleName}/${submoduleName}`)
)
.reduce((prev, curr) => [...prev, ...curr], []),
...singletonDependencies
];
//NOTE: This is only required because of: https://github.com/garronej/ts-ci/blob/c0e207b9677523d4ec97fe672ddd72ccbb3c1cc4/README.md?plain=1#L54-L58
{
let modifiedPackageJsonContent = fs
.readFileSync(pathJoin(rootDirPath, "package.json"))
.toString("utf8");
modifiedPackageJsonContent = (() => {
const o = JSON.parse(modifiedPackageJsonContent);
delete o.files;
return JSON.stringify(o, null, 2);
})();
modifiedPackageJsonContent = modifiedPackageJsonContent
.replace(/"dist\//g, '"')
.replace(/"\.\/dist\//g, '"./')
.replace(/"!dist\//g, '"!')
.replace(/"!\.\/dist\//g, '"!./');
modifiedPackageJsonContent = JSON.stringify(
{
...JSON.parse(modifiedPackageJsonContent),
version: `0.0.0-rc.${~~(Math.random() * 1000000)}`
},
null,
4
);
fs.writeFileSync(
pathJoin(rootDirPath, "dist", "package.json"),
Buffer.from(modifiedPackageJsonContent, "utf8")
);
}
const yarnGlobalDirPath = pathJoin(rootDirPath, ".yarn_home");
fs.rmSync(yarnGlobalDirPath, { recursive: true, force: true });
fs.mkdirSync(yarnGlobalDirPath);
const execYarnLink = (params: { targetModuleName?: string; cwd: string }) => {
const { targetModuleName, cwd } = params;
if (targetModuleName === undefined) {
const packageJsonFilePath = pathJoin(cwd, "package.json");
const packageJson = JSON.parse(
fs.readFileSync(packageJsonFilePath).toString("utf8")
);
delete packageJson["packageManager"];
fs.writeFileSync(
packageJsonFilePath,
Buffer.from(JSON.stringify(packageJson, null, 2))
);
}
const cmd = [
"yarn",
"link",
...(targetModuleName !== undefined ? [targetModuleName] : ["--no-bin-links"])
].join(" ");
console.log(`$ cd ${pathRelative(rootDirPath, cwd) || "."} && ${cmd}`);
execSync(cmd, {
cwd,
env: {
...process.env,
...(os.platform() === "win32"
? {
USERPROFILE: yarnGlobalDirPath,
LOCALAPPDATA: yarnGlobalDirPath
}
: { HOME: yarnGlobalDirPath })
}
});
};
const testAppPaths = (() => {
const [, , ...testAppNames] = process.argv;
return testAppNames
.map(testAppName => {
const testAppPath = pathJoin(rootDirPath, "..", testAppName);
if (fs.existsSync(testAppPath)) {
return testAppPath;
}
console.warn(
`Skipping ${testAppName} since it cant be found here: ${testAppPath}`
);
return undefined;
})
.filter((path): path is string => path !== undefined);
})();
if (testAppPaths.length === 0) {
console.error("No test app to link into!");
process.exit(-1);
}
testAppPaths.forEach(testAppPath => {
const packageJsonFilePath = pathJoin(testAppPath, "package.json");
const packageJsonContent = fs.readFileSync(packageJsonFilePath);
const parsedPackageJson = JSON.parse(packageJsonContent.toString("utf8")) as {
scripts?: Record<string, string>;
};
let hasPostInstallOrPrepareScript = false;
if (parsedPackageJson.scripts !== undefined) {
for (const scriptName of ["postinstall", "prepare"]) {
if (parsedPackageJson.scripts[scriptName] === undefined) {
continue;
}
hasPostInstallOrPrepareScript = true;
delete parsedPackageJson.scripts[scriptName];
}
}
if (hasPostInstallOrPrepareScript) {
fs.writeFileSync(
packageJsonFilePath,
Buffer.from(JSON.stringify(parsedPackageJson, null, 2), "utf8")
);
}
const restorePackageJson = () => {
if (!hasPostInstallOrPrepareScript) {
return;
}
fs.writeFileSync(packageJsonFilePath, packageJsonContent);
};
try {
execSync("yarn install", { cwd: testAppPath });
} catch (error) {
restorePackageJson();
throw error;
}
restorePackageJson();
});
console.log("=== Linking common dependencies ===");
const total = commonThirdPartyDeps.length;
let current = 0;
commonThirdPartyDeps.forEach(commonThirdPartyDep => {
current++;
console.log(`${current}/${total} ${commonThirdPartyDep}`);
const localInstallPath = pathJoin(
...[
rootDirPath,
"node_modules",
...(commonThirdPartyDep.startsWith("@")
? commonThirdPartyDep.split("/")
: [commonThirdPartyDep])
]
);
execYarnLink({ cwd: localInstallPath });
});
commonThirdPartyDeps.forEach(commonThirdPartyDep =>
testAppPaths.forEach(testAppPath =>
execYarnLink({
cwd: testAppPath,
targetModuleName: commonThirdPartyDep
})
)
);
console.log("=== Linking in house dependencies ===");
execYarnLink({ cwd: pathJoin(rootDirPath, "dist") });
testAppPaths.forEach(testAppPath =>
execYarnLink({
cwd: testAppPath,
targetModuleName: JSON.parse(
fs.readFileSync(pathJoin(rootDirPath, "package.json")).toString("utf8")
)["name"]
})
);
testAppPaths.forEach(testAppPath => {
const { scripts = {} } = JSON.parse(
fs.readFileSync(pathJoin(testAppPath, "package.json")).toString("utf8")
) as {
scripts?: Record<string, string>;
};
for (const scriptName of ["postinstall", "prepare"]) {
if (scripts[scriptName] === undefined) {
continue;
}
execSync(`yarn run ${scriptName}`, { cwd: testAppPath });
}
});
export {};

View File

@ -0,0 +1,88 @@
import * as fs from "fs";
import { join as pathJoin, sep as pathSep } from "path";
import { run } from "./shared/run";
import cliSelect from "cli-select";
import { getThisCodebaseRootDirPath } from "../src/bin/tools/getThisCodebaseRootDirPath";
import chalk from "chalk";
import { removeNodeModules } from "./tools/removeNodeModules";
import { startRebuildOnSrcChange } from "./shared/startRebuildOnSrcChange";
(async () => {
const parentDirPath = pathJoin(getThisCodebaseRootDirPath(), "..");
const { starterName } = await (async () => {
const starterNames = fs
.readdirSync(parentDirPath)
.filter(
basename =>
basename.includes("starter") &&
basename.includes("keycloakify") &&
fs.statSync(pathJoin(parentDirPath, basename)).isDirectory()
);
if (starterNames.length === 0) {
console.log(
chalk.red(
`No starter found. Keycloakify Angular starter found in ${parentDirPath}`
)
);
process.exit(-1);
}
const starterName = await (async () => {
if (starterNames.length === 1) {
return starterNames[0];
}
console.log(chalk.cyan(`\nSelect a starter to link in:`));
const { value } = await cliSelect<string>({
values: starterNames.map(starterName => `..${pathSep}${starterName}`)
}).catch(() => {
process.exit(-1);
});
return value.split(pathSep)[1];
})();
return { starterName };
})();
const startTime = Date.now();
console.log(chalk.cyan(`\n\nLinking in ..${pathSep}${starterName}...`));
removeNodeModules({
nodeModulesDirPath: pathJoin(getThisCodebaseRootDirPath(), "node_modules")
});
fs.rmSync(pathJoin(getThisCodebaseRootDirPath(), "dist"), {
recursive: true,
force: true
});
fs.rmSync(pathJoin(getThisCodebaseRootDirPath(), ".yarn_home"), {
recursive: true,
force: true
});
run("yarn install");
run("yarn build");
const starterDirPath = pathJoin(parentDirPath, starterName);
removeNodeModules({
nodeModulesDirPath: pathJoin(starterDirPath, "node_modules")
});
run("yarn install", { cwd: starterDirPath });
run(`npx tsx ${pathJoin("scripts", "link-in-app.ts")} ${starterName}`);
const durationSeconds = Math.round((Date.now() - startTime) / 1000);
await new Promise(resolve => setTimeout(resolve, 1000));
startRebuildOnSrcChange();
console.log(chalk.green(`\n\nLinked in ${starterName} in ${durationSeconds}s`));
})();

View File

@ -0,0 +1,9 @@
import { join as pathJoin } from "path";
import { getThisCodebaseRootDirPath } from "../../src/bin/tools/getThisCodebaseRootDirPath";
export const cacheDirPath = pathJoin(
getThisCodebaseRootDirPath(),
"node_modules",
".cache",
"scripts"
);

View File

@ -0,0 +1,357 @@
import { relative as pathRelative } from "path";
import { downloadAndExtractArchive } from "../../src/bin/tools/downloadAndExtractArchive";
import { getProxyFetchOptions } from "../../src/bin/tools/fetchProxyOptions";
import { join as pathJoin } from "path";
import { assert, type Equals } from "tsafe/assert";
import { cacheDirPath } from "./cacheDirPath";
import { getThisCodebaseRootDirPath } from "../../src/bin/tools/getThisCodebaseRootDirPath";
const KEYCLOAK_VERSION = {
FOR_LOGIN_THEME: "25.0.4",
FOR_ACCOUNT_MULTI_PAGE: "21.1.2"
} as const;
export async function downloadKeycloakDefaultTheme(params: {
keycloakVersionId: keyof typeof KEYCLOAK_VERSION;
}) {
const { keycloakVersionId } = params;
const keycloakVersion = KEYCLOAK_VERSION[keycloakVersionId];
let kcNodeModulesKeepFilePaths: Set<string> | undefined = undefined;
let kcNodeModulesKeepFilePaths_lastAccountV1: Set<string> | undefined = undefined;
const { extractedDirPath } = await downloadAndExtractArchive({
url: `https://repo1.maven.org/maven2/org/keycloak/keycloak-themes/${keycloakVersion}/keycloak-themes-${keycloakVersion}.jar`,
cacheDirPath,
fetchOptions: getProxyFetchOptions({
npmConfigGetCwd: getThisCodebaseRootDirPath()
}),
uniqueIdOfOnArchiveFile: "extractOnlyRequiredFiles",
onArchiveFile: async params => {
const fileRelativePath = pathRelative("theme", params.fileRelativePath);
if (fileRelativePath.startsWith("..")) {
return;
}
const { readFile, writeFile } = params;
if (
!fileRelativePath.startsWith("base") &&
!fileRelativePath.startsWith("keycloak")
) {
return;
}
switch (keycloakVersion) {
case KEYCLOAK_VERSION.FOR_LOGIN_THEME:
if (
!fileRelativePath.startsWith(pathJoin("base", "login")) &&
!fileRelativePath.startsWith(pathJoin("keycloak", "login")) &&
!fileRelativePath.startsWith(pathJoin("keycloak", "common"))
) {
return;
}
if (fileRelativePath.endsWith(".ftl")) {
return;
}
break;
case KEYCLOAK_VERSION.FOR_ACCOUNT_MULTI_PAGE:
if (
!fileRelativePath.startsWith(pathJoin("base", "account")) &&
!fileRelativePath.startsWith(pathJoin("keycloak", "account")) &&
!fileRelativePath.startsWith(pathJoin("keycloak", "common"))
) {
return;
}
break;
default:
assert<Equals<typeof keycloakVersion, never>>(false);
}
last_account_v1_transformations: {
if (keycloakVersion !== KEYCLOAK_VERSION.FOR_ACCOUNT_MULTI_PAGE) {
break last_account_v1_transformations;
}
skip_web_modules: {
if (
!fileRelativePath.startsWith(
pathJoin("keycloak", "common", "resources", "web_modules")
)
) {
break skip_web_modules;
}
return;
}
skip_lib: {
if (
!fileRelativePath.startsWith(
pathJoin("keycloak", "common", "resources", "lib")
)
) {
break skip_lib;
}
return;
}
skip_node_modules: {
const nodeModulesRelativeDirPath = pathJoin(
"keycloak",
"common",
"resources",
"node_modules"
);
if (!fileRelativePath.startsWith(nodeModulesRelativeDirPath)) {
break skip_node_modules;
}
if (kcNodeModulesKeepFilePaths_lastAccountV1 === undefined) {
kcNodeModulesKeepFilePaths_lastAccountV1 = new Set([
pathJoin("patternfly", "dist", "css", "patternfly.min.css"),
pathJoin(
"patternfly",
"dist",
"css",
"patternfly-additions.min.css"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"OpenSans-Regular-webfont.woff2"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"OpenSans-Bold-webfont.woff2"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"OpenSans-Light-webfont.woff2"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"OpenSans-Semibold-webfont.woff2"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"PatternFlyIcons-webfont.ttf"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"PatternFlyIcons-webfont.woff"
)
]);
}
const fileRelativeToNodeModulesPath = fileRelativePath.substring(
nodeModulesRelativeDirPath.length + 1
);
if (
kcNodeModulesKeepFilePaths_lastAccountV1.has(
fileRelativeToNodeModulesPath
)
) {
break skip_node_modules;
}
return;
}
patch_account_css: {
if (
fileRelativePath !==
pathJoin("keycloak", "account", "resources", "css", "account.css")
) {
break patch_account_css;
}
await writeFile({
fileRelativePath,
modifiedData: Buffer.from(
(await readFile())
.toString("utf8")
.replace("top: -34px;", "top: -34px !important;"),
"utf8"
)
});
return;
}
}
skip_unused_resources: {
if (keycloakVersion !== KEYCLOAK_VERSION.FOR_LOGIN_THEME) {
break skip_unused_resources;
}
skip_node_modules: {
const nodeModulesRelativeDirPath = pathJoin(
"keycloak",
"common",
"resources",
"node_modules"
);
if (!fileRelativePath.startsWith(nodeModulesRelativeDirPath)) {
break skip_node_modules;
}
if (kcNodeModulesKeepFilePaths === undefined) {
kcNodeModulesKeepFilePaths = new Set([
pathJoin("@patternfly", "patternfly", "patternfly.min.css"),
pathJoin("patternfly", "dist", "css", "patternfly.min.css"),
pathJoin(
"patternfly",
"dist",
"css",
"patternfly-additions.min.css"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"OpenSans-Regular-webfont.woff2"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"OpenSans-Light-webfont.woff2"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"OpenSans-Bold-webfont.woff2"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"OpenSans-Bold-webfont.woff"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"OpenSans-Bold-webfont.ttf"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"fontawesome-webfont.woff2"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"PatternFlyIcons-webfont.ttf"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"PatternFlyIcons-webfont.woff"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"OpenSans-Semibold-webfont.woff2"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"OpenSans-SemiboldItalic-webfont.woff2"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"OpenSans-SemiboldItalic-webfont.woff"
),
pathJoin(
"patternfly",
"dist",
"fonts",
"OpenSans-SemiboldItalic-webfont.ttf"
),
pathJoin("patternfly", "dist", "img", "bg-login.jpg"),
pathJoin("jquery", "dist", "jquery.min.js"),
pathJoin("rfc4648", "lib", "rfc4648.js")
]);
}
const fileRelativeToNodeModulesPath = fileRelativePath.substring(
nodeModulesRelativeDirPath.length + 1
);
if (kcNodeModulesKeepFilePaths.has(fileRelativeToNodeModulesPath)) {
break skip_node_modules;
}
return;
}
skip_vendor: {
if (
!fileRelativePath.startsWith(
pathJoin("keycloak", "common", "resources", "vendor")
)
) {
break skip_vendor;
}
return;
}
skip_rollup_config: {
if (
fileRelativePath !==
pathJoin("keycloak", "common", "resources", "rollup.config.js")
) {
break skip_rollup_config;
}
return;
}
skip_package_json: {
if (
fileRelativePath !==
pathJoin("keycloak", "common", "resources", "package.json")
) {
break skip_package_json;
}
return;
}
}
await writeFile({ fileRelativePath });
}
});
return { extractedDirPath };
}

8
scripts/shared/run.ts Normal file
View File

@ -0,0 +1,8 @@
import * as child_process from "child_process";
import chalk from "chalk";
export function run(command: string, options?: { cwd: string }) {
console.log(chalk.grey(`$ ${command}`));
child_process.execSync(command, { stdio: "inherit", ...options });
}

View File

@ -0,0 +1,40 @@
import * as child_process from "child_process";
import { waitForDebounceFactory } from "powerhooks/tools/waitForDebounce";
import chokidar from "chokidar";
import * as runExclusive from "run-exclusive";
import { Deferred } from "evt/tools/Deferred";
import chalk from "chalk";
export function startRebuildOnSrcChange() {
const { waitForDebounce } = waitForDebounceFactory({ delay: 400 });
const runYarnBuild = runExclusive.build(async () => {
console.log(chalk.green("Running `yarn build`"));
const dCompleted = new Deferred<void>();
const child = child_process.spawn("yarn", ["build"], { shell: true });
child.stdout.on("data", data => process.stdout.write(data));
child.stderr.on("data", data => process.stderr.write(data));
child.on("exit", () => dCompleted.resolve());
await dCompleted.pr;
console.log("\n\n");
});
console.log(chalk.green("Watching for changes in src/"));
chokidar
.watch(["src", "stories"], { ignoreInitial: true })
.on("all", async (event, path) => {
console.log(chalk.bold(`${event}: ${path}`));
await waitForDebounce();
runYarnBuild();
});
}

View File

@ -0,0 +1,21 @@
import * as child_process from "child_process";
import { startRebuildOnSrcChange } from "./shared/startRebuildOnSrcChange";
import { run } from "./shared/run";
(async () => {
run("yarn build");
{
const child = child_process.spawn("npx", ["start-storybook", "-p", "6006"], {
shell: true
});
child.stdout.on("data", data => process.stdout.write(data));
child.stderr.on("data", data => process.stderr.write(data));
child.on("exit", process.exit.bind(process));
}
startRebuildOnSrcChange();
})();

View File

@ -0,0 +1,27 @@
import * as fs from "fs";
import { crawl } from "../../src/bin/tools/crawl";
export function removeNodeModules(params: { nodeModulesDirPath: string }) {
const { nodeModulesDirPath } = params;
try {
fs.rmSync(nodeModulesDirPath, { recursive: true, force: true });
} catch {
// NOTE: This is a workaround for windows
// we can't remove locked executables.
crawl({
dirPath: nodeModulesDirPath,
returnedPathsType: "absolute"
}).forEach(filePath => {
try {
fs.rmSync(filePath, { force: true });
} catch (error) {
if (filePath.endsWith(".exe")) {
return;
}
throw error;
}
});
}
}

23
src/PUBLIC_URL.ts Normal file
View File

@ -0,0 +1,23 @@
import { WELL_KNOWN_DIRECTORY_BASE_NAME } from "keycloakify/bin/shared/constants";
import { assert } from "tsafe/assert";
/**
* This is an equivalent of process.env.PUBLIC_URL that you can use in Webpack projects.
* This works both in your main app and in your Keycloak theme.
*/
export const PUBLIC_URL = (() => {
const kcContext: { "x-keycloakify": { resourcesPath: string } } | undefined = (
window as any
).kcContext;
if (kcContext === undefined || process.env.NODE_ENV === "development") {
assert(
process.env.PUBLIC_URL !== undefined,
`If you use keycloakify/PUBLIC_URL you should be in Webpack and thus process.env.PUBLIC_URL should be defined`
);
return process.env.PUBLIC_URL;
}
return `${kcContext["x-keycloakify"].resourcesPath}/${WELL_KNOWN_DIRECTORY_BASE_NAME.DIST}`;
})();

View File

@ -0,0 +1,41 @@
import { lazy, Suspense } from "react";
import { assert, type Equals } from "tsafe/assert";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import type { KcContext } from "keycloakify/account/KcContext";
import { I18n } from "keycloakify/account/i18n";
const Password = lazy(() => import("keycloakify/account/pages/Password"));
const Account = lazy(() => import("keycloakify/account/pages/Account"));
const Sessions = lazy(() => import("keycloakify/account/pages/Sessions"));
const Totp = lazy(() => import("keycloakify/account/pages/Totp"));
const Applications = lazy(() => import("keycloakify/account/pages/Applications"));
const Log = lazy(() => import("keycloakify/account/pages/Log"));
const FederatedIdentity = lazy(() => import("keycloakify/account/pages/FederatedIdentity"));
export default function DefaultPage(props: PageProps<KcContext, I18n>) {
const { kcContext, ...rest } = props;
return (
<Suspense>
{(() => {
switch (kcContext.pageId) {
case "password.ftl":
return <Password kcContext={kcContext} {...rest} />;
case "sessions.ftl":
return <Sessions kcContext={kcContext} {...rest} />;
case "account.ftl":
return <Account kcContext={kcContext} {...rest} />;
case "totp.ftl":
return <Totp kcContext={kcContext} {...rest} />;
case "applications.ftl":
return <Applications kcContext={kcContext} {...rest} />;
case "log.ftl":
return <Log kcContext={kcContext} {...rest} />;
case "federatedIdentity.ftl":
return <FederatedIdentity kcContext={kcContext} {...rest} />;
}
assert<Equals<typeof kcContext, never>>(false);
})()}
</Suspense>
);
}

View File

@ -0,0 +1,310 @@
import type { ThemeType, AccountThemePageId } from "keycloakify/bin/shared/constants";
import type { ValueOf } from "keycloakify/tools/ValueOf";
import { assert } from "tsafe/assert";
import type { Equals } from "tsafe";
export type ExtendKcContext<
KcContextExtension extends { properties?: Record<string, string | undefined> },
KcContextExtensionPerPage extends Record<string, Record<string, unknown>>
> = ValueOf<{
[PageId in keyof KcContextExtensionPerPage | KcContext["pageId"]]: Extract<
KcContext,
{ pageId: PageId }
> extends never
? KcContext.Common &
KcContextExtension & {
pageId: PageId;
} & KcContextExtensionPerPage[PageId]
: Extract<KcContext, { pageId: PageId }> &
KcContextExtension &
KcContextExtensionPerPage[PageId];
}>;
export type KcContext =
| KcContext.Password
| KcContext.Account
| KcContext.Sessions
| KcContext.Totp
| KcContext.Applications
| KcContext.Log
| KcContext.FederatedIdentity;
export declare namespace KcContext {
export type Common = {
themeVersion: string;
keycloakifyVersion: string;
themeType: "account";
themeName: string;
locale?: {
supported: {
url: string;
label: string;
languageTag: string;
}[];
currentLanguageTag: string;
};
url: {
accountUrl: string;
passwordUrl: string;
totpUrl: string;
socialUrl: string;
sessionsUrl: string;
applicationsUrl: string;
logUrl: string;
logoutUrl: string;
resourceUrl: string;
resourcesCommonPath: string;
resourcesPath: string;
/** @deprecated, not present in recent keycloak version apparently, use kcContext.referrer instead */
referrerURI?: string;
getLogoutUrl: () => string;
};
features: {
passwordUpdateSupported: boolean;
identityFederation: boolean;
log: boolean;
authorization: boolean;
};
realm: {
internationalizationEnabled: boolean;
userManagedAccessAllowed: boolean;
};
// Present only if redirected to account page with ?referrer=xxx&referrer_uri=http...
message?: {
type: "success" | "warning" | "error" | "info";
summary: string;
};
referrer?: {
url: string; // The url of the App
name: string; // Client id
};
messagesPerField: {
/**
* Return text if message for given field exists. Useful eg. to add css styles for fields with message.
*
* @param fieldName to check for
* @param text to return
* @return text if message exists for given field, else undefined
*/
printIfExists: <T extends string>(
fieldName: string,
text: T
) => T | undefined;
/**
* Check if exists error message for given fields
*
* @param fields
* @return boolean
*/
existsError: (fieldName: string) => boolean;
/**
* Get message for given field.
*
* @param fieldName
* @return message text or empty string
*/
get: (fieldName: string) => string;
/**
* Check if message for given field exists
*
* @param field
* @return boolean
*/
exists: (fieldName: string) => boolean;
};
account: {
email?: string;
firstName: string;
lastName?: string;
username?: string;
};
properties: {};
"x-keycloakify": {
messages: Record<string, string>;
};
};
export type Password = Common & {
pageId: "password.ftl";
password: {
passwordSet: boolean;
};
stateChecker: string;
};
export type Account = Common & {
pageId: "account.ftl";
url: {
accountUrl: string;
};
realm: {
registrationEmailAsUsername: boolean;
editUsernameAllowed: boolean;
};
stateChecker: string;
};
export type Sessions = Common & {
pageId: "sessions.ftl";
sessions: {
sessions: {
expires: string;
clients: string[];
ipAddress: string;
started: string;
lastAccess: string;
id: string;
}[];
};
stateChecker: string;
};
export type Totp = Common & {
pageId: "totp.ftl";
totp: {
enabled: boolean;
totpSecretEncoded: string;
qrUrl: string;
policy: {
algorithm: "HmacSHA1" | "HmacSHA256" | "HmacSHA512";
digits: number;
lookAheadWindow: number;
getAlgorithmKey: () => string;
} & (
| {
type: "totp";
period: number;
}
| {
type: "hotp";
initialCounter: number;
}
);
supportedApplications: string[];
totpSecretQrCode: string;
manualUrl: string;
totpSecret: string;
otpCredentials: { id: string; userLabel: string }[];
};
mode?: "qr" | "manual" | undefined | null;
isAppInitiatedAction: boolean;
stateChecker: string;
};
export type Applications = Common & {
pageId: "applications.ftl";
features: {
log: boolean;
identityFederation: boolean;
authorization: boolean;
passwordUpdateSupported: boolean;
};
stateChecker: string;
applications: {
applications: {
realmRolesAvailable: {
name: string;
description: string;
compositesStream?: Record<string, unknown>;
clientRole?: boolean;
composite?: boolean;
id?: string;
containerId?: string;
attributes?: Record<string, unknown>;
}[];
resourceRolesAvailable: Record<
string,
{
roleName: string;
roleDescription?: string;
clientName: string;
clientId: string;
}[]
>;
additionalGrants: string[];
clientScopesGranted: string[];
effectiveUrl?: string;
client: {
alwaysDisplayInConsole: boolean;
attributes: Record<string, unknown>;
authenticationFlowBindingOverrides: Record<string, unknown>;
baseUrl?: string;
bearerOnly: boolean;
clientAuthenticatorType: string;
clientId: string;
consentRequired: boolean;
consentScreenText: string;
description: string;
directAccessGrantsEnabled: boolean;
displayOnConsentScreen: boolean;
dynamicScope: boolean;
enabled: boolean;
frontchannelLogout: boolean;
fullScopeAllowed: boolean;
id: string;
implicitFlowEnabled: boolean;
includeInTokenScope: boolean;
managementUrl: string;
name?: string;
nodeReRegistrationTimeout: string;
notBefore: string;
protocol: string;
protocolMappersStream: Record<string, unknown>;
publicClient: boolean;
realm: Record<string, unknown>;
realmScopeMappingsStream: Record<string, unknown>;
redirectUris: string[];
registeredNodes: Record<string, unknown>;
rolesStream: Record<string, unknown>;
rootUrl?: string;
scopeMappingsStream: Record<string, unknown>;
secret: string;
serviceAccountsEnabled: boolean;
standardFlowEnabled: boolean;
surrogateAuthRequired: boolean;
webOrigins: string[];
};
}[];
};
};
export type Log = Common & {
pageId: "log.ftl";
log: {
events: {
date: string | number | Date;
event: string;
ipAddress: string;
client: string;
details: { value: string; key: string }[];
}[];
};
};
export type FederatedIdentity = Common & {
pageId: "federatedIdentity.ftl";
stateChecker: string;
federatedIdentity: {
identities: {
providerId: string;
displayName: string;
userName: string;
connected: boolean;
}[];
removeLinkPossible: boolean;
};
};
}
{
type Got = KcContext["pageId"];
type Expected = AccountThemePageId;
type OnlyInGot = Exclude<Got, Expected>;
type OnlyInExpected = Exclude<Expected, Got>;
assert<Equals<OnlyInGot, never>>();
assert<Equals<OnlyInExpected, never>>();
}
assert<KcContext["themeType"] extends ThemeType ? true : false>();

View File

@ -0,0 +1,69 @@
import type { ExtendKcContext, KcContext as KcContextBase } from "./KcContext";
import type { AccountThemePageId } from "keycloakify/bin/shared/constants";
import type { DeepPartial } from "keycloakify/tools/DeepPartial";
import { deepAssign } from "keycloakify/tools/deepAssign";
import { structuredCloneButFunctions } from "keycloakify/tools/structuredCloneButFunctions";
import { kcContextMocks, kcContextCommonMock } from "./kcContextMocks";
import { exclude } from "tsafe/exclude";
export function createGetKcContextMock<
KcContextExtension extends { properties?: Record<string, string | undefined> },
KcContextExtensionPerPage extends Record<`${string}.ftl`, Record<string, unknown>>
>(params: {
kcContextExtension: KcContextExtension;
kcContextExtensionPerPage: KcContextExtensionPerPage;
overrides?: DeepPartial<KcContextExtension & KcContextBase.Common>;
overridesPerPage?: {
[PageId in AccountThemePageId | keyof KcContextExtensionPerPage]?: DeepPartial<
Extract<
ExtendKcContext<KcContextExtension, KcContextExtensionPerPage>,
{ pageId: PageId }
>
>;
};
}) {
const {
kcContextExtension,
kcContextExtensionPerPage,
overrides: overrides_global,
overridesPerPage: overridesPerPage_global
} = params;
type KcContext = ExtendKcContext<KcContextExtension, KcContextExtensionPerPage>;
function getKcContextMock<
PageId extends AccountThemePageId | keyof KcContextExtensionPerPage
>(params: {
pageId: PageId;
overrides?: DeepPartial<Extract<KcContext, { pageId: PageId }>>;
}): Extract<KcContext, { pageId: PageId }> {
const { pageId, overrides } = params;
const kcContextMock = structuredCloneButFunctions(
kcContextMocks.find(kcContextMock => kcContextMock.pageId === pageId) ?? {
...kcContextCommonMock,
pageId
}
);
[
kcContextExtension,
kcContextExtensionPerPage[pageId],
overrides_global,
overridesPerPage_global?.[pageId],
overrides
]
.filter(exclude(undefined))
.forEach(overrides =>
deepAssign({
target: kcContextMock,
source: overrides
})
);
// @ts-expect-error
return kcContextMock;
}
return { getKcContextMock };
}

View File

@ -0,0 +1,2 @@
export type { ExtendKcContext, KcContext } from "./KcContext";
export { createGetKcContextMock } from "./getKcContextMock";

View File

@ -0,0 +1,213 @@
import "keycloakify/tools/Object.fromEntries";
import { WELL_KNOWN_DIRECTORY_BASE_NAME } from "keycloakify/bin/shared/constants";
import { id } from "tsafe/id";
import type { KcContext } from "./KcContext";
import { BASE_URL } from "keycloakify/lib/BASE_URL";
import { assert, type Equals } from "tsafe/assert";
import type { LanguageTag } from "keycloakify/account/i18n/messages_defaultSet/types";
const resourcesPath = `${BASE_URL}${WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES}/account`;
export const kcContextCommonMock: KcContext.Common = {
themeVersion: "0.0.0",
keycloakifyVersion: "0.0.0",
themeType: "account",
themeName: "my-theme-name",
url: {
resourcesPath,
resourcesCommonPath: `${resourcesPath}/${WELL_KNOWN_DIRECTORY_BASE_NAME.RESOURCES_COMMON}`,
resourceUrl: "#",
accountUrl: "#",
applicationsUrl: "#",
logoutUrl: "#",
getLogoutUrl: () => "#",
logUrl: "#",
passwordUrl: "#",
sessionsUrl: "#",
socialUrl: "#",
totpUrl: "#"
},
realm: {
internationalizationEnabled: true,
userManagedAccessAllowed: true
},
messagesPerField: {
printIfExists: () => {
return undefined;
},
existsError: () => false,
get: key => `Fake error for ${key}`,
exists: () => false
},
locale: {
supported: (
[
/* spell-checker: disable */
["de", "Deutsch"],
["no", "Norsk"],
["ru", "Русский"],
["sv", "Svenska"],
["pt-BR", "Português (Brasil)"],
["lt", "Lietuvių"],
["en", "English"],
["it", "Italiano"],
["fr", "Français"],
["zh-CN", "中文简体"],
["es", "Español"],
["cs", "Čeština"],
["ja", "日本語"],
["sk", "Slovenčina"],
["pl", "Polski"],
["ca", "Català"],
["nl", "Nederlands"],
["tr", "Türkçe"],
["ar", "العربية"],
["da", "Dansk"],
["fi", "Suomi"],
["hu", "Magyar"],
["lv", "Latviešu"]
/* spell-checker: enable */
] as const
).map(([languageTag, label]) => {
{
type Got = typeof languageTag;
type Expected = LanguageTag;
type Missing = Exclude<Expected, Got>;
type Unexpected = Exclude<Got, Expected>;
assert<Equals<Missing, never>>;
assert<Equals<Unexpected, never>>;
}
return {
languageTag,
label,
url: "https://gist.github.com/garronej/52baaca1bb925f2296ab32741e062b8e"
} as const;
}),
currentLanguageTag: "en"
},
features: {
authorization: true,
identityFederation: true,
log: true,
passwordUpdateSupported: true
},
referrer: undefined,
account: {
firstName: "john",
lastName: "doe",
email: "john.doe@code.gouv.fr",
username: "doe_j"
},
properties: {},
"x-keycloakify": {
messages: {}
}
};
export const kcContextMocks: KcContext[] = [
id<KcContext.Password>({
...kcContextCommonMock,
pageId: "password.ftl",
password: {
passwordSet: true
},
stateChecker: "state checker"
}),
id<KcContext.Account>({
...kcContextCommonMock,
pageId: "account.ftl",
url: {
...kcContextCommonMock.url,
referrerURI: "#",
accountUrl: "#"
},
realm: {
...kcContextCommonMock.realm,
registrationEmailAsUsername: true,
editUsernameAllowed: true
},
stateChecker: ""
}),
id<KcContext.Sessions>({
...kcContextCommonMock,
pageId: "sessions.ftl",
sessions: {
sessions: [
{
ipAddress: "127.0.0.1",
started: new Date().toString(),
lastAccess: new Date().toString(),
expires: new Date().toString(),
clients: ["Chrome", "Firefox"],
id: "f8951177-817d-4a70-9c02-86d3c170fe51"
}
]
},
stateChecker: "g6WB1FaYnKotTkiy7ZrlxvFztSqS0U8jvHsOOOb2z4g"
}),
id<KcContext.Totp>({
...kcContextCommonMock,
pageId: "totp.ftl",
totp: {
enabled: true,
totpSecretEncoded: "KVVF G2BY N4YX S6LB IUYT K2LH IFYE 4SBV",
qrUrl: "#",
totpSecretQrCode:
"iVBORw0KGgoAAAANSUhEUgAAAPYAAAD2AQAAAADNaUdlAAACM0lEQVR4Xu3OIZJgOQwDUDFd2UxiurLAVnnbHw4YGDKtSiWOn4Gxf81//7r/+q8b4HfLGBZDK9d85NmNR+sB42sXvOYrN5P1DcgYYFTGfOlbzE8gzwy3euweGizw7cfdl34/GRhlkxjKNV+5AebPXPORX1JuB9x8ZfbyyD2y1krWAKsbMq1HnqQDaLfa77p4+MqvzEGSqvSAD/2IHW2yHaigR9tX3m8dDIYGcNf3f+gDpVBZbZU77zyJ6Rlcy+qoTMG887KAPD9hsh6a1Sv3gJUHGHUAxSMzj7zqDDe7Phmt2eG+8UsMxjRGm816MAO+8VMl1R1jGHOrZB/5Zo/WXAPgxixm9Mo96vDGrM1eOto8c4Ax4wF437mifOXlpiPzCnN7Y9l95NnEMxgMY9AAGA8fucH14Y1aVb6N/cqrmyh0BVht7k1e+bU8LK0Cg5vmVq9c5vHIjOfqxDIfeTraNVTwewa4wVe+SW5N+uP1qACeudUZbqGOfA6VZV750Noq2Xx3kpveV44ZelSV1V7KFHzkWyVrrlUwG0Pl9pWnoy3vsQoME6vKI69i5osVgwWzHT7zjmJtMcNUSVn1oYMd7ZodbgowZl45VG0uVuLPUr1yc79uaQBag/mqR34xhlWyHm1prplHboCWdZ4TeZjsK8+dI+jbz1C5hl65mcpgB5dhcj8+dGO+0Ko68+lD37JDD83dpDLzzK+TrQyaVwGj6pUboGV+7+AyN8An/pf84/7rv/4/1l4OCc/1BYMAAAAASUVORK5CYII=",
manualUrl: "#",
totpSecret: "G4nsI8lQagRMUchH8jEG",
otpCredentials: [],
supportedApplications: [
"totpAppFreeOTPName",
"totpAppMicrosoftAuthenticatorName",
"totpAppGoogleName"
],
policy: {
algorithm: "HmacSHA1",
digits: 6,
lookAheadWindow: 1,
type: "totp",
period: 30,
getAlgorithmKey: () => "SHA1"
}
},
mode: "qr",
isAppInitiatedAction: false,
stateChecker: ""
}),
id<KcContext.Log>({
...kcContextCommonMock,
pageId: "log.ftl",
log: {
events: [
{
date: "2/21/2024, 1:28:39 PM",
event: "login",
ipAddress: "172.17.0.1",
client: "security-admin-console",
details: [{ key: "openid-connect", value: "admin" }]
}
]
}
}),
id<KcContext.FederatedIdentity>({
...kcContextCommonMock,
stateChecker: "",
pageId: "federatedIdentity.ftl",
federatedIdentity: {
identities: [
{
providerId: "keycloak-oidc",
displayName: "keycloak-oidc",
userName: "John",
connected: true
}
],
removeLinkPossible: true
}
})
];

141
src/account/Template.tsx Normal file
View File

@ -0,0 +1,141 @@
import { useEffect } from "react";
import { clsx } from "keycloakify/tools/clsx";
import { kcSanitize } from "keycloakify/lib/kcSanitize";
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
import { useSetClassName } from "keycloakify/tools/useSetClassName";
import { useInitialize } from "keycloakify/account/Template.useInitialize";
import type { TemplateProps } from "keycloakify/account/TemplateProps";
import type { I18n } from "./i18n";
import type { KcContext } from "./KcContext";
export default function Template(props: TemplateProps<KcContext, I18n>) {
const { kcContext, i18n, doUseDefaultCss, active, classes, children } = props;
const { kcClsx } = getKcClsx({ doUseDefaultCss, classes });
const { msg, msgStr, currentLanguage, enabledLanguages } = i18n;
const { url, features, realm, message, referrer } = kcContext;
useEffect(() => {
document.title = msgStr("accountManagementTitle");
}, []);
useSetClassName({
qualifiedName: "html",
className: kcClsx("kcHtmlClass")
});
useSetClassName({
qualifiedName: "body",
className: clsx("admin-console", "user", kcClsx("kcBodyClass"))
});
const { isReadyToRender } = useInitialize({ kcContext, doUseDefaultCss });
if (!isReadyToRender) {
return null;
}
return (
<>
<header className="navbar navbar-default navbar-pf navbar-main header">
<nav className="navbar" role="navigation">
<div className="navbar-header">
<div className="container">
<h1 className="navbar-title">Keycloak</h1>
</div>
</div>
<div className="navbar-collapse navbar-collapse-1">
<div className="container">
<ul className="nav navbar-nav navbar-utility">
{enabledLanguages.length > 1 && (
<li>
<div className="kc-dropdown" id="kc-locale-dropdown">
<a href="#" id="kc-current-locale-link">
{currentLanguage.label}
</a>
<ul>
{enabledLanguages.map(({ languageTag, label, href }) => (
<li key={languageTag} className="kc-dropdown-item">
<a href={href}>{label}</a>
</li>
))}
</ul>
</div>
</li>
)}
{referrer?.url && (
<li>
<a href={referrer.url} id="referrer">
{msg("backTo", referrer.name)}
</a>
</li>
)}
<li>
<a href={url.getLogoutUrl()}>{msg("doSignOut")}</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div className="container">
<div className="bs-sidebar col-sm-3">
<ul>
<li className={clsx(active === "account" && "active")}>
<a href={url.accountUrl}>{msg("account")}</a>
</li>
{features.passwordUpdateSupported && (
<li className={clsx(active === "password" && "active")}>
<a href={url.passwordUrl}>{msg("password")}</a>
</li>
)}
<li className={clsx(active === "totp" && "active")}>
<a href={url.totpUrl}>{msg("authenticator")}</a>
</li>
{features.identityFederation && (
<li className={clsx(active === "social" && "active")}>
<a href={url.socialUrl}>{msg("federatedIdentity")}</a>
</li>
)}
<li className={clsx(active === "sessions" && "active")}>
<a href={url.sessionsUrl}>{msg("sessions")}</a>
</li>
<li className={clsx(active === "applications" && "active")}>
<a href={url.applicationsUrl}>{msg("applications")}</a>
</li>
{features.log && (
<li className={clsx(active === "log" && "active")}>
<a href={url.logUrl}>{msg("log")}</a>
</li>
)}
{realm.userManagedAccessAllowed && features.authorization && (
<li className={clsx(active === "authorization" && "active")}>
<a href={url.resourceUrl}>{msg("myResources")}</a>
</li>
)}
</ul>
</div>
<div className="col-sm-9 content-area">
{message !== undefined && (
<div className={clsx("alert", `alert-${message.type}`)}>
{message.type === "success" && <span className="pficon pficon-ok"></span>}
{message.type === "error" && <span className="pficon pficon-error-circle-o"></span>}
<span
className="kc-feedback-text"
dangerouslySetInnerHTML={{
__html: kcSanitize(message.summary)
}}
/>
</div>
)}
{children}
</div>
</div>
</>
);
}

View File

@ -0,0 +1,35 @@
import { assert } from "keycloakify/tools/assert";
import { useInsertLinkTags } from "keycloakify/tools/useInsertLinkTags";
import type { KcContext } from "keycloakify/account/KcContext";
export type KcContextLike = {
url: {
resourcesCommonPath: string;
resourcesPath: string;
};
};
assert<keyof KcContextLike extends keyof KcContext ? true : false>();
assert<KcContext extends KcContextLike ? true : false>();
export function useInitialize(params: {
kcContext: KcContextLike;
doUseDefaultCss: boolean;
}) {
const { kcContext, doUseDefaultCss } = params;
const { url } = kcContext;
const { areAllStyleSheetsLoaded } = useInsertLinkTags({
componentOrHookName: "Template",
hrefs: !doUseDefaultCss
? []
: [
`${url.resourcesCommonPath}/node_modules/patternfly/dist/css/patternfly.min.css`,
`${url.resourcesCommonPath}/node_modules/patternfly/dist/css/patternfly-additions.min.css`,
`${url.resourcesPath}/css/account.css`
]
});
return { isReadyToRender: areAllStyleSheetsLoaded };
}

View File

@ -0,0 +1,14 @@
import type { ReactNode } from "react";
import type { ClassKey } from "keycloakify/account/lib/kcClsx";
export type TemplateProps<KcContext, I18n> = {
kcContext: KcContext;
i18n: I18n;
doUseDefaultCss: boolean;
classes?: Partial<Record<ClassKey, string>>;
children: ReactNode;
active: string;
};
export type { ClassKey };

3
src/account/index.ts Normal file
View File

@ -0,0 +1,3 @@
export type { ExtendKcContext } from "keycloakify/account/KcContext";
export type { ClassKey } from "keycloakify/account/TemplateProps";
export { i18nBuilder, type MessageKey_defaultSet } from "keycloakify/account/i18n";

37
src/account/lib/kcClsx.ts Normal file
View File

@ -0,0 +1,37 @@
import { createGetKcClsx } from "keycloakify/lib/getKcClsx";
export type ClassKey =
| "kcHtmlClass"
| "kcBodyClass"
| "kcButtonClass"
| "kcButtonPrimaryClass"
| "kcButtonLargeClass"
| "kcButtonDefaultClass"
| "kcContentWrapperClass"
| "kcFormClass"
| "kcFormGroupClass"
| "kcInputWrapperClass"
| "kcLabelClass"
| "kcInputClass"
| "kcInputErrorMessageClass";
export const { getKcClsx } = createGetKcClsx<ClassKey>({
defaultClasses: {
kcHtmlClass: undefined,
kcBodyClass: undefined,
kcButtonClass: "btn",
kcContentWrapperClass: "row",
kcButtonPrimaryClass: "btn-primary",
kcButtonLargeClass: "btn-lg",
kcButtonDefaultClass: "btn-default",
kcFormClass: "form-horizontal",
kcFormGroupClass: "form-group",
kcInputWrapperClass: "col-xs-12 col-sm-12 col-md-12 col-lg-12",
kcLabelClass: "control-label",
kcInputClass: "form-control",
kcInputErrorMessageClass:
"pf-c-form__helper-text pf-m-error required kc-feedback-text"
}
});
export type KcClsx = ReturnType<typeof getKcClsx>["kcClsx"];

View File

@ -0,0 +1,127 @@
import { clsx } from "keycloakify/tools/clsx";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
import type { KcContext } from "../KcContext";
import type { I18n } from "../i18n";
export default function Account(props: PageProps<Extract<KcContext, { pageId: "account.ftl" }>, I18n>) {
const { kcContext, i18n, doUseDefaultCss, Template } = props;
const classes = {
...props.classes,
kcBodyClass: clsx(props.classes?.kcBodyClass, "user")
};
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
const { url, realm, messagesPerField, stateChecker, account, referrer } = kcContext;
const { msg } = i18n;
return (
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="account">
<div className="row">
<div className="col-md-10">
<h2>{msg("editAccountHtmlTitle")}</h2>
</div>
<div className="col-md-2 subtitle">
<span className="subtitle">
<span className="required">*</span> {msg("requiredFields")}
</span>
</div>
</div>
<form action={url.accountUrl} className="form-horizontal" method="post">
<input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
{!realm.registrationEmailAsUsername && (
<div className={clsx("form-group", messagesPerField.printIfExists("username", "has-error"))}>
<div className="col-sm-2 col-md-2">
<label htmlFor="username" className="control-label">
{msg("username")}
</label>
{realm.editUsernameAllowed && <span className="required">*</span>}
</div>
<div className="col-sm-10 col-md-10">
<input
type="text"
className="form-control"
id="username"
name="username"
disabled={!realm.editUsernameAllowed}
defaultValue={account.username ?? ""}
/>
</div>
</div>
)}
<div className={clsx("form-group", messagesPerField.printIfExists("email", "has-error"))}>
<div className="col-sm-2 col-md-2">
<label htmlFor="email" className="control-label">
{msg("email")}
</label>{" "}
<span className="required">*</span>
</div>
<div className="col-sm-10 col-md-10">
<input type="text" className="form-control" id="email" name="email" autoFocus defaultValue={account.email ?? ""} />
</div>
</div>
<div className={clsx("form-group", messagesPerField.printIfExists("firstName", "has-error"))}>
<div className="col-sm-2 col-md-2">
<label htmlFor="firstName" className="control-label">
{msg("firstName")}
</label>{" "}
<span className="required">*</span>
</div>
<div className="col-sm-10 col-md-10">
<input type="text" className="form-control" id="firstName" name="firstName" defaultValue={account.firstName ?? ""} />
</div>
</div>
<div className={clsx("form-group", messagesPerField.printIfExists("lastName", "has-error"))}>
<div className="col-sm-2 col-md-2">
<label htmlFor="lastName" className="control-label">
{msg("lastName")}
</label>{" "}
<span className="required">*</span>
</div>
<div className="col-sm-10 col-md-10">
<input type="text" className="form-control" id="lastName" name="lastName" defaultValue={account.lastName ?? ""} />
</div>
</div>
<div className="form-group">
<div id="kc-form-buttons" className="col-md-offset-2 col-md-10 submit">
<div>
{referrer !== undefined && <a href={referrer?.url}>{msg("backToApplication")}</a>}
<button
type="submit"
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
name="submitAction"
value="Save"
>
{msg("doSave")}
</button>
<button
type="submit"
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass")}
name="submitAction"
value="Cancel"
>
{msg("doCancel")}
</button>
</div>
</div>
</div>
</form>
</Template>
);
}

View File

@ -0,0 +1,136 @@
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import type { KcContext } from "../KcContext";
import type { I18n } from "../i18n";
export default function Applications(props: PageProps<Extract<KcContext, { pageId: "applications.ftl" }>, I18n>) {
const { kcContext, i18n, doUseDefaultCss, classes, Template } = props;
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
const {
url,
applications: { applications },
stateChecker
} = kcContext;
const { msg, advancedMsg } = i18n;
return (
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="applications">
<div className="row">
<div className="col-md-10">
<h2>{msg("applicationsHtmlTitle")}</h2>
</div>
<form action={url.applicationsUrl} method="post">
<input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
<input type="hidden" id="referrer" name="referrer" value={stateChecker} />
<table className="table table-striped table-bordered">
<thead>
<tr>
<td>{msg("application")}</td>
<td>{msg("availableRoles")}</td>
<td>{msg("grantedPermissions")}</td>
<td>{msg("additionalGrants")}</td>
<td>{msg("action")}</td>
</tr>
</thead>
<tbody>
{applications.map(application => (
<tr key={application.client.clientId}>
<td>
{application.effectiveUrl && (
<a href={application.effectiveUrl}>
{(application.client.name && advancedMsg(application.client.name)) || application.client.clientId}
</a>
)}
{!application.effectiveUrl &&
((application.client.name && advancedMsg(application.client.name)) || application.client.clientId)}
</td>
<td>
{!isArrayWithEmptyObject(application.realmRolesAvailable) &&
application.realmRolesAvailable.map((role, index) => (
<span key={role.name}>
{role.description ? advancedMsg(role.description) : advancedMsg(role.name)}
{index < application.realmRolesAvailable.length - 1 && ", "}
</span>
))}
{application.resourceRolesAvailable &&
Object.keys(application.resourceRolesAvailable).map(resource => (
<span key={resource}>
{!isArrayWithEmptyObject(application.realmRolesAvailable) && ", "}
{application.resourceRolesAvailable[resource].map(clientRole => (
<span key={clientRole.roleName}>
{clientRole.roleDescription
? advancedMsg(clientRole.roleDescription)
: advancedMsg(clientRole.roleName)}{" "}
{msg("inResource")}{" "}
<strong>
{clientRole.clientName ? advancedMsg(clientRole.clientName) : clientRole.clientId}
</strong>
{clientRole !==
application.resourceRolesAvailable[resource][
application.resourceRolesAvailable[resource].length - 1
] && ", "}
</span>
))}
</span>
))}
</td>
<td>
{application.client.consentRequired ? (
application.clientScopesGranted.map(claim => (
<span key={claim}>
{advancedMsg(claim)}
{claim !== application.clientScopesGranted[application.clientScopesGranted.length - 1] && ", "}
</span>
))
) : (
<strong>{msg("fullAccess")}</strong>
)}
</td>
<td>
{application.additionalGrants.map(grant => (
<span key={grant}>
{advancedMsg(grant)}
{grant !== application.additionalGrants[application.additionalGrants.length - 1] && ", "}
</span>
))}
</td>
<td>
{(application.client.consentRequired && application.clientScopesGranted.length > 0) ||
application.additionalGrants.length > 0 ? (
<button
type="submit"
className={kcClsx("kcButtonPrimaryClass", "kcButtonClass")}
id={`revoke-${application.client.clientId}`}
name="clientId"
value={application.client.id}
>
{msg("revoke")}
</button>
) : null}
</td>
</tr>
))}
</tbody>
</table>
</form>
</div>
</Template>
);
}
function isArrayWithEmptyObject(variable: any): boolean {
return Array.isArray(variable) && variable.length === 1 && typeof variable[0] === "object" && Object.keys(variable[0]).length === 0;
}

View File

@ -0,0 +1,58 @@
import type { PageProps } from "keycloakify/account/pages/PageProps";
import type { KcContext } from "../KcContext";
import type { I18n } from "../i18n";
export default function FederatedIdentity(props: PageProps<Extract<KcContext, { pageId: "federatedIdentity.ftl" }>, I18n>) {
const { kcContext, i18n, doUseDefaultCss, classes, Template } = props;
const { url, federatedIdentity, stateChecker } = kcContext;
const { msg } = i18n;
return (
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="social">
<div className="main-layout social">
<div className="row">
<div className="col-md-10">
<h2>{msg("federatedIdentitiesHtmlTitle")}</h2>
</div>
</div>
<div id="federated-identities">
{federatedIdentity.identities.map(identity => (
<div key={identity.providerId} className="row margin-bottom">
<div className="col-sm-2 col-md-2">
<label htmlFor={identity.providerId} className="control-label">
{identity.displayName}
</label>
</div>
<div className="col-sm-5 col-md-5">
<input disabled className="form-control" value={identity.userName} />
</div>
<div className="col-sm-5 col-md-5">
{identity.connected ? (
federatedIdentity.removeLinkPossible && (
<form action={url.socialUrl} method="post" className="form-inline">
<input type="hidden" name="stateChecker" value={stateChecker} />
<input type="hidden" name="action" value="remove" />
<input type="hidden" name="providerId" value={identity.providerId} />
<button id={`remove-link-${identity.providerId}`} className="btn btn-default">
{msg("doRemove")}
</button>
</form>
)
) : (
<form action={url.socialUrl} method="post" className="form-inline">
<input type="hidden" name="stateChecker" value={stateChecker} />
<input type="hidden" name="action" value="add" />
<input type="hidden" name="providerId" value={identity.providerId} />
<button id={`add-link-${identity.providerId}`} className="btn btn-default">
{msg("doAdd")}
</button>
</form>
)}
</div>
</div>
))}
</div>
</div>
</Template>
);
}

70
src/account/pages/Log.tsx Normal file
View File

@ -0,0 +1,70 @@
import type { Key } from "react";
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import type { KcContext } from "../KcContext";
import type { I18n } from "../i18n";
export default function Log(props: PageProps<Extract<KcContext, { pageId: "log.ftl" }>, I18n>) {
const { kcContext, i18n, doUseDefaultCss, classes, Template } = props;
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
const { log } = kcContext;
const { msg } = i18n;
return (
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="log">
<div className={kcClsx("kcContentWrapperClass")}>
<div className="col-md-10">
<h2>{msg("accountLogHtmlTitle")}</h2>
</div>
<table className="table table-striped table-bordered">
<thead>
<tr>
<td>{msg("date")}</td>
<td>{msg("event")}</td>
<td>{msg("ip")}</td>
<td>{msg("client")}</td>
<td>{msg("details")}</td>
</tr>
</thead>
<tbody>
{log.events.map(
(
event: {
date: string | number | Date;
event: string;
ipAddress: string;
client: any;
details: any[];
},
index: Key | null | undefined
) => (
<tr key={index}>
<td>{event.date ? new Date(event.date).toLocaleString() : ""}</td>
<td>{event.event}</td>
<td>{event.ipAddress}</td>
<td>{event.client || ""}</td>
<td>
{event.details.map((detail, detailIndex) => (
<span key={detailIndex}>
{`${detail.key} = ${detail.value}`}
{detailIndex < event.details.length - 1 && ", "}
</span>
))}
</td>
</tr>
)
)}
</tbody>
</table>
</div>
</Template>
);
}

View File

@ -0,0 +1,11 @@
import type { JSX } from "keycloakify/tools/JSX";
import { type TemplateProps, type ClassKey } from "keycloakify/account/TemplateProps";
import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
export type PageProps<NarrowedKcContext, I18n> = {
Template: LazyOrNot<(props: TemplateProps<any, any>) => JSX.Element | null>;
kcContext: NarrowedKcContext;
i18n: I18n;
doUseDefaultCss: boolean;
classes?: Partial<Record<ClassKey, string>>;
};

View File

@ -0,0 +1,209 @@
import { useState } from "react";
import { clsx } from "keycloakify/tools/clsx";
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import type { KcContext } from "../KcContext";
import type { I18n } from "../i18n";
export default function Password(props: PageProps<Extract<KcContext, { pageId: "password.ftl" }>, I18n>) {
const { kcContext, i18n, doUseDefaultCss, Template } = props;
const classes = {
...props.classes,
kcBodyClass: clsx(props.classes?.kcBodyClass, "password")
};
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
const { url, password, account, stateChecker } = kcContext;
const { msgStr, msg } = i18n;
const [currentPassword, setCurrentPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [newPasswordConfirm, setNewPasswordConfirm] = useState("");
const [newPasswordError, setNewPasswordError] = useState("");
const [newPasswordConfirmError, setNewPasswordConfirmError] = useState("");
const [hasNewPasswordBlurred, setHasNewPasswordBlurred] = useState(false);
const [hasNewPasswordConfirmBlurred, setHasNewPasswordConfirmBlurred] = useState(false);
const checkNewPassword = (newPassword: string) => {
if (!password.passwordSet) {
return;
}
if (newPassword === currentPassword) {
setNewPasswordError(msgStr("newPasswordSameAsOld"));
} else {
setNewPasswordError("");
}
};
const checkNewPasswordConfirm = (newPasswordConfirm: string) => {
if (newPasswordConfirm === "") {
return;
}
if (newPassword !== newPasswordConfirm) {
setNewPasswordConfirmError(msgStr("passwordConfirmNotMatch"));
} else {
setNewPasswordConfirmError("");
}
};
return (
<Template
{...{
kcContext: {
...kcContext,
message: (() => {
if (newPasswordError !== "") {
return {
type: "error",
summary: newPasswordError
};
}
if (newPasswordConfirmError !== "") {
return {
type: "error",
summary: newPasswordConfirmError
};
}
return kcContext.message;
})()
},
i18n,
doUseDefaultCss,
classes
}}
active="password"
>
<div className="row">
<div className="col-md-10">
<h2>{msg("changePasswordHtmlTitle")}</h2>
</div>
<div className="col-md-2 subtitle">
<span className="subtitle">{msg("allFieldsRequired")}</span>
</div>
</div>
<form action={url.passwordUrl} className="form-horizontal" method="post">
<input
type="text"
id="username"
name="username"
value={account.username ?? ""}
autoComplete="username"
readOnly
style={{ display: "none" }}
/>
{password.passwordSet && (
<div className="form-group">
<div className="col-sm-2 col-md-2">
<label htmlFor="password" className="control-label">
{msg("password")}
</label>
</div>
<div className="col-sm-10 col-md-10">
<input
type="password"
className="form-control"
id="password"
name="password"
autoFocus
autoComplete="current-password"
value={currentPassword}
onChange={event => setCurrentPassword(event.target.value)}
/>
</div>
</div>
)}
<input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
<div className="form-group">
<div className="col-sm-2 col-md-2">
<label htmlFor="password-new" className="control-label">
{msg("passwordNew")}
</label>
</div>
<div className="col-sm-10 col-md-10">
<input
type="password"
className="form-control"
id="password-new"
name="password-new"
autoComplete="new-password"
value={newPassword}
onChange={event => {
const newPassword = event.target.value;
setNewPassword(newPassword);
if (hasNewPasswordBlurred) {
checkNewPassword(newPassword);
}
}}
onBlur={() => {
setHasNewPasswordBlurred(true);
checkNewPassword(newPassword);
}}
/>
</div>
</div>
<div className="form-group">
<div className="col-sm-2 col-md-2">
<label htmlFor="password-confirm" className="control-label two-lines">
{msg("passwordConfirm")}
</label>
</div>
<div className="col-sm-10 col-md-10">
<input
type="password"
className="form-control"
id="password-confirm"
name="password-confirm"
autoComplete="new-password"
value={newPasswordConfirm}
onChange={event => {
const newPasswordConfirm = event.target.value;
setNewPasswordConfirm(newPasswordConfirm);
if (hasNewPasswordConfirmBlurred) {
checkNewPasswordConfirm(newPasswordConfirm);
}
}}
onBlur={() => {
setHasNewPasswordConfirmBlurred(true);
checkNewPasswordConfirm(newPasswordConfirm);
}}
/>
</div>
</div>
<div className="form-group">
<div id="kc-form-buttons" className="col-md-offset-2 col-md-10 submit">
<div>
<button
disabled={newPasswordError !== "" || newPasswordConfirmError !== ""}
type="submit"
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
name="submitAction"
value="Save"
>
{msg("doSave")}
</button>
</div>
</div>
</div>
</form>
</Template>
);
}

View File

@ -0,0 +1,64 @@
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import type { KcContext } from "../KcContext";
import type { I18n } from "../i18n";
export default function Sessions(props: PageProps<Extract<KcContext, { pageId: "sessions.ftl" }>, I18n>) {
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
const { url, stateChecker, sessions } = kcContext;
const { msg } = i18n;
return (
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="sessions">
<div className={kcClsx("kcContentWrapperClass")}>
<div className="col-md-10">
<h2>{msg("sessionsHtmlTitle")}</h2>
</div>
</div>
<table className="table table-striped table-bordered">
<thead>
<tr>
<th>{msg("ip")}</th>
<th>{msg("started")}</th>
<th>{msg("lastAccess")}</th>
<th>{msg("expires")}</th>
<th>{msg("clients")}</th>
</tr>
</thead>
<tbody role="rowgroup">
{sessions.sessions.map((session, index: number) => (
<tr key={index}>
<td>{session.ipAddress}</td>
<td>{session?.started}</td>
<td>{session?.lastAccess}</td>
<td>{session?.expires}</td>
<td>
{session.clients.map((client: string, clientIndex: number) => (
<div key={clientIndex}>
{client}
<br />
</div>
))}
</td>
</tr>
))}
</tbody>
</table>
<form action={url.sessionsUrl} method="post">
<input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
<button id="logout-all-sessions" type="submit" className={kcClsx("kcButtonDefaultClass", "kcButtonClass")}>
{msg("doLogOutAllSessions")}
</button>
</form>
</Template>
);
}

226
src/account/pages/Totp.tsx Normal file
View File

@ -0,0 +1,226 @@
import { clsx } from "keycloakify/tools/clsx";
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
import { kcSanitize } from "keycloakify/lib/kcSanitize";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import type { KcContext } from "../KcContext";
import type { I18n } from "../i18n";
export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp.ftl" }>, I18n>) {
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
const { kcClsx } = getKcClsx({
doUseDefaultCss,
classes
});
const { totp, mode, url, messagesPerField, stateChecker } = kcContext;
const { msg, msgStr, advancedMsg } = i18n;
return (
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="totp">
<>
<div className="row">
<div className="col-md-10">
<h2>{msg("authenticatorTitle")}</h2>
</div>
{totp.otpCredentials.length === 0 && (
<div className="subtitle col-md-2">
<span className="required">*</span>
{msg("requiredFields")}
</div>
)}
</div>
{totp.enabled && (
<table className="table table-bordered table-striped">
<thead>
{totp.otpCredentials.length > 1 ? (
<tr>
<th colSpan={4}>{msg("configureAuthenticators")}</th>
</tr>
) : (
<tr>
<th colSpan={3}>{msg("configureAuthenticators")}</th>
</tr>
)}
</thead>
<tbody>
{totp.otpCredentials.map((credential, index) => (
<tr key={index}>
<td className="provider">{msg("mobile")}</td>
{totp.otpCredentials.length > 1 && <td className="provider">{credential.id}</td>}
<td className="provider">{credential.userLabel || ""}</td>
<td className="action">
<form action={url.totpUrl} method="post" className="form-inline">
<input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
<input type="hidden" id="submitAction" name="submitAction" value="Delete" />
<input type="hidden" id="credentialId" name="credentialId" value={credential.id} />
<button id={`remove-mobile-${index}`} className="btn btn-default">
<i className="pficon pficon-delete"></i>
</button>
</form>
</td>
</tr>
))}
</tbody>
</table>
)}
{!totp.enabled && (
<div>
<hr />
<ol id="kc-totp-settings">
<li>
<p>{msg("totpStep1")}</p>
<ul id="kc-totp-supported-apps">{totp.supportedApplications?.map(app => <li key={app}>{advancedMsg(app)}</li>)}</ul>
</li>
{mode && mode == "manual" ? (
<>
<li>
<p>{msg("totpManualStep2")}</p>
<p>
<span id="kc-totp-secret-key">{totp.totpSecretEncoded}</span>
</p>
<p>
<a href={totp.qrUrl} id="mode-barcode">
{msg("totpScanBarcode")}
</a>
</p>
</li>
<li>
<p>{msg("totpManualStep3")}</p>
<ul>
<li id="kc-totp-type">
{msg("totpType")}: {msg(`totp.${totp.policy.type}`)}
</li>
<li id="kc-totp-algorithm">
{msg("totpAlgorithm")}: {totp.policy.getAlgorithmKey()}
</li>
<li id="kc-totp-digits">
{msg("totpDigits")}: {totp.policy.digits}
</li>
{totp.policy.type === "totp" ? (
<li id="kc-totp-period">
{msg("totpInterval")}: {totp.policy.period}
</li>
) : (
<li id="kc-totp-counter">
{msg("totpCounter")}: {totp.policy.initialCounter}
</li>
)}
</ul>
</li>
</>
) : (
<li>
<p>{msg("totpStep2")}</p>
<p>
<img
id="kc-totp-secret-qr-code"
src={`data:image/png;base64, ${totp.totpSecretQrCode}`}
alt="Figure: Barcode"
/>
</p>
<p>
<a href={totp.manualUrl} id="mode-manual">
{msg("totpUnableToScan")}
</a>
</p>
</li>
)}
<li>
<p>{msg("totpStep3")}</p>
<p>{msg("totpStep3DeviceName")}</p>
</li>
</ol>
<hr />
<form action={url.totpUrl} className={kcClsx("kcFormClass")} id="kc-totp-settings-form" method="post">
<input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
<div className={kcClsx("kcFormGroupClass")}>
<div className="col-sm-2 col-md-2">
<label htmlFor="totp" className="control-label">
{msg("authenticatorCode")}
</label>
<span className="required">*</span>
</div>
<div className="col-sm-10 col-md-10">
<input
type="text"
id="totp"
name="totp"
autoComplete="off"
className={kcClsx("kcInputClass")}
aria-invalid={messagesPerField.existsError("totp")}
/>
{messagesPerField.existsError("totp") && (
<span
id="input-error-otp-code"
className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
dangerouslySetInnerHTML={{
__html: kcSanitize(messagesPerField.get("totp"))
}}
/>
)}
</div>
<input type="hidden" id="totpSecret" name="totpSecret" value={totp.totpSecret} />
{mode && <input type="hidden" id="mode" value={mode} />}
</div>
<div className={kcClsx("kcFormGroupClass")}>
<div className="col-sm-2 col-md-2">
<label htmlFor="userLabel" className={kcClsx("kcLabelClass")}>
{msg("totpDeviceName")}
</label>
{totp.otpCredentials.length >= 1 && <span className="required">*</span>}
</div>
<div className="col-sm-10 col-md-10">
<input
type="text"
id="userLabel"
name="userLabel"
autoComplete="off"
className={kcClsx("kcInputClass")}
aria-invalid={messagesPerField.existsError("userLabel")}
/>
{messagesPerField.existsError("userLabel") && (
<span
id="input-error-otp-label"
className={kcClsx("kcInputErrorMessageClass")}
aria-live="polite"
dangerouslySetInnerHTML={{
__html: kcSanitize(messagesPerField.get("userLabel"))
}}
/>
)}
</div>
</div>
<div id="kc-form-buttons" className={clsx(kcClsx("kcFormGroupClass"), "text-right")}>
<div className={kcClsx("kcInputWrapperClass")}>
<input
type="submit"
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
id="saveTOTPBtn"
value={msgStr("doSave")}
/>
<button
type="submit"
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass", "kcButtonLargeClass")}
id="cancelTOTPBtn"
name="submitAction"
value="Cancel"
>
{msg("doCancel")}
</button>
</div>
</div>
</form>
</div>
)}
</>
</Template>
);
}

165
src/bin/add-story.ts Normal file
View File

@ -0,0 +1,165 @@
import { getThisCodebaseRootDirPath } from "./tools/getThisCodebaseRootDirPath";
import cliSelect from "cli-select";
import {
LOGIN_THEME_PAGE_IDS,
ACCOUNT_THEME_PAGE_IDS,
type LoginThemePageId,
type AccountThemePageId,
THEME_TYPES
} from "./shared/constants";
import { capitalize } from "tsafe/capitalize";
import * as fs from "fs";
import { join as pathJoin, relative as pathRelative, dirname as pathDirname } from "path";
import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase";
import { assert, Equals } from "tsafe/assert";
import type { BuildContext } from "./shared/buildContext";
import chalk from "chalk";
import { runPrettier, getIsPrettierAvailable } from "./tools/runPrettier";
import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
export async function command(params: { buildContext: BuildContext }) {
const { buildContext } = params;
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
commandName: "add-story",
buildContext
});
if (hasBeenHandled) {
return;
}
console.log(chalk.cyan("Theme type:"));
const themeType = await (async () => {
const values = THEME_TYPES.filter(themeType => {
switch (themeType) {
case "account":
return buildContext.implementedThemeTypes.account.isImplemented;
case "login":
return buildContext.implementedThemeTypes.login.isImplemented;
case "admin":
return buildContext.implementedThemeTypes.admin.isImplemented;
}
assert<Equals<typeof themeType, never>>(false);
});
assert(values.length > 0, "No theme is implemented in this project");
if (values.length === 1) {
return values[0];
}
const { value } = await cliSelect({
values
}).catch(() => {
process.exit(-1);
});
return value;
})();
if (
themeType === "account" &&
(assert(buildContext.implementedThemeTypes.account.isImplemented),
buildContext.implementedThemeTypes.account.type === "Single-Page")
) {
console.log(
`${chalk.red("✗")} Sorry, there is no Storybook support for Single-Page Account themes.`
);
process.exit(0);
return;
}
if (themeType === "admin") {
console.log(
`${chalk.red("✗")} Sorry, there is no Storybook support for the Admin UI.`
);
process.exit(0);
return;
}
console.log(`${themeType}`);
console.log(chalk.cyan("Select the page you want to create a Storybook for:"));
const { value: pageId } = await cliSelect<LoginThemePageId | AccountThemePageId>({
values: (() => {
switch (themeType) {
case "login":
return [...LOGIN_THEME_PAGE_IDS];
case "account":
return [...ACCOUNT_THEME_PAGE_IDS];
}
assert<Equals<typeof themeType, never>>(false);
})()
}).catch(() => {
process.exit(-1);
});
console.log(`${pageId}`);
const componentBasename = capitalize(kebabCaseToCamelCase(pageId)).replace(
/ftl$/,
"stories.tsx"
);
const targetFilePath = pathJoin(
buildContext.themeSrcDirPath,
themeType,
"pages",
componentBasename
);
if (fs.existsSync(targetFilePath)) {
console.log(`${pathRelative(process.cwd(), targetFilePath)} already exists`);
process.exit(-1);
}
let sourceCode = fs
.readFileSync(
pathJoin(
getThisCodebaseRootDirPath(),
"stories",
themeType,
"pages",
componentBasename
)
)
.toString("utf8")
.replace('import React from "react";\n', "")
.replace(/from "[./]+dist\//, 'from "keycloakify/');
run_prettier: {
if (!(await getIsPrettierAvailable())) {
break run_prettier;
}
sourceCode = await runPrettier({
filePath: targetFilePath,
sourceCode: sourceCode
});
}
{
const targetDirPath = pathDirname(targetFilePath);
if (!fs.existsSync(targetDirPath)) {
fs.mkdirSync(targetDirPath, { recursive: true });
}
}
fs.writeFileSync(targetFilePath, Buffer.from(sourceCode, "utf8"));
console.log(
[
`${chalk.green("✓")} ${chalk.bold(
pathJoin(".", pathRelative(process.cwd(), targetFilePath))
)} copy pasted from the Keycloakify source code into your project`,
`You can start storybook with ${chalk.bold("npm run storybook")}`
].join("\n")
);
}

View File

@ -0,0 +1,96 @@
import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
import { join as pathJoin, dirname as pathDirname } from "path";
import { WELL_KNOWN_DIRECTORY_BASE_NAME } from "./shared/constants";
import { readThisNpmPackageVersion } from "./tools/readThisNpmPackageVersion";
import * as fs from "fs";
import { rmSync } from "./tools/fs.rmSync";
import type { BuildContext } from "./shared/buildContext";
import { transformCodebase } from "./tools/transformCodebase";
import { getThisCodebaseRootDirPath } from "./tools/getThisCodebaseRootDirPath";
export async function command(params: { buildContext: BuildContext }) {
const { buildContext } = params;
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
commandName: "copy-keycloak-resources-to-public",
buildContext
});
if (hasBeenHandled) {
return;
}
const destDirPath = pathJoin(
buildContext.publicDirPath,
WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES
);
const keycloakifyBuildinfoFilePath = pathJoin(destDirPath, "keycloakify.buildinfo");
const keycloakifyBuildinfoRaw = JSON.stringify(
{
keycloakifyVersion: readThisNpmPackageVersion()
},
null,
2
);
skip_if_already_done: {
if (!fs.existsSync(keycloakifyBuildinfoFilePath)) {
break skip_if_already_done;
}
const keycloakifyBuildinfoRaw_previousRun = fs
.readFileSync(keycloakifyBuildinfoFilePath)
.toString("utf8");
if (keycloakifyBuildinfoRaw_previousRun !== keycloakifyBuildinfoRaw) {
break skip_if_already_done;
}
return;
}
rmSync(destDirPath, { force: true, recursive: true });
// NOTE: To remove in a while, remove the legacy keycloak-resources directory
rmSync(pathJoin(pathDirname(destDirPath), "keycloak-resources"), {
force: true,
recursive: true
});
rmSync(pathJoin(pathDirname(destDirPath), ".keycloakify"), {
force: true,
recursive: true
});
fs.mkdirSync(destDirPath, { recursive: true });
fs.writeFileSync(pathJoin(destDirPath, ".gitignore"), Buffer.from("*", "utf8"));
transformCodebase({
srcDirPath: pathJoin(
getThisCodebaseRootDirPath(),
"res",
"public",
WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES
),
destDirPath
});
fs.writeFileSync(
pathJoin(destDirPath, "README.txt"),
Buffer.from(
// prettier-ignore
[
"This directory is only used in dev mode by Keycloakify",
"It won't be included in your final build.",
"Do not modify anything in this directory.",
].join("\n")
)
);
fs.writeFileSync(
keycloakifyBuildinfoFilePath,
Buffer.from(keycloakifyBuildinfoRaw, "utf8")
);
}

View File

@ -1,41 +0,0 @@
#!/usr/bin/env node
import { downloadBuiltinKeycloakTheme } from "./download-builtin-keycloak-theme";
import { keycloakThemeEmailDirPath } from "./keycloakify";
import { join as pathJoin, basename as pathBasename } from "path";
import { transformCodebase } from "./tools/transformCodebase";
import { promptKeycloakVersion } from "./promptKeycloakVersion";
import * as fs from "fs";
import { getCliOptions } from "./tools/cliOptions";
import { getLogger } from "./tools/logger";
if (require.main === module) {
(async () => {
const { isSilent } = getCliOptions(process.argv.slice(2));
const logger = getLogger({ isSilent });
if (fs.existsSync(keycloakThemeEmailDirPath)) {
logger.warn(`There is already a ./${pathBasename(keycloakThemeEmailDirPath)} directory in your project. Aborting.`);
process.exit(-1);
}
const { keycloakVersion } = await promptKeycloakVersion();
const builtinKeycloakThemeTmpDirPath = pathJoin(keycloakThemeEmailDirPath, "..", "tmp_xIdP3_builtin_keycloak_theme");
downloadBuiltinKeycloakTheme({
keycloakVersion,
"destDirPath": builtinKeycloakThemeTmpDirPath,
isSilent
});
transformCodebase({
"srcDirPath": pathJoin(builtinKeycloakThemeTmpDirPath, "base", "email"),
"destDirPath": keycloakThemeEmailDirPath
});
logger.log(`./${pathBasename(keycloakThemeEmailDirPath)} ready to be customized`);
fs.rmSync(builtinKeycloakThemeTmpDirPath, { "recursive": true, "force": true });
})();
}

View File

@ -1,40 +0,0 @@
#!/usr/bin/env node
import { keycloakThemeBuildingDirPath } from "./keycloakify";
import { join as pathJoin } from "path";
import { downloadAndUnzip } from "./tools/downloadAndUnzip";
import { promptKeycloakVersion } from "./promptKeycloakVersion";
import { getCliOptions } from "./tools/cliOptions";
import { getLogger } from "./tools/logger";
export function downloadBuiltinKeycloakTheme(params: { keycloakVersion: string; destDirPath: string; isSilent: boolean }) {
const { keycloakVersion, destDirPath, isSilent } = params;
for (const ext of ["", "-community"]) {
downloadAndUnzip({
"destDirPath": destDirPath,
"url": `https://github.com/keycloak/keycloak/archive/refs/tags/${keycloakVersion}.zip`,
"pathOfDirToExtractInArchive": `keycloak-${keycloakVersion}/themes/src/main/resources${ext}/theme`,
"cacheDirPath": pathJoin(keycloakThemeBuildingDirPath, ".cache"),
isSilent
});
}
}
if (require.main === module) {
(async () => {
const { isSilent } = getCliOptions(process.argv.slice(2));
const logger = getLogger({ isSilent });
const { keycloakVersion } = await promptKeycloakVersion();
const destDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme");
logger.log(`Downloading builtins theme of Keycloak ${keycloakVersion} here ${destDirPath}`);
downloadBuiltinKeycloakTheme({
keycloakVersion,
destDirPath,
isSilent
});
})();
}

332
src/bin/eject-page.ts Normal file
View File

@ -0,0 +1,332 @@
#!/usr/bin/env node
import { getThisCodebaseRootDirPath } from "./tools/getThisCodebaseRootDirPath";
import cliSelect from "cli-select";
import {
LOGIN_THEME_PAGE_IDS,
ACCOUNT_THEME_PAGE_IDS,
type LoginThemePageId,
type AccountThemePageId,
THEME_TYPES
} from "./shared/constants";
import { capitalize } from "tsafe/capitalize";
import * as fs from "fs";
import { join as pathJoin, relative as pathRelative, dirname as pathDirname } from "path";
import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase";
import { assert, Equals } from "tsafe/assert";
import type { BuildContext } from "./shared/buildContext";
import chalk from "chalk";
import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
import { runPrettier, getIsPrettierAvailable } from "./tools/runPrettier";
export async function command(params: { buildContext: BuildContext }) {
const { buildContext } = params;
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
commandName: "eject-page",
buildContext
});
if (hasBeenHandled) {
return;
}
console.log(chalk.cyan("Theme type:"));
const themeType = await (async () => {
const values = THEME_TYPES.filter(themeType => {
switch (themeType) {
case "account":
return buildContext.implementedThemeTypes.account.isImplemented;
case "login":
return buildContext.implementedThemeTypes.login.isImplemented;
case "admin":
return buildContext.implementedThemeTypes.admin.isImplemented;
}
assert<Equals<typeof themeType, never>>(false);
});
assert(values.length > 0, "No theme is implemented in this project");
if (values.length === 1) {
return values[0];
}
const { value } = await cliSelect({
values
}).catch(() => {
process.exit(-1);
});
return value;
})();
if (themeType === "admin") {
console.log("Use `npx keycloakify own` command instead, see documentation");
process.exit(-1);
}
if (
themeType === "account" &&
(assert(buildContext.implementedThemeTypes.account.isImplemented),
buildContext.implementedThemeTypes.account.type === "Single-Page")
) {
console.log(
chalk.yellow(
[
"You are implementing a Single-Page Account theme.",
"The eject-page command isn't applicable in this context"
].join("\n")
)
);
process.exit(1);
return;
}
console.log(`${themeType}`);
console.log(chalk.cyan("Select the page you want to customize:"));
const templateValue = "Template.tsx (Layout common to every page)";
const userProfileFormFieldsValue =
"UserProfileFormFields.tsx (Renders the form of the register.ftl, login-update-profile.ftl, update-email.ftl and idp-review-user-profile.ftl)";
const otherPageValue = "The page you're looking for isn't listed here";
const { value: pageIdOrComponent } = await cliSelect<
| LoginThemePageId
| AccountThemePageId
| typeof templateValue
| typeof userProfileFormFieldsValue
| typeof otherPageValue
>({
values: (() => {
switch (themeType) {
case "login":
return [
templateValue,
userProfileFormFieldsValue,
...LOGIN_THEME_PAGE_IDS,
otherPageValue
];
case "account":
return [templateValue, ...ACCOUNT_THEME_PAGE_IDS, otherPageValue];
}
assert<Equals<typeof themeType, never>>(false);
})()
}).catch(() => {
process.exit(-1);
});
if (pageIdOrComponent === otherPageValue) {
console.log(
[
"To style a page not included in the base Keycloak, such as one added by a third-party Keycloak extension,",
"refer to the documentation: https://docs.keycloakify.dev/features/styling-a-custom-page-not-included-in-base-keycloak"
].join(" ")
);
process.exit(0);
}
console.log(`${pageIdOrComponent}`);
const componentBasename = (() => {
if (pageIdOrComponent === templateValue) {
return "Template.tsx";
}
if (pageIdOrComponent === userProfileFormFieldsValue) {
return "UserProfileFormFields.tsx";
}
return capitalize(kebabCaseToCamelCase(pageIdOrComponent)).replace(/ftl$/, "tsx");
})();
const pagesOrDot = (() => {
if (
pageIdOrComponent === templateValue ||
pageIdOrComponent === userProfileFormFieldsValue
) {
return ".";
}
return "pages";
})();
const targetFilePath = pathJoin(
buildContext.themeSrcDirPath,
themeType,
pagesOrDot,
componentBasename
);
if (fs.existsSync(targetFilePath)) {
console.log(
`${pageIdOrComponent} is already ejected, ${pathRelative(
process.cwd(),
targetFilePath
)} already exists`
);
process.exit(-1);
}
let componentCode = fs
.readFileSync(
pathJoin(
getThisCodebaseRootDirPath(),
"src",
themeType,
pagesOrDot,
componentBasename
)
)
.toString("utf8");
run_prettier: {
if (!(await getIsPrettierAvailable())) {
break run_prettier;
}
componentCode = await runPrettier({
filePath: targetFilePath,
sourceCode: componentCode
});
}
{
const targetDirPath = pathDirname(targetFilePath);
if (!fs.existsSync(targetDirPath)) {
fs.mkdirSync(targetDirPath, { recursive: true });
}
}
fs.writeFileSync(targetFilePath, Buffer.from(componentCode, "utf8"));
console.log(
`${chalk.green("✓")} ${chalk.bold(
pathJoin(".", pathRelative(process.cwd(), targetFilePath))
)} copy pasted from the Keycloakify source code into your project`
);
edit_KcPage: {
if (
pageIdOrComponent !== templateValue &&
pageIdOrComponent !== userProfileFormFieldsValue
) {
break edit_KcPage;
}
const kcAppTsxPath = pathJoin(
buildContext.themeSrcDirPath,
themeType,
"KcPage.tsx"
);
const kcAppTsxCode = fs.readFileSync(kcAppTsxPath).toString("utf8");
const modifiedKcAppTsxCode = (() => {
switch (pageIdOrComponent) {
case templateValue:
return kcAppTsxCode.replace(
`keycloakify/${themeType}/Template`,
"./Template"
);
case userProfileFormFieldsValue:
return kcAppTsxCode.replace(
`keycloakify/login/UserProfileFormFields`,
"./UserProfileFormFields"
);
}
assert<Equals<typeof pageIdOrComponent, never>>(false);
})();
if (kcAppTsxCode === modifiedKcAppTsxCode) {
console.log(
chalk.red(
"Unable to automatically update KcPage.tsx, please update it manually"
)
);
return;
}
fs.writeFileSync(kcAppTsxPath, Buffer.from(modifiedKcAppTsxCode, "utf8"));
console.log(
`${chalk.green("✓")} ${chalk.bold(
pathJoin(".", pathRelative(process.cwd(), kcAppTsxPath))
)} Updated`
);
return;
}
const userProfileFormFieldComponentName = "UserProfileFormFields";
const componentName = componentBasename.replace(/.tsx$/, "");
console.log(
[
``,
`You now need to update your page router:`,
``,
`${chalk.bold(
pathJoin(
".",
pathRelative(process.cwd(), buildContext.themeSrcDirPath),
themeType,
"KcPage.tsx"
)
)}:`,
chalk.grey("```"),
`// ...`,
``,
chalk.green(
`+const ${componentName} = lazy(() => import("./pages/${componentName}"));`
),
...[
``,
` export default function KcPage(props: { kcContext: KcContext; }) {`,
``,
` // ...`,
``,
` return (`,
` <Suspense>`,
` {(() => {`,
` switch (kcContext.pageId) {`,
` // ...`,
`+ case "${pageIdOrComponent}": return (`,
`+ <${componentName}`,
`+ {...{ kcContext, i18n, classes }}`,
`+ Template={Template}`,
`+ doUseDefaultCss={true}`,
...(!componentCode.includes(userProfileFormFieldComponentName)
? []
: [
`+ ${userProfileFormFieldComponentName}={${userProfileFormFieldComponentName}}`,
`+ doMakeUserConfirmPassword={doMakeUserConfirmPassword}`
]),
`+ />`,
`+ );`,
` default: return <Fallback /* .. */ />;`,
` }`,
` })()}`,
` </Suspense>`,
` );`,
` }`
].map(line => {
if (line.startsWith("+")) {
return chalk.green(line);
}
if (line.startsWith("-")) {
return chalk.red(line);
}
return chalk.grey(line);
}),
chalk.grey("```")
].join("\n")
);
}

View File

@ -1,87 +0,0 @@
import "minimal-polyfills/Object.fromEntries";
import * as fs from "fs";
import { join as pathJoin, relative as pathRelative, dirname as pathDirname } from "path";
import { crawl } from "./tools/crawl";
import { downloadBuiltinKeycloakTheme } from "./download-builtin-keycloak-theme";
import { getProjectRoot } from "./tools/getProjectRoot";
import { rm_rf, rm_r } from "./tools/rm";
import { getCliOptions } from "./tools/cliOptions";
import { getLogger } from "./tools/logger";
//NOTE: To run without argument when we want to generate src/i18n/generated_kcMessages files,
// update the version array for generating for newer version.
//@ts-ignore
const propertiesParser = require("properties-parser");
const { isSilent } = getCliOptions(process.argv.slice(2));
const logger = getLogger({ isSilent });
for (const keycloakVersion of ["11.0.3", "15.0.2", "18.0.1"]) {
logger.log(JSON.stringify({ keycloakVersion }));
const tmpDirPath = pathJoin(getProjectRoot(), "tmp_xImOef9dOd44");
rm_rf(tmpDirPath);
downloadBuiltinKeycloakTheme({
keycloakVersion,
"destDirPath": tmpDirPath,
isSilent
});
type Dictionary = { [idiomId: string]: string };
const record: { [typeOfPage: string]: { [language: string]: Dictionary } } = {};
{
const baseThemeDirPath = pathJoin(tmpDirPath, "base");
crawl(baseThemeDirPath).forEach(filePath => {
const match = filePath.match(/^([^/]+)\/messages\/messages_([^.]+)\.properties$/);
if (match === null) {
return;
}
const [, typeOfPage, language] = match;
(record[typeOfPage] ??= {})[language.replace(/_/g, "-")] = Object.fromEntries(
Object.entries(propertiesParser.parse(fs.readFileSync(pathJoin(baseThemeDirPath, filePath)).toString("utf8"))).map(
([key, value]: any) => [key, value.replace(/''/g, "'")]
)
);
});
}
rm_r(tmpDirPath);
Object.keys(record).forEach(pageType => {
const recordForPageType = record[pageType];
Object.keys(recordForPageType).forEach(language => {
const filePath = pathJoin(getProjectRoot(), "src", "lib", "i18n", "generated_messages", keycloakVersion, pageType, `${language}.ts`);
fs.mkdirSync(pathDirname(filePath), { "recursive": true });
fs.writeFileSync(
filePath,
Buffer.from(
[
`//This code was automatically generated by running ${pathRelative(getProjectRoot(), __filename)}`,
"//PLEASE DO NOT EDIT MANUALLY",
"",
"/* spell-checker: disable */",
`const messages= ${JSON.stringify(recordForPageType[language], null, 2)};`,
"",
"export default messages;",
"/* spell-checker: enable */"
].join("\n"),
"utf8"
)
);
logger.log(`${filePath} wrote`);
});
});
}

View File

@ -0,0 +1 @@
export * from "./initialize-account-theme";

View File

@ -0,0 +1,94 @@
import type { BuildContext } from "../shared/buildContext";
import cliSelect from "cli-select";
import chalk from "chalk";
import { join as pathJoin, relative as pathRelative } from "path";
import * as fs from "fs";
import { updateAccountThemeImplementationInConfig } from "./updateAccountThemeImplementationInConfig";
import { command as updateKcGenCommand } from "../update-kc-gen";
import { maybeDelegateCommandToCustomHandler } from "../shared/customHandler_delegate";
import { exitIfUncommittedChanges } from "../shared/exitIfUncommittedChanges";
import { getThisCodebaseRootDirPath } from "../tools/getThisCodebaseRootDirPath";
export async function command(params: { buildContext: BuildContext }) {
const { buildContext } = params;
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
commandName: "initialize-account-theme",
buildContext
});
if (hasBeenHandled) {
return;
}
const accountThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "account");
exitIfUncommittedChanges({
projectDirPath: buildContext.projectDirPath
});
const { value: accountThemeType } = await cliSelect({
values: ["Single-Page" as const, "Multi-Page" as const]
}).catch(() => {
process.exit(-1);
});
switch (accountThemeType) {
case "Multi-Page":
{
if (
fs.existsSync(accountThemeSrcDirPath) &&
fs.readdirSync(accountThemeSrcDirPath).length > 0
) {
console.warn(
chalk.red(
`There is already a ${pathRelative(
process.cwd(),
accountThemeSrcDirPath
)} directory in your project. Aborting.`
)
);
process.exit(-1);
}
fs.cpSync(
pathJoin(
getThisCodebaseRootDirPath(),
"src",
"bin",
"initialize-account-theme",
"multi-page-boilerplate"
),
accountThemeSrcDirPath,
{ recursive: true }
);
}
break;
case "Single-Page":
{
const { initializeSpa } = await import("../shared/initializeSpa");
await initializeSpa({
themeType: "account",
buildContext
});
}
break;
}
updateAccountThemeImplementationInConfig({ buildContext, accountThemeType });
await updateKcGenCommand({
buildContext: {
...buildContext,
implementedThemeTypes: {
...buildContext.implementedThemeTypes,
account: {
isImplemented: true,
type: accountThemeType
}
}
}
});
}

View File

@ -0,0 +1,12 @@
/* eslint-disable @typescript-eslint/ban-types */
import type { ExtendKcContext } from "keycloakify/account";
import type { KcEnvName, ThemeName } from "../kc.gen";
export type KcContextExtension = {
themeName: ThemeName;
properties: Record<KcEnvName, string> & {};
};
export type KcContextExtensionPerPage = {};
export type KcContext = ExtendKcContext<KcContextExtension, KcContextExtensionPerPage>;

View File

@ -0,0 +1,25 @@
import { Suspense } from "react";
import type { ClassKey } from "keycloakify/account";
import type { KcContext } from "./KcContext";
import { useI18n } from "./i18n";
import DefaultPage from "keycloakify/account/DefaultPage";
import Template from "keycloakify/account/Template";
export default function KcPage(props: { kcContext: KcContext }) {
const { kcContext } = props;
const { i18n } = useI18n({ kcContext });
return (
<Suspense>
{(() => {
switch (kcContext.pageId) {
default:
return <DefaultPage kcContext={kcContext} i18n={i18n} classes={classes} Template={Template} doUseDefaultCss={true} />;
}
})()}
</Suspense>
);
}
const classes = {} satisfies { [key in ClassKey]?: string };

View File

@ -0,0 +1,38 @@
import type { DeepPartial } from "keycloakify/tools/DeepPartial";
import type { KcContext } from "./KcContext";
import { createGetKcContextMock } from "keycloakify/account/KcContext";
import type { KcContextExtension, KcContextExtensionPerPage } from "./KcContext";
import KcPage from "./KcPage";
import { themeNames, kcEnvDefaults } from "../kc.gen";
const kcContextExtension: KcContextExtension = {
themeName: themeNames[0],
properties: {
...kcEnvDefaults
}
};
const kcContextExtensionPerPage: KcContextExtensionPerPage = {};
export const { getKcContextMock } = createGetKcContextMock({
kcContextExtension,
kcContextExtensionPerPage,
overrides: {},
overridesPerPage: {}
});
export function createKcPageStory<PageId extends KcContext["pageId"]>(params: { pageId: PageId }) {
const { pageId } = params;
function KcPageStory(props: { kcContext?: DeepPartial<Extract<KcContext, { pageId: PageId }>> }) {
const { kcContext: overrides } = props;
const kcContextMock = getKcContextMock({
pageId,
overrides
});
return <KcPage kcContext={kcContextMock} />;
}
return { KcPageStory };
}

View File

@ -0,0 +1,10 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { i18nBuilder } from "keycloakify/account";
import type { ThemeName } from "../kc.gen";
/** @see: https://docs.keycloakify.dev/features/i18n */
const { useI18n, ofTypeI18n } = i18nBuilder.withThemeName<ThemeName>().build();
type I18n = typeof ofTypeI18n;
export { useI18n, type I18n };

View File

@ -0,0 +1,101 @@
import { join as pathJoin } from "path";
import { assert, type Equals, is } from "tsafe/assert";
import type { BuildContext } from "../shared/buildContext";
import * as fs from "fs";
import chalk from "chalk";
import { z } from "zod";
import { id } from "tsafe/id";
export type BuildContextLike = {
bundler: BuildContext["bundler"];
projectDirPath: string;
packageJsonFilePath: string;
};
assert<BuildContext extends BuildContextLike ? true : false>();
export function updateAccountThemeImplementationInConfig(params: {
buildContext: BuildContextLike;
accountThemeType: "Single-Page" | "Multi-Page";
}) {
const { buildContext, accountThemeType } = params;
switch (buildContext.bundler) {
case "vite":
{
const viteConfigPath = pathJoin(
buildContext.projectDirPath,
"vite.config.ts"
);
if (!fs.existsSync(viteConfigPath)) {
console.log(
chalk.bold(
`You must manually set the accountThemeImplementation to "${accountThemeType}" in your vite config`
)
);
break;
}
const viteConfigContent = fs
.readFileSync(viteConfigPath)
.toString("utf8");
const modifiedViteConfigContent = viteConfigContent.replace(
/accountThemeImplementation\s*:\s*"none"/,
`accountThemeImplementation: "${accountThemeType}"`
);
if (modifiedViteConfigContent === viteConfigContent) {
console.log(
chalk.bold(
`You must manually set the accountThemeImplementation to "${accountThemeType}" in your vite.config.ts`
)
);
break;
}
fs.writeFileSync(viteConfigPath, modifiedViteConfigContent);
}
break;
case "webpack":
{
const parsedPackageJson = (() => {
type ParsedPackageJson = {
keycloakify: Record<string, unknown>;
};
const zParsedPackageJson = (() => {
type TargetType = ParsedPackageJson;
const zTargetType = z.object({
keycloakify: z.record(z.unknown())
});
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
return id<z.ZodType<TargetType>>(zTargetType);
})();
const parsedPackageJson = JSON.parse(
fs.readFileSync(buildContext.packageJsonFilePath).toString("utf8")
);
zParsedPackageJson.parse(parsedPackageJson);
assert(is<ParsedPackageJson>(parsedPackageJson));
return parsedPackageJson;
})();
parsedPackageJson.keycloakify.accountThemeImplementation =
accountThemeType;
fs.writeFileSync(
buildContext.packageJsonFilePath,
Buffer.from(JSON.stringify(parsedPackageJson, undefined, 4), "utf8")
);
}
break;
}
}

View File

@ -0,0 +1,39 @@
import type { BuildContext } from "./shared/buildContext";
import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
import { initializeSpa } from "./shared/initializeSpa";
import { exitIfUncommittedChanges } from "./shared/exitIfUncommittedChanges";
import { command as updateKcGenCommand } from "./update-kc-gen";
export async function command(params: { buildContext: BuildContext }) {
const { buildContext } = params;
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
commandName: "initialize-admin-theme",
buildContext
});
if (hasBeenHandled) {
return;
}
exitIfUncommittedChanges({
projectDirPath: buildContext.projectDirPath
});
await initializeSpa({
themeType: "admin",
buildContext
});
await updateKcGenCommand({
buildContext: {
...buildContext,
implementedThemeTypes: {
...buildContext.implementedThemeTypes,
admin: {
isImplemented: true
}
}
}
});
}

View File

@ -0,0 +1,156 @@
import type { BuildContext } from "./shared/buildContext";
import cliSelect from "cli-select";
import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate";
import { exitIfUncommittedChanges } from "./shared/exitIfUncommittedChanges";
import { dirname as pathDirname, join as pathJoin, relative as pathRelative } from "path";
import * as fs from "fs";
import { assert, is, type Equals } from "tsafe/assert";
import { id } from "tsafe/id";
import { addSyncExtensionsToPostinstallScript } from "./shared/addSyncExtensionsToPostinstallScript";
import { getIsPrettierAvailable, runPrettier } from "./tools/runPrettier";
import { npmInstall } from "./tools/npmInstall";
import * as child_process from "child_process";
import { z } from "zod";
import chalk from "chalk";
export async function command(params: { buildContext: BuildContext }) {
const { buildContext } = params;
const { hasBeenHandled } = await maybeDelegateCommandToCustomHandler({
commandName: "initialize-email-theme",
buildContext
});
if (hasBeenHandled) {
return;
}
exitIfUncommittedChanges({
projectDirPath: buildContext.projectDirPath
});
const emailThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "email");
if (
fs.existsSync(emailThemeSrcDirPath) &&
fs.readdirSync(emailThemeSrcDirPath).length > 0
) {
console.warn(
chalk.red(
`There is already a ${pathRelative(
process.cwd(),
emailThemeSrcDirPath
)} directory in your project. Aborting.`
)
);
process.exit(-1);
}
const { value: emailThemeType } = await cliSelect({
values: [
"native (FreeMarker)" as const,
"Another email templating solution" as const
]
}).catch(() => {
process.exit(-1);
});
if (emailThemeType === "Another email templating solution") {
console.log(
[
"There is currently no automated support for keycloakify-email, it has to be done manually, see documentation:",
"https://docs.keycloakify.dev/theme-types/email-theme"
].join("\n")
);
process.exit(0);
}
const parsedPackageJson = (() => {
type ParsedPackageJson = {
scripts?: Record<string, string | undefined>;
dependencies?: Record<string, string | undefined>;
devDependencies?: Record<string, string | undefined>;
};
const zParsedPackageJson = (() => {
type TargetType = ParsedPackageJson;
const zTargetType = z.object({
scripts: z.record(z.union([z.string(), z.undefined()])).optional(),
dependencies: z.record(z.union([z.string(), z.undefined()])).optional(),
devDependencies: z.record(z.union([z.string(), z.undefined()])).optional()
});
assert<Equals<z.infer<typeof zTargetType>, TargetType>>;
return id<z.ZodType<TargetType>>(zTargetType);
})();
const parsedPackageJson = JSON.parse(
fs.readFileSync(buildContext.packageJsonFilePath).toString("utf8")
);
zParsedPackageJson.parse(parsedPackageJson);
assert(is<ParsedPackageJson>(parsedPackageJson));
return parsedPackageJson;
})();
addSyncExtensionsToPostinstallScript({
parsedPackageJson,
buildContext
});
const moduleName = `@keycloakify/email-native`;
const [version] = ((): string[] => {
const cmdOutput = child_process
.execSync(`npm show ${moduleName} versions --json`)
.toString("utf8")
.trim();
const versions = JSON.parse(cmdOutput) as string | string[];
// NOTE: Bug in some older npm versions
if (typeof versions === "string") {
return [versions];
}
return versions;
})()
.reverse()
.filter(version => !version.includes("-"));
assert(version !== undefined);
(parsedPackageJson.dependencies ??= {})[moduleName] = `~${version}`;
if (parsedPackageJson.devDependencies !== undefined) {
delete parsedPackageJson.devDependencies[moduleName];
}
{
let sourceCode = JSON.stringify(parsedPackageJson, undefined, 2);
if (await getIsPrettierAvailable()) {
sourceCode = await runPrettier({
sourceCode,
filePath: buildContext.packageJsonFilePath
});
}
fs.writeFileSync(
buildContext.packageJsonFilePath,
Buffer.from(sourceCode, "utf8")
);
}
await npmInstall({
packageJsonDirPath: pathDirname(buildContext.packageJsonFilePath)
});
console.log(chalk.green("Email theme initialized."));
}

View File

@ -1,180 +0,0 @@
import { z } from "zod";
import { assert } from "tsafe/assert";
import type { Equals } from "tsafe";
import { id } from "tsafe/id";
import { parse as urlParse } from "url";
type ParsedPackageJson = {
name: string;
version: string;
homepage?: string;
keycloakify?: {
extraPages?: string[];
extraThemeProperties?: string[];
areAppAndKeycloakServerSharingSameDomain?: boolean;
};
};
const zParsedPackageJson = z.object({
"name": z.string(),
"version": z.string(),
"homepage": z.string().optional(),
"keycloakify": z
.object({
"extraPages": z.array(z.string()).optional(),
"extraThemeProperties": z.array(z.string()).optional(),
"areAppAndKeycloakServerSharingSameDomain": z.boolean().optional()
})
.optional()
});
assert<Equals<ReturnType<typeof zParsedPackageJson["parse"]>, ParsedPackageJson>>();
/** Consolidated build option gathered form CLI arguments and config in package.json */
export type BuildOptions = BuildOptions.Standalone | BuildOptions.ExternalAssets;
export namespace BuildOptions {
export type Common = {
isSilent: boolean;
version: string;
themeName: string;
extraPages?: string[];
extraThemeProperties?: string[];
//NOTE: Only for the pom.xml file, questionable utility...
groupId: string;
};
export type Standalone = Common & {
isStandalone: true;
urlPathname: string | undefined;
};
export type ExternalAssets = ExternalAssets.SameDomain | ExternalAssets.DifferentDomains;
export namespace ExternalAssets {
export type CommonExternalAssets = Common & {
isStandalone: false;
};
export type SameDomain = CommonExternalAssets & {
areAppAndKeycloakServerSharingSameDomain: true;
};
export type DifferentDomains = CommonExternalAssets & {
areAppAndKeycloakServerSharingSameDomain: false;
urlOrigin: string;
urlPathname: string | undefined;
};
}
}
export function readBuildOptions(params: {
packageJson: string;
CNAME: string | undefined;
isExternalAssetsCliParamProvided: boolean;
isSilent: boolean;
}): BuildOptions {
const { packageJson, CNAME, isExternalAssetsCliParamProvided, isSilent } = params;
const parsedPackageJson = zParsedPackageJson.parse(JSON.parse(packageJson));
const url = (() => {
const { homepage } = parsedPackageJson;
let url: URL | undefined = undefined;
if (homepage !== undefined) {
url = new URL(homepage);
}
if (CNAME !== undefined) {
url = new URL(`https://${CNAME.replace(/\s+$/, "")}`);
}
if (url === undefined) {
return undefined;
}
return {
"origin": url.origin,
"pathname": (() => {
const out = url.pathname.replace(/([^/])$/, "$1/");
return out === "/" ? undefined : out;
})()
};
})();
const common: BuildOptions.Common = (() => {
const { name, keycloakify = {}, version, homepage } = parsedPackageJson;
const { extraPages, extraThemeProperties } = keycloakify ?? {};
const themeName = name
.replace(/^@(.*)/, "$1")
.split("/")
.join("-");
return {
themeName,
"groupId": (() => {
const fallbackGroupId = `${themeName}.keycloak`;
return (
(!homepage
? fallbackGroupId
: urlParse(homepage)
.host?.replace(/:[0-9]+$/, "")
?.split(".")
.reverse()
.join(".") ?? fallbackGroupId) + ".keycloak"
);
})(),
"version": version,
extraPages,
extraThemeProperties,
isSilent
};
})();
if (isExternalAssetsCliParamProvided) {
const commonExternalAssets = id<BuildOptions.ExternalAssets.CommonExternalAssets>({
...common,
"isStandalone": false
});
if (parsedPackageJson.keycloakify?.areAppAndKeycloakServerSharingSameDomain) {
return id<BuildOptions.ExternalAssets.SameDomain>({
...commonExternalAssets,
"areAppAndKeycloakServerSharingSameDomain": true
});
} else {
assert(
url !== undefined,
[
"Can't compile in external assets mode if we don't know where",
"the app will be hosted.",
"You should provide a homepage field in the package.json (or create a",
"public/CNAME file.",
"Alternatively, if your app and the Keycloak server are on the same domain, ",
"eg https://example.com is your app and https://example.com/auth is the keycloak",
'admin UI, you can set "keycloakify": { "areAppAndKeycloakServerSharingSameDomain": true }',
"in your package.json"
].join(" ")
);
return id<BuildOptions.ExternalAssets.DifferentDomains>({
...commonExternalAssets,
"areAppAndKeycloakServerSharingSameDomain": false,
"urlOrigin": url.origin,
"urlPathname": url.pathname
});
}
}
return id<BuildOptions.Standalone>({
...common,
"isStandalone": true,
"urlPathname": url?.pathname
});
}

View File

@ -0,0 +1,265 @@
import { assert, type Equals } from "tsafe/assert";
import type {
KeycloakAccountV1Version,
KeycloakThemeAdditionalInfoExtensionVersion
} from "./extensionVersions";
import { join as pathJoin, dirname as pathDirname } from "path";
import { transformCodebase } from "../../tools/transformCodebase";
import type { BuildContext } from "../../shared/buildContext";
import * as fs from "fs/promises";
import {
generatePom,
BuildContextLike as BuildContextLike_generatePom
} from "./generatePom";
import { readFileSync } from "fs";
import { isInside } from "../../tools/isInside";
import child_process from "child_process";
import { rmSync } from "../../tools/fs.rmSync";
import { existsAsync } from "../../tools/fs.existsAsync";
export type BuildContextLike = BuildContextLike_generatePom & {
keycloakifyBuildDirPath: string;
themeNames: string[];
artifactId: string;
themeVersion: string;
cacheDirPath: string;
implementedThemeTypes: BuildContext["implementedThemeTypes"];
};
assert<BuildContext extends BuildContextLike ? true : false>();
export async function buildJar(params: {
jarFileBasename: string;
keycloakAccountV1Version: KeycloakAccountV1Version;
keycloakThemeAdditionalInfoExtensionVersion: KeycloakThemeAdditionalInfoExtensionVersion;
resourcesDirPath: string;
doesImplementAccountV1Theme: boolean;
buildContext: BuildContextLike;
}): Promise<void> {
const {
jarFileBasename,
keycloakAccountV1Version,
keycloakThemeAdditionalInfoExtensionVersion,
resourcesDirPath,
doesImplementAccountV1Theme,
buildContext
} = params;
const keycloakifyBuildCacheDirPath = pathJoin(
buildContext.cacheDirPath,
"maven",
jarFileBasename.replace(".jar", "")
);
const tmpResourcesDirPath = pathJoin(
keycloakifyBuildCacheDirPath,
"src",
"main",
"resources"
);
rmSync(tmpResourcesDirPath, { recursive: true, force: true });
transformCodebase({
srcDirPath: resourcesDirPath,
destDirPath: tmpResourcesDirPath,
transformSourceCode:
!doesImplementAccountV1Theme || keycloakAccountV1Version !== null
? undefined
: (params: {
fileRelativePath: string;
sourceCode: Buffer;
}): { modifiedSourceCode: Buffer } | undefined => {
const { fileRelativePath, sourceCode } = params;
if (
isInside({
dirPath: pathJoin("theme", "account-v1"),
filePath: fileRelativePath
})
) {
return undefined;
}
for (const themeName of buildContext.themeNames) {
if (
fileRelativePath ===
pathJoin("theme", themeName, "account", "theme.properties")
) {
const modifiedSourceCode = Buffer.from(
sourceCode
.toString("utf8")
.replace(`parent=account-v1`, "parent=keycloak"),
"utf8"
);
assert(
Buffer.compare(modifiedSourceCode, sourceCode) !== 0
);
return { modifiedSourceCode };
}
}
return { modifiedSourceCode: sourceCode };
}
});
{
const filePath = pathJoin(
tmpResourcesDirPath,
"META-INF",
"keycloak-themes.json"
);
await fs.mkdir(pathDirname(filePath));
await fs.writeFile(
filePath,
Buffer.from(
JSON.stringify(
{
themes: await (async () => {
const dirPath = pathJoin(tmpResourcesDirPath, "theme");
const themeNames = (await fs.readdir(dirPath)).sort(
(a, b) => {
const indexA = buildContext.themeNames.indexOf(a);
const indexB = buildContext.themeNames.indexOf(b);
const orderA = indexA === -1 ? Infinity : indexA;
const orderB = indexB === -1 ? Infinity : indexB;
return orderA - orderB;
}
);
return Promise.all(
themeNames.map(async themeName => {
const types = await fs.readdir(
pathJoin(dirPath, themeName)
);
return {
name: themeName,
types
};
})
);
})()
},
null,
2
),
"utf8"
)
);
}
route_legacy_pages: {
if (!buildContext.implementedThemeTypes.login.isImplemented) {
break route_legacy_pages;
}
await Promise.all(
(["register.ftl", "login-update-profile.ftl"] as const)
.map(pageId =>
buildContext.themeNames.map(async themeName => {
const ftlFilePath = pathJoin(
tmpResourcesDirPath,
"theme",
themeName,
"login",
pageId
);
// NOTE: https://github.com/keycloakify/keycloakify/issues/665
if (!(await existsAsync(ftlFilePath))) {
return;
}
const ftlFileContent = readFileSync(ftlFilePath).toString("utf8");
const ftlFileBasename = (() => {
switch (pageId) {
case "register.ftl":
return "register-user-profile.ftl";
case "login-update-profile.ftl":
return "update-user-profile.ftl";
}
assert<Equals<typeof pageId, never>>(false);
})();
const modifiedFtlFileContent = ftlFileContent.replace(
`"ftlTemplateFileName": "${pageId}"`,
`"ftlTemplateFileName": "${ftlFileBasename}"`
);
assert(modifiedFtlFileContent !== ftlFileContent);
await fs.writeFile(
pathJoin(pathDirname(ftlFilePath), ftlFileBasename),
Buffer.from(modifiedFtlFileContent, "utf8")
);
})
)
.flat()
);
}
{
const { pomFileCode } = generatePom({
buildContext,
keycloakAccountV1Version,
keycloakThemeAdditionalInfoExtensionVersion
});
await fs.writeFile(
pathJoin(keycloakifyBuildCacheDirPath, "pom.xml"),
Buffer.from(pomFileCode, "utf8")
);
}
{
const mvnBuildCmd = `mvn clean install -Dmaven.repo.local="${pathJoin(keycloakifyBuildCacheDirPath, ".m2")}"`;
await new Promise<void>((resolve, reject) =>
child_process.exec(
mvnBuildCmd,
{ cwd: keycloakifyBuildCacheDirPath },
error => {
if (error !== null) {
console.error(
[
`Build jar failed: ${JSON.stringify(
{
jarFileBasename,
keycloakAccountV1Version,
keycloakThemeAdditionalInfoExtensionVersion
},
null,
2
)}`,
"Try running the following command to debug the issue (you are probably under a restricted network and you need to configure your proxy):",
`cd ${keycloakifyBuildCacheDirPath} && ${mvnBuildCmd}`
].join("\n")
);
reject(error);
return;
}
resolve();
}
)
);
}
await fs.rename(
pathJoin(
keycloakifyBuildCacheDirPath,
"target",
`${buildContext.artifactId}-${buildContext.themeVersion}.jar`
),
pathJoin(buildContext.keycloakifyBuildDirPath, jarFileBasename)
);
}

View File

@ -0,0 +1,68 @@
import { assert } from "tsafe/assert";
import {
keycloakAccountV1Versions,
keycloakThemeAdditionalInfoExtensionVersions
} from "./extensionVersions";
import { getKeycloakVersionRangeForJar } from "./getKeycloakVersionRangeForJar";
import { buildJar, BuildContextLike as BuildContextLike_buildJar } from "./buildJar";
import type { BuildContext } from "../../shared/buildContext";
export type BuildContextLike = BuildContextLike_buildJar & {
projectDirPath: string;
keycloakifyBuildDirPath: string;
implementedThemeTypes: BuildContext["implementedThemeTypes"];
jarTargets: BuildContext["jarTargets"];
};
assert<BuildContext extends BuildContextLike ? true : false>();
export async function buildJars(params: {
resourcesDirPath: string;
buildContext: BuildContextLike;
}): Promise<void> {
const { resourcesDirPath, buildContext } = params;
const doesImplementAccountV1Theme =
buildContext.implementedThemeTypes.account.isImplemented &&
buildContext.implementedThemeTypes.account.type === "Multi-Page";
await Promise.all(
keycloakAccountV1Versions
.map(keycloakAccountV1Version =>
keycloakThemeAdditionalInfoExtensionVersions.map(
keycloakThemeAdditionalInfoExtensionVersion => {
const keycloakVersionRange = getKeycloakVersionRangeForJar({
doesImplementAccountV1Theme,
keycloakAccountV1Version,
keycloakThemeAdditionalInfoExtensionVersion
});
if (keycloakVersionRange === undefined) {
return undefined;
}
const jarTarget = buildContext.jarTargets.find(
jarTarget =>
jarTarget.keycloakVersionRange === keycloakVersionRange
);
if (jarTarget === undefined) {
return undefined;
}
const { jarFileBasename } = jarTarget;
return buildJar({
jarFileBasename,
keycloakAccountV1Version,
keycloakThemeAdditionalInfoExtensionVersion,
resourcesDirPath,
doesImplementAccountV1Theme,
buildContext
});
}
)
)
.flat()
);
}

View File

@ -0,0 +1,17 @@
// NOTE: v0.5 is a dummy version.
export const keycloakAccountV1Versions = [null, "0.3", "0.4", "0.6"] as const;
/**
* https://central.sonatype.com/artifact/io.phasetwo.keycloak/keycloak-account-v1
* https://github.com/p2-inc/keycloak-account-v1
*/
export type KeycloakAccountV1Version = (typeof keycloakAccountV1Versions)[number];
export const keycloakThemeAdditionalInfoExtensionVersions = [null, "1.1.5"] as const;
/**
* https://central.sonatype.com/artifact/dev.jcputney/keycloak-theme-additional-info-extension
* https://github.com/jcputney/keycloak-theme-additional-info-extension
* */
export type KeycloakThemeAdditionalInfoExtensionVersion =
(typeof keycloakThemeAdditionalInfoExtensionVersions)[number];

View File

@ -0,0 +1,94 @@
import { assert } from "tsafe/assert";
import type { BuildContext } from "../../shared/buildContext";
import type {
KeycloakAccountV1Version,
KeycloakThemeAdditionalInfoExtensionVersion
} from "./extensionVersions";
export type BuildContextLike = {
groupId: string;
artifactId: string;
themeVersion: string;
};
assert<BuildContext extends BuildContextLike ? true : false>();
export function generatePom(params: {
keycloakAccountV1Version: KeycloakAccountV1Version;
keycloakThemeAdditionalInfoExtensionVersion: KeycloakThemeAdditionalInfoExtensionVersion;
buildContext: BuildContextLike;
}) {
const {
keycloakAccountV1Version,
keycloakThemeAdditionalInfoExtensionVersion,
buildContext
} = params;
const { pomFileCode } = (function generatePomFileCode(): {
pomFileCode: string;
} {
const pomFileCode = [
`<?xml version="1.0"?>`,
`<project xmlns="http://maven.apache.org/POM/4.0.0"`,
` xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"`,
` xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">`,
` <modelVersion>4.0.0</modelVersion>`,
` <groupId>${buildContext.groupId}</groupId>`,
` <artifactId>${buildContext.artifactId}</artifactId>`,
` <version>${buildContext.themeVersion}</version>`,
` <name>${buildContext.artifactId}</name>`,
` <description />`,
` <packaging>jar</packaging>`,
` <properties>`,
` <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>`,
` </properties>`,
...(keycloakAccountV1Version !== null ||
keycloakThemeAdditionalInfoExtensionVersion !== null
? [
` <build>`,
` <plugins>`,
` <plugin>`,
` <groupId>org.apache.maven.plugins</groupId>`,
` <artifactId>maven-shade-plugin</artifactId>`,
` <version>3.5.1</version>`,
` <executions>`,
` <execution>`,
` <phase>package</phase>`,
` <goals>`,
` <goal>shade</goal>`,
` </goals>`,
` </execution>`,
` </executions>`,
` </plugin>`,
` </plugins>`,
` </build>`,
` <dependencies>`,
...(keycloakAccountV1Version !== null
? [
` <dependency>`,
` <groupId>io.phasetwo.keycloak</groupId>`,
` <artifactId>keycloak-account-v1</artifactId>`,
` <version>${keycloakAccountV1Version}</version>`,
` </dependency>`
]
: []),
...(keycloakThemeAdditionalInfoExtensionVersion !== null
? [
` <dependency>`,
` <groupId>dev.jcputney</groupId>`,
` <artifactId>keycloak-theme-additional-info-extension</artifactId>`,
` <version>${keycloakThemeAdditionalInfoExtensionVersion}</version>`,
` </dependency>`
]
: []),
` </dependencies>`
]
: []),
`</project>`
].join("\n");
return { pomFileCode };
})();
return { pomFileCode };
}

View File

@ -0,0 +1,96 @@
import { assert, type Equals } from "tsafe/assert";
import type {
KeycloakAccountV1Version,
KeycloakThemeAdditionalInfoExtensionVersion
} from "./extensionVersions";
import type { KeycloakVersionRange } from "../../shared/KeycloakVersionRange";
export function getKeycloakVersionRangeForJar(params: {
doesImplementAccountV1Theme: boolean;
keycloakAccountV1Version: KeycloakAccountV1Version;
keycloakThemeAdditionalInfoExtensionVersion: KeycloakThemeAdditionalInfoExtensionVersion;
}): KeycloakVersionRange | undefined {
const {
keycloakAccountV1Version,
keycloakThemeAdditionalInfoExtensionVersion,
doesImplementAccountV1Theme
} = params;
if (doesImplementAccountV1Theme) {
const keycloakVersionRange = (() => {
switch (keycloakAccountV1Version) {
case null:
switch (keycloakThemeAdditionalInfoExtensionVersion) {
case null:
return "21-and-below" as const;
case "1.1.5":
return undefined;
}
assert<
Equals<typeof keycloakThemeAdditionalInfoExtensionVersion, never>
>(false);
case "0.3":
switch (keycloakThemeAdditionalInfoExtensionVersion) {
case null:
return undefined;
case "1.1.5":
return "23" as const;
}
assert<
Equals<typeof keycloakThemeAdditionalInfoExtensionVersion, never>
>(false);
case "0.4":
switch (keycloakThemeAdditionalInfoExtensionVersion) {
case null:
return undefined;
case "1.1.5":
return "24" as const;
}
assert<
Equals<typeof keycloakThemeAdditionalInfoExtensionVersion, never>
>(false);
case "0.6":
switch (keycloakThemeAdditionalInfoExtensionVersion) {
case null:
return "26-and-above" as const;
case "1.1.5":
return "25" as const;
}
}
assert<Equals<typeof keycloakAccountV1Version, never>>(false);
})();
assert<
Equals<
typeof keycloakVersionRange,
KeycloakVersionRange.WithAccountV1Theme | undefined
>
>();
return keycloakVersionRange;
} else {
const keycloakVersionRange = (() => {
if (keycloakAccountV1Version !== null) {
return undefined;
}
switch (keycloakThemeAdditionalInfoExtensionVersion) {
case null:
return "all-other-versions";
case "1.1.5":
return "22-to-25";
}
assert<Equals<typeof keycloakThemeAdditionalInfoExtensionVersion, never>>(
false
);
})();
assert<
Equals<
typeof keycloakVersionRange,
KeycloakVersionRange.WithoutAccountV1Theme | undefined
>
>();
return keycloakVersionRange;
}
}

View File

@ -0,0 +1 @@
export * from "./buildJars";

View File

@ -1 +0,0 @@
export const ftlValuesGlobalName = "kcContext";

View File

@ -1,350 +0,0 @@
<script>const _=
<#assign pageId="PAGE_ID_xIgLsPgGId9D8e">
(()=>{
const out =
${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
out["msg"]= function(){ throw new Error("use import { useKcMessage } from 'keycloakify'"); };
out["advancedMsg"]= function(){ throw new Error("use import { useKcMessage } from 'keycloakify'"); };
out["messagesPerField"]= {
<#assign fieldNames = [
"global", "userLabel", "username", "email", "firstName", "lastName", "password", "password-confirm",
"totp", "totpSecret", "SAMLRequest", "SAMLResponse", "relayState", "device_user_code", "code",
"password-new", "rememberMe", "login", "authenticationExecution", "cancel-aia", "clientDataJSON",
"authenticatorData", "signature", "credentialId", "userHandle", "error", "authn_use_chk", "authenticationExecution",
"isSetRetry", "try-again", "attestationObject", "publicKeyCredentialId", "authenticatorLabel"
]>
<#attempt>
<#if profile?? && profile.attributes?? && profile.attributes?is_enumerable>
<#list profile.attributes as attribute>
<#if fieldNames?seq_contains(attribute.name)>
<#continue>
</#if>
<#assign fieldNames += [attribute.name]>
</#list>
</#if>
<#recover>
</#attempt>
"printIfExists": function (fieldName, x) {
<#if !messagesPerField?? >
return undefined;
<#else>
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
return "${messagesPerField.printIfExists(fieldName,'1')}" ? x : undefined;
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
</#if>
},
"existsError": function (fieldName) {
<#if !messagesPerField?? >
return false;
<#else>
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
return <#if messagesPerField.existsError('${fieldName}')>true<#else>false</#if>;
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
</#if>
},
"get": function (fieldName) {
<#if !messagesPerField?? >
return '';
<#else>
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
<#if messagesPerField.existsError('${fieldName}')>
return "${messagesPerField.get('${fieldName}')?no_esc}";
</#if>
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
</#if>
},
"exists": function (fieldName) {
<#if !messagesPerField?? >
return false;
<#else>
<#list fieldNames as fieldName>
if(fieldName === "${fieldName}" ){
<#attempt>
return <#if messagesPerField.exists('${fieldName}')>true<#else>false</#if>;
<#recover>
</#attempt>
}
</#list>
throw new Error("There is no " + fieldName + " field");
</#if>
}
};
out["pageId"] = "${pageId}";
return out;
})()
<#function ftl_object_to_js_code_declaring_an_object object path>
<#local isHash = "">
<#attempt>
<#local isHash = object?is_hash || object?is_hash_ex>
<#recover>
<#return "ABORT: Can't evaluate if " + path?join(".") + " is hash">
</#attempt>
<#if isHash>
<#if path?size gt 10>
<#return "ABORT: Too many recursive calls">
</#if>
<#local keys = "">
<#attempt>
<#local keys = object?keys>
<#recover>
<#return "ABORT: We can't list keys on this object">
</#attempt>
<#local out_seq = []>
<#list keys as key>
<#if ["class","declaredConstructors","superclass","declaringClass" ]?seq_contains(key) >
<#continue>
</#if>
<#if
(
["loginUpdatePasswordUrl", "loginUpdateProfileUrl", "loginUsernameReminderUrl", "loginUpdateTotpUrl"]?seq_contains(key) &&
are_same_path(path, ["url"])
) || (
key == "updateProfileCtx" &&
are_same_path(path, [])
) || (
<#-- https://github.com/InseeFrLab/keycloakify/pull/65#issuecomment-991896344 (reports with saml-post-form.ftl) -->
<#-- https://github.com/InseeFrLab/keycloakify/issues/91#issue-1212319466 (reports with error.ftl and Kc18) -->
<#-- https://github.com/InseeFrLab/keycloakify/issues/109#issuecomment-1134610163 -->
key == "loginAction" &&
are_same_path(path, ["url"]) &&
["saml-post-form.ftl", "error.ftl", "info.ftl"]?seq_contains(pageId) &&
!(auth?has_content && auth.showTryAnotherWayLink())
) || (
["contextData", "idpConfig", "idp", "authenticationSession"]?seq_contains(key) &&
are_same_path(path, ["brokerContext"]) &&
["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(pageId)
) || (
key == "identityProviderBrokerCtx" &&
are_same_path(path, []) &&
["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(pageId)
) || (
["masterAdminClient", "delegateForUpdate", "defaultRole"]?seq_contains(key) &&
are_same_path(path, ["realm"])
)
>
<#local out_seq += ["/*If you need '" + key + "' on " + pageId + ", please submit an issue to the Keycloakify repo*/"]>
<#continue>
</#if>
<#if key == "attemptedUsername" && are_same_path(path, ["auth"])>
<#attempt>
<#-- https://github.com/keycloak/keycloak/blob/3a2bf0c04bcde185e497aaa32d0bb7ab7520cf4a/themes/src/main/resources/theme/base/login/template.ftl#L63 -->
<#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())>
<#continue>
</#if>
<#recover>
</#attempt>
</#if>
<#attempt>
<#if !object[key]??>
<#continue>
</#if>
<#recover>
<#local out_seq += ["/*Couldn't test if '" + key + "' is available on this object*/"]>
<#continue>
</#attempt>
<#local propertyValue = "">
<#attempt>
<#local propertyValue = object[key]>
<#recover>
<#local out_seq += ["/*Couldn't dereference '" + key + "' on this object*/"]>
<#continue>
</#attempt>
<#local rec_out = ftl_object_to_js_code_declaring_an_object(propertyValue, path + [ key ])>
<#if rec_out?starts_with("ABORT:")>
<#local errorMessage = rec_out?remove_beginning("ABORT:")>
<#if errorMessage != " It's a method" >
<#local out_seq += ["/*" + key + ": " + errorMessage + "*/"]>
</#if>
<#continue>
</#if>
<#local out_seq += ['"' + key + '": ' + rec_out + ","]>
</#list>
<#return (["{"] + out_seq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "}"])?join("\n")>
</#if>
<#local isMethod = "">
<#attempt>
<#local isMethod = object?is_method>
<#recover>
<#return "ABORT: Can't test if it'sa method.">
</#attempt>
<#if isMethod>
<#if are_same_path(path, ["auth", "showUsername"])>
<#attempt>
<#return auth.showUsername()?c>
<#recover>
<#return "ABORT: Couldn't evaluate auth.showUsername()">
</#attempt>
</#if>
<#if are_same_path(path, ["auth", "showResetCredentials"])>
<#attempt>
<#return auth.showResetCredentials()?c>
<#recover>
<#return "ABORT: Couldn't evaluate auth.showResetCredentials()">
</#attempt>
</#if>
<#if are_same_path(path, ["auth", "showTryAnotherWayLink"])>
<#attempt>
<#return auth.showTryAnotherWayLink()?c>
<#recover>
<#return "ABORT: Couldn't evaluate auth.showTryAnotherWayLink()">
</#attempt>
</#if>
<#return "ABORT: It's a method">
</#if>
<#local isBoolean = "">
<#attempt>
<#local isBoolean = object?is_boolean>
<#recover>
<#return "ABORT: Can't test if it's a boolean">
</#attempt>
<#if isBoolean>
<#return object?c>
</#if>
<#local isEnumerable = "">
<#attempt>
<#local isEnumerable = object?is_enumerable>
<#recover>
<#return "ABORT: Can't test if it's an enumerable">
</#attempt>
<#if isEnumerable>
<#local out_seq = []>
<#local i = 0>
<#list object as array_item>
<#if !array_item??>
<#local out_seq += ["null,"]>
<#continue>
</#if>
<#local rec_out = ftl_object_to_js_code_declaring_an_object(array_item, path + [ i ])>
<#local i = i + 1>
<#if rec_out?starts_with("ABORT:")>
<#local errorMessage = rec_out?remove_beginning("ABORT:")>
<#if errorMessage != " It's a method" >
<#local out_seq += ["/*" + i?string + ": " + errorMessage + "*/"]>
</#if>
<#continue>
</#if>
<#local out_seq += [rec_out + ","]>
</#list>
<#return (["["] + out_seq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "]"])?join("\n")>
</#if>
<#attempt>
<#return '"' + object?js_string + '"'>;
<#recover>
</#attempt>
<#return "ABORT: Couldn't convert into string non hash, non method, non boolean, non enumerable object">
</#function>
<#function are_same_path path searchedPath>
<#if path?size != searchedPath?size>
<#return false>
</#if>
<#local i=0>
<#list path as property>
<#local searchedProperty=searchedPath[i]>
<#if searchedProperty?is_string && searchedProperty == "*">
<#continue>
</#if>
<#if searchedProperty?is_string && !property?is_string>
<#return false>
</#if>
<#if searchedProperty?is_number && !property?is_number>
<#return false>
</#if>
<#if searchedProperty?string != property?string>
<#return false>
</#if>
<#local i+= 1>
</#list>
<#return true>
</#function>
</script>

View File

@ -1,103 +1,74 @@
import cheerio from "cheerio";
import { replaceImportsFromStaticInJsCode } from "../replacers/replaceImportsFromStaticInJsCode";
import { generateCssCodeToDefineGlobals } from "../replacers/replaceImportsInCssCode";
import { replaceImportsInInlineCssCode } from "../replacers/replaceImportsInInlineCssCode";
import * as cheerio from "cheerio";
import {
replaceImportsInJsCode,
BuildContextLike as BuildContextLike_replaceImportsInJsCode
} from "../replacers/replaceImportsInJsCode";
import {
replaceImportsInCssCode,
BuildContextLike as BuildContextLike_replaceImportsInCssCode
} from "../replacers/replaceImportsInCssCode";
import * as fs from "fs";
import { join as pathJoin } from "path";
import { objectKeys } from "tsafe/objectKeys";
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
import type { BuildOptions } from "../BuildOptions";
import type { BuildContext } from "../../shared/buildContext";
import { assert } from "tsafe/assert";
import { Reflect } from "tsafe/Reflect";
import {
type ThemeType,
WELL_KNOWN_DIRECTORY_BASE_NAME,
KEYCLOAKIFY_SPA_DEV_SERVER_PORT
} from "../../shared/constants";
import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
// https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java
export const pageIds = [
"login.ftl",
"login-username.ftl",
"login-password.ftl",
"webauthn-authenticate.ftl",
"register.ftl",
"register-user-profile.ftl",
"info.ftl",
"error.ftl",
"login-reset-password.ftl",
"login-verify-email.ftl",
"terms.ftl",
"login-otp.ftl",
"login-update-profile.ftl",
"login-update-password.ftl",
"login-idp-link-confirm.ftl",
"login-idp-link-email.ftl",
"login-page-expired.ftl",
"login-config-totp.ftl",
"logout-confirm.ftl",
"update-user-profile.ftl",
"idp-review-user-profile.ftl"
] as const;
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
export namespace BuildOptionsLike {
export type Standalone = {
isStandalone: true;
export type BuildContextLike = BuildContextLike_replaceImportsInJsCode &
BuildContextLike_replaceImportsInCssCode & {
urlPathname: string | undefined;
themeVersion: string;
kcContextExclusionsFtlCode: string | undefined;
};
export type ExternalAssets = ExternalAssets.SameDomain | ExternalAssets.DifferentDomains;
export namespace ExternalAssets {
export type CommonExternalAssets = {
isStandalone: false;
};
export type SameDomain = CommonExternalAssets & {
areAppAndKeycloakServerSharingSameDomain: true;
};
export type DifferentDomains = CommonExternalAssets & {
areAppAndKeycloakServerSharingSameDomain: false;
urlOrigin: string;
urlPathname: string | undefined;
};
}
}
{
const buildOptions = Reflect<BuildOptions>();
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
}
export type PageId = typeof pageIds[number];
assert<BuildContext extends BuildContextLike ? true : false>();
export function generateFtlFilesCodeFactory(params: {
themeName: string;
indexHtmlCode: string;
//NOTE: Expected to be an empty object if external assets mode is enabled.
cssGlobalsToDefine: Record<string, string>;
buildOptions: BuildOptionsLike;
buildContext: BuildContextLike;
keycloakifyVersion: string;
themeType: ThemeType;
fieldNames: string[];
}) {
const { cssGlobalsToDefine, indexHtmlCode, buildOptions } = params;
const {
themeName,
indexHtmlCode,
buildContext,
keycloakifyVersion,
themeType,
fieldNames
} = params;
const $ = cheerio.load(indexHtmlCode);
fix_imports_statements: {
if (!buildOptions.isStandalone && buildOptions.areAppAndKeycloakServerSharingSameDomain) {
break fix_imports_statements;
}
$("script:not([src])").each((...[, element]) => {
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
"jsCode": $(element).html()!,
buildOptions
const jsCode = $(element).html();
assert(jsCode !== null);
const { fixedJsCode } = replaceImportsInJsCode({
jsCode,
buildContext
});
$(element).text(fixedJsCode);
});
$("style").each((...[, element]) => {
const { fixedCssCode } = replaceImportsInInlineCssCode({
"cssCode": $(element).html()!,
buildOptions
const cssCode = $(element).html();
assert(cssCode !== null);
const { fixedCssCode } = replaceImportsInCssCode({
cssCode,
cssFileRelativeDirPath: undefined,
buildContext
});
$(element).text(fixedCssCode);
@ -106,7 +77,8 @@ export function generateFtlFilesCodeFactory(params: {
(
[
["link", "href"],
["script", "src"]
["script", "src"],
["script", "data-src"]
] as const
).forEach(([selector, attrName]) =>
$(selector).each((...[, element]) => {
@ -118,54 +90,72 @@ export function generateFtlFilesCodeFactory(params: {
$(element).attr(
attrName,
buildOptions.isStandalone
? href.replace(new RegExp(`^${(buildOptions.urlPathname ?? "/").replace(/\//g, "\\/")}`), "${url.resourcesPath}/build/")
: href.replace(/^\//, `${buildOptions.urlOrigin}/`)
href.replace(
new RegExp(
`^${(buildContext.urlPathname ?? "/").replace(/\//g, "\\/")}`
),
`\${xKeycloakify.resourcesPath}/${WELL_KNOWN_DIRECTORY_BASE_NAME.DIST}/`
)
);
})
);
if (Object.keys(cssGlobalsToDefine).length !== 0) {
$("head").prepend(
[
"",
"<style>",
generateCssCodeToDefineGlobals({
cssGlobalsToDefine,
buildOptions
}).cssCodeToPrependInHead,
"</style>",
""
].join("\n")
);
}
}
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
const replaceValueBySearchValue = {
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': fs
.readFileSync(pathJoin(__dirname, "ftl_object_to_js_code_declaring_an_object.ftl"))
.toString("utf8")
.match(/^<script>const _=((?:.|\n)+)<\/script>[\n]?$/)![1],
"<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->": [
"<#if scripts??>",
" <#list scripts as script>",
' <script src="${script}" type="text/javascript"></script>',
" </#list>",
"</#if>"
].join("\n")
};
const kcContextDeclarationTemplateFtl = fs
.readFileSync(
pathJoin(
getThisCodebaseRootDirPath(),
"src",
"bin",
"keycloakify",
"generateFtl",
"kcContextDeclarationTemplate.ftl"
)
)
.toString("utf8")
.replace("{{themeType}}", themeType)
.replace("{{themeName}}", themeName)
.replace("{{keycloakifyVersion}}", keycloakifyVersion)
.replace("{{themeVersion}}", buildContext.themeVersion)
.replace("{{fieldNames}}", fieldNames.map(name => `"${name}"`).join(", "))
.replace("{{RESOURCES_COMMON}}", WELL_KNOWN_DIRECTORY_BASE_NAME.RESOURCES_COMMON)
.replace("{{KEYCLOAKIFY_SPA_DEV_SERVER_PORT}}", KEYCLOAKIFY_SPA_DEV_SERVER_PORT)
.replace(
"{{userDefinedExclusions}}",
buildContext.kcContextExclusionsFtlCode ?? ""
);
const ftlObjectToJsCodeDeclaringAnObjectPlaceholder =
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }';
$("head").prepend(
[
"<script>",
` window.${ftlValuesGlobalName}= ${objectKeys(replaceValueBySearchValue)[0]};`,
"</script>",
"",
objectKeys(replaceValueBySearchValue)[1]
].join("\n")
`<script>\n${ftlObjectToJsCodeDeclaringAnObjectPlaceholder}\n</script>`
);
// Remove part of the document marked as ignored.
{
const startTags = $('meta[name="keycloakify-ignore-start"]');
startTags.each((...[, startTag]) => {
const $startTag = $(startTag);
const $endTag = $startTag
.nextAll('meta[name="keycloakify-ignore-end"]')
.first();
if ($endTag.length) {
let currentNode = $startTag.next();
while (currentNode.length && !currentNode.is($endTag)) {
currentNode.remove();
currentNode = $startTag.next();
}
$startTag.remove();
$endTag.remove();
}
});
}
const partiallyFixedIndexHtmlCode = $.html();
function generateFtlFilesCode(params: { pageId: string }): {
@ -178,10 +168,14 @@ export function generateFtlFilesCodeFactory(params: {
let ftlCode = $.html();
Object.entries({
...replaceValueBySearchValue,
//If updated, don't forget to change in the ftl script as well.
"PAGE_ID_xIgLsPgGId9D8e": pageId
}).map(([searchValue, replaceValue]) => (ftlCode = ftlCode.replace(searchValue, replaceValue)));
[ftlObjectToJsCodeDeclaringAnObjectPlaceholder]:
kcContextDeclarationTemplateFtl,
"{{pageId}}": pageId,
"{{ftlTemplateFileName}}": pageId
}).map(
([searchValue, replaceValue]) =>
(ftlCode = ftlCode.replace(searchValue, replaceValue))
);
return { ftlCode };
}

View File

@ -0,0 +1,724 @@
<#assign xKeycloakify={
"messages": {},
"pageId": "{{pageId}}",
"ftlTemplateFileName": "{{ftlTemplateFileName}}",
"themeType": "{{themeType}}",
"themeName": "{{themeName}}",
"keycloakifyVersion": "{{keycloakifyVersion}}",
"themeVersion": "{{themeVersion}}",
"resourcesPath": ""
}>
<#if url?? && url?is_hash && url.resourcesPath?? && url.resourcesPath?is_string>
<#assign xKeycloakify = xKeycloakify + { "resourcesPath": url.resourcesPath }>
</#if>
<#if resourceUrl?? && resourceUrl?is_string>
<#assign xKeycloakify = xKeycloakify + { "resourcesPath": resourceUrl }>
</#if>
const kcContext = ${toJsDeclarationString(.data_model, [])?no_esc};
kcContext.keycloakifyVersion = "${xKeycloakify.keycloakifyVersion}";
kcContext.themeVersion = "${xKeycloakify.themeVersion}";
kcContext.themeType = "${xKeycloakify.themeType}";
kcContext.themeName = "${xKeycloakify.themeName}";
kcContext.pageId = "${xKeycloakify.pageId}";
kcContext.ftlTemplateFileName = "${xKeycloakify.ftlTemplateFileName}";
<@addNonAutomaticallyGatherableMessagesToXKeycloakifyMessages />
kcContext["x-keycloakify"] = {};
kcContext["x-keycloakify"].resourcesPath = "${xKeycloakify.resourcesPath}";
{
var messages = {};
<#list xKeycloakify.messages as key, resolvedMsg>
messages["${key}"] = decodeHtmlEntities("${resolvedMsg?js_string}");
</#list>
kcContext["x-keycloakify"].messages = messages;
}
if(
kcContext.url instanceof Object &&
typeof kcContext.url.resourcesPath === "string"
){
kcContext.url.resourcesCommonPath = kcContext.url.resourcesPath + "/{{RESOURCES_COMMON}}";
}
if( kcContext.messagesPerField ){
var existsError_singleFieldName = kcContext.messagesPerField.existsError;
kcContext.messagesPerField.existsError = function (){
for( let i = 0; i < arguments.length; i++ ){
if( existsError_singleFieldName(arguments[i]) ){
return true;
}
}
return false;
};
kcContext.messagesPerField.exists = function (fieldName) {
return kcContext.messagesPerField.get(fieldName) !== "";
};
kcContext.messagesPerField.printIfExists = function (fieldName, text) {
return kcContext.messagesPerField.exists(fieldName) ? text : undefined;
};
kcContext.messagesPerField.getFirstError = function () {
for( let i = 0; i < arguments.length; i++ ){
const fieldName = arguments[i];
if( kcContext.messagesPerField.existsError(fieldName) ){
return kcContext.messagesPerField.get(fieldName);
}
}
};
}
attributes_to_attributesByName: {
if( !kcContext.profile ){
break attributes_to_attributesByName;
}
if( !kcContext.profile.attributes ){
break attributes_to_attributesByName;
}
var attributes = kcContext.profile.attributes;
delete kcContext.profile.attributes;
kcContext.profile.attributesByName = {};
attributes.forEach(function(attribute){
kcContext.profile.attributesByName[attribute.name] = attribute;
});
}
redirect_to_dev_server: {
switch(kcContext.themeType){
case "login":
break redirect_to_dev_server;
case "account":
if( kcContext.pageId !== "index.ftl" ){
break redirect_to_dev_server;
}
break;
case "admin":
break;
default:
break redirect_to_dev_server;
}
const devSeverPort = kcContext.properties.{{KEYCLOAKIFY_SPA_DEV_SERVER_PORT}};
if( !devSeverPort ){
break redirect_to_dev_server;
}
const redirectUrl = new URL(window.location.href);
redirectUrl.port = devSeverPort;
delete kcContext.msgJSON;
console.log(kcContext);
redirectUrl.searchParams.set("kcContext", encodeURIComponent(JSON.stringify(kcContext)));
window.location.href = redirectUrl.toString();
}
window.kcContext = kcContext;
<#if xKeycloakify.themeType == "login" >
{
const script = document.createElement("script");
script.type = "importmap";
script.textContent = JSON.stringify({
imports: {
"rfc4648": kcContext.url.resourcesCommonPath + "/node_modules/rfc4648/lib/rfc4648.js"
}
}, null, 2);
document.head.appendChild(script);
}
</#if>
function decodeHtmlEntities(htmlStr){
var element = decodeHtmlEntities.element;
if (!element) {
element = document.createElement("textarea");
decodeHtmlEntities.element = element;
}
element.innerHTML = htmlStr;
return element.value;
}
<#function toJsDeclarationString object path>
<#local isHash = -1>
<#attempt>
<#local isHash = object?is_hash || object?is_hash_ex>
<#recover>
<#return "ABORT: Can't evaluate if " + path?join(".") + " is a hash">
</#attempt>
<#if isHash>
<#if path?size gt 10>
<#return "ABORT: Too many recursive calls, path: " + path?join(".")>
</#if>
<#local keys = -1>
<#attempt>
<#local keys = object?keys>
<#recover>
<#return "ABORT: We can't list keys on object">
</#attempt>
<#local outSeq = []>
<#list keys as key>
<#if ["class","declaredConstructors","superclass","declaringClass" ]?seq_contains(key) >
<#continue>
</#if>
<#if (
areSamePath(path, ["url"]) &&
["loginUpdatePasswordUrl", "loginUpdateProfileUrl", "loginUsernameReminderUrl", "loginUpdateTotpUrl"]?seq_contains(key)
) || (
key == "updateProfileCtx" &&
areSamePath(path, [])
) || (
<#-- https://github.com/keycloakify/keycloakify/pull/65#issuecomment-991896344 (reports with saml-post-form.ftl) -->
<#-- https://github.com/keycloakify/keycloakify/issues/91#issue-1212319466 (reports with error.ftl and Kc18) -->
<#-- https://github.com/keycloakify/keycloakify/issues/109#issuecomment-1134610163 -->
<#-- https://github.com/keycloakify/keycloakify/issues/357 -->
<#-- https://github.com/keycloakify/keycloakify/discussions/406#discussioncomment-7514787 -->
key == "loginAction" &&
areSamePath(path, ["url"]) &&
["saml-post-form.ftl", "error.ftl", "info.ftl", "login-oauth-grant.ftl", "logout-confirm.ftl", "login-oauth2-device-verify-user-code.ftl", "frontchannel-logout.ftl"]?seq_contains(xKeycloakify.pageId) &&
!(auth?has_content && auth.showTryAnotherWayLink())
) || (
<#-- https://github.com/keycloakify/keycloakify/issues/362 -->
["secretData", "value"]?seq_contains(key) &&
areSamePath(path, [ "totp", "otpCredentials", "*" ])
) || (
["contextData", "idpConfig", "idp", "authenticationSession"]?seq_contains(key) &&
areSamePath(path, ["brokerContext"]) &&
["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(xKeycloakify.pageId)
) || (
key == "identityProviderBrokerCtx" &&
areSamePath(path, []) &&
["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(xKeycloakify.pageId)
) || (
["masterAdminClient", "delegateForUpdate", "defaultRole", "smtpConfig"]?seq_contains(key) &&
areSamePath(path, ["realm"])
) || (
xKeycloakify.pageId == "error.ftl" &&
areSamePath(path, ["realm"]) &&
!["name", "displayName", "displayNameHtml", "internationalizationEnabled", "registrationEmailAsUsername" ]?seq_contains(key)
) || (
xKeycloakify.pageId == "applications.ftl" &&
(
key == "realm" ||
key == "container"
) &&
isSubpath(path, ["applications", "applications"])
) || (
key == "delegateForUpdate" &&
areSamePath(path, ["user"])
) || (
<#-- Security audit forwarded by Garth (Gmail) -->
key == "saml.signing.private.key" &&
areSamePath(path, ["client", "attributes"])
) || (
<#-- See: https://github.com/keycloakify/keycloakify/issues/534 -->
key == "password" &&
areSamePath(path, ["login"])
) || (
<#-- Remove realmAttributes added by https://github.com/jcputney/keycloak-theme-additional-info-extension for peace of mind. -->
key == "realmAttributes" &&
areSamePath(path, [])
) || (
<#-- attributesByName adds a lot of noise to the output and is not needed, we already have profile.attributes -->
key == "attributesByName" &&
areSamePath(path, ["profile"])
) || (
<#-- We already have the attributes in profile speedup the rendering by filtering it out from the register object -->
(key == "attributes" || key == "attributesByName") &&
areSamePath(path, ["register"])
) || (
areSamePath(path, ["properties"]) &&
(
key?starts_with("kc") ||
key == "locales" ||
key == "import" ||
key == "parent" ||
key == "meta" ||
key == "stylesCommon" ||
key == "styles" ||
key == "accountResourceProvider"
)
) || (
key == "execution" &&
areSamePath(path, [])
) || (
key == "entity" &&
areSamePath(path, ["user"])
) || (
key == "attributes" &&
areSamePath(path, ["realm"])
) || (
xKeycloakify.pageId == "index.ftl" &&
xKeycloakify.themeType == "account" &&
areSamePath(path, ["realm"]) &&
![
"name",
"registrationEmailAsUsername",
"editUsernameAllowed",
"isInternationalizationEnabled",
"identityFederationEnabled",
"userManagedAccessAllowed"
]?seq_contains(key)
) || (
["flowContext", "session", "realm"]?seq_contains(key) &&
areSamePath(path, ["social"])
)
>
<#-- <#local outSeq += ["/*" + path?join(".") + "." + key + " excluded*/"]> -->
<#continue>
</#if>
<#-- https://github.com/keycloakify/keycloakify/discussions/406 -->
<#if (
key == "attemptedUsername" &&
areSamePath(path, ["auth"]) &&
[
"register.ftl", "terms.ftl", "info.ftl", "login.ftl",
"login-update-password.ftl", "login-oauth2-device-verify-user-code.ftl"
]?seq_contains(xKeycloakify.pageId)
)>
<#attempt>
<#-- https://github.com/keycloak/keycloak/blob/3a2bf0c04bcde185e497aaa32d0bb7ab7520cf4a/themes/src/main/resources/theme/base/login/template.ftl#L63 -->
<#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())>
<#local outSeq += ["/*" + path?join(".") + "." + key + " excluded*/"]>
<#continue>
</#if>
<#recover>
<#local outSeq += ["/*Accessing attemptedUsername throwed an exception */"]>
</#attempt>
</#if>
{{userDefinedExclusions}}
<#attempt>
<#if !object[key]??>
<#continue>
</#if>
<#recover>
<#local outSeq += ["/*Couldn't test if '" + key + "' is available on this object*/"]>
<#continue>
</#attempt>
<#local propertyValue = -1>
<#attempt>
<#local propertyValue = object[key]>
<#recover>
<#local outSeq += ["/*Couldn't dereference '" + key + "' on this object*/"]>
<#continue>
</#attempt>
<#local recOut = toJsDeclarationString(propertyValue, path + [ key ])>
<#if recOut?starts_with("ABORT:")>
<#local errorMessage = recOut?remove_beginning("ABORT:")>
<#if errorMessage != " It's a method" >
<#local outSeq += ["/*" + key + ": " + errorMessage + "*/"]>
</#if>
<#continue>
</#if>
<#local outSeq += ['"' + key + '": ' + recOut + ","]>
</#list>
<#return (["{"] + outSeq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "}"])?join("\n")>
</#if>
<#local isMethod = -1>
<#attempt>
<#local isMethod = object?is_method>
<#recover>
<#return "ABORT: Can't test if it'sa method.">
</#attempt>
<#if isMethod>
<#if areSamePath(path, ["auth", "showUsername"])>
<#attempt>
<#return auth.showUsername()?c>
<#recover>
<#return "ABORT: Couldn't evaluate auth.showUsername()">
</#attempt>
</#if>
<#if areSamePath(path, ["auth", "showResetCredentials"])>
<#attempt>
<#return auth.showResetCredentials()?c>
<#recover>
<#return "ABORT: Couldn't evaluate auth.showResetCredentials()">
</#attempt>
</#if>
<#if areSamePath(path, ["auth", "showTryAnotherWayLink"])>
<#attempt>
<#return auth.showTryAnotherWayLink()?c>
<#recover>
<#return "ABORT: Couldn't evaluate auth.showTryAnotherWayLink()">
</#attempt>
</#if>
<#if areSamePath(path, ["url", "getLogoutUrl"])>
<#local returnValue = -1>
<#attempt>
<#local returnValue = url.getLogoutUrl()>
<#recover>
<#return "ABORT: Couldn't evaluate url.getLogoutUrl()">
</#attempt>
<#return 'function(){ return "' + returnValue + '"; }'>
</#if>
<#if areSamePath(path, ["totp", "policy", "getAlgorithmKey"])>
<#local returnValue = "error">
<#if mode?? && mode = "manual">
<#attempt>
<#local returnValue = totp.policy.getAlgorithmKey()>
<#recover>
<#return "ABORT: Couldn't evaluate totp.policy.getAlgorithmKey()">
</#attempt>
</#if>
<#return 'function(){ return "' + returnValue + '"; }'>
</#if>
<#assign fieldNames = [{{fieldNames}}]>
<#if profile?? && profile.attributes??>
<#list profile.attributes as attribute>
<#if fieldNames?seq_contains(attribute.name)>
<#continue>
</#if>
<#assign fieldNames += [attribute.name]>
</#list>
</#if>
<#if areSamePath(path, ["messagesPerField", "get"])>
<#local jsFunctionCode = "function (fieldName) { ">
<#list fieldNames as fieldName>
<#-- See: https://github.com/keycloakify/keycloakify/issues/217 -->
<#if xKeycloakify.pageId == "login.ftl" >
<#if fieldName == "username">
<#local jsFunctionCode += "if(fieldName === 'username' || fieldName === 'password' ){ ">
<#if messagesPerField.exists('username') || messagesPerField.exists('password')>
<#local jsFunctionCode += "return kcContext.message && kcContext.message.summary ? kcContext.message.summary : 'error'; ">
<#else>
<#local jsFunctionCode += "return ''; ">
</#if>
<#local jsFunctionCode += "} ">
<#continue>
</#if>
<#if fieldName == "password">
<#continue>
</#if>
</#if>
<#local jsFunctionCode += "if(fieldName === '" + fieldName + "'){ ">
<#if messagesPerField.exists('${fieldName}')>
<#local jsFunctionCode += 'return decodeHtmlEntities("' + messagesPerField.get('${fieldName}')?js_string + '"); '>
<#else>
<#local jsFunctionCode += "return ''; ">
</#if>
<#local jsFunctionCode += "} ">
</#list>
<#local jsFunctionCode += "}">
<#return jsFunctionCode>
</#if>
<#if areSamePath(path, ["messagesPerField", "existsError"])>
<#local jsFunctionCode = "function (fieldName) { ">
<#list fieldNames as fieldName>
<#-- See: https://github.com/keycloakify/keycloakify/issues/217 -->
<#if xKeycloakify.pageId == "login.ftl" >
<#if fieldName == "username">
<#local jsFunctionCode += "if(fieldName === 'username' || fieldName === 'password' ){ ">
<#if messagesPerField.existsError('username') || messagesPerField.existsError('password')>
<#local jsFunctionCode += "return true; ">
<#else>
<#local jsFunctionCode += "return false; ">
</#if>
<#local jsFunctionCode += "} ">
<#continue>
</#if>
<#if fieldName == "password">
<#continue>
</#if>
</#if>
<#local jsFunctionCode += "if(fieldName === '" + fieldName + "' ){ ">
<#if messagesPerField.existsError('${fieldName}')>
<#local jsFunctionCode += 'return true; '>
<#else>
<#local jsFunctionCode += "return false; ">
</#if>
<#local jsFunctionCode += "}">
</#list>
<#local jsFunctionCode += "}">
<#return jsFunctionCode>
</#if>
<#if xKeycloakify.themeType == "account" && areSamePath(path, ["realm", "isInternationalizationEnabled"])>
<#attempt>
<#return realm.isInternationalizationEnabled()?c>
<#recover>
<#return "ABORT: Couldn't evaluate realm.isInternationalizationEnabled()">
</#attempt>
</#if>
<#return "ABORT: It's a method">
</#if>
<#local isBoolean = -1>
<#attempt>
<#local isBoolean = object?is_boolean>
<#recover>
<#return "ABORT: Can't test if it's a boolean">
</#attempt>
<#if isBoolean>
<#return object?c>
</#if>
<#local isEnumerable = -1>
<#attempt>
<#local isEnumerable = object?is_enumerable>
<#recover>
<#return "ABORT: Can't test if it's an enumerable">
</#attempt>
<#if isEnumerable>
<#local outSeq = []>
<#local i = 0>
<#list object as array_item>
<#if !array_item??>
<#local outSeq += ["null,"]>
<#continue>
</#if>
<#local recOut = toJsDeclarationString(array_item, path + [ i ])>
<#local i = i + 1>
<#if recOut?starts_with("ABORT:")>
<#local errorMessage = recOut?remove_beginning("ABORT:")>
<#if errorMessage != " It's a method" >
<#local outSeq += ["/*" + i?string + ": " + errorMessage + "*/"]>
</#if>
<#continue>
</#if>
<#local outSeq += [recOut + ","]>
</#list>
<#return (["["] + outSeq?map(str -> ""?right_pad(4 * (path?size + 1)) + str) + [ ""?right_pad(4 * path?size) + "]"])?join("\n")>
</#if>
<#local isDate = -1>
<#attempt>
<#local isDate = object?is_date_like>
<#recover>
<#return "ABORT: Can't test if it's a date">
</#attempt>
<#if isDate>
<#return '"' + object?datetime?iso_utc + '"'>
</#if>
<#local isNumber = -1>
<#attempt>
<#local isNumber = object?is_number>
<#recover>
<#return "ABORT: Can't test if it's a number">
</#attempt>
<#if isNumber>
<#return object?c>
</#if>
<#local isString = -1>
<#attempt>
<#local isString = object?is_string>
<#recover>
<#return "ABORT: Can't test if it's a string">
</#attempt>
<#if isString>
<@addToXKeycloakifyMessagesIfMessageKey str=object />
</#if>
<#attempt>
<#return '"' + object?js_string + '"'>;
<#recover>
</#attempt>
<#return "ABORT: Couldn't convert into string non hash, non method, non boolean, non number, non enumerable object">
</#function>
<#function isSubpath path searchedPath>
<#if path?size < searchedPath?size>
<#return false>
</#if>
<#local i=0>
<#list path as property>
<#if i == searchedPath?size >
<#continue>
</#if>
<#local searchedProperty=searchedPath[i]>
<#local i+= 1>
<#if searchedProperty?is_string && searchedProperty == "*">
<#continue>
</#if>
<#if searchedProperty?is_string && !property?is_string>
<#return false>
</#if>
<#if searchedProperty?is_number && !property?is_number>
<#return false>
</#if>
<#if searchedProperty?string != property?string>
<#return false>
</#if>
</#list>
<#return true>
</#function>
<#function areSamePath path searchedPath>
<#return path?size == searchedPath?size && isSubpath(path, searchedPath)>
</#function>
<#macro addToXKeycloakifyMessagesIfMessageKey str>
<#if !msg?? || !msg?is_method>
<#return>
</#if>
<#if (str?length > 200)>
<#return>
</#if>
<#local key=removeBrackets(str)>
<#if key?length==0>
<#return>
</#if>
<#if !(key?matches(r"^[a-zA-Z0-9-_.]*$"))>
<#return>
</#if>
<#local resolvedMsg=msg(key)>
<#if resolvedMsg==key>
<#return>
</#if>
<#local messages=xKeycloakify.messages>
<#local messages = messages + { key: resolvedMsg }>
<#assign xKeycloakify = xKeycloakify + { "messages": messages }>
</#macro>
<#function removeBrackets str>
<#if str?starts_with("${") && str?ends_with("}")>
<#return str[2..(str?length-2)]>
<#else>
<#return str>
</#if>
</#function>
<#macro addNonAutomaticallyGatherableMessagesToXKeycloakifyMessages>
<#if profile?? && profile?is_hash && profile.attributes?? && profile.attributes?is_enumerable>
<#list profile.attributes as attribute>
<#if !(
attribute.annotations?? && attribute.annotations?is_hash &&
attribute.annotations.inputOptionLabelsI18nPrefix?? && attribute.annotations.inputOptionLabelsI18nPrefix?is_string
)>
<#continue>
</#if>
<#local prefix=attribute.annotations.inputOptionLabelsI18nPrefix>
<#if !(
attribute.validators?? && attribute.validators?is_hash &&
attribute.validators.options?? && attribute.validators.options?is_hash &&
attribute.validators.options.options?? && attribute.validators.options.options?is_enumerable
)>
<#continue>
</#if>
<#list attribute.validators.options.options as option>
<#if !option?is_string>
<#continue>
</#if>
<@addToXKeycloakifyMessagesIfMessageKey str="${prefix}.${option}" />
</#list>
</#list>
</#if>
<#if xKeycloakify.pageId == "terms.ftl" || termsAcceptanceRequired?? && termsAcceptanceRequired>
<@addToXKeycloakifyMessagesIfMessageKey str="termsText" />
</#if>
<#if requiredActions?? && requiredActions?is_enumerable>
<#list requiredActions as requiredAction>
<#if !requiredAction?is_string>
<#continue>
</#if>
<@addToXKeycloakifyMessagesIfMessageKey str="requiredAction.${requiredAction}" />
</#list>
</#if>
</#macro>

View File

@ -1,89 +0,0 @@
import * as fs from "fs";
import { join as pathJoin, dirname as pathDirname } from "path";
import { assert } from "tsafe/assert";
import { Reflect } from "tsafe/Reflect";
import type { BuildOptions } from "./BuildOptions";
export type BuildOptionsLike = {
themeName: string;
groupId: string;
};
{
const buildOptions = Reflect<BuildOptions>();
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
}
export function generateJavaStackFiles(params: {
version: string;
keycloakThemeBuildingDirPath: string;
doBundlesEmailTemplate: boolean;
buildOptions: BuildOptionsLike;
}): {
jarFilePath: string;
} {
const {
version,
buildOptions: { groupId, themeName },
keycloakThemeBuildingDirPath,
doBundlesEmailTemplate
} = params;
{
const { pomFileCode } = (function generatePomFileCode(): {
pomFileCode: string;
} {
const artefactId = `${themeName}-keycloak-theme`;
const pomFileCode = [
`<?xml version="1.0"?>`,
`<project xmlns="http://maven.apache.org/POM/4.0.0"`,
` xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"`,
` xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">`,
` <modelVersion>4.0.0</modelVersion>`,
` <groupId>${groupId}</groupId>`,
` <artifactId>${artefactId}</artifactId>`,
` <version>${version}</version>`,
` <name>${artefactId}</name>`,
` <description />`,
`</project>`
].join("\n");
return { pomFileCode };
})();
fs.writeFileSync(pathJoin(keycloakThemeBuildingDirPath, "pom.xml"), Buffer.from(pomFileCode, "utf8"));
}
{
const themeManifestFilePath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "META-INF", "keycloak-themes.json");
try {
fs.mkdirSync(pathDirname(themeManifestFilePath));
} catch {}
fs.writeFileSync(
themeManifestFilePath,
Buffer.from(
JSON.stringify(
{
"themes": [
{
"name": themeName,
"types": ["login", ...(doBundlesEmailTemplate ? ["email"] : [])]
}
]
},
null,
2
),
"utf8"
)
);
}
return {
"jarFilePath": pathJoin(keycloakThemeBuildingDirPath, "target", `${themeName}-${version}.jar`)
};
}

View File

@ -1,203 +0,0 @@
import { transformCodebase } from "../tools/transformCodebase";
import * as fs from "fs";
import { join as pathJoin, basename as pathBasename } from "path";
import { replaceImportsFromStaticInJsCode } from "./replacers/replaceImportsFromStaticInJsCode";
import { replaceImportsInCssCode } from "./replacers/replaceImportsInCssCode";
import { generateFtlFilesCodeFactory, pageIds } from "./generateFtl";
import { downloadBuiltinKeycloakTheme } from "../download-builtin-keycloak-theme";
import * as child_process from "child_process";
import { mockTestingResourcesCommonPath, mockTestingResourcesPath, mockTestingSubDirOfPublicDirBasename } from "../mockTestingResourcesPath";
import { isInside } from "../tools/isInside";
import type { BuildOptions } from "./BuildOptions";
import { assert } from "tsafe/assert";
import { Reflect } from "tsafe/Reflect";
import { getLogger } from "../tools/logger";
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
export namespace BuildOptionsLike {
export type Common = {
themeName: string;
extraPages?: string[];
extraThemeProperties?: string[];
isSilent: boolean;
};
export type Standalone = Common & {
isStandalone: true;
urlPathname: string | undefined;
};
export type ExternalAssets = ExternalAssets.SameDomain | ExternalAssets.DifferentDomains;
export namespace ExternalAssets {
export type CommonExternalAssets = Common & {
isStandalone: false;
};
export type SameDomain = CommonExternalAssets & {
areAppAndKeycloakServerSharingSameDomain: true;
};
export type DifferentDomains = CommonExternalAssets & {
areAppAndKeycloakServerSharingSameDomain: false;
urlOrigin: string;
urlPathname: string | undefined;
};
}
}
{
const buildOptions = Reflect<BuildOptions>();
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
}
export function generateKeycloakThemeResources(params: {
reactAppBuildDirPath: string;
keycloakThemeBuildingDirPath: string;
keycloakThemeEmailDirPath: string;
keycloakVersion: string;
buildOptions: BuildOptionsLike;
}): { doBundlesEmailTemplate: boolean } {
const { reactAppBuildDirPath, keycloakThemeBuildingDirPath, keycloakThemeEmailDirPath, keycloakVersion, buildOptions } = params;
const logger = getLogger({ isSilent: buildOptions.isSilent });
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", buildOptions.themeName, "login");
let allCssGlobalsToDefine: Record<string, string> = {};
transformCodebase({
"destDirPath": buildOptions.isStandalone ? pathJoin(themeDirPath, "resources", "build") : reactAppBuildDirPath,
"srcDirPath": reactAppBuildDirPath,
"transformSourceCode": ({ filePath, sourceCode }) => {
//NOTE: Prevent cycles, excludes the folder we generated for debug in public/
if (
buildOptions.isStandalone &&
isInside({
"dirPath": pathJoin(reactAppBuildDirPath, mockTestingSubDirOfPublicDirBasename),
filePath
})
) {
return undefined;
}
if (/\.css?$/i.test(filePath)) {
if (!buildOptions.isStandalone) {
return undefined;
}
const { cssGlobalsToDefine, fixedCssCode } = replaceImportsInCssCode({
"cssCode": sourceCode.toString("utf8")
});
allCssGlobalsToDefine = {
...allCssGlobalsToDefine,
...cssGlobalsToDefine
};
return { "modifiedSourceCode": Buffer.from(fixedCssCode, "utf8") };
}
if (/\.js?$/i.test(filePath)) {
if (!buildOptions.isStandalone && buildOptions.areAppAndKeycloakServerSharingSameDomain) {
return undefined;
}
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
"jsCode": sourceCode.toString("utf8"),
buildOptions
});
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
}
return buildOptions.isStandalone ? { "modifiedSourceCode": sourceCode } : undefined;
}
});
let doBundlesEmailTemplate: boolean;
email: {
if (!fs.existsSync(keycloakThemeEmailDirPath)) {
logger.log(
[
`Not bundling email template because ${pathBasename(keycloakThemeEmailDirPath)} does not exist`,
`To start customizing the email template, run: 👉 npx create-keycloak-email-directory 👈`
].join("\n")
);
doBundlesEmailTemplate = false;
break email;
}
doBundlesEmailTemplate = true;
transformCodebase({
"srcDirPath": keycloakThemeEmailDirPath,
"destDirPath": pathJoin(themeDirPath, "..", "email")
});
}
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
"indexHtmlCode": fs.readFileSync(pathJoin(reactAppBuildDirPath, "index.html")).toString("utf8"),
"cssGlobalsToDefine": allCssGlobalsToDefine,
"buildOptions": buildOptions
});
[...pageIds, ...(buildOptions.extraPages ?? [])].forEach(pageId => {
const { ftlCode } = generateFtlFilesCode({ pageId });
fs.mkdirSync(themeDirPath, { "recursive": true });
fs.writeFileSync(pathJoin(themeDirPath, pageId), Buffer.from(ftlCode, "utf8"));
});
{
const tmpDirPath = pathJoin(themeDirPath, "..", "tmp_xxKdLpdIdLd");
downloadBuiltinKeycloakTheme({
keycloakVersion,
"destDirPath": tmpDirPath,
isSilent: buildOptions.isSilent
});
const themeResourcesDirPath = pathJoin(themeDirPath, "resources");
transformCodebase({
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "login", "resources"),
"destDirPath": themeResourcesDirPath
});
const reactAppPublicDirPath = pathJoin(reactAppBuildDirPath, "..", "public");
transformCodebase({
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "common", "resources"),
"destDirPath": pathJoin(themeResourcesDirPath, pathBasename(mockTestingResourcesCommonPath))
});
transformCodebase({
"srcDirPath": themeResourcesDirPath,
"destDirPath": pathJoin(reactAppPublicDirPath, mockTestingResourcesPath)
});
const keycloakResourcesWithinPublicDirPath = pathJoin(reactAppPublicDirPath, mockTestingSubDirOfPublicDirBasename);
fs.writeFileSync(
pathJoin(keycloakResourcesWithinPublicDirPath, "README.txt"),
Buffer.from(
["This is just a test folder that helps develop", "the login and register page without having to run a Keycloak container"].join(" ")
)
);
fs.writeFileSync(pathJoin(keycloakResourcesWithinPublicDirPath, ".gitignore"), Buffer.from("*", "utf8"));
child_process.execSync(`rm -r ${tmpDirPath}`);
}
fs.writeFileSync(
pathJoin(themeDirPath, "theme.properties"),
Buffer.from(["parent=keycloak", ...(buildOptions.extraThemeProperties ?? [])].join("\n\n"), "utf8")
);
return { doBundlesEmailTemplate };
}

View File

@ -0,0 +1,442 @@
import { type ThemeType, FALLBACK_LANGUAGE_TAG } from "../../shared/constants";
import { crawl } from "../../tools/crawl";
import { join as pathJoin, dirname as pathDirname } from "path";
import { symToStr } from "tsafe/symToStr";
import * as recast from "recast";
import * as babelParser from "@babel/parser";
import babelGenerate from "@babel/generator";
import * as babelTypes from "@babel/types";
import { escapeStringForPropertiesFile } from "../../tools/escapeStringForPropertiesFile";
import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
import * as fs from "fs";
import { assert } from "tsafe/assert";
import type { BuildContext } from "../../shared/buildContext";
import { getAbsoluteAndInOsFormatPath } from "../../tools/getAbsoluteAndInOsFormatPath";
export type BuildContextLike = {
themeNames: string[];
themeSrcDirPath: string;
};
assert<BuildContext extends BuildContextLike ? true : false>();
export function generateMessageProperties(params: {
buildContext: BuildContextLike;
themeType: Exclude<ThemeType, "admin">;
}): {
languageTags: string[];
writeMessagePropertiesFiles: (params: {
messageDirPath: string;
themeName: string;
}) => void;
} {
const { buildContext, themeType } = params;
const baseMessagesDirPath = pathJoin(
getThisCodebaseRootDirPath(),
"src",
themeType,
"i18n",
"messages_defaultSet"
);
const messages_defaultSet_by_languageTag_defaultSet: {
[languageTag_defaultSet: string]: Record<string, string>;
} = Object.fromEntries(
fs
.readdirSync(baseMessagesDirPath)
.filter(basename => basename !== "index.ts" && basename !== "types.ts")
.map(basename => ({
languageTag: basename.replace(/\.ts$/, ""),
filePath: pathJoin(baseMessagesDirPath, basename)
}))
.map(({ languageTag, filePath }) => {
const lines = fs.readFileSync(filePath).toString("utf8").split(/\r?\n/);
let messagesJson = "{";
let isInDeclaration = false;
for (const line of lines) {
if (!isInDeclaration) {
if (line.startsWith("const messages")) {
isInDeclaration = true;
}
continue;
}
if (line.startsWith("}")) {
messagesJson += "}";
break;
}
messagesJson += line;
}
const messages = JSON.parse(messagesJson) as Record<string, string>;
return [languageTag, messages];
})
);
const { i18nTsFilePath } = (() => {
let files = crawl({
dirPath: pathJoin(buildContext.themeSrcDirPath, themeType),
returnedPathsType: "absolute"
});
files = files.filter(file => {
const regex = /\.(js|ts|tsx)$/;
return regex.test(file);
});
files = files.sort((a, b) => {
const regex = /\.i18n\.(ts|js|tsx)$/;
const aIsI18nFile = regex.test(a);
const bIsI18nFile = regex.test(b);
return aIsI18nFile === bIsI18nFile ? 0 : aIsI18nFile ? -1 : 1;
});
files = files.sort((a, b) => a.length - b.length);
files = files.filter(file =>
fs.readFileSync(file).toString("utf8").includes("i18nBuilder")
);
const i18nTsFilePath: string | undefined = files[0];
return { i18nTsFilePath };
})();
const i18nTsRoot = (() => {
if (i18nTsFilePath === undefined) {
return undefined;
}
const root = recastParseTs(i18nTsFilePath);
return root;
})();
const messages_defaultSet_by_languageTag_notInDefaultSet:
| { [languageTag_notInDefaultSet: string]: Record<string, string> }
| undefined = (() => {
if (i18nTsRoot === undefined) {
return undefined;
}
let extraLanguageEntryByLanguageTag: Record<
string,
{ label: string; path: string }
> = {};
recast.visit(i18nTsRoot, {
visitCallExpression: function (path) {
const node = path.node;
// Check if the callee is a MemberExpression with property 'withExtraLanguages'
if (
node.callee.type === "MemberExpression" &&
node.callee.property.type === "Identifier" &&
node.callee.property.name === "withExtraLanguages"
) {
const arg = node.arguments[0];
if (arg && arg.type === "ObjectExpression") {
// Iterate over the properties of the object
arg.properties.forEach(prop => {
if (
prop.type === "ObjectProperty" &&
prop.key.type === "Identifier"
) {
const lang = prop.key.name;
const value = prop.value;
if (value.type === "ObjectExpression") {
let label: string | undefined = undefined;
let pathStr: string | undefined = undefined;
// Iterate over the properties of the language object
value.properties.forEach(p => {
if (
p.type === "ObjectProperty" &&
p.key.type === "Identifier"
) {
if (
p.key.name === "label" &&
p.value.type === "StringLiteral"
) {
label = p.value.value;
}
if (
p.key.name === "getMessages" &&
(p.value.type ===
"ArrowFunctionExpression" ||
p.value.type === "FunctionExpression")
) {
// Extract the import path from the function body
const body = p.value.body;
if (
body.type === "CallExpression" &&
body.callee.type === "Import"
) {
const importArg = body.arguments[0];
if (
importArg.type === "StringLiteral"
) {
pathStr = importArg.value;
}
} else if (
body.type === "BlockStatement"
) {
// If the function body is a block (e.g., function with braces {})
// Look for return statement
body.body.forEach(statement => {
if (
statement.type ===
"ReturnStatement" &&
statement.argument &&
statement.argument.type ===
"CallExpression" &&
statement.argument.callee
.type === "Import"
) {
const importArg =
statement.argument
.arguments[0];
if (
importArg.type ===
"StringLiteral"
) {
pathStr = importArg.value;
}
}
});
}
}
}
});
if (label && pathStr) {
extraLanguageEntryByLanguageTag[lang] = {
label,
path: pathStr
};
}
}
}
});
}
return false; // Stop traversing this path
}
this.traverse(path); // Continue traversing other paths
}
});
const messages_defaultSet_by_languageTag_notInDefaultSet = Object.fromEntries(
Object.entries(extraLanguageEntryByLanguageTag).map(
([languageTag, { path: relativePathWithoutExt }]) => [
languageTag,
(() => {
const filePath = getAbsoluteAndInOsFormatPath({
pathIsh: relativePathWithoutExt.endsWith(".ts")
? relativePathWithoutExt
: `${relativePathWithoutExt}.ts`,
cwd: pathDirname(i18nTsFilePath)
});
const root = recastParseTs(filePath);
let declarationCode: string | undefined = "";
recast.visit(root, {
visitVariableDeclarator: function (path) {
const node = path.node;
// Check if the variable name is 'messages'
if (
node.id.type === "Identifier" &&
node.id.name === "messages"
) {
// Ensure there is an initializer
if (node.init) {
// Generate code from the initializer, preserving comments
declarationCode = recast
.print(node.init)
.code.replace(/}.*$/, "}");
}
return false; // Stop traversing this path
}
this.traverse(path); // Continue traversing other paths
}
});
assert(
declarationCode !== undefined,
`${filePath} does not contain a 'messages' variable declaration`
);
let messages: Record<string, string> = {};
try {
eval(`${symToStr({ messages })} = ${declarationCode};`);
} catch {
throw new Error(
`The declaration of 'message' in ${filePath} cannot be statically evaluated: ${declarationCode}`
);
}
return messages;
})()
]
)
);
return messages_defaultSet_by_languageTag_notInDefaultSet;
})();
const messages_defaultSet_by_languageTag = {
...messages_defaultSet_by_languageTag_defaultSet,
...messages_defaultSet_by_languageTag_notInDefaultSet
};
const messages_themeDefined_by_languageTag:
| {
[languageTag: string]:
| Record<string, string | Record<string, string>>
| undefined;
}
| undefined = (() => {
if (i18nTsRoot === undefined) {
return undefined;
}
let firstArgumentCode: string | undefined = undefined;
recast.visit(i18nTsRoot, {
visitCallExpression: function (path) {
const node = path.node;
if (
node.callee.type === "MemberExpression" &&
node.callee.property.type === "Identifier" &&
node.callee.property.name === "withCustomTranslations"
) {
firstArgumentCode = babelGenerate(node.arguments[0] as any).code;
return false;
}
this.traverse(path);
}
});
if (firstArgumentCode === undefined) {
return undefined;
}
let messages_themeDefined_by_languageTag: {
[languageTag: string]: Record<string, string | Record<string, string>>;
} = {};
try {
eval(
`${symToStr({ messages_themeDefined_by_languageTag })} = ${firstArgumentCode}`
);
} catch {
console.warn(
[
"WARNING: The argument of withCustomTranslations can't be statically evaluated!",
"This needs to be fixed refer to the documentation: https://docs.keycloakify.dev/i18n",
firstArgumentCode
].join(" ")
);
return undefined;
}
return messages_themeDefined_by_languageTag;
})();
const languageTags = Object.keys(messages_defaultSet_by_languageTag);
return {
languageTags,
writeMessagePropertiesFiles: ({ messageDirPath, themeName }) => {
for (const languageTag of languageTags) {
const messages = {
...messages_defaultSet_by_languageTag[languageTag]
};
add_theme_defined_messages: {
if (messages_themeDefined_by_languageTag === undefined) {
break add_theme_defined_messages;
}
let messages_themeDefined =
messages_themeDefined_by_languageTag[languageTag];
if (messages_themeDefined === undefined) {
messages_themeDefined =
messages_themeDefined_by_languageTag[FALLBACK_LANGUAGE_TAG];
}
if (messages_themeDefined === undefined) {
messages_themeDefined =
messages_themeDefined_by_languageTag[
Object.keys(messages_themeDefined_by_languageTag)[0]
];
}
if (messages_themeDefined === undefined) {
break add_theme_defined_messages;
}
for (const [key, messageOrMessageByThemeName] of Object.entries(
messages_themeDefined
)) {
const message = (() => {
if (typeof messageOrMessageByThemeName === "string") {
return messageOrMessageByThemeName;
}
const message = messageOrMessageByThemeName[themeName];
assert(message !== undefined);
return message;
})();
messages[key] = message;
}
}
const propertiesFileSource = [
"",
...Object.entries(messages).map(
([key, value]) => `${key}=${escapeStringForPropertiesFile(value)}`
),
""
].join("\n");
fs.mkdirSync(messageDirPath, { recursive: true });
fs.writeFileSync(
pathJoin(messageDirPath, `messages_${languageTag}.properties`),
Buffer.from(propertiesFileSource, "utf8")
);
}
}
};
}
function recastParseTs(filePath: string): recast.types.ASTNode {
return recast.parse(fs.readFileSync(filePath).toString("utf8"), {
parser: {
parse: (code: string) =>
babelParser.parse(code, {
sourceType: "module",
plugins: ["typescript"]
}),
generator: babelGenerate,
types: babelTypes
}
});
}

View File

@ -0,0 +1,756 @@
import type { BuildContext } from "../../shared/buildContext";
import fs from "fs";
import { rmSync } from "../../tools/fs.rmSync";
import { transformCodebase } from "../../tools/transformCodebase";
import {
join as pathJoin,
relative as pathRelative,
dirname as pathDirname,
basename as pathBasename
} from "path";
import { replaceImportsInJsCode } from "../replacers/replaceImportsInJsCode";
import { replaceImportsInCssCode } from "../replacers/replaceImportsInCssCode";
import {
generateFtlFilesCodeFactory,
type BuildContextLike as BuildContextLike_kcContextExclusionsFtlCode
} from "../generateFtl";
import {
type ThemeType,
LOGIN_THEME_PAGE_IDS,
ACCOUNT_THEME_PAGE_IDS,
WELL_KNOWN_DIRECTORY_BASE_NAME,
THEME_TYPES,
KEYCLOAKIFY_SPA_DEV_SERVER_PORT
} from "../../shared/constants";
import { assert, type Equals } from "tsafe/assert";
import { readFieldNameUsage } from "./readFieldNameUsage";
import { readExtraPagesNames } from "./readExtraPageNames";
import {
generateMessageProperties,
type BuildContextLike as BuildContextLike_generateMessageProperties
} from "./generateMessageProperties";
import { readThisNpmPackageVersion } from "../../tools/readThisNpmPackageVersion";
import { escapeStringForPropertiesFile } from "../../tools/escapeStringForPropertiesFile";
import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
import propertiesParser from "properties-parser";
import { createObjectThatThrowsIfAccessed } from "../../tools/createObjectThatThrowsIfAccessed";
import { listInstalledModules } from "../../tools/listInstalledModules";
import { isInside } from "../../tools/isInside";
import { id } from "tsafe/id";
export type BuildContextLike = BuildContextLike_kcContextExclusionsFtlCode &
BuildContextLike_generateMessageProperties & {
themeNames: string[];
extraThemeProperties: string[] | undefined;
projectDirPath: string;
projectBuildDirPath: string;
environmentVariables: { name: string; default: string }[];
implementedThemeTypes: BuildContext["implementedThemeTypes"];
themeSrcDirPath: string;
bundler: "vite" | "webpack";
packageJsonFilePath: string;
};
assert<BuildContext extends BuildContextLike ? true : false>();
export async function generateResources(params: {
buildContext: BuildContextLike;
resourcesDirPath: string;
}): Promise<void> {
const start = Date.now();
const { resourcesDirPath, buildContext } = params;
const [themeName] = buildContext.themeNames;
if (fs.existsSync(resourcesDirPath)) {
rmSync(resourcesDirPath, { recursive: true });
}
const getThemeTypeDirPath = (params: {
themeType: ThemeType | "email";
themeName: string;
}) => {
const { themeType, themeName } = params;
return pathJoin(resourcesDirPath, "theme", themeName, themeType);
};
const writeMessagePropertiesFilesByThemeType: Partial<
Record<
ThemeType | "email",
(params: { messageDirPath: string; themeName: string }) => void
>
> = {};
for (const themeType of [...THEME_TYPES, "email"] as const) {
let isNative: boolean;
{
const v = buildContext.implementedThemeTypes[themeType];
if (!v.isImplemented && !v.isImplemented_native) {
continue;
}
isNative = !v.isImplemented && v.isImplemented_native;
}
const getAccountThemeType = () => {
assert(themeType === "account");
assert(buildContext.implementedThemeTypes.account.isImplemented);
return buildContext.implementedThemeTypes.account.type;
};
const isSpa = (() => {
switch (themeType) {
case "login":
return false;
case "account":
return getAccountThemeType() === "Single-Page";
case "admin":
return true;
case "email":
return false;
}
})();
const themeTypeDirPath = getThemeTypeDirPath({ themeName, themeType });
apply_replacers_and_move_to_theme_resources: {
if (isNative) {
break apply_replacers_and_move_to_theme_resources;
}
const destDirPath = pathJoin(
themeTypeDirPath,
"resources",
WELL_KNOWN_DIRECTORY_BASE_NAME.DIST
);
// NOTE: Prevent accumulation of files in the assets dir, as names are hashed they pile up.
rmSync(destDirPath, { recursive: true, force: true });
if (
themeType !== "login" &&
buildContext.implementedThemeTypes.login.isImplemented
) {
// NOTE: We prevent doing it twice, it has been done for the login theme.
transformCodebase({
srcDirPath: pathJoin(
getThemeTypeDirPath({
themeName,
themeType: "login"
}),
"resources",
WELL_KNOWN_DIRECTORY_BASE_NAME.DIST
),
destDirPath
});
break apply_replacers_and_move_to_theme_resources;
}
{
const dirPath = pathJoin(
buildContext.projectBuildDirPath,
WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES
);
if (fs.existsSync(dirPath)) {
assert(buildContext.bundler === "webpack");
throw new Error(
[
`Keycloakify build error: The ${WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES} directory shouldn't exist in your build directory.`,
`(${pathRelative(process.cwd(), dirPath)}).\n`,
`Theses assets are only required for local development with Storybook.",
"Please remove this directory as an additional step of your command.\n`,
`For example: \`"build": "... && rimraf ${pathRelative(buildContext.projectDirPath, dirPath)}"\``
].join(" ")
);
}
}
transformCodebase({
srcDirPath: buildContext.projectBuildDirPath,
destDirPath,
transformSourceCode: ({ filePath, fileRelativePath, sourceCode }) => {
if (filePath.endsWith(".css")) {
const { fixedCssCode } = replaceImportsInCssCode({
cssCode: sourceCode.toString("utf8"),
cssFileRelativeDirPath: pathDirname(fileRelativePath),
buildContext
});
return {
modifiedSourceCode: Buffer.from(fixedCssCode, "utf8")
};
}
if (filePath.endsWith(".js")) {
const { fixedJsCode } = replaceImportsInJsCode({
jsCode: sourceCode.toString("utf8"),
buildContext
});
return {
modifiedSourceCode: Buffer.from(fixedJsCode, "utf8")
};
}
return { modifiedSourceCode: sourceCode };
}
});
}
generate_ftl_files: {
if (isNative) {
break generate_ftl_files;
}
assert(themeType !== "email");
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
themeName,
indexHtmlCode: fs
.readFileSync(
pathJoin(buildContext.projectBuildDirPath, "index.html")
)
.toString("utf8"),
buildContext,
keycloakifyVersion: readThisNpmPackageVersion(),
themeType,
fieldNames: isSpa
? []
: (assert(themeType !== "admin"),
readFieldNameUsage({
themeSrcDirPath: buildContext.themeSrcDirPath,
themeType
}))
});
[
...(() => {
switch (themeType) {
case "login":
return LOGIN_THEME_PAGE_IDS;
case "account":
return getAccountThemeType() === "Single-Page"
? ["index.ftl"]
: ACCOUNT_THEME_PAGE_IDS;
case "admin":
return ["index.ftl"];
}
})(),
...(isSpa
? []
: readExtraPagesNames({
themeType,
themeSrcDirPath: buildContext.themeSrcDirPath
}))
].forEach(pageId => {
const { ftlCode } = generateFtlFilesCode({ pageId });
fs.writeFileSync(
pathJoin(themeTypeDirPath, pageId),
Buffer.from(ftlCode, "utf8")
);
});
}
copy_native_theme: {
if (!isNative) {
break copy_native_theme;
}
const dirPath = pathJoin(buildContext.themeSrcDirPath, themeType);
transformCodebase({
srcDirPath: dirPath,
destDirPath: getThemeTypeDirPath({ themeName, themeType }),
transformSourceCode: ({ fileRelativePath, sourceCode }) => {
if (isInside({ dirPath: "messages", filePath: fileRelativePath })) {
return undefined;
}
return { modifiedSourceCode: sourceCode };
}
});
}
let languageTags: string[] | undefined = undefined;
i18n_multi_page: {
if (isNative) {
break i18n_multi_page;
}
if (isSpa) {
break i18n_multi_page;
}
assert(themeType !== "admin" && themeType !== "email");
const wrap = generateMessageProperties({
buildContext,
themeType
});
languageTags = wrap.languageTags;
const { writeMessagePropertiesFiles } = wrap;
writeMessagePropertiesFilesByThemeType[themeType] =
writeMessagePropertiesFiles;
}
let isLegacyAccountSpa = false;
// NOTE: Eventually remove this block.
i18n_single_page_account_legacy: {
if (!isSpa) {
break i18n_single_page_account_legacy;
}
if (themeType !== "account") {
break i18n_single_page_account_legacy;
}
const [moduleMeta] = await listInstalledModules({
packageJsonFilePath: buildContext.packageJsonFilePath,
filter: ({ moduleName }) =>
moduleName === "@keycloakify/keycloak-account-ui"
});
assert(
moduleMeta !== undefined,
`@keycloakify/keycloak-account-ui is supposed to be installed`
);
{
const [majorStr] = moduleMeta.version.split(".");
if (majorStr.length === 6) {
// NOTE: Now we use the format MMmmpp (Major, minor, patch) for example for
// 26.0.7 it would be 260007.
break i18n_single_page_account_legacy;
} else {
// 25.0.4-rc.5 or later
isLegacyAccountSpa = true;
}
}
const messageDirPath_defaults = pathJoin(moduleMeta.dirPath, "messages");
if (!fs.existsSync(messageDirPath_defaults)) {
throw new Error(
`Please update @keycloakify/keycloak-account-ui to 25.0.4-rc.5 or later.`
);
}
isLegacyAccountSpa = true;
const messagesDirPath_dest = pathJoin(
getThemeTypeDirPath({ themeName, themeType: "account" }),
"messages"
);
transformCodebase({
srcDirPath: messageDirPath_defaults,
destDirPath: messagesDirPath_dest
});
apply_theme_changes: {
const messagesDirPath_theme = pathJoin(
buildContext.themeSrcDirPath,
"account",
"messages"
);
if (!fs.existsSync(messagesDirPath_theme)) {
break apply_theme_changes;
}
fs.readdirSync(messagesDirPath_theme).forEach(basename => {
const filePath_src = pathJoin(messagesDirPath_theme, basename);
const filePath_dest = pathJoin(messagesDirPath_dest, basename);
if (!fs.existsSync(filePath_dest)) {
fs.cpSync(filePath_src, filePath_dest);
}
const messages_src = propertiesParser.parse(
fs.readFileSync(filePath_src).toString("utf8")
);
const messages_dest = propertiesParser.parse(
fs.readFileSync(filePath_dest).toString("utf8")
);
const messages = {
...messages_dest,
...messages_src
};
const editor = propertiesParser.createEditor();
Object.entries(messages).forEach(([key, value]) => {
editor.set(key, value);
});
fs.writeFileSync(
filePath_dest,
Buffer.from(editor.toString(), "utf8")
);
});
}
languageTags = fs
.readdirSync(messagesDirPath_dest)
.map(basename =>
basename.replace(/^messages_/, "").replace(/\.properties$/, "")
);
}
i18n_for_spas_and_native: {
if (!isSpa && !isNative) {
break i18n_for_spas_and_native;
}
if (isLegacyAccountSpa) {
break i18n_for_spas_and_native;
}
const messagesDirPath_theme = pathJoin(
buildContext.themeSrcDirPath,
themeType,
isNative ? "messages" : "i18n"
);
if (!fs.existsSync(messagesDirPath_theme)) {
break i18n_for_spas_and_native;
}
const propertiesByLang: Record<
string,
{
base: Buffer;
override: Buffer | undefined;
overrideByThemeName: Record<string, Buffer>;
}
> = {};
fs.readdirSync(messagesDirPath_theme).forEach(basename => {
type ParsedBasename = { lang: string } & (
| {
isOverride: false;
}
| {
isOverride: true;
themeName: string | undefined;
}
);
const parsedBasename = ((): ParsedBasename | undefined => {
const match = basename.match(/^messages_([^.]+)\.properties$/);
if (match === null) {
return undefined;
}
const discriminator = match[1];
const split = discriminator.split("_override");
if (split.length === 1) {
return {
lang: discriminator,
isOverride: false
};
}
assert(split.length === 2);
if (split[1] === "") {
return {
lang: split[0],
isOverride: true,
themeName: undefined
};
}
const match2 = split[1].match(/^_(.+)$/);
assert(match2 !== null);
return {
lang: split[0],
isOverride: true,
themeName: match2[1]
};
})();
if (parsedBasename === undefined) {
return;
}
propertiesByLang[parsedBasename.lang] ??= {
base: createObjectThatThrowsIfAccessed<Buffer>({
debugMessage: `No base ${parsedBasename.lang} translation for ${themeType} theme`
}),
override: undefined,
overrideByThemeName: {}
};
const buffer = fs.readFileSync(pathJoin(messagesDirPath_theme, basename));
if (parsedBasename.isOverride === false) {
propertiesByLang[parsedBasename.lang].base = buffer;
return;
}
if (parsedBasename.themeName === undefined) {
propertiesByLang[parsedBasename.lang].override = buffer;
return;
}
propertiesByLang[parsedBasename.lang].overrideByThemeName[
parsedBasename.themeName
] = buffer;
});
languageTags = Object.keys(propertiesByLang);
writeMessagePropertiesFilesByThemeType[themeType] = ({
messageDirPath,
themeName
}) => {
if (!fs.existsSync(messageDirPath)) {
fs.mkdirSync(messageDirPath, { recursive: true });
}
Object.entries(propertiesByLang).forEach(
([lang, { base, override, overrideByThemeName }]) => {
const messages = propertiesParser.parse(base.toString("utf8"));
if (override !== undefined) {
const overrideMessages = propertiesParser.parse(
override.toString("utf8")
);
Object.entries(overrideMessages).forEach(
([key, value]) => (messages[key] = value)
);
}
if (themeName in overrideByThemeName) {
const overrideMessages = propertiesParser.parse(
overrideByThemeName[themeName].toString("utf8")
);
Object.entries(overrideMessages).forEach(
([key, value]) => (messages[key] = value)
);
}
const editor = propertiesParser.createEditor();
Object.entries(messages).forEach(([key, value]) => {
editor.set(key, value);
});
fs.writeFileSync(
pathJoin(messageDirPath, `messages_${lang}.properties`),
Buffer.from(editor.toString(), "utf8")
);
}
);
};
}
keycloak_static_resources: {
if (isNative) {
break keycloak_static_resources;
}
if (isSpa) {
break keycloak_static_resources;
}
transformCodebase({
srcDirPath: pathJoin(
getThisCodebaseRootDirPath(),
"res",
"public",
WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES,
themeType
),
destDirPath: pathJoin(themeTypeDirPath, "resources")
});
}
bring_in_account_v1: {
if (isNative) {
break bring_in_account_v1;
}
if (themeType !== "account") {
break bring_in_account_v1;
}
assert(buildContext.implementedThemeTypes.account.isImplemented);
if (buildContext.implementedThemeTypes.account.type !== "Multi-Page") {
break bring_in_account_v1;
}
transformCodebase({
srcDirPath: pathJoin(getThisCodebaseRootDirPath(), "res", "account-v1"),
destDirPath: getThemeTypeDirPath({
themeName: "account-v1",
themeType: "account"
})
});
}
generate_theme_properties: {
if (isNative) {
break generate_theme_properties;
}
assert(themeType !== "email");
fs.writeFileSync(
pathJoin(themeTypeDirPath, "theme.properties"),
Buffer.from(
[
`parent=${(() => {
switch (themeType) {
case "account":
switch (getAccountThemeType()) {
case "Multi-Page":
return "account-v1";
case "Single-Page":
return "base";
}
case "login":
return "keycloak";
case "admin":
return "base";
}
assert<Equals<typeof themeType, never>>;
})()}`,
...(themeType === "account" &&
getAccountThemeType() === "Single-Page"
? ["deprecatedMode=false"]
: []),
...(buildContext.extraThemeProperties ?? []),
...[
...buildContext.environmentVariables,
{ name: KEYCLOAKIFY_SPA_DEV_SERVER_PORT, default: "" }
].map(
({ name, default: defaultValue }) =>
`${name}=\${env.${name}:${escapeStringForPropertiesFile(defaultValue)}}`
),
...(languageTags === undefined
? []
: [`locales=${languageTags.join(",")}`])
].join("\n\n"),
"utf8"
)
);
}
}
for (const themeVariantName of [...buildContext.themeNames].reverse()) {
for (const themeType of [...THEME_TYPES, "email"] as const) {
copy_main_theme_to_theme_variant_theme: {
let isNative: boolean;
{
const v = buildContext.implementedThemeTypes[themeType];
if (!v.isImplemented && !v.isImplemented_native) {
break copy_main_theme_to_theme_variant_theme;
}
isNative = !v.isImplemented && v.isImplemented_native;
}
if (!isNative && themeVariantName === themeName) {
break copy_main_theme_to_theme_variant_theme;
}
transformCodebase({
srcDirPath: getThemeTypeDirPath({ themeName, themeType }),
destDirPath: getThemeTypeDirPath({
themeName: themeVariantName,
themeType
}),
transformSourceCode: ({ fileRelativePath, sourceCode }) => {
patch_xKeycloakify_themeName: {
if (!fileRelativePath.endsWith(".ftl")) {
break patch_xKeycloakify_themeName;
}
if (
!isNative &&
pathBasename(fileRelativePath) !== fileRelativePath
) {
break patch_xKeycloakify_themeName;
}
const modifiedSourceCode = Buffer.from(
Buffer.from(sourceCode)
.toString("utf-8")
.replace(
...id<[string | RegExp, string]>(
isNative
? [
/xKeycloakify\.themeName/g,
`"${themeVariantName}"`
]
: [
`"themeName": "${themeName}"`,
`"themeName": "${themeVariantName}"`
]
)
),
"utf8"
);
if (!isNative) {
assert(
Buffer.compare(modifiedSourceCode, sourceCode) !== 0
);
}
return { modifiedSourceCode };
}
return { modifiedSourceCode: sourceCode };
}
});
}
run_writeMessagePropertiesFiles: {
const writeMessagePropertiesFiles =
writeMessagePropertiesFilesByThemeType[themeType];
if (writeMessagePropertiesFiles === undefined) {
break run_writeMessagePropertiesFiles;
}
writeMessagePropertiesFiles({
messageDirPath: pathJoin(
getThemeTypeDirPath({ themeName: themeVariantName, themeType }),
"messages"
),
themeName: themeVariantName
});
}
}
}
console.log(`Generated resources in ${Date.now() - start}ms`);
}

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