Compare commits
1 Commits
v7.0.0-rc.
...
v0.0.8
Author | SHA1 | Date | |
---|---|---|---|
f6b6c9e7d0 |
3
.gitattributes
vendored
3
.gitattributes
vendored
@ -1,3 +0,0 @@
|
|||||||
src/lib/i18n/generated_kcMessages/* linguist-documentation
|
|
||||||
src/bin/keycloakify/index.ts -linguist-detectable
|
|
||||||
src/bin/install-builtin-keycloak-themes.ts -linguist-detectable
|
|
4
.github/FUNDING.yaml
vendored
4
.github/FUNDING.yaml
vendored
@ -1,4 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: [garronej]
|
|
||||||
custom: ['https://www.ringerhq.com/experts/garronej']
|
|
25
.github/release.yaml
vendored
25
.github/release.yaml
vendored
@ -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:
|
|
||||||
- '*'
|
|
139
.github/workflows/ci.yaml
vendored
139
.github/workflows/ci.yaml
vendored
@ -1,139 +0,0 @@
|
|||||||
name: ci
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
test_lint:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ !github.event.created && github.repository != 'garronej/ts-ci' }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
- 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
|
|
||||||
test:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
needs: test_lint
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node: [ '14','16' ]
|
|
||||||
os: [ windows-latest, 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
|
|
||||||
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
|
|
||||||
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.
|
|
||||||
if: |
|
|
||||||
github.event_name == 'push' ||
|
|
||||||
github.event.pull_request.head.repo.owner.login == github.event.pull_request.base.repo.owner.login
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: test
|
|
||||||
outputs:
|
|
||||||
from_version: ${{ steps.step1.outputs.from_version }}
|
|
||||||
to_version: ${{ steps.step1.outputs.to_version }}
|
|
||||||
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
|
|
||||||
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.
|
|
||||||
if: |
|
|
||||||
needs.check_if_version_upgraded.outputs.is_upgraded_version == 'true' &&
|
|
||||||
(
|
|
||||||
github.event_name == 'push' ||
|
|
||||||
needs.check_if_version_upgraded.outputs.is_pre_release == 'true'
|
|
||||||
)
|
|
||||||
needs:
|
|
||||||
- check_if_version_upgraded
|
|
||||||
steps:
|
|
||||||
- uses: softprops/action-gh-release@v1
|
|
||||||
with:
|
|
||||||
name: Release v${{ needs.check_if_version_upgraded.outputs.to_version }}
|
|
||||||
tag_name: v${{ needs.check_if_version_upgraded.outputs.to_version }}
|
|
||||||
target_commitish: ${{ github.head_ref || github.ref }}
|
|
||||||
generate_release_notes: true
|
|
||||||
draft: false
|
|
||||||
prerelease: ${{ needs.check_if_version_upgraded.outputs.is_pre_release == 'true' }}
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
publish_on_npm:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- create_github_release
|
|
||||||
- check_if_version_upgraded
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
ref: ${{ github.ref }}
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
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
|
|
||||||
env:
|
|
||||||
DRY_RUN: "0"
|
|
||||||
- name: Publishing on NPM
|
|
||||||
run: |
|
|
||||||
if [ "$(npm show . version)" = "$VERSION" ]; then
|
|
||||||
echo "This version is already published"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
if [ "$NODE_AUTH_TOKEN" = "" ]; then
|
|
||||||
echo "Can't publish on NPM, You must first create a secret called NPM_TOKEN that contains your NPM auth token. https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets"
|
|
||||||
false
|
|
||||||
fi
|
|
||||||
EXTRA_ARGS=""
|
|
||||||
if [ "$IS_PRE_RELEASE" = "true" ]; then
|
|
||||||
EXTRA_ARGS="--tag next"
|
|
||||||
fi
|
|
||||||
npm publish $EXTRA_ARGS
|
|
||||||
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 }}
|
|
7
.gitignore
vendored
7
.gitignore
vendored
@ -41,12 +41,5 @@ jspm_packages
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
/dist
|
/dist
|
||||||
/dist_test
|
|
||||||
|
|
||||||
/sample_react_project/
|
/sample_react_project/
|
||||||
/.yarn_home/
|
|
||||||
|
|
||||||
.idea
|
|
||||||
|
|
||||||
/keycloak_email
|
|
||||||
/build_keycloak
|
|
@ -1,11 +0,0 @@
|
|||||||
node_modules/
|
|
||||||
/dist/
|
|
||||||
/dist_test/
|
|
||||||
/CHANGELOG.md
|
|
||||||
/.yarn_home/
|
|
||||||
/src/test/apps/
|
|
||||||
/src/tools/types/
|
|
||||||
/sample_react_project
|
|
||||||
/build_keycloak/
|
|
||||||
/src/i18n/generated_messages/
|
|
||||||
/.vscode/
|
|
@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"printWidth": 150,
|
|
||||||
"tabWidth": 4,
|
|
||||||
"useTabs": false,
|
|
||||||
"semi": true,
|
|
||||||
"singleQuote": false,
|
|
||||||
"quoteProps": "preserve",
|
|
||||||
"trailingComma": "none",
|
|
||||||
"bracketSpacing": true,
|
|
||||||
"arrowParens": "avoid"
|
|
||||||
}
|
|
39
CHANGELOG.md
Normal file
39
CHANGELOG.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
### **0.0.8** (2021-02-21)
|
||||||
|
|
||||||
|
- Include theme properties
|
||||||
|
|
||||||
|
### **0.0.7** (2021-02-21)
|
||||||
|
|
||||||
|
- fix build
|
||||||
|
- Fix bundle
|
||||||
|
|
||||||
|
### **0.0.6** (2021-02-21)
|
||||||
|
|
||||||
|
- Include missing files in the release bundle
|
||||||
|
|
||||||
|
### **0.0.5** (2021-02-21)
|
||||||
|
|
||||||
|
- Bump version number
|
||||||
|
- Make the install faster
|
||||||
|
|
||||||
|
### **0.0.4** (2021-02-21)
|
||||||
|
|
||||||
|
- Fix script visibility
|
||||||
|
|
||||||
|
### **0.0.3** (2021-02-21)
|
||||||
|
|
||||||
|
- Do not run tests on window
|
||||||
|
- Add script for downloading base themes
|
||||||
|
- Generate debug files to be able to test the container
|
||||||
|
- Fix many little bugs
|
||||||
|
- refactor
|
||||||
|
- Almoste there
|
||||||
|
- Things are starting to take form
|
||||||
|
- Seems to be working
|
||||||
|
- First draft
|
||||||
|
- Remove eslint and prettyer
|
||||||
|
|
||||||
|
### **0.0.2** (2021-02-20)
|
||||||
|
|
||||||
|
- Update package.json
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
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.
|
|
197
README.md
197
README.md
@ -1,191 +1,40 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://user-images.githubusercontent.com/6702424/109387840-eba11f80-7903-11eb-9050-db1dad883f78.png">
|
<img src="https://user-images.githubusercontent.com/6702424/80216211-00ef5280-863e-11ea-81de-59f3a3d4b8e4.png">
|
||||||
</p>
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<i>🔏 Create Keycloak themes using React 🔏</i>
|
<i>Provides a way to customize Keycloak login and register pages with React</i>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<a href="https://github.com/garronej/keycloakify/actions">
|
<img src="https://github.com/garronej/keycloak-react-theming/workflows/ci/badge.svg?branch=develop">
|
||||||
<img src="https://github.com/garronej/keycloakify/workflows/ci/badge.svg?branch=main">
|
<img src="https://img.shields.io/bundlephobia/minzip/keycloak-react-theming">
|
||||||
</a>
|
<img src="https://img.shields.io/npm/dw/keycloak-react-theming">
|
||||||
<a href="https://www.npmjs.com/package/keycloakify">
|
<img src="https://img.shields.io/npm/l/keycloak-react-theming">
|
||||||
<img src="https://img.shields.io/npm/dm/keycloakify">
|
|
||||||
</a>
|
|
||||||
<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">
|
|
||||||
<a href="https://www.keycloakify.dev">Home</a>
|
|
||||||
-
|
|
||||||
<a href="https://docs.keycloakify.dev">Documentation</a>
|
|
||||||
-
|
|
||||||
<a href="https://github.com/codegouvfr/keycloakify-starter">Starter project</a>
|
|
||||||
</p>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<i>Ultimately this build tool generates a Keycloak theme <a href="https://www.keycloakify.dev">Learn more</a></i>
|
<a href="https://github.com/garronej/keycloak-react-theming">Home</a>
|
||||||
<img src="https://user-images.githubusercontent.com/6702424/110260457-a1c3d380-7fac-11eb-853a-80459b65626b.png">
|
-
|
||||||
|
<a href="https://github.com/garronej/keycloak-react-theming">Documentation</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
> 🗣 V6 have been released 🎉
|
# REQUIREMENT
|
||||||
> [It features major improvements](https://github.com/InseeFrLab/keycloakify#600).
|
## For building the theme:
|
||||||
> Checkout [the migration guide](https://docs.keycloakify.dev/v5-to-v6).
|
|
||||||
|
|
||||||
# Changelog highlights
|
- `mvn` must be installed
|
||||||
|
|
||||||
## 6.13
|
## For development, (testing the theme on a docker container ect ):
|
||||||
|
|
||||||
- Build work behind corporate proxies, [see issue](https://github.com/InseeFrLab/keycloakify/issues/257).
|
- `rm`
|
||||||
|
- `mkdir` )
|
||||||
|
- `wget`
|
||||||
|
- `unzip`
|
||||||
|
|
||||||
## 6.12
|
Tested on MacOS
|
||||||
|
|
||||||
Massive improvement in the developer experience:
|
# USAGE
|
||||||
|
|
||||||
- There is now only one starter repo: https://github.com/codegouvfr/keycloakify-starter
|
## Build the theme:
|
||||||
- A lot of comments have been added in the code of the starter to make it easier to get started.
|
`npx build-keycloak-theme`
|
||||||
- The doc has been updated: https://docs.keycloakify.dev
|
|
||||||
- A lot of improvements in the type system.
|
|
||||||
|
|
||||||
## 6.11.4
|
## (Optional/Debug) Download more themes:
|
||||||
|
|
||||||
- You no longer need to have Maven installed to build the theme. Thanks to @lordvlad, [see PR](https://github.com/InseeFrLab/keycloakify/pull/239).
|
`npx download-sample-keycloak-themes`
|
||||||
- Feature new build options: [`bundler`](https://docs.keycloakify.dev/build-options#keycloakify.bundler), [`groupId`](https://docs.keycloakify.dev/build-options#keycloakify.groupid), [`artifactId`](https://docs.keycloakify.dev/build-options#keycloakify.artifactid), [`version`](https://docs.keycloakify.dev/build-options#version).
|
|
||||||
Theses options can be user to customize the output name of the .jar. You can use environnement variables to overrides the values read in the package.json. Thanks to @lordvlad.
|
|
||||||
|
|
||||||
## 6.10.0
|
|
||||||
|
|
||||||
- Widows compat (thanks to @lordvlad, [see PR](https://github.com/InseeFrLab/keycloakify/pull/226)). WSL is no longer required 🎉
|
|
||||||
|
|
||||||
## 6.8.4
|
|
||||||
|
|
||||||
- `@emotion/react` is no longer a peer dependency of Keycloakify.
|
|
||||||
|
|
||||||
## 6.8.0
|
|
||||||
|
|
||||||
- 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).
|
|
||||||
|
|
||||||
## 6.7.0
|
|
||||||
|
|
||||||
- Add support for `webauthn-authenticate.ftl` thanks to [@mstrodl](https://github.com/Mstrodl)'s hacktoberfest [PR](https://github.com/InseeFrLab/keycloakify/pull/185).
|
|
||||||
|
|
||||||
## 6.6.0
|
|
||||||
|
|
||||||
- Add support for `login-password.ftl` thanks to [@mstrodl](https://github.com/Mstrodl)'s hacktoberfest [PR](https://github.com/InseeFrLab/keycloakify/pull/184).
|
|
||||||
|
|
||||||
## 6.5.0
|
|
||||||
|
|
||||||
- Add support for `login-username.ftl` thanks to [@mstrodl](https://github.com/Mstrodl)'s hacktoberfest [PR](https://github.com/InseeFrLab/keycloakify/pull/183).
|
|
||||||
|
|
||||||
## 6.4.0
|
|
||||||
|
|
||||||
- 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).
|
|
||||||
|
|
||||||
## 6.0.0
|
|
||||||
|
|
||||||
- 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.
|
|
||||||
|
|
||||||
Checkout [the migration guide](https://docs.keycloakify.dev/v5-to-v6)
|
|
||||||
|
|
||||||
## 5.8.0
|
|
||||||
|
|
||||||
- [React.lazy()](https://reactjs.org/docs/code-splitting.html#reactlazy) support 🎉. [#141](https://github.com/InseeFrLab/keycloakify/issues/141)
|
|
||||||
|
|
||||||
## 5.7.0
|
|
||||||
|
|
||||||
- Feat `logout-confirm.ftl`. [PR](https://github.com/InseeFrLab/keycloakify/pull/120)
|
|
||||||
|
|
||||||
## 5.6.4
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
## v5.6.0
|
|
||||||
|
|
||||||
Add support for `login-config-totp.ftl` page [#127](https://github.com/InseeFrLab/keycloakify/pull/127).
|
|
||||||
|
|
||||||
## v5.3.0
|
|
||||||
|
|
||||||
Rename `keycloak_theme_email` to `keycloak_email`.
|
|
||||||
If you already had a `keycloak_theme_email` you should rename it `keycloak_email`.
|
|
||||||
|
|
||||||
## v5.0.0
|
|
||||||
|
|
||||||
[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).
|
|
||||||
|
|
||||||
## v4.10.0
|
|
||||||
|
|
||||||
Add `login-idp-link-email.ftl` page [See PR](https://github.com/InseeFrLab/keycloakify/pull/92).
|
|
||||||
|
|
||||||
## v4.8.0
|
|
||||||
|
|
||||||
[Email template customization.](#email-template-customization)
|
|
||||||
|
|
||||||
## v4.7.4
|
|
||||||
|
|
||||||
**M1 Mac** support (for testing locally with a dockerized Keycloak).
|
|
||||||
|
|
||||||
## 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`).
|
|
||||||
|
5
bin/build-keycloak-theme/generateDebugFiles/index.d.ts
vendored
Normal file
5
bin/build-keycloak-theme/generateDebugFiles/index.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/** Files for being able to run a hot reload keycloak container */
|
||||||
|
export declare function generateDebugFiles(params: {
|
||||||
|
packageJsonName: string;
|
||||||
|
keycloakThemeBuildingDirPath: string;
|
||||||
|
}): void;
|
65
bin/build-keycloak-theme/generateDebugFiles/index.js
Normal file
65
bin/build-keycloak-theme/generateDebugFiles/index.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
"use strict";
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.generateDebugFiles = void 0;
|
||||||
|
var fs = __importStar(require("fs"));
|
||||||
|
var path_1 = require("path");
|
||||||
|
/** Files for being able to run a hot reload keycloak container */
|
||||||
|
function generateDebugFiles(params) {
|
||||||
|
var packageJsonName = params.packageJsonName, keycloakThemeBuildingDirPath = params.keycloakThemeBuildingDirPath;
|
||||||
|
fs.writeFileSync(path_1.join(keycloakThemeBuildingDirPath, "Dockerfile"), Buffer.from([
|
||||||
|
"FROM jboss/keycloak:11.0.3",
|
||||||
|
"",
|
||||||
|
"USER root",
|
||||||
|
"",
|
||||||
|
"WORKDIR /",
|
||||||
|
"",
|
||||||
|
"ADD configuration /opt/jboss/keycloak/standalone/configuration/",
|
||||||
|
"",
|
||||||
|
'ENTRYPOINT [ "/opt/jboss/tools/docker-entrypoint.sh" ]',
|
||||||
|
].join("\n"), "utf8"));
|
||||||
|
var dockerImage = packageJsonName + "/keycloak-hot-reload";
|
||||||
|
var containerName = "keycloak-testing-container";
|
||||||
|
fs.writeFileSync(path_1.join(keycloakThemeBuildingDirPath, "start_keycloak_testing_container.sh"), Buffer.from([
|
||||||
|
"#!/bin/bash",
|
||||||
|
"",
|
||||||
|
"docker rm " + containerName + " || true",
|
||||||
|
"",
|
||||||
|
"docker build . -t " + dockerImage,
|
||||||
|
"",
|
||||||
|
"docker run \\",
|
||||||
|
" -p 8080:8080 \\",
|
||||||
|
"\t--name " + containerName + " \\",
|
||||||
|
" -e KEYCLOAK_USER=admin \\",
|
||||||
|
" -e KEYCLOAK_PASSWORD=admin \\",
|
||||||
|
"\t-v " + path_1.join(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", "onyxia") + ":/opt/jboss/keycloak/themes/onyxia:rw \\",
|
||||||
|
"\t-it " + dockerImage + ":latest",
|
||||||
|
""
|
||||||
|
].join("\n"), "utf8"), { "mode": 493 });
|
||||||
|
var standaloneHaFilePath = path_1.join(keycloakThemeBuildingDirPath, "configuration", "standalone-ha.xml");
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(path_1.dirname(standaloneHaFilePath));
|
||||||
|
}
|
||||||
|
catch (_a) { }
|
||||||
|
fs.writeFileSync(standaloneHaFilePath, fs.readFileSync(path_1.join(__dirname, path_1.basename(standaloneHaFilePath))));
|
||||||
|
}
|
||||||
|
exports.generateDebugFiles = generateDebugFiles;
|
||||||
|
//# sourceMappingURL=index.js.map
|
1
bin/build-keycloak-theme/generateDebugFiles/index.js.map
Normal file
1
bin/build-keycloak-theme/generateDebugFiles/index.js.map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/bin/build-keycloak-theme/generateDebugFiles/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AACA,qCAAyB;AACzB,6BAA0F;AAE1F,kEAAkE;AAClE,SAAgB,kBAAkB,CAC9B,MAGC;IAGO,IAAA,eAAe,GAAmC,MAAM,gBAAzC,EAAE,4BAA4B,GAAK,MAAM,6BAAX,CAAY;IAEjE,EAAE,CAAC,aAAa,CACZ,WAAQ,CAAC,4BAA4B,EAAE,YAAY,CAAC,EACpD,MAAM,CAAC,IAAI,CACP;QACI,4BAA4B;QAC5B,EAAE;QACF,WAAW;QACX,EAAE;QACF,WAAW;QACX,EAAE;QACF,iEAAiE;QACjE,EAAE;QACF,wDAAwD;KAC3D,CAAC,IAAI,CAAC,IAAI,CAAC,EACZ,MAAM,CACT,CACJ,CAAC;IAEF,IAAM,WAAW,GAAM,eAAe,yBAAsB,CAAC;IAC7D,IAAM,aAAa,GAAG,4BAA4B,CAAC;IAEnD,EAAE,CAAC,aAAa,CACZ,WAAQ,CAAC,4BAA4B,EAAE,qCAAqC,CAAC,EAC7E,MAAM,CAAC,IAAI,CACP;QACI,aAAa;QACb,EAAE;QACF,eAAa,aAAa,aAAU;QACpC,EAAE;QACF,uBAAqB,WAAa;QAClC,EAAE;QACF,eAAe;QACf,kBAAkB;QAClB,cAAW,aAAa,QAAK;QAC7B,4BAA4B;QAC5B,gCAAgC;QAChC,UAAO,WAAQ,CAAC,4BAA4B,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,6CAA0C;QACtI,WAAQ,WAAW,YAAS;QAC5B,EAAE;KACL,CAAC,IAAI,CAAC,IAAI,CAAC,EACZ,MAAM,CACT,EACD,EAAE,MAAM,EAAE,GAAK,EAAE,CACpB,CAAC;IAEF,IAAM,oBAAoB,GAAG,WAAQ,CAAC,4BAA4B,EAAE,eAAe,EAAE,mBAAmB,CAAC,CAAC;IAE1G,IAAI;QAAE,EAAE,CAAC,SAAS,CAAC,cAAW,CAAC,oBAAoB,CAAC,CAAC,CAAC;KAAE;IAAC,WAAM,GAAG;IAElE,EAAE,CAAC,aAAa,CACZ,oBAAoB,EACpB,EAAE,CAAC,YAAY,CAAC,WAAQ,CAAC,SAAS,EAAE,eAAY,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAC3E,CAAC;AAEN,CAAC;AA/DD,gDA+DC"}
|
666
bin/build-keycloak-theme/generateDebugFiles/standalone-ha.xml
Normal file
666
bin/build-keycloak-theme/generateDebugFiles/standalone-ha.xml
Normal file
@ -0,0 +1,666 @@
|
|||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
|
||||||
|
<server xmlns="urn:jboss:domain:13.0">
|
||||||
|
<extensions>
|
||||||
|
<extension module="org.jboss.as.clustering.infinispan"/>
|
||||||
|
<extension module="org.jboss.as.clustering.jgroups"/>
|
||||||
|
<extension module="org.jboss.as.connector"/>
|
||||||
|
<extension module="org.jboss.as.deployment-scanner"/>
|
||||||
|
<extension module="org.jboss.as.ee"/>
|
||||||
|
<extension module="org.jboss.as.ejb3"/>
|
||||||
|
<extension module="org.jboss.as.jaxrs"/>
|
||||||
|
<extension module="org.jboss.as.jmx"/>
|
||||||
|
<extension module="org.jboss.as.jpa"/>
|
||||||
|
<extension module="org.jboss.as.logging"/>
|
||||||
|
<extension module="org.jboss.as.mail"/>
|
||||||
|
<extension module="org.jboss.as.modcluster"/>
|
||||||
|
<extension module="org.jboss.as.naming"/>
|
||||||
|
<extension module="org.jboss.as.remoting"/>
|
||||||
|
<extension module="org.jboss.as.security"/>
|
||||||
|
<extension module="org.jboss.as.transactions"/>
|
||||||
|
<extension module="org.jboss.as.weld"/>
|
||||||
|
<extension module="org.keycloak.keycloak-server-subsystem"/>
|
||||||
|
<extension module="org.wildfly.extension.bean-validation"/>
|
||||||
|
<extension module="org.wildfly.extension.core-management"/>
|
||||||
|
<extension module="org.wildfly.extension.elytron"/>
|
||||||
|
<extension module="org.wildfly.extension.io"/>
|
||||||
|
<extension module="org.wildfly.extension.microprofile.config-smallrye"/>
|
||||||
|
<extension module="org.wildfly.extension.microprofile.health-smallrye"/>
|
||||||
|
<extension module="org.wildfly.extension.microprofile.metrics-smallrye"/>
|
||||||
|
<extension module="org.wildfly.extension.request-controller"/>
|
||||||
|
<extension module="org.wildfly.extension.security.manager"/>
|
||||||
|
<extension module="org.wildfly.extension.undertow"/>
|
||||||
|
</extensions>
|
||||||
|
<management>
|
||||||
|
<security-realms>
|
||||||
|
<security-realm name="ManagementRealm">
|
||||||
|
<authentication>
|
||||||
|
<local default-user="$local" skip-group-loading="true"/>
|
||||||
|
<properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
|
||||||
|
</authentication>
|
||||||
|
<authorization map-groups-to-roles="false">
|
||||||
|
<properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
|
||||||
|
</authorization>
|
||||||
|
</security-realm>
|
||||||
|
<security-realm name="ApplicationRealm">
|
||||||
|
<server-identities>
|
||||||
|
<ssl>
|
||||||
|
<keystore path="application.keystore" relative-to="jboss.server.config.dir" keystore-password="password" alias="server" key-password="password" generate-self-signed-certificate-host="localhost"/>
|
||||||
|
</ssl>
|
||||||
|
</server-identities>
|
||||||
|
<authentication>
|
||||||
|
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
|
||||||
|
<properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
|
||||||
|
</authentication>
|
||||||
|
<authorization>
|
||||||
|
<properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
|
||||||
|
</authorization>
|
||||||
|
</security-realm>
|
||||||
|
</security-realms>
|
||||||
|
<audit-log>
|
||||||
|
<formatters>
|
||||||
|
<json-formatter name="json-formatter"/>
|
||||||
|
</formatters>
|
||||||
|
<handlers>
|
||||||
|
<file-handler name="file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.server.data.dir"/>
|
||||||
|
</handlers>
|
||||||
|
<logger log-boot="true" log-read-only="false" enabled="false">
|
||||||
|
<handlers>
|
||||||
|
<handler name="file"/>
|
||||||
|
</handlers>
|
||||||
|
</logger>
|
||||||
|
</audit-log>
|
||||||
|
<management-interfaces>
|
||||||
|
<http-interface security-realm="ManagementRealm">
|
||||||
|
<http-upgrade enabled="true"/>
|
||||||
|
<socket-binding http="management-http"/>
|
||||||
|
</http-interface>
|
||||||
|
</management-interfaces>
|
||||||
|
<access-control provider="simple">
|
||||||
|
<role-mapping>
|
||||||
|
<role name="SuperUser">
|
||||||
|
<include>
|
||||||
|
<user name="$local"/>
|
||||||
|
</include>
|
||||||
|
</role>
|
||||||
|
</role-mapping>
|
||||||
|
</access-control>
|
||||||
|
</management>
|
||||||
|
<profile>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:logging:8.0">
|
||||||
|
<console-handler name="CONSOLE">
|
||||||
|
<formatter>
|
||||||
|
<named-formatter name="COLOR-PATTERN"/>
|
||||||
|
</formatter>
|
||||||
|
</console-handler>
|
||||||
|
<logger category="com.arjuna">
|
||||||
|
<level name="WARN"/>
|
||||||
|
</logger>
|
||||||
|
<logger category="io.jaegertracing.Configuration">
|
||||||
|
<level name="WARN"/>
|
||||||
|
</logger>
|
||||||
|
<logger category="org.jboss.as.config">
|
||||||
|
<level name="DEBUG"/>
|
||||||
|
</logger>
|
||||||
|
<logger category="sun.rmi">
|
||||||
|
<level name="WARN"/>
|
||||||
|
</logger>
|
||||||
|
<logger category="org.keycloak">
|
||||||
|
<level name="${env.KEYCLOAK_LOGLEVEL:INFO}"/>
|
||||||
|
</logger>
|
||||||
|
<root-logger>
|
||||||
|
<level name="${env.ROOT_LOGLEVEL:INFO}"/>
|
||||||
|
<handlers>
|
||||||
|
<handler name="CONSOLE"/>
|
||||||
|
</handlers>
|
||||||
|
</root-logger>
|
||||||
|
<formatter name="PATTERN">
|
||||||
|
<pattern-formatter pattern="%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
|
||||||
|
</formatter>
|
||||||
|
<formatter name="COLOR-PATTERN">
|
||||||
|
<pattern-formatter pattern="%K{level}%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
|
||||||
|
</formatter>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:bean-validation:1.0"/>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:core-management:1.0"/>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:datasources:6.0">
|
||||||
|
<datasources>
|
||||||
|
<datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
|
||||||
|
<connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
|
||||||
|
<driver>h2</driver>
|
||||||
|
<security>
|
||||||
|
<user-name>sa</user-name>
|
||||||
|
<password>sa</password>
|
||||||
|
</security>
|
||||||
|
</datasource>
|
||||||
|
<datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
|
||||||
|
<connection-url>jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE</connection-url>
|
||||||
|
<driver>h2</driver>
|
||||||
|
<pool>
|
||||||
|
<max-pool-size>100</max-pool-size>
|
||||||
|
</pool>
|
||||||
|
<security>
|
||||||
|
<user-name>sa</user-name>
|
||||||
|
<password>sa</password>
|
||||||
|
</security>
|
||||||
|
</datasource>
|
||||||
|
<drivers>
|
||||||
|
<driver name="h2" module="com.h2database.h2">
|
||||||
|
<xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
|
||||||
|
</driver>
|
||||||
|
</drivers>
|
||||||
|
</datasources>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:deployment-scanner:2.0">
|
||||||
|
<deployment-scanner path="deployments" relative-to="jboss.server.base.dir" scan-interval="5000" runtime-failure-causes-rollback="${jboss.deployment.scanner.rollback.on.failure:false}"/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:ee:5.0">
|
||||||
|
<spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
|
||||||
|
<concurrent>
|
||||||
|
<context-services>
|
||||||
|
<context-service name="default" jndi-name="java:jboss/ee/concurrency/context/default" use-transaction-setup-provider="true"/>
|
||||||
|
</context-services>
|
||||||
|
<managed-thread-factories>
|
||||||
|
<managed-thread-factory name="default" jndi-name="java:jboss/ee/concurrency/factory/default" context-service="default"/>
|
||||||
|
</managed-thread-factories>
|
||||||
|
<managed-executor-services>
|
||||||
|
<managed-executor-service name="default" jndi-name="java:jboss/ee/concurrency/executor/default" context-service="default" hung-task-threshold="60000" keepalive-time="5000"/>
|
||||||
|
</managed-executor-services>
|
||||||
|
<managed-scheduled-executor-services>
|
||||||
|
<managed-scheduled-executor-service name="default" jndi-name="java:jboss/ee/concurrency/scheduler/default" context-service="default" hung-task-threshold="60000" keepalive-time="3000"/>
|
||||||
|
</managed-scheduled-executor-services>
|
||||||
|
</concurrent>
|
||||||
|
<default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/ExampleDS" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:ejb3:7.0">
|
||||||
|
<session-bean>
|
||||||
|
<stateless>
|
||||||
|
<bean-instance-pool-ref pool-name="slsb-strict-max-pool"/>
|
||||||
|
</stateless>
|
||||||
|
<stateful default-access-timeout="5000" cache-ref="distributable" passivation-disabled-cache-ref="simple"/>
|
||||||
|
<singleton default-access-timeout="5000"/>
|
||||||
|
</session-bean>
|
||||||
|
<pools>
|
||||||
|
<bean-instance-pools>
|
||||||
|
<strict-max-pool name="mdb-strict-max-pool" derive-size="from-cpu-count" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
|
||||||
|
<strict-max-pool name="slsb-strict-max-pool" derive-size="from-worker-pools" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
|
||||||
|
</bean-instance-pools>
|
||||||
|
</pools>
|
||||||
|
<caches>
|
||||||
|
<cache name="simple"/>
|
||||||
|
<cache name="distributable" passivation-store-ref="infinispan" aliases="passivating clustered"/>
|
||||||
|
</caches>
|
||||||
|
<passivation-stores>
|
||||||
|
<passivation-store name="infinispan" cache-container="ejb" max-size="10000"/>
|
||||||
|
</passivation-stores>
|
||||||
|
<async thread-pool-name="default"/>
|
||||||
|
<timer-service thread-pool-name="default" default-data-store="default-file-store">
|
||||||
|
<data-stores>
|
||||||
|
<file-data-store name="default-file-store" path="timer-service-data" relative-to="jboss.server.data.dir"/>
|
||||||
|
</data-stores>
|
||||||
|
</timer-service>
|
||||||
|
<remote connector-ref="http-remoting-connector" thread-pool-name="default">
|
||||||
|
<channel-creation-options>
|
||||||
|
<option name="MAX_OUTBOUND_MESSAGES" value="1234" type="remoting"/>
|
||||||
|
</channel-creation-options>
|
||||||
|
</remote>
|
||||||
|
<thread-pools>
|
||||||
|
<thread-pool name="default">
|
||||||
|
<max-threads count="10"/>
|
||||||
|
<keepalive-time time="60" unit="seconds"/>
|
||||||
|
</thread-pool>
|
||||||
|
</thread-pools>
|
||||||
|
<default-security-domain value="other"/>
|
||||||
|
<default-missing-method-permissions-deny-access value="true"/>
|
||||||
|
<statistics enabled="${wildfly.ejb3.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
|
||||||
|
<log-system-exceptions value="true"/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:wildfly:elytron:10.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
|
||||||
|
<providers>
|
||||||
|
<aggregate-providers name="combined-providers">
|
||||||
|
<providers name="elytron"/>
|
||||||
|
<providers name="openssl"/>
|
||||||
|
</aggregate-providers>
|
||||||
|
<provider-loader name="elytron" module="org.wildfly.security.elytron"/>
|
||||||
|
<provider-loader name="openssl" module="org.wildfly.openssl"/>
|
||||||
|
</providers>
|
||||||
|
<audit-logging>
|
||||||
|
<file-audit-log name="local-audit" path="audit.log" relative-to="jboss.server.log.dir" format="JSON"/>
|
||||||
|
</audit-logging>
|
||||||
|
<security-domains>
|
||||||
|
<security-domain name="ApplicationDomain" default-realm="ApplicationRealm" permission-mapper="default-permission-mapper">
|
||||||
|
<realm name="ApplicationRealm" role-decoder="groups-to-roles"/>
|
||||||
|
<realm name="local"/>
|
||||||
|
</security-domain>
|
||||||
|
<security-domain name="ManagementDomain" default-realm="ManagementRealm" permission-mapper="default-permission-mapper">
|
||||||
|
<realm name="ManagementRealm" role-decoder="groups-to-roles"/>
|
||||||
|
<realm name="local" role-mapper="super-user-mapper"/>
|
||||||
|
</security-domain>
|
||||||
|
</security-domains>
|
||||||
|
<security-realms>
|
||||||
|
<identity-realm name="local" identity="$local"/>
|
||||||
|
<properties-realm name="ApplicationRealm">
|
||||||
|
<users-properties path="application-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ApplicationRealm"/>
|
||||||
|
<groups-properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
|
||||||
|
</properties-realm>
|
||||||
|
<properties-realm name="ManagementRealm">
|
||||||
|
<users-properties path="mgmt-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ManagementRealm"/>
|
||||||
|
<groups-properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
|
||||||
|
</properties-realm>
|
||||||
|
</security-realms>
|
||||||
|
<mappers>
|
||||||
|
<simple-permission-mapper name="default-permission-mapper" mapping-mode="first">
|
||||||
|
<permission-mapping>
|
||||||
|
<principal name="anonymous"/>
|
||||||
|
<permission-set name="default-permissions"/>
|
||||||
|
</permission-mapping>
|
||||||
|
<permission-mapping match-all="true">
|
||||||
|
<permission-set name="login-permission"/>
|
||||||
|
<permission-set name="default-permissions"/>
|
||||||
|
</permission-mapping>
|
||||||
|
</simple-permission-mapper>
|
||||||
|
<constant-realm-mapper name="local" realm-name="local"/>
|
||||||
|
<simple-role-decoder name="groups-to-roles" attribute="groups"/>
|
||||||
|
<constant-role-mapper name="super-user-mapper">
|
||||||
|
<role name="SuperUser"/>
|
||||||
|
</constant-role-mapper>
|
||||||
|
</mappers>
|
||||||
|
<permission-sets>
|
||||||
|
<permission-set name="login-permission">
|
||||||
|
<permission class-name="org.wildfly.security.auth.permission.LoginPermission"/>
|
||||||
|
</permission-set>
|
||||||
|
<permission-set name="default-permissions">
|
||||||
|
<permission class-name="org.wildfly.extension.batch.jberet.deployment.BatchPermission" module="org.wildfly.extension.batch.jberet" target-name="*"/>
|
||||||
|
<permission class-name="org.wildfly.transaction.client.RemoteTransactionPermission" module="org.wildfly.transaction.client"/>
|
||||||
|
<permission class-name="org.jboss.ejb.client.RemoteEJBPermission" module="org.jboss.ejb-client"/>
|
||||||
|
</permission-set>
|
||||||
|
</permission-sets>
|
||||||
|
<http>
|
||||||
|
<http-authentication-factory name="management-http-authentication" security-domain="ManagementDomain" http-server-mechanism-factory="global">
|
||||||
|
<mechanism-configuration>
|
||||||
|
<mechanism mechanism-name="DIGEST">
|
||||||
|
<mechanism-realm realm-name="ManagementRealm"/>
|
||||||
|
</mechanism>
|
||||||
|
</mechanism-configuration>
|
||||||
|
</http-authentication-factory>
|
||||||
|
<provider-http-server-mechanism-factory name="global"/>
|
||||||
|
</http>
|
||||||
|
<sasl>
|
||||||
|
<sasl-authentication-factory name="application-sasl-authentication" sasl-server-factory="configured" security-domain="ApplicationDomain">
|
||||||
|
<mechanism-configuration>
|
||||||
|
<mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
|
||||||
|
<mechanism mechanism-name="DIGEST-MD5">
|
||||||
|
<mechanism-realm realm-name="ApplicationRealm"/>
|
||||||
|
</mechanism>
|
||||||
|
</mechanism-configuration>
|
||||||
|
</sasl-authentication-factory>
|
||||||
|
<sasl-authentication-factory name="management-sasl-authentication" sasl-server-factory="configured" security-domain="ManagementDomain">
|
||||||
|
<mechanism-configuration>
|
||||||
|
<mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
|
||||||
|
<mechanism mechanism-name="DIGEST-MD5">
|
||||||
|
<mechanism-realm realm-name="ManagementRealm"/>
|
||||||
|
</mechanism>
|
||||||
|
</mechanism-configuration>
|
||||||
|
</sasl-authentication-factory>
|
||||||
|
<configurable-sasl-server-factory name="configured" sasl-server-factory="elytron">
|
||||||
|
<properties>
|
||||||
|
<property name="wildfly.sasl.local-user.default-user" value="$local"/>
|
||||||
|
</properties>
|
||||||
|
</configurable-sasl-server-factory>
|
||||||
|
<mechanism-provider-filtering-sasl-server-factory name="elytron" sasl-server-factory="global">
|
||||||
|
<filters>
|
||||||
|
<filter provider-name="WildFlyElytron"/>
|
||||||
|
</filters>
|
||||||
|
</mechanism-provider-filtering-sasl-server-factory>
|
||||||
|
<provider-sasl-server-factory name="global"/>
|
||||||
|
</sasl>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:infinispan:10.0">
|
||||||
|
<cache-container name="keycloak" module="org.keycloak.keycloak-model-infinispan">
|
||||||
|
<transport lock-timeout="60000"/>
|
||||||
|
<local-cache name="realms">
|
||||||
|
<object-memory size="10000"/>
|
||||||
|
</local-cache>
|
||||||
|
<local-cache name="users">
|
||||||
|
<object-memory size="10000"/>
|
||||||
|
</local-cache>
|
||||||
|
<local-cache name="authorization">
|
||||||
|
<object-memory size="10000"/>
|
||||||
|
</local-cache>
|
||||||
|
<local-cache name="keys">
|
||||||
|
<object-memory size="1000"/>
|
||||||
|
<expiration max-idle="3600000"/>
|
||||||
|
</local-cache>
|
||||||
|
<replicated-cache name="work"/>
|
||||||
|
<distributed-cache name="sessions" owners="1"/>
|
||||||
|
<distributed-cache name="authenticationSessions" owners="1"/>
|
||||||
|
<distributed-cache name="offlineSessions" owners="1"/>
|
||||||
|
<distributed-cache name="clientSessions" owners="1"/>
|
||||||
|
<distributed-cache name="offlineClientSessions" owners="1"/>
|
||||||
|
<distributed-cache name="loginFailures" owners="1"/>
|
||||||
|
<distributed-cache name="actionTokens" owners="2">
|
||||||
|
<object-memory size="-1"/>
|
||||||
|
<expiration interval="300000" max-idle="-1"/>
|
||||||
|
</distributed-cache>
|
||||||
|
</cache-container>
|
||||||
|
<cache-container name="server" aliases="singleton cluster" default-cache="default" module="org.wildfly.clustering.server">
|
||||||
|
<transport lock-timeout="60000"/>
|
||||||
|
<replicated-cache name="default">
|
||||||
|
<transaction mode="BATCH"/>
|
||||||
|
</replicated-cache>
|
||||||
|
</cache-container>
|
||||||
|
<cache-container name="web" default-cache="dist" module="org.wildfly.clustering.web.infinispan">
|
||||||
|
<transport lock-timeout="60000"/>
|
||||||
|
<replicated-cache name="sso">
|
||||||
|
<locking isolation="REPEATABLE_READ"/>
|
||||||
|
<transaction mode="BATCH"/>
|
||||||
|
</replicated-cache>
|
||||||
|
<distributed-cache name="dist">
|
||||||
|
<locking isolation="REPEATABLE_READ"/>
|
||||||
|
<transaction mode="BATCH"/>
|
||||||
|
<file-store/>
|
||||||
|
</distributed-cache>
|
||||||
|
<distributed-cache name="routing"/>
|
||||||
|
</cache-container>
|
||||||
|
<cache-container name="ejb" aliases="sfsb" default-cache="dist" module="org.wildfly.clustering.ejb.infinispan">
|
||||||
|
<transport lock-timeout="60000"/>
|
||||||
|
<distributed-cache name="dist">
|
||||||
|
<locking isolation="REPEATABLE_READ"/>
|
||||||
|
<transaction mode="BATCH"/>
|
||||||
|
<file-store/>
|
||||||
|
</distributed-cache>
|
||||||
|
</cache-container>
|
||||||
|
<cache-container name="hibernate" module="org.infinispan.hibernate-cache">
|
||||||
|
<transport lock-timeout="60000"/>
|
||||||
|
<local-cache name="local-query">
|
||||||
|
<object-memory size="10000"/>
|
||||||
|
<expiration max-idle="100000"/>
|
||||||
|
</local-cache>
|
||||||
|
<invalidation-cache name="entity">
|
||||||
|
<transaction mode="NON_XA"/>
|
||||||
|
<object-memory size="10000"/>
|
||||||
|
<expiration max-idle="100000"/>
|
||||||
|
</invalidation-cache>
|
||||||
|
<replicated-cache name="timestamps"/>
|
||||||
|
</cache-container>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:io:3.0">
|
||||||
|
<worker name="default"/>
|
||||||
|
<buffer-pool name="default"/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:jaxrs:2.0"/>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:jca:5.0">
|
||||||
|
<archive-validation enabled="true" fail-on-error="true" fail-on-warn="false"/>
|
||||||
|
<bean-validation enabled="true"/>
|
||||||
|
<default-workmanager>
|
||||||
|
<short-running-threads>
|
||||||
|
<core-threads count="50"/>
|
||||||
|
<queue-length count="50"/>
|
||||||
|
<max-threads count="50"/>
|
||||||
|
<keepalive-time time="10" unit="seconds"/>
|
||||||
|
</short-running-threads>
|
||||||
|
<long-running-threads>
|
||||||
|
<core-threads count="50"/>
|
||||||
|
<queue-length count="50"/>
|
||||||
|
<max-threads count="50"/>
|
||||||
|
<keepalive-time time="10" unit="seconds"/>
|
||||||
|
</long-running-threads>
|
||||||
|
</default-workmanager>
|
||||||
|
<cached-connection-manager/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:jgroups:8.0">
|
||||||
|
<channels default="ee">
|
||||||
|
<channel name="ee" stack="udp" cluster="ejb"/>
|
||||||
|
</channels>
|
||||||
|
<stacks>
|
||||||
|
<stack name="udp">
|
||||||
|
<transport type="UDP" socket-binding="jgroups-udp"/>
|
||||||
|
<protocol type="PING"/>
|
||||||
|
<protocol type="MERGE3"/>
|
||||||
|
<socket-protocol type="FD_SOCK" socket-binding="jgroups-udp-fd"/>
|
||||||
|
<protocol type="FD_ALL"/>
|
||||||
|
<protocol type="VERIFY_SUSPECT"/>
|
||||||
|
<protocol type="pbcast.NAKACK2"/>
|
||||||
|
<protocol type="UNICAST3"/>
|
||||||
|
<protocol type="pbcast.STABLE"/>
|
||||||
|
<protocol type="pbcast.GMS"/>
|
||||||
|
<protocol type="UFC"/>
|
||||||
|
<protocol type="MFC"/>
|
||||||
|
<protocol type="FRAG3"/>
|
||||||
|
</stack>
|
||||||
|
<stack name="tcp">
|
||||||
|
<transport type="TCP" socket-binding="jgroups-tcp"/>
|
||||||
|
<socket-protocol type="MPING" socket-binding="jgroups-mping"/>
|
||||||
|
<protocol type="MERGE3"/>
|
||||||
|
<socket-protocol type="FD_SOCK" socket-binding="jgroups-tcp-fd"/>
|
||||||
|
<protocol type="FD_ALL"/>
|
||||||
|
<protocol type="VERIFY_SUSPECT"/>
|
||||||
|
<protocol type="pbcast.NAKACK2"/>
|
||||||
|
<protocol type="UNICAST3"/>
|
||||||
|
<protocol type="pbcast.STABLE"/>
|
||||||
|
<protocol type="pbcast.GMS"/>
|
||||||
|
<protocol type="MFC"/>
|
||||||
|
<protocol type="FRAG3"/>
|
||||||
|
</stack>
|
||||||
|
</stacks>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:jmx:1.3">
|
||||||
|
<expose-resolved-model/>
|
||||||
|
<expose-expression-model/>
|
||||||
|
<remoting-connector/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:jpa:1.1">
|
||||||
|
<jpa default-datasource="" default-extended-persistence-inheritance="DEEP"/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
|
||||||
|
<web-context>auth</web-context>
|
||||||
|
<providers>
|
||||||
|
<provider>
|
||||||
|
classpath:${jboss.home.dir}/providers/*
|
||||||
|
</provider>
|
||||||
|
</providers>
|
||||||
|
<master-realm-name>master</master-realm-name>
|
||||||
|
<scheduled-task-interval>900</scheduled-task-interval>
|
||||||
|
<theme>
|
||||||
|
<staticMaxAge>-1</staticMaxAge>
|
||||||
|
<cacheThemes>false</cacheThemes>
|
||||||
|
<cacheTemplates>false</cacheTemplates>
|
||||||
|
<welcomeTheme>${env.KEYCLOAK_WELCOME_THEME:keycloak}</welcomeTheme>
|
||||||
|
<default>${env.KEYCLOAK_DEFAULT_THEME:keycloak}</default>
|
||||||
|
<dir>${jboss.home.dir}/themes</dir>
|
||||||
|
</theme>
|
||||||
|
<spi name="eventsStore">
|
||||||
|
<provider name="jpa" enabled="true">
|
||||||
|
<properties>
|
||||||
|
<property name="exclude-events" value="["REFRESH_TOKEN"]"/>
|
||||||
|
</properties>
|
||||||
|
</provider>
|
||||||
|
</spi>
|
||||||
|
<spi name="userCache">
|
||||||
|
<provider name="default" enabled="true"/>
|
||||||
|
</spi>
|
||||||
|
<spi name="userSessionPersister">
|
||||||
|
<default-provider>jpa</default-provider>
|
||||||
|
</spi>
|
||||||
|
<spi name="timer">
|
||||||
|
<default-provider>basic</default-provider>
|
||||||
|
</spi>
|
||||||
|
<spi name="connectionsHttpClient">
|
||||||
|
<provider name="default" enabled="true"/>
|
||||||
|
</spi>
|
||||||
|
<spi name="connectionsJpa">
|
||||||
|
<provider name="default" enabled="true">
|
||||||
|
<properties>
|
||||||
|
<property name="dataSource" value="java:jboss/datasources/KeycloakDS"/>
|
||||||
|
<property name="initializeEmpty" value="true"/>
|
||||||
|
<property name="migrationStrategy" value="update"/>
|
||||||
|
<property name="migrationExport" value="${jboss.home.dir}/keycloak-database-update.sql"/>
|
||||||
|
</properties>
|
||||||
|
</provider>
|
||||||
|
</spi>
|
||||||
|
<spi name="realmCache">
|
||||||
|
<provider name="default" enabled="true"/>
|
||||||
|
</spi>
|
||||||
|
<spi name="connectionsInfinispan">
|
||||||
|
<default-provider>default</default-provider>
|
||||||
|
<provider name="default" enabled="true">
|
||||||
|
<properties>
|
||||||
|
<property name="cacheContainer" value="java:jboss/infinispan/container/keycloak"/>
|
||||||
|
</properties>
|
||||||
|
</provider>
|
||||||
|
</spi>
|
||||||
|
<spi name="jta-lookup">
|
||||||
|
<default-provider>${keycloak.jta.lookup.provider:jboss}</default-provider>
|
||||||
|
<provider name="jboss" enabled="true"/>
|
||||||
|
</spi>
|
||||||
|
<spi name="publicKeyStorage">
|
||||||
|
<provider name="infinispan" enabled="true">
|
||||||
|
<properties>
|
||||||
|
<property name="minTimeBetweenRequests" value="10"/>
|
||||||
|
</properties>
|
||||||
|
</provider>
|
||||||
|
</spi>
|
||||||
|
<spi name="x509cert-lookup">
|
||||||
|
<default-provider>${keycloak.x509cert.lookup.provider:default}</default-provider>
|
||||||
|
<provider name="default" enabled="true"/>
|
||||||
|
</spi>
|
||||||
|
<spi name="hostname">
|
||||||
|
<default-provider>${keycloak.hostname.provider:default}</default-provider>
|
||||||
|
<provider name="default" enabled="true">
|
||||||
|
<properties>
|
||||||
|
<property name="frontendUrl" value="${keycloak.frontendUrl:}"/>
|
||||||
|
<property name="forceBackendUrlToFrontendUrl" value="false"/>
|
||||||
|
</properties>
|
||||||
|
</provider>
|
||||||
|
<provider name="fixed" enabled="true">
|
||||||
|
<properties>
|
||||||
|
<property name="hostname" value="${keycloak.hostname.fixed.hostname:localhost}"/>
|
||||||
|
<property name="httpPort" value="${keycloak.hostname.fixed.httpPort:-1}"/>
|
||||||
|
<property name="httpsPort" value="${keycloak.hostname.fixed.httpsPort:-1}"/>
|
||||||
|
<property name="alwaysHttps" value="${keycloak.hostname.fixed.alwaysHttps:false}"/>
|
||||||
|
</properties>
|
||||||
|
</provider>
|
||||||
|
</spi>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:mail:4.0">
|
||||||
|
<mail-session name="default" jndi-name="java:jboss/mail/Default">
|
||||||
|
<smtp-server outbound-socket-binding-ref="mail-smtp"/>
|
||||||
|
</mail-session>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:wildfly:microprofile-config-smallrye:1.0"/>
|
||||||
|
<subsystem xmlns="urn:wildfly:microprofile-health-smallrye:2.0" security-enabled="false" empty-liveness-checks-status="${env.MP_HEALTH_EMPTY_LIVENESS_CHECKS_STATUS:UP}" empty-readiness-checks-status="${env.MP_HEALTH_EMPTY_READINESS_CHECKS_STATUS:UP}"/>
|
||||||
|
<subsystem xmlns="urn:wildfly:microprofile-metrics-smallrye:2.0" security-enabled="false" exposed-subsystems="*" prefix="${wildfly.metrics.prefix:wildfly}"/>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:modcluster:5.0">
|
||||||
|
<proxy name="default" advertise-socket="modcluster" listener="ajp">
|
||||||
|
<dynamic-load-provider>
|
||||||
|
<load-metric type="cpu"/>
|
||||||
|
</dynamic-load-provider>
|
||||||
|
</proxy>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:naming:2.0">
|
||||||
|
<remote-naming/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:remoting:4.0">
|
||||||
|
<http-connector name="http-remoting-connector" connector-ref="default" security-realm="ApplicationRealm"/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:request-controller:1.0"/>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:security:2.0">
|
||||||
|
<security-domains>
|
||||||
|
<security-domain name="other" cache-type="default">
|
||||||
|
<authentication>
|
||||||
|
<login-module code="Remoting" flag="optional">
|
||||||
|
<module-option name="password-stacking" value="useFirstPass"/>
|
||||||
|
</login-module>
|
||||||
|
<login-module code="RealmDirect" flag="required">
|
||||||
|
<module-option name="password-stacking" value="useFirstPass"/>
|
||||||
|
</login-module>
|
||||||
|
</authentication>
|
||||||
|
</security-domain>
|
||||||
|
<security-domain name="jboss-web-policy" cache-type="default">
|
||||||
|
<authorization>
|
||||||
|
<policy-module code="Delegating" flag="required"/>
|
||||||
|
</authorization>
|
||||||
|
</security-domain>
|
||||||
|
<security-domain name="jaspitest" cache-type="default">
|
||||||
|
<authentication-jaspi>
|
||||||
|
<login-module-stack name="dummy">
|
||||||
|
<login-module code="Dummy" flag="optional"/>
|
||||||
|
</login-module-stack>
|
||||||
|
<auth-module code="Dummy"/>
|
||||||
|
</authentication-jaspi>
|
||||||
|
</security-domain>
|
||||||
|
<security-domain name="jboss-ejb-policy" cache-type="default">
|
||||||
|
<authorization>
|
||||||
|
<policy-module code="Delegating" flag="required"/>
|
||||||
|
</authorization>
|
||||||
|
</security-domain>
|
||||||
|
</security-domains>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:security-manager:1.0">
|
||||||
|
<deployment-permissions>
|
||||||
|
<maximum-set>
|
||||||
|
<permission class="java.security.AllPermission"/>
|
||||||
|
</maximum-set>
|
||||||
|
</deployment-permissions>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:transactions:5.0">
|
||||||
|
<core-environment node-identifier="${jboss.tx.node.id:1}">
|
||||||
|
<process-id>
|
||||||
|
<uuid/>
|
||||||
|
</process-id>
|
||||||
|
</core-environment>
|
||||||
|
<recovery-environment socket-binding="txn-recovery-environment" status-socket-binding="txn-status-manager"/>
|
||||||
|
<coordinator-environment statistics-enabled="${wildfly.transactions.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
|
||||||
|
<object-store path="tx-object-store" relative-to="jboss.server.data.dir"/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:undertow:11.0" default-server="default-server" default-virtual-host="default-host" default-servlet-container="default" default-security-domain="other" statistics-enabled="${wildfly.undertow.statistics-enabled:${wildfly.statistics-enabled:false}}">
|
||||||
|
<buffer-cache name="default"/>
|
||||||
|
<server name="default-server">
|
||||||
|
<ajp-listener name="ajp" socket-binding="ajp"/>
|
||||||
|
<http-listener name="default" read-timeout="30000" socket-binding="http" redirect-socket="https" proxy-address-forwarding="${env.PROXY_ADDRESS_FORWARDING:false}" enable-http2="true"/>
|
||||||
|
<https-listener name="https" read-timeout="30000" socket-binding="https" proxy-address-forwarding="${env.PROXY_ADDRESS_FORWARDING:false}" security-realm="ApplicationRealm" enable-http2="true"/>
|
||||||
|
<host name="default-host" alias="localhost">
|
||||||
|
<location name="/" handler="welcome-content"/>
|
||||||
|
<http-invoker security-realm="ApplicationRealm"/>
|
||||||
|
</host>
|
||||||
|
</server>
|
||||||
|
<servlet-container name="default">
|
||||||
|
<jsp-config/>
|
||||||
|
<websockets/>
|
||||||
|
</servlet-container>
|
||||||
|
<handlers>
|
||||||
|
<file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
|
||||||
|
</handlers>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:weld:4.0"/>
|
||||||
|
</profile>
|
||||||
|
<interfaces>
|
||||||
|
<interface name="management">
|
||||||
|
<inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
|
||||||
|
</interface>
|
||||||
|
<interface name="private">
|
||||||
|
<inet-address value="${jboss.bind.address.private:127.0.0.1}"/>
|
||||||
|
</interface>
|
||||||
|
<interface name="public">
|
||||||
|
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
|
||||||
|
</interface>
|
||||||
|
</interfaces>
|
||||||
|
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
|
||||||
|
<socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
|
||||||
|
<socket-binding name="http" port="${jboss.http.port:8080}"/>
|
||||||
|
<socket-binding name="https" port="${jboss.https.port:8443}"/>
|
||||||
|
<socket-binding name="jgroups-mping" interface="private" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45700"/>
|
||||||
|
<socket-binding name="jgroups-tcp" interface="private" port="7600"/>
|
||||||
|
<socket-binding name="jgroups-tcp-fd" interface="private" port="57600"/>
|
||||||
|
<socket-binding name="jgroups-udp" interface="private" port="55200" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45688"/>
|
||||||
|
<socket-binding name="jgroups-udp-fd" interface="private" port="54200"/>
|
||||||
|
<socket-binding name="management-http" interface="management" port="${jboss.management.http.port:9990}"/>
|
||||||
|
<socket-binding name="management-https" interface="management" port="${jboss.management.https.port:9993}"/>
|
||||||
|
<socket-binding name="modcluster" multicast-address="${jboss.modcluster.multicast.address:224.0.1.105}" multicast-port="23364"/>
|
||||||
|
<socket-binding name="txn-recovery-environment" port="4712"/>
|
||||||
|
<socket-binding name="txn-status-manager" port="4713"/>
|
||||||
|
<outbound-socket-binding name="mail-smtp">
|
||||||
|
<remote-destination host="localhost" port="25"/>
|
||||||
|
</outbound-socket-binding>
|
||||||
|
</socket-binding-group>
|
||||||
|
</server>
|
11
bin/build-keycloak-theme/generateFtl.d.ts
vendored
Normal file
11
bin/build-keycloak-theme/generateFtl.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export declare function generateFtlFilesCodeFactory(params: {
|
||||||
|
ftlValuesGlobalName: string;
|
||||||
|
cssGlobalsToDefine: Record<string, string>;
|
||||||
|
indexHtmlCode: string;
|
||||||
|
}): {
|
||||||
|
generateFtlFilesCode: (params: {
|
||||||
|
pageBasename: "login.ftl" | "register.ftl";
|
||||||
|
}) => {
|
||||||
|
ftlCode: string;
|
||||||
|
};
|
||||||
|
};
|
93
bin/build-keycloak-theme/generateFtl.js
Normal file
93
bin/build-keycloak-theme/generateFtl.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
"use strict";
|
||||||
|
var __read = (this && this.__read) || function (o, n) {
|
||||||
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||||
|
if (!m) return o;
|
||||||
|
var i = m.call(o), r, ar = [], e;
|
||||||
|
try {
|
||||||
|
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
||||||
|
}
|
||||||
|
catch (error) { e = { error: error }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (r && !r.done && (m = i["return"])) m.call(i);
|
||||||
|
}
|
||||||
|
finally { if (e) throw e.error; }
|
||||||
|
}
|
||||||
|
return ar;
|
||||||
|
};
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.generateFtlFilesCodeFactory = void 0;
|
||||||
|
var cheerio_1 = __importDefault(require("cheerio"));
|
||||||
|
var replaceImportFromStatic_1 = require("./replaceImportFromStatic");
|
||||||
|
function generateFtlFilesCodeFactory(params) {
|
||||||
|
var ftlValuesGlobalName = params.ftlValuesGlobalName, cssGlobalsToDefine = params.cssGlobalsToDefine, indexHtmlCode = params.indexHtmlCode;
|
||||||
|
var $ = cheerio_1.default.load(indexHtmlCode);
|
||||||
|
$("script:not([src])").each(function () {
|
||||||
|
var _a = [];
|
||||||
|
for (var _i = 0; _i < arguments.length; _i++) {
|
||||||
|
_a[_i] = arguments[_i];
|
||||||
|
}
|
||||||
|
var _b = __read(_a, 2), element = _b[1];
|
||||||
|
var fixedJsCode = replaceImportFromStatic_1.replaceImportFromStaticInJsCode({
|
||||||
|
ftlValuesGlobalName: ftlValuesGlobalName,
|
||||||
|
"jsCode": $(element).html()
|
||||||
|
}).fixedJsCode;
|
||||||
|
$(element).text(fixedJsCode);
|
||||||
|
});
|
||||||
|
[
|
||||||
|
["link", "href"],
|
||||||
|
["script", "src"],
|
||||||
|
].forEach(function (_a) {
|
||||||
|
var _b = __read(_a, 2), selector = _b[0], attrName = _b[1];
|
||||||
|
return $(selector).each(function () {
|
||||||
|
var _a = [];
|
||||||
|
for (var _i = 0; _i < arguments.length; _i++) {
|
||||||
|
_a[_i] = arguments[_i];
|
||||||
|
}
|
||||||
|
var _b = __read(_a, 2), element = _b[1];
|
||||||
|
var href = $(element).attr(attrName);
|
||||||
|
if (!(href === null || href === void 0 ? void 0 : href.startsWith("/"))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$(element).attr(attrName, "${url.resourcesPath}" + href);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$("head").prepend([
|
||||||
|
'',
|
||||||
|
'<style>',
|
||||||
|
replaceImportFromStatic_1.generateCssCodeToDefineGlobals({ cssGlobalsToDefine: cssGlobalsToDefine }).cssCodeToPrependInHead,
|
||||||
|
'</style>',
|
||||||
|
'',
|
||||||
|
'<script>',
|
||||||
|
' Object.assign(',
|
||||||
|
" window." + ftlValuesGlobalName + ",",
|
||||||
|
' {',
|
||||||
|
' "url": {',
|
||||||
|
' "loginAction": "${url.loginAction}",',
|
||||||
|
' "resourcesPath": "${url.resourcesPath}"',
|
||||||
|
' }',
|
||||||
|
' }',
|
||||||
|
' });',
|
||||||
|
'</script>',
|
||||||
|
''
|
||||||
|
].join("\n"));
|
||||||
|
var partiallyFixedIndexHtmlCode = $.html();
|
||||||
|
function generateFtlFilesCode(params) {
|
||||||
|
var pageBasename = params.pageBasename;
|
||||||
|
var $ = cheerio_1.default.load(partiallyFixedIndexHtmlCode);
|
||||||
|
$("head").prepend([
|
||||||
|
'',
|
||||||
|
'<script>',
|
||||||
|
" window." + ftlValuesGlobalName + " = { \"pageBasename\": \"" + pageBasename + "\" };",
|
||||||
|
'</script>',
|
||||||
|
''
|
||||||
|
].join("\n"));
|
||||||
|
return { "ftlCode": $.html() };
|
||||||
|
}
|
||||||
|
return { generateFtlFilesCode: generateFtlFilesCode };
|
||||||
|
}
|
||||||
|
exports.generateFtlFilesCodeFactory = generateFtlFilesCodeFactory;
|
||||||
|
//# sourceMappingURL=generateFtl.js.map
|
1
bin/build-keycloak-theme/generateFtl.js.map
Normal file
1
bin/build-keycloak-theme/generateFtl.js.map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"generateFtl.js","sourceRoot":"","sources":["../../src/bin/build-keycloak-theme/generateFtl.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAEA,oDAA8B;AAC9B,qEAGmC;AAEnC,SAAgB,2BAA2B,CACvC,MAIC;IAGO,IAAA,mBAAmB,GAAwC,MAAM,oBAA9C,EAAE,kBAAkB,GAAoB,MAAM,mBAA1B,EAAE,aAAa,GAAK,MAAM,cAAX,CAAY;IAE1E,IAAM,CAAC,GAAG,iBAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAEtC,CAAC,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC;QAAC,YAAc;aAAd,UAAc,EAAd,qBAAc,EAAd,IAAc;YAAd,uBAAc;;QAAd,IAAA,KAAA,aAAc,EAAR,OAAO,QAAA,CAAC;QAE/B,IAAA,WAAW,GAAK,yDAA+B,CAAC;YACpD,mBAAmB,qBAAA;YACnB,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAG;SAC/B,CAAC,YAHiB,CAGhB;QAEH,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAEjC,CAAC,CAAC,CAAC;IAGF;QACG,CAAC,MAAM,EAAE,MAAM,CAAC;QAChB,CAAC,QAAQ,EAAE,KAAK,CAAC;KACV,CAAC,OAAO,CAAC,UAAC,EAAoB;YAApB,KAAA,aAAoB,EAAnB,QAAQ,QAAA,EAAE,QAAQ,QAAA;QACpC,OAAA,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;YAAC,YAAc;iBAAd,UAAc,EAAd,qBAAc,EAAd,IAAc;gBAAd,uBAAc;;YAAd,IAAA,KAAA,aAAc,EAAR,OAAO,QAAA,CAAC;YAE5B,IAAM,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAEvC,IAAI,EAAC,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,UAAU,CAAC,GAAG,EAAC,EAAE;gBACxB,OAAO;aACV;YAED,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI,CAAC,CAAC;QAE7D,CAAC,CAAC;IAVF,CAUE,CACL,CAAC;IAEF,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CACb;QACI,EAAE;QACF,SAAS;QACT,wDAA8B,CAC1B,EAAE,kBAAkB,oBAAA,EAAE,CACzB,CAAC,sBAAsB;QACxB,UAAU;QACV,EAAE;QACF,UAAU;QACV,oBAAoB;QACpB,oBAAkB,mBAAmB,MAAG;QACxC,WAAW;QACX,sBAAsB;QACtB,sDAAsD;QACtD,yDAAyD;QACzD,eAAe;QACf,WAAW;QACX,SAAS;QACT,WAAW;QACX,EAAE;KACL,CAAC,IAAI,CAAC,IAAI,CAAC,CACf,CAAC;IAGF,IAAM,2BAA2B,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAE7C,SAAS,oBAAoB,CACzB,MAEC;QAGO,IAAA,YAAY,GAAK,MAAM,aAAX,CAAY;QAEhC,IAAM,CAAC,GAAG,iBAAO,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAEpD,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CACb;YACI,EAAE;YACF,UAAU;YACV,eAAa,mBAAmB,iCAAyB,YAAY,UAAM;YAC3E,WAAW;YACX,EAAE;SACL,CAAC,IAAI,CAAC,IAAI,CAAC,CAEf,CAAC;QAEF,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;IAEnC,CAAC;IAED,OAAO,EAAE,oBAAoB,sBAAA,EAAE,CAAC;AAGpC,CAAC;AAhGD,kEAgGC"}
|
9
bin/build-keycloak-theme/generateJavaStackFiles.d.ts
vendored
Normal file
9
bin/build-keycloak-theme/generateJavaStackFiles.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export declare type ParsedPackageJson = {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
homepage?: string;
|
||||||
|
};
|
||||||
|
export declare function generateJavaStackFiles(params: {
|
||||||
|
parsedPackageJson: ParsedPackageJson;
|
||||||
|
keycloakThemeBuildingDirPath: string;
|
||||||
|
}): void;
|
71
bin/build-keycloak-theme/generateJavaStackFiles.js
Normal file
71
bin/build-keycloak-theme/generateJavaStackFiles.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
"use strict";
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.generateJavaStackFiles = void 0;
|
||||||
|
var url = __importStar(require("url"));
|
||||||
|
var fs = __importStar(require("fs"));
|
||||||
|
var path_1 = require("path");
|
||||||
|
function generateJavaStackFiles(params) {
|
||||||
|
var _a = params.parsedPackageJson, name = _a.name, version = _a.version, homepage = _a.homepage, keycloakThemeBuildingDirPath = params.keycloakThemeBuildingDirPath;
|
||||||
|
{
|
||||||
|
var pomFileCode = (function generatePomFileCode() {
|
||||||
|
var groupId = (function () {
|
||||||
|
var _a, _b;
|
||||||
|
var fallbackGroupId = "there.was.no.homepage.field.in.the.package.json." + name;
|
||||||
|
return (!homepage ?
|
||||||
|
fallbackGroupId : (_b = (_a = url.parse(homepage).host) === null || _a === void 0 ? void 0 : _a.split(".").reverse().join(".")) !== null && _b !== void 0 ? _b : fallbackGroupId) + ".keycloak";
|
||||||
|
})();
|
||||||
|
var artefactId = name + "-keycloak-theme";
|
||||||
|
var pomFileCode = [
|
||||||
|
"<?xml version=\"1.0\"?>",
|
||||||
|
"<project xmlns=\"http://maven.apache.org/POM/4.0.0\"",
|
||||||
|
"\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"",
|
||||||
|
"\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">",
|
||||||
|
"\t<modelVersion>4.0.0</modelVersion>",
|
||||||
|
"\t<groupId>" + groupId + "</groupId>",
|
||||||
|
"\t<artifactId>" + artefactId + "</artifactId>",
|
||||||
|
"\t<version>" + version + "</version>",
|
||||||
|
"\t<name>" + artefactId + "</name>",
|
||||||
|
"\t<description />",
|
||||||
|
"</project>"
|
||||||
|
].join("\n");
|
||||||
|
return { pomFileCode: pomFileCode };
|
||||||
|
})().pomFileCode;
|
||||||
|
fs.writeFileSync(path_1.join(keycloakThemeBuildingDirPath, "pom.xml"), Buffer.from(pomFileCode, "utf8"));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var themeManifestFilePath = path_1.join(keycloakThemeBuildingDirPath, "src", "main", "resources", "META-INF", "keycloak-themes.json");
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(path_1.dirname(themeManifestFilePath));
|
||||||
|
}
|
||||||
|
catch (_b) { }
|
||||||
|
fs.writeFileSync(themeManifestFilePath, Buffer.from(JSON.stringify({
|
||||||
|
"themes": [
|
||||||
|
{
|
||||||
|
"name": name,
|
||||||
|
"types": ["login"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, null, 2), "utf8"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.generateJavaStackFiles = generateJavaStackFiles;
|
||||||
|
//# sourceMappingURL=generateJavaStackFiles.js.map
|
1
bin/build-keycloak-theme/generateJavaStackFiles.js.map
Normal file
1
bin/build-keycloak-theme/generateJavaStackFiles.js.map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"generateJavaStackFiles.js","sourceRoot":"","sources":["../../src/bin/build-keycloak-theme/generateJavaStackFiles.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AACA,uCAA2B;AAC3B,qCAAyB;AACzB,6BAAgE;AAQhE,SAAgB,sBAAsB,CAClC,MAGC;IAIG,IAAA,KAEA,MAAM,kBAFwC,EAAzB,IAAI,UAAA,EAAE,OAAO,aAAA,EAAE,QAAQ,cAAA,EAC5C,4BAA4B,GAC5B,MAAM,6BADsB,CACrB;IAEX;QAEY,IAAA,WAAW,GAAK,CAAC,SAAS,mBAAmB;YAGjD,IAAM,OAAO,GAAG,CAAC;;gBAEb,IAAM,eAAe,GAAG,qDAAmD,IAAM,CAAC;gBAElF,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC;oBACf,eAAe,CAAC,CAAC,aACjB,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,0CAAE,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC,GAAG,oCAAK,eAAe,CAC9E,GAAG,WAAW,CAAC;YAEpB,CAAC,CAAC,EAAE,CAAC;YAEL,IAAM,UAAU,GAAM,IAAI,oBAAiB,CAAC;YAE5C,IAAM,WAAW,GAAG;gBAChB,yBAAuB;gBACvB,sDAAoD;gBACpD,2DAAwD;gBACxD,sGAAmG;gBACnG,sCAAqC;gBACrC,gBAAa,OAAO,eAAY;gBAChC,mBAAgB,UAAU,kBAAe;gBACzC,gBAAa,OAAO,eAAY;gBAChC,aAAU,UAAU,YAAS;gBAC7B,mBAAkB;gBAClB,YAAY;aACf,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAEb,OAAO,EAAE,WAAW,aAAA,EAAE,CAAC;QAE3B,CAAC,CAAC,EAAE,YAhCe,CAgCd;QAEL,EAAE,CAAC,aAAa,CACZ,WAAQ,CAAC,4BAA4B,EAAE,SAAS,CAAC,EACjD,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CACnC,CAAC;KAEL;IAED;QAEI,IAAM,qBAAqB,GAAG,WAAQ,CAClC,4BAA4B,EAAE,KAAK,EAAE,MAAM,EAC3C,WAAW,EAAE,UAAU,EAAE,sBAAsB,CAClD,CAAC;QAEF,IAAI;YAEA,EAAE,CAAC,SAAS,CAAC,cAAW,CAAC,qBAAqB,CAAC,CAAC,CAAC;SAEpD;QAAC,WAAM,GAAG;QAEX,EAAE,CAAC,aAAa,CACZ,qBAAqB,EACrB,MAAM,CAAC,IAAI,CACP,IAAI,CAAC,SAAS,CAAC;YACX,QAAQ,EAAE;gBACN;oBACI,MAAM,EAAE,IAAI;oBACZ,OAAO,EAAE,CAAC,OAAO,CAAC;iBACrB;aACJ;SACJ,EAAE,IAAI,EAAE,CAAC,CAAC,EACX,MAAM,CACT,CACJ,CAAC;KAEL;AAEL,CAAC;AArFD,wDAqFC"}
|
5
bin/build-keycloak-theme/generateKeycloakThemeResources.d.ts
vendored
Normal file
5
bin/build-keycloak-theme/generateKeycloakThemeResources.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export declare function generateKeycloakThemeResources(params: {
|
||||||
|
themeName: string;
|
||||||
|
reactAppBuildDirPath: string;
|
||||||
|
keycloakThemeBuildingDirPath: string;
|
||||||
|
}): void;
|
76
bin/build-keycloak-theme/generateKeycloakThemeResources.js
Normal file
76
bin/build-keycloak-theme/generateKeycloakThemeResources.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
"use strict";
|
||||||
|
var __assign = (this && this.__assign) || function () {
|
||||||
|
__assign = Object.assign || function(t) {
|
||||||
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||||
|
s = arguments[i];
|
||||||
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||||
|
t[p] = s[p];
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
return __assign.apply(this, arguments);
|
||||||
|
};
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.generateKeycloakThemeResources = void 0;
|
||||||
|
var transformCodebase_1 = require("../../tools/transformCodebase");
|
||||||
|
var fs = __importStar(require("fs"));
|
||||||
|
var path_1 = require("path");
|
||||||
|
var replaceImportFromStatic_1 = require("./replaceImportFromStatic");
|
||||||
|
var generateFtl_1 = require("./generateFtl");
|
||||||
|
var ftlValuesGlobalName = "keycloakFtlValues";
|
||||||
|
function generateKeycloakThemeResources(params) {
|
||||||
|
var themeName = params.themeName, reactAppBuildDirPath = params.reactAppBuildDirPath, keycloakThemeBuildingDirPath = params.keycloakThemeBuildingDirPath;
|
||||||
|
var themeDirPath = path_1.join(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login");
|
||||||
|
var allCssGlobalsToDefine = {};
|
||||||
|
transformCodebase_1.transformCodebase({
|
||||||
|
"destDirPath": path_1.join(themeDirPath, "resources"),
|
||||||
|
"srcDirPath": reactAppBuildDirPath,
|
||||||
|
"transformSourceCodeString": function (_a) {
|
||||||
|
var filePath = _a.filePath, sourceCode = _a.sourceCode;
|
||||||
|
if (/\.css?$/i.test(filePath)) {
|
||||||
|
var _b = replaceImportFromStatic_1.replaceImportFromStaticInCssCode({ "cssCode": sourceCode.toString("utf8") }), cssGlobalsToDefine = _b.cssGlobalsToDefine, fixedCssCode = _b.fixedCssCode;
|
||||||
|
allCssGlobalsToDefine = __assign(__assign({}, allCssGlobalsToDefine), cssGlobalsToDefine);
|
||||||
|
return { "modifiedSourceCode": Buffer.from(fixedCssCode, "utf8") };
|
||||||
|
}
|
||||||
|
if (/\.js?$/i.test(filePath)) {
|
||||||
|
var fixedJsCode = replaceImportFromStatic_1.replaceImportFromStaticInJsCode({
|
||||||
|
"jsCode": sourceCode.toString("utf8"),
|
||||||
|
ftlValuesGlobalName: ftlValuesGlobalName
|
||||||
|
}).fixedJsCode;
|
||||||
|
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
|
||||||
|
}
|
||||||
|
return { "modifiedSourceCode": sourceCode };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var generateFtlFilesCode = generateFtl_1.generateFtlFilesCodeFactory({
|
||||||
|
"cssGlobalsToDefine": allCssGlobalsToDefine,
|
||||||
|
ftlValuesGlobalName: ftlValuesGlobalName,
|
||||||
|
"indexHtmlCode": fs.readFileSync(path_1.join(reactAppBuildDirPath, "index.html")).toString("utf8")
|
||||||
|
}).generateFtlFilesCode;
|
||||||
|
["login.ftl", "register.ftl"].forEach(function (pageBasename) {
|
||||||
|
var ftlCode = generateFtlFilesCode({ pageBasename: pageBasename }).ftlCode;
|
||||||
|
fs.writeFileSync(path_1.join(themeDirPath, pageBasename), Buffer.from(ftlCode, "utf8"));
|
||||||
|
});
|
||||||
|
fs.writeFileSync(path_1.join(themeDirPath, "theme.properties"), Buffer.from("parent=base\n", "utf8"));
|
||||||
|
}
|
||||||
|
exports.generateKeycloakThemeResources = generateKeycloakThemeResources;
|
||||||
|
//# sourceMappingURL=generateKeycloakThemeResources.js.map
|
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"generateKeycloakThemeResources.js","sourceRoot":"","sources":["../../src/bin/build-keycloak-theme/generateKeycloakThemeResources.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,mEAAkE;AAClE,qCAAyB;AACzB,6BAAwC;AACxC,qEAGmC;AACnC,6CAA4D;AAE5D,IAAM,mBAAmB,GAAG,mBAAmB,CAAC;AAEhD,SAAgB,8BAA8B,CAC1C,MAIC;IAGO,IAAA,SAAS,GAAyD,MAAM,UAA/D,EAAE,oBAAoB,GAAmC,MAAM,qBAAzC,EAAE,4BAA4B,GAAK,MAAM,6BAAX,CAAY;IAEjF,IAAM,YAAY,GAAG,WAAQ,CAAC,4BAA4B,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAErH,IAAI,qBAAqB,GAA2B,EAAE,CAAC;IAEvD,qCAAiB,CAAC;QACd,aAAa,EAAE,WAAQ,CAAC,YAAY,EAAE,WAAW,CAAC;QAClD,YAAY,EAAE,oBAAoB;QAClC,2BAA2B,EAAE,UAAC,EAAwB;gBAAtB,QAAQ,cAAA,EAAE,UAAU,gBAAA;YAEhD,IAAI,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAErB,IAAA,KAAuC,0DAAgC,CACzE,EAAE,SAAS,EAAE,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAC7C,EAFO,kBAAkB,wBAAA,EAAE,YAAY,kBAEvC,CAAC;gBAEF,qBAAqB,yBACd,qBAAqB,GACrB,kBAAkB,CACxB,CAAC;gBAEF,OAAO,EAAE,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,EAAE,CAAC;aAEtE;YAED,IAAI,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAElB,IAAA,WAAW,GAAK,yDAA+B,CAAC;oBACpD,QAAQ,EAAE,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC;oBACrC,mBAAmB,qBAAA;iBACtB,CAAC,YAHiB,CAGhB;gBAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,CAAC;aAErE;YAED,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,CAAC;QAEhD,CAAC;KACJ,CAAC,CAAC;IAEK,IAAA,oBAAoB,GAAK,yCAA2B,CAAC;QACzD,oBAAoB,EAAE,qBAAqB;QAC3C,mBAAmB,qBAAA;QACnB,eAAe,EAAE,EAAE,CAAC,YAAY,CAC5B,WAAQ,CAAC,oBAAoB,EAAE,YAAY,CAAC,CAC/C,CAAC,QAAQ,CAAC,MAAM,CAAC;KACrB,CAAC,qBAN0B,CAMzB;IAEF,CAAC,WAAW,EAAE,cAAc,CAAW,CAAC,OAAO,CAAC,UAAA,YAAY;QAEjD,IAAA,OAAO,GAAK,oBAAoB,CAAC,EAAE,YAAY,cAAA,EAAE,CAAC,QAA3C,CAA4C;QAE3D,EAAE,CAAC,aAAa,CACZ,WAAQ,CAAC,YAAY,EAAE,YAAY,CAAC,EACpC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAC/B,CAAA;IAEL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,aAAa,CACZ,WAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAC,EAC1C,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,CACvC,CAAC;AAEN,CAAC;AA1ED,wEA0EC"}
|
2
bin/build-keycloak-theme/index.d.ts
vendored
Normal file
2
bin/build-keycloak-theme/index.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
export declare const keycloakThemeBuildingDirPath: string;
|
48
bin/build-keycloak-theme/index.js
Executable file
48
bin/build-keycloak-theme/index.js
Executable file
@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
"use strict";
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.keycloakThemeBuildingDirPath = void 0;
|
||||||
|
var generateKeycloakThemeResources_1 = require("./generateKeycloakThemeResources");
|
||||||
|
var generateJavaStackFiles_1 = require("./generateJavaStackFiles");
|
||||||
|
var path_1 = require("path");
|
||||||
|
var child_process = __importStar(require("child_process"));
|
||||||
|
var generateDebugFiles_1 = require("./generateDebugFiles");
|
||||||
|
var reactProjectDirPath = process.cwd();
|
||||||
|
var parsedPackageJson = require(path_1.join(reactProjectDirPath, "package.json"));
|
||||||
|
exports.keycloakThemeBuildingDirPath = path_1.join(reactProjectDirPath, "build_keycloak");
|
||||||
|
if (require.main === module) {
|
||||||
|
generateKeycloakThemeResources_1.generateKeycloakThemeResources({
|
||||||
|
keycloakThemeBuildingDirPath: exports.keycloakThemeBuildingDirPath,
|
||||||
|
"reactAppBuildDirPath": path_1.join(reactProjectDirPath, "build"),
|
||||||
|
"themeName": parsedPackageJson.name
|
||||||
|
});
|
||||||
|
generateJavaStackFiles_1.generateJavaStackFiles({
|
||||||
|
parsedPackageJson: parsedPackageJson,
|
||||||
|
keycloakThemeBuildingDirPath: exports.keycloakThemeBuildingDirPath
|
||||||
|
});
|
||||||
|
child_process.execSync("mvn package", { "cwd": exports.keycloakThemeBuildingDirPath });
|
||||||
|
generateDebugFiles_1.generateDebugFiles({
|
||||||
|
keycloakThemeBuildingDirPath: exports.keycloakThemeBuildingDirPath,
|
||||||
|
"packageJsonName": parsedPackageJson.name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=index.js.map
|
1
bin/build-keycloak-theme/index.js.map
Normal file
1
bin/build-keycloak-theme/index.js.map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bin/build-keycloak-theme/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAEA,mFAAkF;AAClF,mEAAkE;AAElE,6BAAwC;AACxC,2DAA+C;AAC/C,2DAA0D;AAE1D,IAAM,mBAAmB,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;AAE1C,IAAM,iBAAiB,GAAsB,OAAO,CAAC,WAAQ,CAAC,mBAAmB,EAAE,cAAc,CAAC,CAAC,CAAC;AAEvF,QAAA,4BAA4B,GAAG,WAAQ,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,CAAC;AAE5F,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE;IAEzB,+DAA8B,CAAC;QAC3B,4BAA4B,sCAAA;QAC5B,sBAAsB,EAAE,WAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;QAC9D,WAAW,EAAE,iBAAiB,CAAC,IAAI;KACtC,CAAC,CAAC;IAEH,+CAAsB,CAAC;QACnB,iBAAiB,mBAAA;QACjB,4BAA4B,sCAAA;KAC/B,CAAC,CAAC;IAEH,aAAa,CAAC,QAAQ,CAClB,aAAa,EACb,EAAE,KAAK,EAAE,oCAA4B,EAAE,CAC1C,CAAC;IAEF,uCAAkB,CAAC;QACf,4BAA4B,sCAAA;QAC5B,iBAAiB,EAAE,iBAAiB,CAAC,IAAI;KAC5C,CAAC,CAAC;CAEN"}
|
17
bin/build-keycloak-theme/replaceImportFromStatic.d.ts
vendored
Normal file
17
bin/build-keycloak-theme/replaceImportFromStatic.d.ts
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export declare function replaceImportFromStaticInJsCode(params: {
|
||||||
|
ftlValuesGlobalName: string;
|
||||||
|
jsCode: string;
|
||||||
|
}): {
|
||||||
|
fixedJsCode: string;
|
||||||
|
};
|
||||||
|
export declare function replaceImportFromStaticInCssCode(params: {
|
||||||
|
cssCode: string;
|
||||||
|
}): {
|
||||||
|
fixedCssCode: string;
|
||||||
|
cssGlobalsToDefine: Record<string, string>;
|
||||||
|
};
|
||||||
|
export declare function generateCssCodeToDefineGlobals(params: {
|
||||||
|
cssGlobalsToDefine: Record<string, string>;
|
||||||
|
}): {
|
||||||
|
cssCodeToPrependInHead: string;
|
||||||
|
};
|
93
bin/build-keycloak-theme/replaceImportFromStatic.js
Normal file
93
bin/build-keycloak-theme/replaceImportFromStatic.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
"use strict";
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
var __read = (this && this.__read) || function (o, n) {
|
||||||
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||||
|
if (!m) return o;
|
||||||
|
var i = m.call(o), r, ar = [], e;
|
||||||
|
try {
|
||||||
|
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
||||||
|
}
|
||||||
|
catch (error) { e = { error: error }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (r && !r.done && (m = i["return"])) m.call(i);
|
||||||
|
}
|
||||||
|
finally { if (e) throw e.error; }
|
||||||
|
}
|
||||||
|
return ar;
|
||||||
|
};
|
||||||
|
var __spread = (this && this.__spread) || function () {
|
||||||
|
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
|
||||||
|
return ar;
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.generateCssCodeToDefineGlobals = exports.replaceImportFromStaticInCssCode = exports.replaceImportFromStaticInJsCode = void 0;
|
||||||
|
var crypto = __importStar(require("crypto"));
|
||||||
|
function replaceImportFromStaticInJsCode(params) {
|
||||||
|
var jsCode = params.jsCode, ftlValuesGlobalName = params.ftlValuesGlobalName;
|
||||||
|
var fixedJsCode = jsCode.replace(/"static\//g, "window." + ftlValuesGlobalName + ".url.resourcesPath.replace(/^\\//,\"\") + \"/\" + \"static/");
|
||||||
|
return { fixedJsCode: fixedJsCode };
|
||||||
|
}
|
||||||
|
exports.replaceImportFromStaticInJsCode = replaceImportFromStaticInJsCode;
|
||||||
|
function replaceImportFromStaticInCssCode(params) {
|
||||||
|
var _a;
|
||||||
|
var cssCode = params.cssCode;
|
||||||
|
var cssGlobalsToDefine = {};
|
||||||
|
new Set((_a = cssCode.match(/(url\(\/[^)]+\))/g)) !== null && _a !== void 0 ? _a : [])
|
||||||
|
.forEach(function (match) {
|
||||||
|
return cssGlobalsToDefine["url" + crypto
|
||||||
|
.createHash("sha256")
|
||||||
|
.update(match)
|
||||||
|
.digest("hex")
|
||||||
|
.substring(0, 15)] = match;
|
||||||
|
});
|
||||||
|
var fixedCssCode = cssCode;
|
||||||
|
Object.keys(cssGlobalsToDefine).forEach(function (cssVariableName) {
|
||||||
|
//NOTE: split/join pattern ~ replace all
|
||||||
|
return fixedCssCode =
|
||||||
|
fixedCssCode.split(cssGlobalsToDefine[cssVariableName])
|
||||||
|
.join("var(--" + cssVariableName + ")");
|
||||||
|
});
|
||||||
|
return { fixedCssCode: fixedCssCode, cssGlobalsToDefine: cssGlobalsToDefine };
|
||||||
|
}
|
||||||
|
exports.replaceImportFromStaticInCssCode = replaceImportFromStaticInCssCode;
|
||||||
|
function generateCssCodeToDefineGlobals(params) {
|
||||||
|
var cssGlobalsToDefine = params.cssGlobalsToDefine;
|
||||||
|
return {
|
||||||
|
"cssCodeToPrependInHead": __spread([
|
||||||
|
":root {"
|
||||||
|
], Object.keys(cssGlobalsToDefine)
|
||||||
|
.map(function (cssVariableName) { return [
|
||||||
|
"--" + cssVariableName + ":",
|
||||||
|
[
|
||||||
|
"url(",
|
||||||
|
"${url.resourcesPath}" +
|
||||||
|
cssGlobalsToDefine[cssVariableName].match(/^url\(([^)]+)\)$/)[1],
|
||||||
|
")"
|
||||||
|
].join("")
|
||||||
|
].join(" "); })
|
||||||
|
.map(function (line) { return " " + line + ";"; }), [
|
||||||
|
"}"
|
||||||
|
]).join("\n")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
exports.generateCssCodeToDefineGlobals = generateCssCodeToDefineGlobals;
|
||||||
|
//# sourceMappingURL=replaceImportFromStatic.js.map
|
1
bin/build-keycloak-theme/replaceImportFromStatic.js.map
Normal file
1
bin/build-keycloak-theme/replaceImportFromStatic.js.map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"replaceImportFromStatic.js","sourceRoot":"","sources":["../../src/bin/build-keycloak-theme/replaceImportFromStatic.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,6CAAiC;AAEjC,SAAgB,+BAA+B,CAC3C,MAGC;IAGO,IAAA,MAAM,GAA0B,MAAM,OAAhC,EAAE,mBAAmB,GAAK,MAAM,oBAAX,CAAY;IAE/C,IAAM,WAAW,GAAG,MAAO,CAAC,OAAO,CAC/B,YAAY,EACZ,YAAU,mBAAmB,gEAAwD,CACxF,CAAC;IAEF,OAAO,EAAE,WAAW,aAAA,EAAE,CAAC;AAE3B,CAAC;AAhBD,0EAgBC;AAED,SAAgB,gCAAgC,CAC5C,MAEC;;IAMO,IAAA,OAAO,GAAK,MAAM,QAAX,CAAY;IAE3B,IAAM,kBAAkB,GAA2B,EAAE,CAAC;IAEtD,IAAI,GAAG,OAAC,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,mCAAI,EAAE,CAAC;SAC5C,OAAO,CAAC,UAAA,KAAK;QACV,OAAA,kBAAkB,CAClB,KAAK,GAAG,MAAM;aACT,UAAU,CAAC,QAAQ,CAAC;aACpB,MAAM,CAAC,KAAK,CAAC;aACb,MAAM,CAAC,KAAK,CAAC;aACb,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CACpB,GAAG,KAAK;IANT,CAMS,CACZ,CAAC;IAEN,IAAI,YAAY,GAAG,OAAO,CAAC;IAE3B,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,OAAO,CACnC,UAAA,eAAe;QACX,wCAAwC;QACxC,OAAA,YAAY;YACZ,YAAY,CAAC,KAAK,CAAC,kBAAkB,CAAC,eAAe,CAAC,CAAC;iBAClD,IAAI,CAAC,WAAS,eAAe,MAAG,CAAC;IAFtC,CAEsC,CAC7C,CAAC;IAEF,OAAO,EAAE,YAAY,cAAA,EAAE,kBAAkB,oBAAA,EAAE,CAAC;AAEhD,CAAC;AApCD,4EAoCC;AAED,SAAgB,8BAA8B,CAC1C,MAEC;IAKO,IAAA,kBAAkB,GAAK,MAAM,mBAAX,CAAY;IAEtC,OAAO;QACH,wBAAwB,EAAE;YACtB,SAAS;WACN,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC;aAC7B,GAAG,CAAC,UAAA,eAAe,IAAI,OAAA;YACpB,OAAK,eAAe,MAAG;YACvB;gBACI,MAAM;gBACN,sBAAsB;oBACtB,kBAAkB,CAAC,eAAe,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAE,CAAC,CAAC,CAAC;gBACjE,GAAG;aACN,CAAC,IAAI,CAAC,EAAE,CAAC;SACb,CAAC,IAAI,CAAC,GAAG,CAAC,EARa,CAQb,CAAC;aACX,GAAG,CAAC,UAAA,IAAI,IAAI,OAAA,SAAO,IAAI,MAAG,EAAd,CAAc,CAAC;YAChC,GAAG;WACL,IAAI,CAAC,IAAI,CAAC;KACf,CAAC;AAEN,CAAC;AA5BD,wEA4BC"}
|
2
bin/download-sample-keycloak-themes.d.ts
vendored
Normal file
2
bin/download-sample-keycloak-themes.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
export {};
|
58
bin/download-sample-keycloak-themes.js
Executable file
58
bin/download-sample-keycloak-themes.js
Executable file
@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
"use strict";
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
var __read = (this && this.__read) || function (o, n) {
|
||||||
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||||
|
if (!m) return o;
|
||||||
|
var i = m.call(o), r, ar = [], e;
|
||||||
|
try {
|
||||||
|
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
||||||
|
}
|
||||||
|
catch (error) { e = { error: error }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (r && !r.done && (m = i["return"])) m.call(i);
|
||||||
|
}
|
||||||
|
finally { if (e) throw e.error; }
|
||||||
|
}
|
||||||
|
return ar;
|
||||||
|
};
|
||||||
|
var __spread = (this && this.__spread) || function () {
|
||||||
|
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
|
||||||
|
return ar;
|
||||||
|
};
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
var fs = __importStar(require("fs"));
|
||||||
|
var path_1 = require("path");
|
||||||
|
var build_keycloak_theme_1 = require("./build-keycloak-theme");
|
||||||
|
var child_process_1 = __importDefault(require("child_process"));
|
||||||
|
if (!fs.existsSync(build_keycloak_theme_1.keycloakThemeBuildingDirPath)) {
|
||||||
|
console.log("Error: The keycloak theme need to be build");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
var url = "https://github.com/garronej/keycloak-react-theming/releases/download/v0.0.1/other_keycloak_thems.zip";
|
||||||
|
__spread([
|
||||||
|
"wget " + url
|
||||||
|
], ["unzip", "rm"].map(function (prg) { return prg + " " + path_1.basename(url); })).forEach(function (cmd) { return child_process_1.default.execSync(cmd, { "cwd": path_1.join(build_keycloak_theme_1.keycloakThemeBuildingDirPath, "src", "main", "resources", "theme") }); });
|
||||||
|
//# sourceMappingURL=download-sample-keycloak-themes.js.map
|
1
bin/download-sample-keycloak-themes.js.map
Normal file
1
bin/download-sample-keycloak-themes.js.map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"download-sample-keycloak-themes.js","sourceRoot":"","sources":["../src/bin/download-sample-keycloak-themes.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,qCAAyB;AACzB,6BAAkE;AAClE,+DAAsE;AACtE,gEAA0C;AAE1C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,mDAA4B,CAAC,EAAE;IAC9C,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;IAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;CACnB;AAED,IAAM,GAAG,GAAG,sGAAsG,CAAC;AAEnH;IACI,UAAQ,GAAK;GACV,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,UAAA,GAAG,IAAI,OAAG,GAAG,SAAI,eAAY,CAAC,GAAG,CAAG,EAA7B,CAA6B,CAAC,EAC9D,OAAO,CAAC,UAAA,GAAG,IAAI,OAAA,uBAAa,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,WAAQ,CAAC,mDAA4B,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,CAAC,EAAnH,CAAmH,CAAC,CAAC"}
|
1
lib/inex.d.ts
vendored
Normal file
1
lib/inex.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export {};
|
3
lib/inex.js
Normal file
3
lib/inex.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
//# sourceMappingURL=inex.js.map
|
1
lib/inex.js.map
Normal file
1
lib/inex.js.map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"inex.js","sourceRoot":"","sources":["../src/lib/inex.ts"],"names":[],"mappings":""}
|
1708
package-lock.json
generated
Normal file
1708
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
133
package.json
Normal file → Executable file
133
package.json
Normal file → Executable file
@ -1,98 +1,77 @@
|
|||||||
{
|
{
|
||||||
"name": "keycloakify",
|
"name": "keycloak-react-theming",
|
||||||
"version": "7.0.0-rc.3",
|
"version": "0.0.8",
|
||||||
"description": "Create Keycloak themes using React",
|
"description": "Provides a way to customize Keycloak login and register pages with React",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/inseefrlab/keycloakify.git"
|
"url": "git://github.com/garronej/keycloak-react-theming.git"
|
||||||
},
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"types": "dist/index.d.ts",
|
|
||||||
"scripts": {
|
|
||||||
"build": "rimraf dist/ && tsc -p src/bin && tsc -p src/tsconfig.json && tsc-alias -p src/tsconfig.json && yarn grant-exec-perms && yarn copy-files dist/",
|
|
||||||
"build:watch": "tsc -p src/tsconfig.json && (concurrently \"tsc -p src/tsconfig.json -w\" \"tsc-alias -p src/tsconfig.json\")",
|
|
||||||
"build:test": "rimraf dist_test/ && tsc -p test/tsconfig.json && tsc-alias -p test/tsconfig.json && yarn copy-files dist_test/src",
|
|
||||||
"grant-exec-perms": "node dist/bin/tools/grant-exec-perms.js",
|
|
||||||
"copy-files": "copyfiles -u 1 src/**/*.ftl",
|
|
||||||
"test": "yarn build:test && node dist_test/test/bin && node dist_test/test/lib",
|
|
||||||
"test:sample-app": "yarn build:test && node dist_test/test/bin/main.js",
|
|
||||||
"_format": "prettier '**/*.{ts,tsx,json,md}'",
|
|
||||||
"format": "yarn _format --write",
|
|
||||||
"format:check": "yarn _format --list-different",
|
|
||||||
"generate-messages": "ts-node --skipProject scripts/generate-i18n-messages.ts",
|
|
||||||
"link-in-app": "ts-node --skipProject scripts/link-in-app.ts",
|
|
||||||
"link-in-starter": "yarn link-in-app keycloakify-starter",
|
|
||||||
"tsc-watch": "tsc -p src/bin -w & tsc -p src/lib -w "
|
|
||||||
},
|
},
|
||||||
|
"main": "dist/lib/index.js",
|
||||||
|
"types": "dist/lib/index.d.ts",
|
||||||
"bin": {
|
"bin": {
|
||||||
"keycloakify": "dist/bin/keycloakify/index.js",
|
"build-keycloak-theme": "bin/build-keycloak-theme/index.js",
|
||||||
"create-keycloak-email-directory": "dist/bin/create-keycloak-email-directory.js",
|
"download-sample-keycloak-themes": "bin/download-sample-keycloak-themes.js"
|
||||||
"download-builtin-keycloak-theme": "dist/bin/download-builtin-keycloak-theme.js",
|
|
||||||
"eject-keycloak-page": "dist/bin/eject-keycloak-page.js"
|
|
||||||
},
|
|
||||||
"lint-staged": {
|
|
||||||
"*.{ts,tsx,json,md}": [
|
|
||||||
"prettier --write"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"husky": {
|
|
||||||
"hooks": {
|
|
||||||
"pre-commit": "lint-staged -v"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"author": "u/garronej",
|
"author": "u/garronej",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"files": [
|
"files": [
|
||||||
"src/",
|
"src/bin/build-keycloak-theme/generateDebugFiles/index.ts",
|
||||||
"dist/",
|
"src/bin/build-keycloak-theme/generateDebugFiles/standalone-ha.xml",
|
||||||
"!dist/tsconfig.tsbuildinfo",
|
"src/bin/build-keycloak-theme/generateFtl.ts",
|
||||||
"!dist/bin/tsconfig.tsbuildinfo"
|
"src/bin/build-keycloak-theme/generateJavaStackFiles.ts",
|
||||||
|
"src/bin/build-keycloak-theme/generateKeycloakThemeResources.ts",
|
||||||
|
"src/bin/build-keycloak-theme/index.ts",
|
||||||
|
"src/bin/build-keycloak-theme/replaceImportFromStatic.ts",
|
||||||
|
"src/bin/download-sample-keycloak-themes.ts",
|
||||||
|
"src/lib/inex.ts",
|
||||||
|
"src/tools/crawl.ts",
|
||||||
|
"src/tools/transformCodebase.ts",
|
||||||
|
"bin/build-keycloak-theme/generateDebugFiles/index.d.ts",
|
||||||
|
"bin/build-keycloak-theme/generateDebugFiles/index.js",
|
||||||
|
"bin/build-keycloak-theme/generateDebugFiles/index.js.map",
|
||||||
|
"bin/build-keycloak-theme/generateDebugFiles/standalone-ha.xml",
|
||||||
|
"bin/build-keycloak-theme/generateFtl.d.ts",
|
||||||
|
"bin/build-keycloak-theme/generateFtl.js",
|
||||||
|
"bin/build-keycloak-theme/generateFtl.js.map",
|
||||||
|
"bin/build-keycloak-theme/generateJavaStackFiles.d.ts",
|
||||||
|
"bin/build-keycloak-theme/generateJavaStackFiles.js",
|
||||||
|
"bin/build-keycloak-theme/generateJavaStackFiles.js.map",
|
||||||
|
"bin/build-keycloak-theme/generateKeycloakThemeResources.d.ts",
|
||||||
|
"bin/build-keycloak-theme/generateKeycloakThemeResources.js",
|
||||||
|
"bin/build-keycloak-theme/generateKeycloakThemeResources.js.map",
|
||||||
|
"bin/build-keycloak-theme/index.d.ts",
|
||||||
|
"bin/build-keycloak-theme/index.js",
|
||||||
|
"bin/build-keycloak-theme/index.js.map",
|
||||||
|
"bin/build-keycloak-theme/replaceImportFromStatic.d.ts",
|
||||||
|
"bin/build-keycloak-theme/replaceImportFromStatic.js",
|
||||||
|
"bin/build-keycloak-theme/replaceImportFromStatic.js.map",
|
||||||
|
"bin/download-sample-keycloak-themes.d.ts",
|
||||||
|
"bin/download-sample-keycloak-themes.js",
|
||||||
|
"bin/download-sample-keycloak-themes.js.map",
|
||||||
|
"lib/inex.d.ts",
|
||||||
|
"lib/inex.js",
|
||||||
|
"lib/inex.js.map",
|
||||||
|
"tools/crawl.d.ts",
|
||||||
|
"tools/crawl.js",
|
||||||
|
"tools/crawl.js.map",
|
||||||
|
"tools/transformCodebase.d.ts",
|
||||||
|
"tools/transformCodebase.js",
|
||||||
|
"tools/transformCodebase.js.map"
|
||||||
],
|
],
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"bluehats",
|
|
||||||
"keycloak",
|
"keycloak",
|
||||||
"react",
|
"react",
|
||||||
"theme",
|
"theme"
|
||||||
"FreeMarker",
|
|
||||||
"ftl",
|
|
||||||
"login",
|
|
||||||
"register"
|
|
||||||
],
|
],
|
||||||
"homepage": "https://www.keycloakify.dev",
|
"homepage": "https://github.com/garronej/keycloak-react-theming",
|
||||||
"peerDependencies": {
|
|
||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.0.0",
|
"@types/node": "^10.0.0",
|
||||||
"@types/make-fetch-happen": "^10.0.1",
|
|
||||||
"@types/minimist": "^1.2.2",
|
|
||||||
"@types/node": "^18.15.3",
|
|
||||||
"@types/react": "18.0.9",
|
|
||||||
"concurrently": "^7.6.0",
|
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
"husky": "^4.3.8",
|
"denoify": "^0.6.4",
|
||||||
"lint-staged": "^11.0.0",
|
|
||||||
"prettier": "^2.3.0",
|
|
||||||
"properties-parser": "^0.3.1",
|
|
||||||
"react": "18.1.0",
|
|
||||||
"rimraf": "^3.0.2",
|
|
||||||
"scripting-tools": "^0.19.13",
|
"scripting-tools": "^0.19.13",
|
||||||
"ts-node": "^10.9.1",
|
"typescript": "^4.1.5"
|
||||||
"tsc-alias": "^1.8.3",
|
|
||||||
"typescript": "^5.0.1-rc"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/rest": "^18.12.0",
|
"cheerio": "^1.0.0-rc.5"
|
||||||
"cheerio": "^1.0.0-rc.5",
|
|
||||||
"cli-select": "^1.1.2",
|
|
||||||
"evt": "^2.4.18",
|
|
||||||
"make-fetch-happen": "^11.0.3",
|
|
||||||
"minimal-polyfills": "^2.2.2",
|
|
||||||
"minimist": "^1.2.6",
|
|
||||||
"path-browserify": "^1.0.1",
|
|
||||||
"react-markdown": "^5.0.3",
|
|
||||||
"rfc4648": "^1.5.2",
|
|
||||||
"tsafe": "^1.6.0",
|
|
||||||
"zod": "^3.17.10"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
||||||
"baseBranches": ["main", "landingpage"],
|
|
||||||
"extends": ["config:base"],
|
|
||||||
"dependencyDashboard": false,
|
|
||||||
"bumpVersion": "patch",
|
|
||||||
"rangeStrategy": "bump",
|
|
||||||
"ignorePaths": [".github/**"],
|
|
||||||
"branchPrefix": "renovate_",
|
|
||||||
"vulnerabilityAlerts": {
|
|
||||||
"enabled": false
|
|
||||||
},
|
|
||||||
"packageRules": [
|
|
||||||
{
|
|
||||||
"packagePatterns": ["*"],
|
|
||||||
"excludePackagePatterns": ["tsafe", "evt"],
|
|
||||||
"enabled": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"packagePatterns": ["tsafe", "evt"],
|
|
||||||
"matchUpdateTypes": ["minor", "patch"],
|
|
||||||
"automerge": true,
|
|
||||||
"automergeType": "branch",
|
|
||||||
"groupName": "garronej_modules_update"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,86 +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 "../src/bin/tools/crawl";
|
|
||||||
import { downloadBuiltinKeycloakTheme } from "../src/bin/download-builtin-keycloak-theme";
|
|
||||||
import { getProjectRoot } from "../src/bin/tools/getProjectRoot";
|
|
||||||
import { getCliOptions } from "../src/bin/tools/cliOptions";
|
|
||||||
import { getLogger } from "../src/bin/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");
|
|
||||||
|
|
||||||
fs.rmSync(tmpDirPath, { "recursive": true, "force": true });
|
|
||||||
|
|
||||||
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, "'")]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.rmSync(tmpDirPath, { recursive: true, force: true });
|
|
||||||
|
|
||||||
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`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,143 +0,0 @@
|
|||||||
import { execSync } from "child_process";
|
|
||||||
import { join as pathJoin, relative as pathRelative } from "path";
|
|
||||||
import { getProjectRoot } from "../src/bin/tools/getProjectRoot";
|
|
||||||
import * as fs from "fs";
|
|
||||||
|
|
||||||
const singletonDependencies: string[] = ["react", "@types/react"];
|
|
||||||
|
|
||||||
const rootDirPath = getProjectRoot();
|
|
||||||
|
|
||||||
//NOTE: This is only required because of: https://github.com/garronej/ts-ci/blob/c0e207b9677523d4ec97fe672ddd72ccbb3c1cc4/README.md?plain=1#L54-L58
|
|
||||||
fs.writeFileSync(
|
|
||||||
pathJoin(rootDirPath, "dist", "package.json"),
|
|
||||||
Buffer.from(
|
|
||||||
JSON.stringify(
|
|
||||||
(() => {
|
|
||||||
const packageJsonParsed = JSON.parse(fs.readFileSync(pathJoin(rootDirPath, "package.json")).toString("utf8"));
|
|
||||||
|
|
||||||
return {
|
|
||||||
...packageJsonParsed,
|
|
||||||
"main": packageJsonParsed["main"]?.replace(/^dist\//, ""),
|
|
||||||
"types": packageJsonParsed["types"]?.replace(/^dist\//, ""),
|
|
||||||
"module": packageJsonParsed["module"]?.replace(/^dist\//, ""),
|
|
||||||
"exports": !("exports" in packageJsonParsed)
|
|
||||||
? undefined
|
|
||||||
: Object.fromEntries(
|
|
||||||
Object.entries(packageJsonParsed["exports"]).map(([key, value]) => [
|
|
||||||
key,
|
|
||||||
(value as string).replace(/^\.\/dist\//, "./")
|
|
||||||
])
|
|
||||||
)
|
|
||||||
};
|
|
||||||
})(),
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
),
|
|
||||||
"utf8"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
fs.cpSync(pathJoin(rootDirPath, "src"), pathJoin(rootDirPath, "dist", "src"), { "recursive": true });
|
|
||||||
|
|
||||||
const commonThirdPartyDeps = (() => {
|
|
||||||
// For example [ "@emotion" ] it's more convenient than
|
|
||||||
// having to list every sub emotion packages (@emotion/css @emotion/utils ...)
|
|
||||||
// in singletonDependencies
|
|
||||||
const namespaceSingletonDependencies: string[] = [];
|
|
||||||
|
|
||||||
return [
|
|
||||||
...namespaceSingletonDependencies
|
|
||||||
.map(namespaceModuleName =>
|
|
||||||
fs
|
|
||||||
.readdirSync(pathJoin(rootDirPath, "node_modules", namespaceModuleName))
|
|
||||||
.map(submoduleName => `${namespaceModuleName}/${submoduleName}`)
|
|
||||||
)
|
|
||||||
.reduce((prev, curr) => [...prev, ...curr], []),
|
|
||||||
...singletonDependencies
|
|
||||||
];
|
|
||||||
})();
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
const cmd = ["yarn", "link", ...(targetModuleName !== undefined ? [targetModuleName] : ["--no-bin-links"])].join(" ");
|
|
||||||
|
|
||||||
console.log(`$ cd ${pathRelative(rootDirPath, cwd) || "."} && ${cmd}`);
|
|
||||||
|
|
||||||
execSync(cmd, {
|
|
||||||
cwd,
|
|
||||||
"env": {
|
|
||||||
...process.env,
|
|
||||||
"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 => execSync("yarn install", { "cwd": testAppPath }));
|
|
||||||
|
|
||||||
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"]
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
export {};
|
|
@ -1,81 +0,0 @@
|
|||||||
import { lazy, Suspense } from "react";
|
|
||||||
import type { PageProps } from "keycloakify/pages/PageProps";
|
|
||||||
import type { I18n } from "keycloakify/i18n";
|
|
||||||
import type { KcContext } from "./kcContext";
|
|
||||||
|
|
||||||
const Login = lazy(() => import("keycloakify/pages/Login"));
|
|
||||||
const Register = lazy(() => import("keycloakify/pages/Register"));
|
|
||||||
const RegisterUserProfile = lazy(() => import("keycloakify/pages/RegisterUserProfile"));
|
|
||||||
const Info = lazy(() => import("keycloakify/pages/Info"));
|
|
||||||
const Error = lazy(() => import("keycloakify/pages/Error"));
|
|
||||||
const LoginResetPassword = lazy(() => import("keycloakify/pages/LoginResetPassword"));
|
|
||||||
const LoginVerifyEmail = lazy(() => import("keycloakify/pages/LoginVerifyEmail"));
|
|
||||||
const Terms = lazy(() => import("keycloakify/pages/Terms"));
|
|
||||||
const LoginOtp = lazy(() => import("keycloakify/pages/LoginOtp"));
|
|
||||||
const LoginPassword = lazy(() => import("keycloakify/pages/LoginPassword"));
|
|
||||||
const LoginUsername = lazy(() => import("keycloakify/pages/LoginUsername"));
|
|
||||||
const WebauthnAuthenticate = lazy(() => import("keycloakify/pages/WebauthnAuthenticate"));
|
|
||||||
const LoginUpdatePassword = lazy(() => import("keycloakify/pages/LoginUpdatePassword"));
|
|
||||||
const LoginUpdateProfile = lazy(() => import("keycloakify/pages/LoginUpdateProfile"));
|
|
||||||
const LoginIdpLinkConfirm = lazy(() => import("keycloakify/pages/LoginIdpLinkConfirm"));
|
|
||||||
const LoginPageExpired = lazy(() => import("keycloakify/pages/LoginPageExpired"));
|
|
||||||
const LoginIdpLinkEmail = lazy(() => import("keycloakify/pages/LoginIdpLinkEmail"));
|
|
||||||
const LoginConfigTotp = lazy(() => import("keycloakify/pages/LoginConfigTotp"));
|
|
||||||
const LogoutConfirm = lazy(() => import("keycloakify/pages/LogoutConfirm"));
|
|
||||||
const UpdateUserProfile = lazy(() => import("keycloakify/pages/UpdateUserProfile"));
|
|
||||||
const IdpReviewUserProfile = lazy(() => import("keycloakify/pages/IdpReviewUserProfile"));
|
|
||||||
|
|
||||||
export default function Fallback(props: PageProps<KcContext, I18n>) {
|
|
||||||
const { kcContext, ...rest } = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Suspense>
|
|
||||||
{(() => {
|
|
||||||
switch (kcContext.pageId) {
|
|
||||||
case "login.ftl":
|
|
||||||
return <Login kcContext={kcContext} {...rest} />;
|
|
||||||
case "register.ftl":
|
|
||||||
return <Register kcContext={kcContext} {...rest} />;
|
|
||||||
case "register-user-profile.ftl":
|
|
||||||
return <RegisterUserProfile kcContext={kcContext} {...rest} />;
|
|
||||||
case "info.ftl":
|
|
||||||
return <Info kcContext={kcContext} {...rest} />;
|
|
||||||
case "error.ftl":
|
|
||||||
return <Error kcContext={kcContext} {...rest} />;
|
|
||||||
case "login-reset-password.ftl":
|
|
||||||
return <LoginResetPassword kcContext={kcContext} {...rest} />;
|
|
||||||
case "login-verify-email.ftl":
|
|
||||||
return <LoginVerifyEmail kcContext={kcContext} {...rest} />;
|
|
||||||
case "terms.ftl":
|
|
||||||
return <Terms kcContext={kcContext} {...rest} />;
|
|
||||||
case "login-otp.ftl":
|
|
||||||
return <LoginOtp kcContext={kcContext} {...rest} />;
|
|
||||||
case "login-username.ftl":
|
|
||||||
return <LoginUsername kcContext={kcContext} {...rest} />;
|
|
||||||
case "login-password.ftl":
|
|
||||||
return <LoginPassword kcContext={kcContext} {...rest} />;
|
|
||||||
case "webauthn-authenticate.ftl":
|
|
||||||
return <WebauthnAuthenticate kcContext={kcContext} {...rest} />;
|
|
||||||
case "login-update-password.ftl":
|
|
||||||
return <LoginUpdatePassword kcContext={kcContext} {...rest} />;
|
|
||||||
case "login-update-profile.ftl":
|
|
||||||
return <LoginUpdateProfile kcContext={kcContext} {...rest} />;
|
|
||||||
case "login-idp-link-confirm.ftl":
|
|
||||||
return <LoginIdpLinkConfirm kcContext={kcContext} {...rest} />;
|
|
||||||
case "login-idp-link-email.ftl":
|
|
||||||
return <LoginIdpLinkEmail kcContext={kcContext} {...rest} />;
|
|
||||||
case "login-page-expired.ftl":
|
|
||||||
return <LoginPageExpired kcContext={kcContext} {...rest} />;
|
|
||||||
case "login-config-totp.ftl":
|
|
||||||
return <LoginConfigTotp kcContext={kcContext} {...rest} />;
|
|
||||||
case "logout-confirm.ftl":
|
|
||||||
return <LogoutConfirm kcContext={kcContext} {...rest} />;
|
|
||||||
case "update-user-profile.ftl":
|
|
||||||
return <UpdateUserProfile kcContext={kcContext} {...rest} />;
|
|
||||||
case "idp-review-user-profile.ftl":
|
|
||||||
return <IdpReviewUserProfile kcContext={kcContext} {...rest} />;
|
|
||||||
}
|
|
||||||
})()}
|
|
||||||
</Suspense>
|
|
||||||
);
|
|
||||||
}
|
|
197
src/Template.tsx
197
src/Template.tsx
@ -1,197 +0,0 @@
|
|||||||
import { assert } from "keycloakify/tools/assert";
|
|
||||||
import { clsx } from "keycloakify/tools/clsx";
|
|
||||||
import { usePrepareTemplate } from "keycloakify/lib/usePrepareTemplate";
|
|
||||||
import { type TemplateProps, defaultTemplateClasses } from "keycloakify/TemplateProps";
|
|
||||||
import { useGetClassName } from "keycloakify/lib/useGetClassName";
|
|
||||||
import type { KcContext } from "./kcContext";
|
|
||||||
import type { I18n } from "./i18n";
|
|
||||||
|
|
||||||
export default function Template(props: TemplateProps<KcContext, I18n>) {
|
|
||||||
const {
|
|
||||||
displayInfo = false,
|
|
||||||
displayMessage = true,
|
|
||||||
displayRequiredFields = false,
|
|
||||||
displayWide = false,
|
|
||||||
showAnotherWayIfPresent = true,
|
|
||||||
headerNode,
|
|
||||||
showUsernameNode = null,
|
|
||||||
formNode,
|
|
||||||
infoNode = null,
|
|
||||||
kcContext,
|
|
||||||
i18n,
|
|
||||||
doUseDefaultCss,
|
|
||||||
classes
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const { getClassName } = useGetClassName({
|
|
||||||
"defaultClasses": !doUseDefaultCss ? undefined : defaultTemplateClasses,
|
|
||||||
classes
|
|
||||||
});
|
|
||||||
|
|
||||||
const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n;
|
|
||||||
|
|
||||||
const { realm, locale, auth, url, message, isAppInitiatedAction } = kcContext;
|
|
||||||
|
|
||||||
const { isReady } = usePrepareTemplate({
|
|
||||||
"doFetchDefaultThemeResources": doUseDefaultCss,
|
|
||||||
url,
|
|
||||||
"stylesCommon": [
|
|
||||||
"node_modules/patternfly/dist/css/patternfly.min.css",
|
|
||||||
"node_modules/patternfly/dist/css/patternfly-additions.min.css",
|
|
||||||
"lib/zocial/zocial.css"
|
|
||||||
],
|
|
||||||
"styles": ["css/login.css"],
|
|
||||||
"htmlClassName": getClassName("kcHtmlClass")
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isReady) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={getClassName("kcLoginClass")}>
|
|
||||||
<div id="kc-header" className={getClassName("kcHeaderClass")}>
|
|
||||||
<div id="kc-header-wrapper" className={getClassName("kcHeaderWrapperClass")}>
|
|
||||||
{msg("loginTitleHtml", realm.displayNameHtml)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={clsx(getClassName("kcFormCardClass"), displayWide && getClassName("kcFormCardAccountClass"))}>
|
|
||||||
<header className={getClassName("kcFormHeaderClass")}>
|
|
||||||
{realm.internationalizationEnabled && (assert(locale !== undefined), true) && locale.supported.length > 1 && (
|
|
||||||
<div id="kc-locale">
|
|
||||||
<div id="kc-locale-wrapper" className={getClassName("kcLocaleWrapperClass")}>
|
|
||||||
<div className="kc-dropdown" id="kc-locale-dropdown">
|
|
||||||
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
|
||||||
<a href="#" id="kc-current-locale-link">
|
|
||||||
{labelBySupportedLanguageTag[currentLanguageTag]}
|
|
||||||
</a>
|
|
||||||
<ul>
|
|
||||||
{locale.supported.map(({ languageTag }) => (
|
|
||||||
<li key={languageTag} className="kc-dropdown-item">
|
|
||||||
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
|
||||||
<a href="#" onClick={() => changeLocale(languageTag)}>
|
|
||||||
{labelBySupportedLanguageTag[languageTag]}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{!(auth !== undefined && auth.showUsername && !auth.showResetCredentials) ? (
|
|
||||||
displayRequiredFields ? (
|
|
||||||
<div className={getClassName("kcContentWrapperClass")}>
|
|
||||||
<div className={clsx(getClassName("kcLabelWrapperClass"), "subtitle")}>
|
|
||||||
<span className="subtitle">
|
|
||||||
<span className="required">*</span>
|
|
||||||
{msg("requiredFields")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="col-md-10">
|
|
||||||
<h1 id="kc-page-title">{headerNode}</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<h1 id="kc-page-title">{headerNode}</h1>
|
|
||||||
)
|
|
||||||
) : displayRequiredFields ? (
|
|
||||||
<div className={getClassName("kcContentWrapperClass")}>
|
|
||||||
<div className={clsx(getClassName("kcLabelWrapperClass"), "subtitle")}>
|
|
||||||
<span className="subtitle">
|
|
||||||
<span className="required">*</span> {msg("requiredFields")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="col-md-10">
|
|
||||||
{showUsernameNode}
|
|
||||||
<div className={getClassName("kcFormGroupClass")}>
|
|
||||||
<div id="kc-username">
|
|
||||||
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
|
|
||||||
<a id="reset-login" href={url.loginRestartFlowUrl}>
|
|
||||||
<div className="kc-login-tooltip">
|
|
||||||
<i className={getClassName("kcResetFlowIcon")}></i>
|
|
||||||
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{showUsernameNode}
|
|
||||||
<div className={getClassName("kcFormGroupClass")}>
|
|
||||||
<div id="kc-username">
|
|
||||||
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
|
|
||||||
<a id="reset-login" href={url.loginRestartFlowUrl}>
|
|
||||||
<div className="kc-login-tooltip">
|
|
||||||
<i className={getClassName("kcResetFlowIcon")}></i>
|
|
||||||
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</header>
|
|
||||||
<div id="kc-content">
|
|
||||||
<div id="kc-content-wrapper">
|
|
||||||
{/* App-initiated actions should not see warning messages about the need to complete the action during login. */}
|
|
||||||
{displayMessage && message !== undefined && (message.type !== "warning" || !isAppInitiatedAction) && (
|
|
||||||
<div className={clsx("alert", `alert-${message.type}`)}>
|
|
||||||
{message.type === "success" && <span className={getClassName("kcFeedbackSuccessIcon")}></span>}
|
|
||||||
{message.type === "warning" && <span className={getClassName("kcFeedbackWarningIcon")}></span>}
|
|
||||||
{message.type === "error" && <span className={getClassName("kcFeedbackErrorIcon")}></span>}
|
|
||||||
{message.type === "info" && <span className={getClassName("kcFeedbackInfoIcon")}></span>}
|
|
||||||
<span
|
|
||||||
className="kc-feedback-text"
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
"__html": message.summary
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{formNode}
|
|
||||||
{auth !== undefined && auth.showTryAnotherWayLink && showAnotherWayIfPresent && (
|
|
||||||
<form
|
|
||||||
id="kc-select-try-another-way-form"
|
|
||||||
action={url.loginAction}
|
|
||||||
method="post"
|
|
||||||
className={clsx(displayWide && getClassName("kcContentWrapperClass"))}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={clsx(
|
|
||||||
displayWide && [getClassName("kcFormSocialAccountContentClass"), getClassName("kcFormSocialAccountClass")]
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className={getClassName("kcFormGroupClass")}>
|
|
||||||
<input type="hidden" name="tryAnotherWay" value="on" />
|
|
||||||
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
id="try-another-way"
|
|
||||||
onClick={() => {
|
|
||||||
document.forms["kc-select-try-another-way-form" as never].submit();
|
|
||||||
return false;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{msg("doTryAnotherWay")}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
)}
|
|
||||||
{displayInfo && (
|
|
||||||
<div id="kc-info" className={getClassName("kcSignUpClass")}>
|
|
||||||
<div id="kc-info-wrapper" className={getClassName("kcInfoAreaWrapperClass")}>
|
|
||||||
{infoNode}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
import type { ReactNode } from "react";
|
|
||||||
import type { KcContext } from "./kcContext";
|
|
||||||
import type { I18n } from "./i18n";
|
|
||||||
|
|
||||||
export type TemplateProps<KcContext extends KcContext.Common, I18nExtended extends I18n> = {
|
|
||||||
kcContext: KcContext;
|
|
||||||
i18n: I18nExtended;
|
|
||||||
doUseDefaultCss: boolean;
|
|
||||||
classes?: Partial<Record<TemplateClassKey, string>>;
|
|
||||||
|
|
||||||
formNode: ReactNode;
|
|
||||||
displayInfo?: boolean;
|
|
||||||
displayMessage?: boolean;
|
|
||||||
displayRequiredFields?: boolean;
|
|
||||||
displayWide?: boolean;
|
|
||||||
showAnotherWayIfPresent?: boolean;
|
|
||||||
headerNode: ReactNode;
|
|
||||||
showUsernameNode?: ReactNode;
|
|
||||||
infoNode?: ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TemplateClassKey =
|
|
||||||
| "kcHtmlClass"
|
|
||||||
| "kcLoginClass"
|
|
||||||
| "kcHeaderClass"
|
|
||||||
| "kcHeaderWrapperClass"
|
|
||||||
| "kcFormCardClass"
|
|
||||||
| "kcFormCardAccountClass"
|
|
||||||
| "kcFormHeaderClass"
|
|
||||||
| "kcLocaleWrapperClass"
|
|
||||||
| "kcContentWrapperClass"
|
|
||||||
| "kcLabelWrapperClass"
|
|
||||||
| "kcFormGroupClass"
|
|
||||||
| "kcResetFlowIcon"
|
|
||||||
| "kcFeedbackSuccessIcon"
|
|
||||||
| "kcFeedbackWarningIcon"
|
|
||||||
| "kcFeedbackErrorIcon"
|
|
||||||
| "kcFeedbackInfoIcon"
|
|
||||||
| "kcFormSocialAccountContentClass"
|
|
||||||
| "kcFormSocialAccountClass"
|
|
||||||
| "kcSignUpClass"
|
|
||||||
| "kcInfoAreaWrapperClass";
|
|
||||||
|
|
||||||
export const defaultTemplateClasses: Record<TemplateClassKey, string | undefined> = {
|
|
||||||
"kcHtmlClass": "login-pf",
|
|
||||||
"kcLoginClass": "login-pf-page",
|
|
||||||
"kcContentWrapperClass": "row",
|
|
||||||
"kcHeaderClass": "login-pf-page-header",
|
|
||||||
"kcHeaderWrapperClass": undefined,
|
|
||||||
"kcFormCardClass": "card-pf",
|
|
||||||
"kcFormCardAccountClass": "login-pf-accounts",
|
|
||||||
"kcFormSocialAccountClass": "login-pf-social-section",
|
|
||||||
"kcFormSocialAccountContentClass": "col-xs-12 col-sm-6",
|
|
||||||
"kcFormHeaderClass": "login-pf-header",
|
|
||||||
"kcLocaleWrapperClass": undefined,
|
|
||||||
"kcFeedbackErrorIcon": "pficon pficon-error-circle-o",
|
|
||||||
"kcFeedbackWarningIcon": "pficon pficon-warning-triangle-o",
|
|
||||||
"kcFeedbackSuccessIcon": "pficon pficon-ok",
|
|
||||||
"kcFeedbackInfoIcon": "pficon pficon-info",
|
|
||||||
"kcResetFlowIcon": "pficon pficon-arrow fa-2x",
|
|
||||||
"kcFormGroupClass": "form-group",
|
|
||||||
"kcLabelWrapperClass": "col-xs-12 col-sm-12 col-md-12 col-lg-12",
|
|
||||||
"kcSignUpClass": "login-pf-signup",
|
|
||||||
"kcInfoAreaWrapperClass": undefined
|
|
||||||
};
|
|
69
src/bin/build-keycloak-theme/generateDebugFiles/index.ts
Normal file
69
src/bin/build-keycloak-theme/generateDebugFiles/index.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { join as pathJoin, dirname as pathDirname, basename as pathBasename } from "path";
|
||||||
|
|
||||||
|
/** Files for being able to run a hot reload keycloak container */
|
||||||
|
export function generateDebugFiles(
|
||||||
|
params: {
|
||||||
|
packageJsonName: string;
|
||||||
|
keycloakThemeBuildingDirPath: string;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
|
const { packageJsonName, keycloakThemeBuildingDirPath } = params;
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
pathJoin(keycloakThemeBuildingDirPath, "Dockerfile"),
|
||||||
|
Buffer.from(
|
||||||
|
[
|
||||||
|
"FROM jboss/keycloak:11.0.3",
|
||||||
|
"",
|
||||||
|
"USER root",
|
||||||
|
"",
|
||||||
|
"WORKDIR /",
|
||||||
|
"",
|
||||||
|
"ADD configuration /opt/jboss/keycloak/standalone/configuration/",
|
||||||
|
"",
|
||||||
|
'ENTRYPOINT [ "/opt/jboss/tools/docker-entrypoint.sh" ]',
|
||||||
|
].join("\n"),
|
||||||
|
"utf8"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const dockerImage = `${packageJsonName}/keycloak-hot-reload`;
|
||||||
|
const containerName = "keycloak-testing-container";
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
pathJoin(keycloakThemeBuildingDirPath, "start_keycloak_testing_container.sh"),
|
||||||
|
Buffer.from(
|
||||||
|
[
|
||||||
|
"#!/bin/bash",
|
||||||
|
"",
|
||||||
|
`docker rm ${containerName} || true`,
|
||||||
|
"",
|
||||||
|
`docker build . -t ${dockerImage}`,
|
||||||
|
"",
|
||||||
|
"docker run \\",
|
||||||
|
" -p 8080:8080 \\",
|
||||||
|
` --name ${containerName} \\`,
|
||||||
|
" -e KEYCLOAK_USER=admin \\",
|
||||||
|
" -e KEYCLOAK_PASSWORD=admin \\",
|
||||||
|
` -v ${pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", "onyxia")}:/opt/jboss/keycloak/themes/onyxia:rw \\`,
|
||||||
|
` -it ${dockerImage}:latest`,
|
||||||
|
""
|
||||||
|
].join("\n"),
|
||||||
|
"utf8"
|
||||||
|
),
|
||||||
|
{ "mode": 0o755 }
|
||||||
|
);
|
||||||
|
|
||||||
|
const standaloneHaFilePath = pathJoin(keycloakThemeBuildingDirPath, "configuration", "standalone-ha.xml");
|
||||||
|
|
||||||
|
try { fs.mkdirSync(pathDirname(standaloneHaFilePath)); } catch { }
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
standaloneHaFilePath,
|
||||||
|
fs.readFileSync(pathJoin(__dirname, pathBasename(standaloneHaFilePath)))
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,666 @@
|
|||||||
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
|
|
||||||
|
<server xmlns="urn:jboss:domain:13.0">
|
||||||
|
<extensions>
|
||||||
|
<extension module="org.jboss.as.clustering.infinispan"/>
|
||||||
|
<extension module="org.jboss.as.clustering.jgroups"/>
|
||||||
|
<extension module="org.jboss.as.connector"/>
|
||||||
|
<extension module="org.jboss.as.deployment-scanner"/>
|
||||||
|
<extension module="org.jboss.as.ee"/>
|
||||||
|
<extension module="org.jboss.as.ejb3"/>
|
||||||
|
<extension module="org.jboss.as.jaxrs"/>
|
||||||
|
<extension module="org.jboss.as.jmx"/>
|
||||||
|
<extension module="org.jboss.as.jpa"/>
|
||||||
|
<extension module="org.jboss.as.logging"/>
|
||||||
|
<extension module="org.jboss.as.mail"/>
|
||||||
|
<extension module="org.jboss.as.modcluster"/>
|
||||||
|
<extension module="org.jboss.as.naming"/>
|
||||||
|
<extension module="org.jboss.as.remoting"/>
|
||||||
|
<extension module="org.jboss.as.security"/>
|
||||||
|
<extension module="org.jboss.as.transactions"/>
|
||||||
|
<extension module="org.jboss.as.weld"/>
|
||||||
|
<extension module="org.keycloak.keycloak-server-subsystem"/>
|
||||||
|
<extension module="org.wildfly.extension.bean-validation"/>
|
||||||
|
<extension module="org.wildfly.extension.core-management"/>
|
||||||
|
<extension module="org.wildfly.extension.elytron"/>
|
||||||
|
<extension module="org.wildfly.extension.io"/>
|
||||||
|
<extension module="org.wildfly.extension.microprofile.config-smallrye"/>
|
||||||
|
<extension module="org.wildfly.extension.microprofile.health-smallrye"/>
|
||||||
|
<extension module="org.wildfly.extension.microprofile.metrics-smallrye"/>
|
||||||
|
<extension module="org.wildfly.extension.request-controller"/>
|
||||||
|
<extension module="org.wildfly.extension.security.manager"/>
|
||||||
|
<extension module="org.wildfly.extension.undertow"/>
|
||||||
|
</extensions>
|
||||||
|
<management>
|
||||||
|
<security-realms>
|
||||||
|
<security-realm name="ManagementRealm">
|
||||||
|
<authentication>
|
||||||
|
<local default-user="$local" skip-group-loading="true"/>
|
||||||
|
<properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
|
||||||
|
</authentication>
|
||||||
|
<authorization map-groups-to-roles="false">
|
||||||
|
<properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
|
||||||
|
</authorization>
|
||||||
|
</security-realm>
|
||||||
|
<security-realm name="ApplicationRealm">
|
||||||
|
<server-identities>
|
||||||
|
<ssl>
|
||||||
|
<keystore path="application.keystore" relative-to="jboss.server.config.dir" keystore-password="password" alias="server" key-password="password" generate-self-signed-certificate-host="localhost"/>
|
||||||
|
</ssl>
|
||||||
|
</server-identities>
|
||||||
|
<authentication>
|
||||||
|
<local default-user="$local" allowed-users="*" skip-group-loading="true"/>
|
||||||
|
<properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
|
||||||
|
</authentication>
|
||||||
|
<authorization>
|
||||||
|
<properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
|
||||||
|
</authorization>
|
||||||
|
</security-realm>
|
||||||
|
</security-realms>
|
||||||
|
<audit-log>
|
||||||
|
<formatters>
|
||||||
|
<json-formatter name="json-formatter"/>
|
||||||
|
</formatters>
|
||||||
|
<handlers>
|
||||||
|
<file-handler name="file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.server.data.dir"/>
|
||||||
|
</handlers>
|
||||||
|
<logger log-boot="true" log-read-only="false" enabled="false">
|
||||||
|
<handlers>
|
||||||
|
<handler name="file"/>
|
||||||
|
</handlers>
|
||||||
|
</logger>
|
||||||
|
</audit-log>
|
||||||
|
<management-interfaces>
|
||||||
|
<http-interface security-realm="ManagementRealm">
|
||||||
|
<http-upgrade enabled="true"/>
|
||||||
|
<socket-binding http="management-http"/>
|
||||||
|
</http-interface>
|
||||||
|
</management-interfaces>
|
||||||
|
<access-control provider="simple">
|
||||||
|
<role-mapping>
|
||||||
|
<role name="SuperUser">
|
||||||
|
<include>
|
||||||
|
<user name="$local"/>
|
||||||
|
</include>
|
||||||
|
</role>
|
||||||
|
</role-mapping>
|
||||||
|
</access-control>
|
||||||
|
</management>
|
||||||
|
<profile>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:logging:8.0">
|
||||||
|
<console-handler name="CONSOLE">
|
||||||
|
<formatter>
|
||||||
|
<named-formatter name="COLOR-PATTERN"/>
|
||||||
|
</formatter>
|
||||||
|
</console-handler>
|
||||||
|
<logger category="com.arjuna">
|
||||||
|
<level name="WARN"/>
|
||||||
|
</logger>
|
||||||
|
<logger category="io.jaegertracing.Configuration">
|
||||||
|
<level name="WARN"/>
|
||||||
|
</logger>
|
||||||
|
<logger category="org.jboss.as.config">
|
||||||
|
<level name="DEBUG"/>
|
||||||
|
</logger>
|
||||||
|
<logger category="sun.rmi">
|
||||||
|
<level name="WARN"/>
|
||||||
|
</logger>
|
||||||
|
<logger category="org.keycloak">
|
||||||
|
<level name="${env.KEYCLOAK_LOGLEVEL:INFO}"/>
|
||||||
|
</logger>
|
||||||
|
<root-logger>
|
||||||
|
<level name="${env.ROOT_LOGLEVEL:INFO}"/>
|
||||||
|
<handlers>
|
||||||
|
<handler name="CONSOLE"/>
|
||||||
|
</handlers>
|
||||||
|
</root-logger>
|
||||||
|
<formatter name="PATTERN">
|
||||||
|
<pattern-formatter pattern="%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
|
||||||
|
</formatter>
|
||||||
|
<formatter name="COLOR-PATTERN">
|
||||||
|
<pattern-formatter pattern="%K{level}%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
|
||||||
|
</formatter>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:bean-validation:1.0"/>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:core-management:1.0"/>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:datasources:6.0">
|
||||||
|
<datasources>
|
||||||
|
<datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
|
||||||
|
<connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
|
||||||
|
<driver>h2</driver>
|
||||||
|
<security>
|
||||||
|
<user-name>sa</user-name>
|
||||||
|
<password>sa</password>
|
||||||
|
</security>
|
||||||
|
</datasource>
|
||||||
|
<datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
|
||||||
|
<connection-url>jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE</connection-url>
|
||||||
|
<driver>h2</driver>
|
||||||
|
<pool>
|
||||||
|
<max-pool-size>100</max-pool-size>
|
||||||
|
</pool>
|
||||||
|
<security>
|
||||||
|
<user-name>sa</user-name>
|
||||||
|
<password>sa</password>
|
||||||
|
</security>
|
||||||
|
</datasource>
|
||||||
|
<drivers>
|
||||||
|
<driver name="h2" module="com.h2database.h2">
|
||||||
|
<xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
|
||||||
|
</driver>
|
||||||
|
</drivers>
|
||||||
|
</datasources>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:deployment-scanner:2.0">
|
||||||
|
<deployment-scanner path="deployments" relative-to="jboss.server.base.dir" scan-interval="5000" runtime-failure-causes-rollback="${jboss.deployment.scanner.rollback.on.failure:false}"/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:ee:5.0">
|
||||||
|
<spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
|
||||||
|
<concurrent>
|
||||||
|
<context-services>
|
||||||
|
<context-service name="default" jndi-name="java:jboss/ee/concurrency/context/default" use-transaction-setup-provider="true"/>
|
||||||
|
</context-services>
|
||||||
|
<managed-thread-factories>
|
||||||
|
<managed-thread-factory name="default" jndi-name="java:jboss/ee/concurrency/factory/default" context-service="default"/>
|
||||||
|
</managed-thread-factories>
|
||||||
|
<managed-executor-services>
|
||||||
|
<managed-executor-service name="default" jndi-name="java:jboss/ee/concurrency/executor/default" context-service="default" hung-task-threshold="60000" keepalive-time="5000"/>
|
||||||
|
</managed-executor-services>
|
||||||
|
<managed-scheduled-executor-services>
|
||||||
|
<managed-scheduled-executor-service name="default" jndi-name="java:jboss/ee/concurrency/scheduler/default" context-service="default" hung-task-threshold="60000" keepalive-time="3000"/>
|
||||||
|
</managed-scheduled-executor-services>
|
||||||
|
</concurrent>
|
||||||
|
<default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/ExampleDS" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:ejb3:7.0">
|
||||||
|
<session-bean>
|
||||||
|
<stateless>
|
||||||
|
<bean-instance-pool-ref pool-name="slsb-strict-max-pool"/>
|
||||||
|
</stateless>
|
||||||
|
<stateful default-access-timeout="5000" cache-ref="distributable" passivation-disabled-cache-ref="simple"/>
|
||||||
|
<singleton default-access-timeout="5000"/>
|
||||||
|
</session-bean>
|
||||||
|
<pools>
|
||||||
|
<bean-instance-pools>
|
||||||
|
<strict-max-pool name="mdb-strict-max-pool" derive-size="from-cpu-count" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
|
||||||
|
<strict-max-pool name="slsb-strict-max-pool" derive-size="from-worker-pools" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
|
||||||
|
</bean-instance-pools>
|
||||||
|
</pools>
|
||||||
|
<caches>
|
||||||
|
<cache name="simple"/>
|
||||||
|
<cache name="distributable" passivation-store-ref="infinispan" aliases="passivating clustered"/>
|
||||||
|
</caches>
|
||||||
|
<passivation-stores>
|
||||||
|
<passivation-store name="infinispan" cache-container="ejb" max-size="10000"/>
|
||||||
|
</passivation-stores>
|
||||||
|
<async thread-pool-name="default"/>
|
||||||
|
<timer-service thread-pool-name="default" default-data-store="default-file-store">
|
||||||
|
<data-stores>
|
||||||
|
<file-data-store name="default-file-store" path="timer-service-data" relative-to="jboss.server.data.dir"/>
|
||||||
|
</data-stores>
|
||||||
|
</timer-service>
|
||||||
|
<remote connector-ref="http-remoting-connector" thread-pool-name="default">
|
||||||
|
<channel-creation-options>
|
||||||
|
<option name="MAX_OUTBOUND_MESSAGES" value="1234" type="remoting"/>
|
||||||
|
</channel-creation-options>
|
||||||
|
</remote>
|
||||||
|
<thread-pools>
|
||||||
|
<thread-pool name="default">
|
||||||
|
<max-threads count="10"/>
|
||||||
|
<keepalive-time time="60" unit="seconds"/>
|
||||||
|
</thread-pool>
|
||||||
|
</thread-pools>
|
||||||
|
<default-security-domain value="other"/>
|
||||||
|
<default-missing-method-permissions-deny-access value="true"/>
|
||||||
|
<statistics enabled="${wildfly.ejb3.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
|
||||||
|
<log-system-exceptions value="true"/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:wildfly:elytron:10.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
|
||||||
|
<providers>
|
||||||
|
<aggregate-providers name="combined-providers">
|
||||||
|
<providers name="elytron"/>
|
||||||
|
<providers name="openssl"/>
|
||||||
|
</aggregate-providers>
|
||||||
|
<provider-loader name="elytron" module="org.wildfly.security.elytron"/>
|
||||||
|
<provider-loader name="openssl" module="org.wildfly.openssl"/>
|
||||||
|
</providers>
|
||||||
|
<audit-logging>
|
||||||
|
<file-audit-log name="local-audit" path="audit.log" relative-to="jboss.server.log.dir" format="JSON"/>
|
||||||
|
</audit-logging>
|
||||||
|
<security-domains>
|
||||||
|
<security-domain name="ApplicationDomain" default-realm="ApplicationRealm" permission-mapper="default-permission-mapper">
|
||||||
|
<realm name="ApplicationRealm" role-decoder="groups-to-roles"/>
|
||||||
|
<realm name="local"/>
|
||||||
|
</security-domain>
|
||||||
|
<security-domain name="ManagementDomain" default-realm="ManagementRealm" permission-mapper="default-permission-mapper">
|
||||||
|
<realm name="ManagementRealm" role-decoder="groups-to-roles"/>
|
||||||
|
<realm name="local" role-mapper="super-user-mapper"/>
|
||||||
|
</security-domain>
|
||||||
|
</security-domains>
|
||||||
|
<security-realms>
|
||||||
|
<identity-realm name="local" identity="$local"/>
|
||||||
|
<properties-realm name="ApplicationRealm">
|
||||||
|
<users-properties path="application-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ApplicationRealm"/>
|
||||||
|
<groups-properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
|
||||||
|
</properties-realm>
|
||||||
|
<properties-realm name="ManagementRealm">
|
||||||
|
<users-properties path="mgmt-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ManagementRealm"/>
|
||||||
|
<groups-properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
|
||||||
|
</properties-realm>
|
||||||
|
</security-realms>
|
||||||
|
<mappers>
|
||||||
|
<simple-permission-mapper name="default-permission-mapper" mapping-mode="first">
|
||||||
|
<permission-mapping>
|
||||||
|
<principal name="anonymous"/>
|
||||||
|
<permission-set name="default-permissions"/>
|
||||||
|
</permission-mapping>
|
||||||
|
<permission-mapping match-all="true">
|
||||||
|
<permission-set name="login-permission"/>
|
||||||
|
<permission-set name="default-permissions"/>
|
||||||
|
</permission-mapping>
|
||||||
|
</simple-permission-mapper>
|
||||||
|
<constant-realm-mapper name="local" realm-name="local"/>
|
||||||
|
<simple-role-decoder name="groups-to-roles" attribute="groups"/>
|
||||||
|
<constant-role-mapper name="super-user-mapper">
|
||||||
|
<role name="SuperUser"/>
|
||||||
|
</constant-role-mapper>
|
||||||
|
</mappers>
|
||||||
|
<permission-sets>
|
||||||
|
<permission-set name="login-permission">
|
||||||
|
<permission class-name="org.wildfly.security.auth.permission.LoginPermission"/>
|
||||||
|
</permission-set>
|
||||||
|
<permission-set name="default-permissions">
|
||||||
|
<permission class-name="org.wildfly.extension.batch.jberet.deployment.BatchPermission" module="org.wildfly.extension.batch.jberet" target-name="*"/>
|
||||||
|
<permission class-name="org.wildfly.transaction.client.RemoteTransactionPermission" module="org.wildfly.transaction.client"/>
|
||||||
|
<permission class-name="org.jboss.ejb.client.RemoteEJBPermission" module="org.jboss.ejb-client"/>
|
||||||
|
</permission-set>
|
||||||
|
</permission-sets>
|
||||||
|
<http>
|
||||||
|
<http-authentication-factory name="management-http-authentication" security-domain="ManagementDomain" http-server-mechanism-factory="global">
|
||||||
|
<mechanism-configuration>
|
||||||
|
<mechanism mechanism-name="DIGEST">
|
||||||
|
<mechanism-realm realm-name="ManagementRealm"/>
|
||||||
|
</mechanism>
|
||||||
|
</mechanism-configuration>
|
||||||
|
</http-authentication-factory>
|
||||||
|
<provider-http-server-mechanism-factory name="global"/>
|
||||||
|
</http>
|
||||||
|
<sasl>
|
||||||
|
<sasl-authentication-factory name="application-sasl-authentication" sasl-server-factory="configured" security-domain="ApplicationDomain">
|
||||||
|
<mechanism-configuration>
|
||||||
|
<mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
|
||||||
|
<mechanism mechanism-name="DIGEST-MD5">
|
||||||
|
<mechanism-realm realm-name="ApplicationRealm"/>
|
||||||
|
</mechanism>
|
||||||
|
</mechanism-configuration>
|
||||||
|
</sasl-authentication-factory>
|
||||||
|
<sasl-authentication-factory name="management-sasl-authentication" sasl-server-factory="configured" security-domain="ManagementDomain">
|
||||||
|
<mechanism-configuration>
|
||||||
|
<mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
|
||||||
|
<mechanism mechanism-name="DIGEST-MD5">
|
||||||
|
<mechanism-realm realm-name="ManagementRealm"/>
|
||||||
|
</mechanism>
|
||||||
|
</mechanism-configuration>
|
||||||
|
</sasl-authentication-factory>
|
||||||
|
<configurable-sasl-server-factory name="configured" sasl-server-factory="elytron">
|
||||||
|
<properties>
|
||||||
|
<property name="wildfly.sasl.local-user.default-user" value="$local"/>
|
||||||
|
</properties>
|
||||||
|
</configurable-sasl-server-factory>
|
||||||
|
<mechanism-provider-filtering-sasl-server-factory name="elytron" sasl-server-factory="global">
|
||||||
|
<filters>
|
||||||
|
<filter provider-name="WildFlyElytron"/>
|
||||||
|
</filters>
|
||||||
|
</mechanism-provider-filtering-sasl-server-factory>
|
||||||
|
<provider-sasl-server-factory name="global"/>
|
||||||
|
</sasl>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:infinispan:10.0">
|
||||||
|
<cache-container name="keycloak" module="org.keycloak.keycloak-model-infinispan">
|
||||||
|
<transport lock-timeout="60000"/>
|
||||||
|
<local-cache name="realms">
|
||||||
|
<object-memory size="10000"/>
|
||||||
|
</local-cache>
|
||||||
|
<local-cache name="users">
|
||||||
|
<object-memory size="10000"/>
|
||||||
|
</local-cache>
|
||||||
|
<local-cache name="authorization">
|
||||||
|
<object-memory size="10000"/>
|
||||||
|
</local-cache>
|
||||||
|
<local-cache name="keys">
|
||||||
|
<object-memory size="1000"/>
|
||||||
|
<expiration max-idle="3600000"/>
|
||||||
|
</local-cache>
|
||||||
|
<replicated-cache name="work"/>
|
||||||
|
<distributed-cache name="sessions" owners="1"/>
|
||||||
|
<distributed-cache name="authenticationSessions" owners="1"/>
|
||||||
|
<distributed-cache name="offlineSessions" owners="1"/>
|
||||||
|
<distributed-cache name="clientSessions" owners="1"/>
|
||||||
|
<distributed-cache name="offlineClientSessions" owners="1"/>
|
||||||
|
<distributed-cache name="loginFailures" owners="1"/>
|
||||||
|
<distributed-cache name="actionTokens" owners="2">
|
||||||
|
<object-memory size="-1"/>
|
||||||
|
<expiration interval="300000" max-idle="-1"/>
|
||||||
|
</distributed-cache>
|
||||||
|
</cache-container>
|
||||||
|
<cache-container name="server" aliases="singleton cluster" default-cache="default" module="org.wildfly.clustering.server">
|
||||||
|
<transport lock-timeout="60000"/>
|
||||||
|
<replicated-cache name="default">
|
||||||
|
<transaction mode="BATCH"/>
|
||||||
|
</replicated-cache>
|
||||||
|
</cache-container>
|
||||||
|
<cache-container name="web" default-cache="dist" module="org.wildfly.clustering.web.infinispan">
|
||||||
|
<transport lock-timeout="60000"/>
|
||||||
|
<replicated-cache name="sso">
|
||||||
|
<locking isolation="REPEATABLE_READ"/>
|
||||||
|
<transaction mode="BATCH"/>
|
||||||
|
</replicated-cache>
|
||||||
|
<distributed-cache name="dist">
|
||||||
|
<locking isolation="REPEATABLE_READ"/>
|
||||||
|
<transaction mode="BATCH"/>
|
||||||
|
<file-store/>
|
||||||
|
</distributed-cache>
|
||||||
|
<distributed-cache name="routing"/>
|
||||||
|
</cache-container>
|
||||||
|
<cache-container name="ejb" aliases="sfsb" default-cache="dist" module="org.wildfly.clustering.ejb.infinispan">
|
||||||
|
<transport lock-timeout="60000"/>
|
||||||
|
<distributed-cache name="dist">
|
||||||
|
<locking isolation="REPEATABLE_READ"/>
|
||||||
|
<transaction mode="BATCH"/>
|
||||||
|
<file-store/>
|
||||||
|
</distributed-cache>
|
||||||
|
</cache-container>
|
||||||
|
<cache-container name="hibernate" module="org.infinispan.hibernate-cache">
|
||||||
|
<transport lock-timeout="60000"/>
|
||||||
|
<local-cache name="local-query">
|
||||||
|
<object-memory size="10000"/>
|
||||||
|
<expiration max-idle="100000"/>
|
||||||
|
</local-cache>
|
||||||
|
<invalidation-cache name="entity">
|
||||||
|
<transaction mode="NON_XA"/>
|
||||||
|
<object-memory size="10000"/>
|
||||||
|
<expiration max-idle="100000"/>
|
||||||
|
</invalidation-cache>
|
||||||
|
<replicated-cache name="timestamps"/>
|
||||||
|
</cache-container>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:io:3.0">
|
||||||
|
<worker name="default"/>
|
||||||
|
<buffer-pool name="default"/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:jaxrs:2.0"/>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:jca:5.0">
|
||||||
|
<archive-validation enabled="true" fail-on-error="true" fail-on-warn="false"/>
|
||||||
|
<bean-validation enabled="true"/>
|
||||||
|
<default-workmanager>
|
||||||
|
<short-running-threads>
|
||||||
|
<core-threads count="50"/>
|
||||||
|
<queue-length count="50"/>
|
||||||
|
<max-threads count="50"/>
|
||||||
|
<keepalive-time time="10" unit="seconds"/>
|
||||||
|
</short-running-threads>
|
||||||
|
<long-running-threads>
|
||||||
|
<core-threads count="50"/>
|
||||||
|
<queue-length count="50"/>
|
||||||
|
<max-threads count="50"/>
|
||||||
|
<keepalive-time time="10" unit="seconds"/>
|
||||||
|
</long-running-threads>
|
||||||
|
</default-workmanager>
|
||||||
|
<cached-connection-manager/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:jgroups:8.0">
|
||||||
|
<channels default="ee">
|
||||||
|
<channel name="ee" stack="udp" cluster="ejb"/>
|
||||||
|
</channels>
|
||||||
|
<stacks>
|
||||||
|
<stack name="udp">
|
||||||
|
<transport type="UDP" socket-binding="jgroups-udp"/>
|
||||||
|
<protocol type="PING"/>
|
||||||
|
<protocol type="MERGE3"/>
|
||||||
|
<socket-protocol type="FD_SOCK" socket-binding="jgroups-udp-fd"/>
|
||||||
|
<protocol type="FD_ALL"/>
|
||||||
|
<protocol type="VERIFY_SUSPECT"/>
|
||||||
|
<protocol type="pbcast.NAKACK2"/>
|
||||||
|
<protocol type="UNICAST3"/>
|
||||||
|
<protocol type="pbcast.STABLE"/>
|
||||||
|
<protocol type="pbcast.GMS"/>
|
||||||
|
<protocol type="UFC"/>
|
||||||
|
<protocol type="MFC"/>
|
||||||
|
<protocol type="FRAG3"/>
|
||||||
|
</stack>
|
||||||
|
<stack name="tcp">
|
||||||
|
<transport type="TCP" socket-binding="jgroups-tcp"/>
|
||||||
|
<socket-protocol type="MPING" socket-binding="jgroups-mping"/>
|
||||||
|
<protocol type="MERGE3"/>
|
||||||
|
<socket-protocol type="FD_SOCK" socket-binding="jgroups-tcp-fd"/>
|
||||||
|
<protocol type="FD_ALL"/>
|
||||||
|
<protocol type="VERIFY_SUSPECT"/>
|
||||||
|
<protocol type="pbcast.NAKACK2"/>
|
||||||
|
<protocol type="UNICAST3"/>
|
||||||
|
<protocol type="pbcast.STABLE"/>
|
||||||
|
<protocol type="pbcast.GMS"/>
|
||||||
|
<protocol type="MFC"/>
|
||||||
|
<protocol type="FRAG3"/>
|
||||||
|
</stack>
|
||||||
|
</stacks>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:jmx:1.3">
|
||||||
|
<expose-resolved-model/>
|
||||||
|
<expose-expression-model/>
|
||||||
|
<remoting-connector/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:jpa:1.1">
|
||||||
|
<jpa default-datasource="" default-extended-persistence-inheritance="DEEP"/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
|
||||||
|
<web-context>auth</web-context>
|
||||||
|
<providers>
|
||||||
|
<provider>
|
||||||
|
classpath:${jboss.home.dir}/providers/*
|
||||||
|
</provider>
|
||||||
|
</providers>
|
||||||
|
<master-realm-name>master</master-realm-name>
|
||||||
|
<scheduled-task-interval>900</scheduled-task-interval>
|
||||||
|
<theme>
|
||||||
|
<staticMaxAge>-1</staticMaxAge>
|
||||||
|
<cacheThemes>false</cacheThemes>
|
||||||
|
<cacheTemplates>false</cacheTemplates>
|
||||||
|
<welcomeTheme>${env.KEYCLOAK_WELCOME_THEME:keycloak}</welcomeTheme>
|
||||||
|
<default>${env.KEYCLOAK_DEFAULT_THEME:keycloak}</default>
|
||||||
|
<dir>${jboss.home.dir}/themes</dir>
|
||||||
|
</theme>
|
||||||
|
<spi name="eventsStore">
|
||||||
|
<provider name="jpa" enabled="true">
|
||||||
|
<properties>
|
||||||
|
<property name="exclude-events" value="["REFRESH_TOKEN"]"/>
|
||||||
|
</properties>
|
||||||
|
</provider>
|
||||||
|
</spi>
|
||||||
|
<spi name="userCache">
|
||||||
|
<provider name="default" enabled="true"/>
|
||||||
|
</spi>
|
||||||
|
<spi name="userSessionPersister">
|
||||||
|
<default-provider>jpa</default-provider>
|
||||||
|
</spi>
|
||||||
|
<spi name="timer">
|
||||||
|
<default-provider>basic</default-provider>
|
||||||
|
</spi>
|
||||||
|
<spi name="connectionsHttpClient">
|
||||||
|
<provider name="default" enabled="true"/>
|
||||||
|
</spi>
|
||||||
|
<spi name="connectionsJpa">
|
||||||
|
<provider name="default" enabled="true">
|
||||||
|
<properties>
|
||||||
|
<property name="dataSource" value="java:jboss/datasources/KeycloakDS"/>
|
||||||
|
<property name="initializeEmpty" value="true"/>
|
||||||
|
<property name="migrationStrategy" value="update"/>
|
||||||
|
<property name="migrationExport" value="${jboss.home.dir}/keycloak-database-update.sql"/>
|
||||||
|
</properties>
|
||||||
|
</provider>
|
||||||
|
</spi>
|
||||||
|
<spi name="realmCache">
|
||||||
|
<provider name="default" enabled="true"/>
|
||||||
|
</spi>
|
||||||
|
<spi name="connectionsInfinispan">
|
||||||
|
<default-provider>default</default-provider>
|
||||||
|
<provider name="default" enabled="true">
|
||||||
|
<properties>
|
||||||
|
<property name="cacheContainer" value="java:jboss/infinispan/container/keycloak"/>
|
||||||
|
</properties>
|
||||||
|
</provider>
|
||||||
|
</spi>
|
||||||
|
<spi name="jta-lookup">
|
||||||
|
<default-provider>${keycloak.jta.lookup.provider:jboss}</default-provider>
|
||||||
|
<provider name="jboss" enabled="true"/>
|
||||||
|
</spi>
|
||||||
|
<spi name="publicKeyStorage">
|
||||||
|
<provider name="infinispan" enabled="true">
|
||||||
|
<properties>
|
||||||
|
<property name="minTimeBetweenRequests" value="10"/>
|
||||||
|
</properties>
|
||||||
|
</provider>
|
||||||
|
</spi>
|
||||||
|
<spi name="x509cert-lookup">
|
||||||
|
<default-provider>${keycloak.x509cert.lookup.provider:default}</default-provider>
|
||||||
|
<provider name="default" enabled="true"/>
|
||||||
|
</spi>
|
||||||
|
<spi name="hostname">
|
||||||
|
<default-provider>${keycloak.hostname.provider:default}</default-provider>
|
||||||
|
<provider name="default" enabled="true">
|
||||||
|
<properties>
|
||||||
|
<property name="frontendUrl" value="${keycloak.frontendUrl:}"/>
|
||||||
|
<property name="forceBackendUrlToFrontendUrl" value="false"/>
|
||||||
|
</properties>
|
||||||
|
</provider>
|
||||||
|
<provider name="fixed" enabled="true">
|
||||||
|
<properties>
|
||||||
|
<property name="hostname" value="${keycloak.hostname.fixed.hostname:localhost}"/>
|
||||||
|
<property name="httpPort" value="${keycloak.hostname.fixed.httpPort:-1}"/>
|
||||||
|
<property name="httpsPort" value="${keycloak.hostname.fixed.httpsPort:-1}"/>
|
||||||
|
<property name="alwaysHttps" value="${keycloak.hostname.fixed.alwaysHttps:false}"/>
|
||||||
|
</properties>
|
||||||
|
</provider>
|
||||||
|
</spi>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:mail:4.0">
|
||||||
|
<mail-session name="default" jndi-name="java:jboss/mail/Default">
|
||||||
|
<smtp-server outbound-socket-binding-ref="mail-smtp"/>
|
||||||
|
</mail-session>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:wildfly:microprofile-config-smallrye:1.0"/>
|
||||||
|
<subsystem xmlns="urn:wildfly:microprofile-health-smallrye:2.0" security-enabled="false" empty-liveness-checks-status="${env.MP_HEALTH_EMPTY_LIVENESS_CHECKS_STATUS:UP}" empty-readiness-checks-status="${env.MP_HEALTH_EMPTY_READINESS_CHECKS_STATUS:UP}"/>
|
||||||
|
<subsystem xmlns="urn:wildfly:microprofile-metrics-smallrye:2.0" security-enabled="false" exposed-subsystems="*" prefix="${wildfly.metrics.prefix:wildfly}"/>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:modcluster:5.0">
|
||||||
|
<proxy name="default" advertise-socket="modcluster" listener="ajp">
|
||||||
|
<dynamic-load-provider>
|
||||||
|
<load-metric type="cpu"/>
|
||||||
|
</dynamic-load-provider>
|
||||||
|
</proxy>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:naming:2.0">
|
||||||
|
<remote-naming/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:remoting:4.0">
|
||||||
|
<http-connector name="http-remoting-connector" connector-ref="default" security-realm="ApplicationRealm"/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:request-controller:1.0"/>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:security:2.0">
|
||||||
|
<security-domains>
|
||||||
|
<security-domain name="other" cache-type="default">
|
||||||
|
<authentication>
|
||||||
|
<login-module code="Remoting" flag="optional">
|
||||||
|
<module-option name="password-stacking" value="useFirstPass"/>
|
||||||
|
</login-module>
|
||||||
|
<login-module code="RealmDirect" flag="required">
|
||||||
|
<module-option name="password-stacking" value="useFirstPass"/>
|
||||||
|
</login-module>
|
||||||
|
</authentication>
|
||||||
|
</security-domain>
|
||||||
|
<security-domain name="jboss-web-policy" cache-type="default">
|
||||||
|
<authorization>
|
||||||
|
<policy-module code="Delegating" flag="required"/>
|
||||||
|
</authorization>
|
||||||
|
</security-domain>
|
||||||
|
<security-domain name="jaspitest" cache-type="default">
|
||||||
|
<authentication-jaspi>
|
||||||
|
<login-module-stack name="dummy">
|
||||||
|
<login-module code="Dummy" flag="optional"/>
|
||||||
|
</login-module-stack>
|
||||||
|
<auth-module code="Dummy"/>
|
||||||
|
</authentication-jaspi>
|
||||||
|
</security-domain>
|
||||||
|
<security-domain name="jboss-ejb-policy" cache-type="default">
|
||||||
|
<authorization>
|
||||||
|
<policy-module code="Delegating" flag="required"/>
|
||||||
|
</authorization>
|
||||||
|
</security-domain>
|
||||||
|
</security-domains>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:security-manager:1.0">
|
||||||
|
<deployment-permissions>
|
||||||
|
<maximum-set>
|
||||||
|
<permission class="java.security.AllPermission"/>
|
||||||
|
</maximum-set>
|
||||||
|
</deployment-permissions>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:transactions:5.0">
|
||||||
|
<core-environment node-identifier="${jboss.tx.node.id:1}">
|
||||||
|
<process-id>
|
||||||
|
<uuid/>
|
||||||
|
</process-id>
|
||||||
|
</core-environment>
|
||||||
|
<recovery-environment socket-binding="txn-recovery-environment" status-socket-binding="txn-status-manager"/>
|
||||||
|
<coordinator-environment statistics-enabled="${wildfly.transactions.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
|
||||||
|
<object-store path="tx-object-store" relative-to="jboss.server.data.dir"/>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:undertow:11.0" default-server="default-server" default-virtual-host="default-host" default-servlet-container="default" default-security-domain="other" statistics-enabled="${wildfly.undertow.statistics-enabled:${wildfly.statistics-enabled:false}}">
|
||||||
|
<buffer-cache name="default"/>
|
||||||
|
<server name="default-server">
|
||||||
|
<ajp-listener name="ajp" socket-binding="ajp"/>
|
||||||
|
<http-listener name="default" read-timeout="30000" socket-binding="http" redirect-socket="https" proxy-address-forwarding="${env.PROXY_ADDRESS_FORWARDING:false}" enable-http2="true"/>
|
||||||
|
<https-listener name="https" read-timeout="30000" socket-binding="https" proxy-address-forwarding="${env.PROXY_ADDRESS_FORWARDING:false}" security-realm="ApplicationRealm" enable-http2="true"/>
|
||||||
|
<host name="default-host" alias="localhost">
|
||||||
|
<location name="/" handler="welcome-content"/>
|
||||||
|
<http-invoker security-realm="ApplicationRealm"/>
|
||||||
|
</host>
|
||||||
|
</server>
|
||||||
|
<servlet-container name="default">
|
||||||
|
<jsp-config/>
|
||||||
|
<websockets/>
|
||||||
|
</servlet-container>
|
||||||
|
<handlers>
|
||||||
|
<file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
|
||||||
|
</handlers>
|
||||||
|
</subsystem>
|
||||||
|
<subsystem xmlns="urn:jboss:domain:weld:4.0"/>
|
||||||
|
</profile>
|
||||||
|
<interfaces>
|
||||||
|
<interface name="management">
|
||||||
|
<inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
|
||||||
|
</interface>
|
||||||
|
<interface name="private">
|
||||||
|
<inet-address value="${jboss.bind.address.private:127.0.0.1}"/>
|
||||||
|
</interface>
|
||||||
|
<interface name="public">
|
||||||
|
<inet-address value="${jboss.bind.address:127.0.0.1}"/>
|
||||||
|
</interface>
|
||||||
|
</interfaces>
|
||||||
|
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
|
||||||
|
<socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
|
||||||
|
<socket-binding name="http" port="${jboss.http.port:8080}"/>
|
||||||
|
<socket-binding name="https" port="${jboss.https.port:8443}"/>
|
||||||
|
<socket-binding name="jgroups-mping" interface="private" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45700"/>
|
||||||
|
<socket-binding name="jgroups-tcp" interface="private" port="7600"/>
|
||||||
|
<socket-binding name="jgroups-tcp-fd" interface="private" port="57600"/>
|
||||||
|
<socket-binding name="jgroups-udp" interface="private" port="55200" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45688"/>
|
||||||
|
<socket-binding name="jgroups-udp-fd" interface="private" port="54200"/>
|
||||||
|
<socket-binding name="management-http" interface="management" port="${jboss.management.http.port:9990}"/>
|
||||||
|
<socket-binding name="management-https" interface="management" port="${jboss.management.https.port:9993}"/>
|
||||||
|
<socket-binding name="modcluster" multicast-address="${jboss.modcluster.multicast.address:224.0.1.105}" multicast-port="23364"/>
|
||||||
|
<socket-binding name="txn-recovery-environment" port="4712"/>
|
||||||
|
<socket-binding name="txn-status-manager" port="4713"/>
|
||||||
|
<outbound-socket-binding name="mail-smtp">
|
||||||
|
<remote-destination host="localhost" port="25"/>
|
||||||
|
</outbound-socket-binding>
|
||||||
|
</socket-binding-group>
|
||||||
|
</server>
|
105
src/bin/build-keycloak-theme/generateFtl.ts
Normal file
105
src/bin/build-keycloak-theme/generateFtl.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import cheerio from "cheerio";
|
||||||
|
import {
|
||||||
|
replaceImportFromStaticInJsCode,
|
||||||
|
generateCssCodeToDefineGlobals
|
||||||
|
} from "./replaceImportFromStatic";
|
||||||
|
|
||||||
|
export function generateFtlFilesCodeFactory(
|
||||||
|
params: {
|
||||||
|
ftlValuesGlobalName: string;
|
||||||
|
cssGlobalsToDefine: Record<string, string>;
|
||||||
|
indexHtmlCode: string;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
|
const { ftlValuesGlobalName, cssGlobalsToDefine, indexHtmlCode } = params;
|
||||||
|
|
||||||
|
const $ = cheerio.load(indexHtmlCode);
|
||||||
|
|
||||||
|
$("script:not([src])").each((...[, element]) => {
|
||||||
|
|
||||||
|
const { fixedJsCode } = replaceImportFromStaticInJsCode({
|
||||||
|
ftlValuesGlobalName,
|
||||||
|
"jsCode": $(element).html()!
|
||||||
|
});
|
||||||
|
|
||||||
|
$(element).text(fixedJsCode);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
([
|
||||||
|
["link", "href"],
|
||||||
|
["script", "src"],
|
||||||
|
] as const).forEach(([selector, attrName]) =>
|
||||||
|
$(selector).each((...[, element]) => {
|
||||||
|
|
||||||
|
const href = $(element).attr(attrName);
|
||||||
|
|
||||||
|
if (!href?.startsWith("/")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(element).attr(attrName, "${url.resourcesPath}" + href);
|
||||||
|
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$("head").prepend(
|
||||||
|
[
|
||||||
|
'',
|
||||||
|
'<style>',
|
||||||
|
generateCssCodeToDefineGlobals(
|
||||||
|
{ cssGlobalsToDefine }
|
||||||
|
).cssCodeToPrependInHead,
|
||||||
|
'</style>',
|
||||||
|
'',
|
||||||
|
'<script>',
|
||||||
|
' Object.assign(',
|
||||||
|
` window.${ftlValuesGlobalName},`,
|
||||||
|
' {',
|
||||||
|
' "url": {',
|
||||||
|
' "loginAction": "${url.loginAction}",',
|
||||||
|
' "resourcesPath": "${url.resourcesPath}"',
|
||||||
|
' }',
|
||||||
|
' }',
|
||||||
|
' });',
|
||||||
|
'</script>',
|
||||||
|
''
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const partiallyFixedIndexHtmlCode = $.html();
|
||||||
|
|
||||||
|
function generateFtlFilesCode(
|
||||||
|
params: {
|
||||||
|
pageBasename: "login.ftl" | "register.ftl"
|
||||||
|
}
|
||||||
|
): { ftlCode: string; } {
|
||||||
|
|
||||||
|
const { pageBasename } = params;
|
||||||
|
|
||||||
|
const $ = cheerio.load(partiallyFixedIndexHtmlCode);
|
||||||
|
|
||||||
|
$("head").prepend(
|
||||||
|
[
|
||||||
|
'',
|
||||||
|
'<script>',
|
||||||
|
` window.${ftlValuesGlobalName} = { "pageBasename": "${pageBasename}" };`,
|
||||||
|
'</script>',
|
||||||
|
''
|
||||||
|
].join("\n"),
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
return { "ftlCode": $.html() };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return { generateFtlFilesCode };
|
||||||
|
|
||||||
|
|
||||||
|
}
|
98
src/bin/build-keycloak-theme/generateJavaStackFiles.ts
Normal file
98
src/bin/build-keycloak-theme/generateJavaStackFiles.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
|
||||||
|
import * as url from "url";
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { join as pathJoin, dirname as pathDirname } from "path";
|
||||||
|
|
||||||
|
export type ParsedPackageJson = {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
homepage?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function generateJavaStackFiles(
|
||||||
|
params: {
|
||||||
|
parsedPackageJson: ParsedPackageJson;
|
||||||
|
keycloakThemeBuildingDirPath: string;
|
||||||
|
}
|
||||||
|
): void {
|
||||||
|
|
||||||
|
const {
|
||||||
|
parsedPackageJson: { name, version, homepage },
|
||||||
|
keycloakThemeBuildingDirPath
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
const { pomFileCode } = (function generatePomFileCode(): { pomFileCode: string; } {
|
||||||
|
|
||||||
|
|
||||||
|
const groupId = (() => {
|
||||||
|
|
||||||
|
const fallbackGroupId = `there.was.no.homepage.field.in.the.package.json.${name}`;
|
||||||
|
|
||||||
|
return (!homepage ?
|
||||||
|
fallbackGroupId :
|
||||||
|
url.parse(homepage).host?.split(".").reverse().join(".") ?? fallbackGroupId
|
||||||
|
) + ".keycloak";
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
const artefactId = `${name}-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": name,
|
||||||
|
"types": ["login"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, null, 2),
|
||||||
|
"utf8"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
|||||||
|
|
||||||
|
import { transformCodebase } from "../../tools/transformCodebase";
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { join as pathJoin } from "path";
|
||||||
|
import {
|
||||||
|
replaceImportFromStaticInCssCode,
|
||||||
|
replaceImportFromStaticInJsCode
|
||||||
|
} from "./replaceImportFromStatic";
|
||||||
|
import { generateFtlFilesCodeFactory } from "./generateFtl";
|
||||||
|
|
||||||
|
const ftlValuesGlobalName = "keycloakFtlValues";
|
||||||
|
|
||||||
|
export function generateKeycloakThemeResources(
|
||||||
|
params: {
|
||||||
|
themeName: string;
|
||||||
|
reactAppBuildDirPath: string;
|
||||||
|
keycloakThemeBuildingDirPath: string;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
|
const { themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath } = params;
|
||||||
|
|
||||||
|
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login");
|
||||||
|
|
||||||
|
let allCssGlobalsToDefine: Record<string, string> = {};
|
||||||
|
|
||||||
|
transformCodebase({
|
||||||
|
"destDirPath": pathJoin(themeDirPath, "resources"),
|
||||||
|
"srcDirPath": reactAppBuildDirPath,
|
||||||
|
"transformSourceCodeString": ({ filePath, sourceCode }) => {
|
||||||
|
|
||||||
|
if (/\.css?$/i.test(filePath)) {
|
||||||
|
|
||||||
|
const { cssGlobalsToDefine, fixedCssCode } = replaceImportFromStaticInCssCode(
|
||||||
|
{ "cssCode": sourceCode.toString("utf8") }
|
||||||
|
);
|
||||||
|
|
||||||
|
allCssGlobalsToDefine = {
|
||||||
|
...allCssGlobalsToDefine,
|
||||||
|
...cssGlobalsToDefine
|
||||||
|
};
|
||||||
|
|
||||||
|
return { "modifiedSourceCode": Buffer.from(fixedCssCode, "utf8") };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/\.js?$/i.test(filePath)) {
|
||||||
|
|
||||||
|
const { fixedJsCode } = replaceImportFromStaticInJsCode({
|
||||||
|
"jsCode": sourceCode.toString("utf8"),
|
||||||
|
ftlValuesGlobalName
|
||||||
|
});
|
||||||
|
|
||||||
|
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return { "modifiedSourceCode": sourceCode };
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
|
||||||
|
"cssGlobalsToDefine": allCssGlobalsToDefine,
|
||||||
|
ftlValuesGlobalName,
|
||||||
|
"indexHtmlCode": fs.readFileSync(
|
||||||
|
pathJoin(reactAppBuildDirPath, "index.html")
|
||||||
|
).toString("utf8")
|
||||||
|
});
|
||||||
|
|
||||||
|
(["login.ftl", "register.ftl"] as const).forEach(pageBasename => {
|
||||||
|
|
||||||
|
const { ftlCode } = generateFtlFilesCode({ pageBasename });
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
pathJoin(themeDirPath, pageBasename),
|
||||||
|
Buffer.from(ftlCode, "utf8")
|
||||||
|
)
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
pathJoin(themeDirPath, "theme.properties"),
|
||||||
|
Buffer.from("parent=base\n", "utf8")
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
39
src/bin/build-keycloak-theme/index.ts
Normal file
39
src/bin/build-keycloak-theme/index.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import { generateKeycloakThemeResources } from "./generateKeycloakThemeResources";
|
||||||
|
import { generateJavaStackFiles } from "./generateJavaStackFiles";
|
||||||
|
import type { ParsedPackageJson } from "./generateJavaStackFiles";
|
||||||
|
import { join as pathJoin } from "path";
|
||||||
|
import * as child_process from "child_process";
|
||||||
|
import { generateDebugFiles } from "./generateDebugFiles";
|
||||||
|
|
||||||
|
const reactProjectDirPath = process.cwd();
|
||||||
|
|
||||||
|
const parsedPackageJson: ParsedPackageJson = require(pathJoin(reactProjectDirPath, "package.json"));
|
||||||
|
|
||||||
|
export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build_keycloak");
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
|
||||||
|
generateKeycloakThemeResources({
|
||||||
|
keycloakThemeBuildingDirPath,
|
||||||
|
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
|
||||||
|
"themeName": parsedPackageJson.name
|
||||||
|
});
|
||||||
|
|
||||||
|
generateJavaStackFiles({
|
||||||
|
parsedPackageJson,
|
||||||
|
keycloakThemeBuildingDirPath
|
||||||
|
});
|
||||||
|
|
||||||
|
child_process.execSync(
|
||||||
|
"mvn package",
|
||||||
|
{ "cwd": keycloakThemeBuildingDirPath }
|
||||||
|
);
|
||||||
|
|
||||||
|
generateDebugFiles({
|
||||||
|
keycloakThemeBuildingDirPath,
|
||||||
|
"packageJsonName": parsedPackageJson.name
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
91
src/bin/build-keycloak-theme/replaceImportFromStatic.ts
Normal file
91
src/bin/build-keycloak-theme/replaceImportFromStatic.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
import * as crypto from "crypto";
|
||||||
|
|
||||||
|
export function replaceImportFromStaticInJsCode(
|
||||||
|
params: {
|
||||||
|
ftlValuesGlobalName: string;
|
||||||
|
jsCode: string;
|
||||||
|
}
|
||||||
|
): { fixedJsCode: string; } {
|
||||||
|
|
||||||
|
const { jsCode, ftlValuesGlobalName } = params;
|
||||||
|
|
||||||
|
const fixedJsCode = jsCode!.replace(
|
||||||
|
/"static\//g,
|
||||||
|
`window.${ftlValuesGlobalName}.url.resourcesPath.replace(/^\\//,"") + "/" + "static/`
|
||||||
|
);
|
||||||
|
|
||||||
|
return { fixedJsCode };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function replaceImportFromStaticInCssCode(
|
||||||
|
params: {
|
||||||
|
cssCode: string;
|
||||||
|
}
|
||||||
|
): {
|
||||||
|
fixedCssCode: string;
|
||||||
|
cssGlobalsToDefine: Record<string, string>;
|
||||||
|
} {
|
||||||
|
|
||||||
|
const { cssCode } = params;
|
||||||
|
|
||||||
|
const cssGlobalsToDefine: Record<string, string> = {};
|
||||||
|
|
||||||
|
new Set(cssCode.match(/(url\(\/[^)]+\))/g) ?? [])
|
||||||
|
.forEach(match =>
|
||||||
|
cssGlobalsToDefine[
|
||||||
|
"url" + crypto
|
||||||
|
.createHash("sha256")
|
||||||
|
.update(match)
|
||||||
|
.digest("hex")
|
||||||
|
.substring(0, 15)
|
||||||
|
] = match
|
||||||
|
);
|
||||||
|
|
||||||
|
let fixedCssCode = cssCode;
|
||||||
|
|
||||||
|
Object.keys(cssGlobalsToDefine).forEach(
|
||||||
|
cssVariableName =>
|
||||||
|
//NOTE: split/join pattern ~ replace all
|
||||||
|
fixedCssCode =
|
||||||
|
fixedCssCode.split(cssGlobalsToDefine[cssVariableName])
|
||||||
|
.join(`var(--${cssVariableName})`)
|
||||||
|
);
|
||||||
|
|
||||||
|
return { fixedCssCode, cssGlobalsToDefine };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateCssCodeToDefineGlobals(
|
||||||
|
params: {
|
||||||
|
cssGlobalsToDefine: Record<string, string>;
|
||||||
|
}
|
||||||
|
): {
|
||||||
|
cssCodeToPrependInHead: string;
|
||||||
|
} {
|
||||||
|
|
||||||
|
const { cssGlobalsToDefine } = params;
|
||||||
|
|
||||||
|
return {
|
||||||
|
"cssCodeToPrependInHead": [
|
||||||
|
":root {",
|
||||||
|
...Object.keys(cssGlobalsToDefine)
|
||||||
|
.map(cssVariableName => [
|
||||||
|
`--${cssVariableName}:`,
|
||||||
|
[
|
||||||
|
"url(",
|
||||||
|
"${url.resourcesPath}" +
|
||||||
|
cssGlobalsToDefine[cssVariableName].match(/^url\(([^)]+)\)$/)![1],
|
||||||
|
")"
|
||||||
|
].join("")
|
||||||
|
].join(" "))
|
||||||
|
.map(line => ` ${line};`),
|
||||||
|
"}"
|
||||||
|
].join("\n")
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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 });
|
|
||||||
})();
|
|
||||||
}
|
|
@ -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 async function downloadBuiltinKeycloakTheme(params: { keycloakVersion: string; destDirPath: string; isSilent: boolean }) {
|
|
||||||
const { keycloakVersion, destDirPath, isSilent } = params;
|
|
||||||
|
|
||||||
for (const ext of ["", "-community"]) {
|
|
||||||
await 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}`);
|
|
||||||
|
|
||||||
await downloadBuiltinKeycloakTheme({
|
|
||||||
keycloakVersion,
|
|
||||||
destDirPath,
|
|
||||||
isSilent
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
}
|
|
18
src/bin/download-sample-keycloak-themes.ts
Normal file
18
src/bin/download-sample-keycloak-themes.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { join as pathJoin, basename as pathBasename } from "path";
|
||||||
|
import { keycloakThemeBuildingDirPath } from "./build-keycloak-theme";
|
||||||
|
import child_process from "child_process";
|
||||||
|
|
||||||
|
if (!fs.existsSync(keycloakThemeBuildingDirPath)) {
|
||||||
|
console.log("Error: The keycloak theme need to be build");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = "https://github.com/garronej/keycloak-react-theming/releases/download/v0.0.1/other_keycloak_thems.zip";
|
||||||
|
|
||||||
|
[
|
||||||
|
`wget ${url}`,
|
||||||
|
...["unzip", "rm"].map(prg => `${prg} ${pathBasename(url)}`),
|
||||||
|
].forEach(cmd => child_process.execSync(cmd, { "cwd": pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme") }));
|
@ -1,38 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
import { getProjectRoot } from "./tools/getProjectRoot";
|
|
||||||
import cliSelect from "cli-select";
|
|
||||||
import { loginThemePageIds, type PageId } from "./keycloakify/generateFtl/generateFtl";
|
|
||||||
import { capitalize } from "tsafe/capitalize";
|
|
||||||
import { readFile, writeFile } from "fs/promises";
|
|
||||||
import { existsSync } from "fs";
|
|
||||||
import { join as pathJoin, relative as pathRelative } from "path";
|
|
||||||
import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase";
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
const projectRootDir = getProjectRoot();
|
|
||||||
|
|
||||||
const { value: pageId } = await cliSelect<PageId>({
|
|
||||||
"values": [...loginThemePageIds]
|
|
||||||
}).catch(() => {
|
|
||||||
console.log("Aborting");
|
|
||||||
|
|
||||||
process.exit(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
const pageBasename = capitalize(kebabCaseToCamelCase(pageId)).replace(/ftl$/, "tsx");
|
|
||||||
|
|
||||||
console.log(pageId);
|
|
||||||
|
|
||||||
const targetFilePath = pathJoin(process.cwd(), "src", "keycloak-theme", "pages", pageBasename);
|
|
||||||
|
|
||||||
if (existsSync(targetFilePath)) {
|
|
||||||
console.log(`${pageId} is already ejected, ${pathRelative(process.cwd(), targetFilePath)} already exists`);
|
|
||||||
|
|
||||||
process.exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeFile(targetFilePath, await readFile(pathJoin(projectRootDir, "src", "pages", pageBasename)));
|
|
||||||
|
|
||||||
console.log(`${pathRelative(process.cwd(), targetFilePath)} created`);
|
|
||||||
})();
|
|
@ -1,214 +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";
|
|
||||||
import { typeGuard } from "tsafe/typeGuard";
|
|
||||||
import { symToStr } from "tsafe/symToStr";
|
|
||||||
|
|
||||||
const bundlers = ["mvn", "keycloakify", "none"] as const;
|
|
||||||
type Bundler = (typeof bundlers)[number];
|
|
||||||
type ParsedPackageJson = {
|
|
||||||
name: string;
|
|
||||||
version: string;
|
|
||||||
homepage?: string;
|
|
||||||
keycloakify?: {
|
|
||||||
/** @deprecated: use extraLoginPages instead */
|
|
||||||
extraPages?: string[];
|
|
||||||
extraLoginPages?: string[];
|
|
||||||
extraAccountPages?: string[];
|
|
||||||
extraThemeProperties?: string[];
|
|
||||||
areAppAndKeycloakServerSharingSameDomain?: boolean;
|
|
||||||
artifactId?: string;
|
|
||||||
groupId?: string;
|
|
||||||
bundler?: Bundler;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const zParsedPackageJson = z.object({
|
|
||||||
"name": z.string(),
|
|
||||||
"version": z.string(),
|
|
||||||
"homepage": z.string().optional(),
|
|
||||||
"keycloakify": z
|
|
||||||
.object({
|
|
||||||
"extraPages": z.array(z.string()).optional(),
|
|
||||||
"extraLoginPages": z.array(z.string()).optional(),
|
|
||||||
"extraAccountPages": z.array(z.string()).optional(),
|
|
||||||
"extraThemeProperties": z.array(z.string()).optional(),
|
|
||||||
"areAppAndKeycloakServerSharingSameDomain": z.boolean().optional(),
|
|
||||||
"artifactId": z.string().optional(),
|
|
||||||
"groupId": z.string().optional(),
|
|
||||||
"bundler": z.enum(bundlers).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;
|
|
||||||
extraLoginPages: string[] | undefined;
|
|
||||||
extraAccountPages: string[] | undefined;
|
|
||||||
extraThemeProperties?: string[];
|
|
||||||
groupId: string;
|
|
||||||
artifactId: string;
|
|
||||||
bundler: Bundler;
|
|
||||||
};
|
|
||||||
|
|
||||||
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, extraLoginPages, extraAccountPages, extraThemeProperties, groupId, artifactId, bundler } = keycloakify ?? {};
|
|
||||||
|
|
||||||
const themeName = name
|
|
||||||
.replace(/^@(.*)/, "$1")
|
|
||||||
.split("/")
|
|
||||||
.join("-");
|
|
||||||
|
|
||||||
return {
|
|
||||||
themeName,
|
|
||||||
"bundler": (() => {
|
|
||||||
const { KEYCLOAKIFY_BUNDLER } = process.env;
|
|
||||||
|
|
||||||
assert(
|
|
||||||
typeGuard<Bundler | undefined>(
|
|
||||||
KEYCLOAKIFY_BUNDLER,
|
|
||||||
[undefined, ...id<readonly string[]>(bundlers)].includes(KEYCLOAKIFY_BUNDLER)
|
|
||||||
),
|
|
||||||
`${symToStr({ KEYCLOAKIFY_BUNDLER })} should be one of ${bundlers.join(", ")}`
|
|
||||||
);
|
|
||||||
|
|
||||||
return KEYCLOAKIFY_BUNDLER ?? bundler ?? "keycloakify";
|
|
||||||
})(),
|
|
||||||
"artifactId": process.env.KEYCLOAKIFY_ARTIFACT_ID ?? artifactId ?? `${themeName}-keycloak-theme`,
|
|
||||||
"groupId": (() => {
|
|
||||||
const fallbackGroupId = `${themeName}.keycloak`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
process.env.KEYCLOAKIFY_GROUP_ID ??
|
|
||||||
groupId ??
|
|
||||||
(!homepage
|
|
||||||
? fallbackGroupId
|
|
||||||
: urlParse(homepage)
|
|
||||||
.host?.replace(/:[0-9]+$/, "")
|
|
||||||
?.split(".")
|
|
||||||
.reverse()
|
|
||||||
.join(".") ?? fallbackGroupId) + ".keycloak"
|
|
||||||
);
|
|
||||||
})(),
|
|
||||||
"version": process.env.KEYCLOAKIFY_VERSION ?? version,
|
|
||||||
"extraLoginPages": [...(extraPages ?? []), ...(extraLoginPages ?? [])],
|
|
||||||
extraAccountPages,
|
|
||||||
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
|
|
||||||
});
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export const ftlValuesGlobalName = "kcContext";
|
|
@ -1,372 +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>
|
|
||||||
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
|
||||||
return <#if messagesPerField.existsError('username', 'password')>x<#else>undefined</#if>;
|
|
||||||
<#else>
|
|
||||||
return <#if messagesPerField.existsError('${fieldName}')>x<#else>undefined</#if>;
|
|
||||||
</#if>
|
|
||||||
<#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>
|
|
||||||
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
|
||||||
return <#if messagesPerField.existsError('username', 'password')>true<#else>false</#if>;
|
|
||||||
<#else>
|
|
||||||
return <#if messagesPerField.existsError('${fieldName}')>true<#else>false</#if>;
|
|
||||||
</#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 '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
|
||||||
<#if messagesPerField.existsError('username', 'password')>
|
|
||||||
return 'Invalid username or password.';
|
|
||||||
</#if>
|
|
||||||
<#else>
|
|
||||||
<#if messagesPerField.existsError('${fieldName}')>
|
|
||||||
return "${messagesPerField.get('${fieldName}')?no_esc}";
|
|
||||||
</#if>
|
|
||||||
</#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>
|
|
||||||
<#if '${fieldName}' == 'username' || '${fieldName}' == 'password'>
|
|
||||||
return <#if messagesPerField.exists('username') || messagesPerField.exists('password')>true<#else>false</#if>;
|
|
||||||
<#else>
|
|
||||||
return <#if messagesPerField.exists('${fieldName}')>true<#else>false</#if>;
|
|
||||||
</#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"])
|
|
||||||
) || (
|
|
||||||
"error.ftl" == pageId &&
|
|
||||||
are_same_path(path, ["realm"]) &&
|
|
||||||
!["name", "displayName", "displayNameHtml", "internationalizationEnabled", "registrationEmailAsUsername" ]?seq_contains(key)
|
|
||||||
)
|
|
||||||
>
|
|
||||||
<#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>
|
|
@ -1,196 +0,0 @@
|
|||||||
import cheerio from "cheerio";
|
|
||||||
import { replaceImportsFromStaticInJsCode } from "../replacers/replaceImportsFromStaticInJsCode";
|
|
||||||
import { generateCssCodeToDefineGlobals } from "../replacers/replaceImportsInCssCode";
|
|
||||||
import { replaceImportsInInlineCssCode } from "../replacers/replaceImportsInInlineCssCode";
|
|
||||||
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 { assert } from "tsafe/assert";
|
|
||||||
import { Reflect } from "tsafe/Reflect";
|
|
||||||
|
|
||||||
export const themeTypes = ["login", "account"] as const;
|
|
||||||
|
|
||||||
export type ThemeType = (typeof themeTypes)[number];
|
|
||||||
|
|
||||||
export const loginThemePageIds = [
|
|
||||||
"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 const accountThemePageIds = ["password.ftl"] as const;
|
|
||||||
|
|
||||||
export type PageId = (typeof loginThemePageIds)[number];
|
|
||||||
export type AccountThemePageId = (typeof accountThemePageIds)[number];
|
|
||||||
|
|
||||||
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
|
|
||||||
|
|
||||||
export namespace BuildOptionsLike {
|
|
||||||
export type Standalone = {
|
|
||||||
isStandalone: true;
|
|
||||||
urlPathname: 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 function generateFtlFilesCodeFactory(params: {
|
|
||||||
indexHtmlCode: string;
|
|
||||||
//NOTE: Expected to be an empty object if external assets mode is enabled.
|
|
||||||
cssGlobalsToDefine: Record<string, string>;
|
|
||||||
buildOptions: BuildOptionsLike;
|
|
||||||
}) {
|
|
||||||
const { cssGlobalsToDefine, indexHtmlCode, buildOptions } = 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
|
|
||||||
});
|
|
||||||
|
|
||||||
$(element).text(fixedJsCode);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("style").each((...[, element]) => {
|
|
||||||
const { fixedCssCode } = replaceImportsInInlineCssCode({
|
|
||||||
"cssCode": $(element).html()!,
|
|
||||||
buildOptions
|
|
||||||
});
|
|
||||||
|
|
||||||
$(element).text(fixedCssCode);
|
|
||||||
});
|
|
||||||
|
|
||||||
(
|
|
||||||
[
|
|
||||||
["link", "href"],
|
|
||||||
["script", "src"]
|
|
||||||
] as const
|
|
||||||
).forEach(([selector, attrName]) =>
|
|
||||||
$(selector).each((...[, element]) => {
|
|
||||||
const href = $(element).attr(attrName);
|
|
||||||
|
|
||||||
if (href === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$(element).attr(
|
|
||||||
attrName,
|
|
||||||
buildOptions.isStandalone
|
|
||||||
? href.replace(new RegExp(`^${(buildOptions.urlPathname ?? "/").replace(/\//g, "\\/")}`), "${url.resourcesPath}/build/")
|
|
||||||
: href.replace(/^\//, `${buildOptions.urlOrigin}/`)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
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")
|
|
||||||
};
|
|
||||||
|
|
||||||
$("head").prepend(
|
|
||||||
[
|
|
||||||
"<script>",
|
|
||||||
` window.${ftlValuesGlobalName}= ${objectKeys(replaceValueBySearchValue)[0]};`,
|
|
||||||
"</script>",
|
|
||||||
"",
|
|
||||||
objectKeys(replaceValueBySearchValue)[1]
|
|
||||||
].join("\n")
|
|
||||||
);
|
|
||||||
|
|
||||||
const partiallyFixedIndexHtmlCode = $.html();
|
|
||||||
|
|
||||||
function generateFtlFilesCode(params: { pageId: string }): {
|
|
||||||
ftlCode: string;
|
|
||||||
} {
|
|
||||||
const { pageId } = params;
|
|
||||||
|
|
||||||
const $ = cheerio.load(partiallyFixedIndexHtmlCode);
|
|
||||||
|
|
||||||
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)));
|
|
||||||
|
|
||||||
return { ftlCode };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { generateFtlFilesCode };
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export * from "./generateFtl";
|
|
@ -1,88 +0,0 @@
|
|||||||
import * as fs from "fs";
|
|
||||||
import { join as pathJoin, dirname as pathDirname } from "path";
|
|
||||||
import { themeTypes } from "./generateFtl/generateFtl";
|
|
||||||
import { assert } from "tsafe/assert";
|
|
||||||
import { Reflect } from "tsafe/Reflect";
|
|
||||||
import type { BuildOptions } from "./BuildOptions";
|
|
||||||
|
|
||||||
export type BuildOptionsLike = {
|
|
||||||
themeName: string;
|
|
||||||
groupId: string;
|
|
||||||
artifactId?: string;
|
|
||||||
version: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
const buildOptions = Reflect<BuildOptions>();
|
|
||||||
|
|
||||||
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateJavaStackFiles(params: {
|
|
||||||
keycloakThemeBuildingDirPath: string;
|
|
||||||
doBundlesEmailTemplate: boolean;
|
|
||||||
buildOptions: BuildOptionsLike;
|
|
||||||
}): {
|
|
||||||
jarFilePath: string;
|
|
||||||
} {
|
|
||||||
const {
|
|
||||||
buildOptions: { groupId, themeName, version, artifactId },
|
|
||||||
keycloakThemeBuildingDirPath,
|
|
||||||
doBundlesEmailTemplate
|
|
||||||
} = 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>${groupId}</groupId>`,
|
|
||||||
` <artifactId>${artifactId}</artifactId>`,
|
|
||||||
` <version>${version}</version>`,
|
|
||||||
` <name>${artifactId}</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": [...themeTypes, ...(doBundlesEmailTemplate ? ["email"] : [])]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
),
|
|
||||||
"utf8"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
"jarFilePath": pathJoin(keycloakThemeBuildingDirPath, "target", `${artifactId}-${version}.jar`)
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,238 +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, loginThemePageIds, themeTypes, ThemeType } from "./generateFtl";
|
|
||||||
import { downloadBuiltinKeycloakTheme } from "../download-builtin-keycloak-theme";
|
|
||||||
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;
|
|
||||||
extraLoginPages?: string[];
|
|
||||||
extraAccountPages?: 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 async function generateKeycloakThemeResources(params: {
|
|
||||||
reactAppBuildDirPath: string;
|
|
||||||
keycloakThemeBuildingDirPath: string;
|
|
||||||
keycloakThemeEmailDirPath: string;
|
|
||||||
keycloakVersion: string;
|
|
||||||
buildOptions: BuildOptionsLike;
|
|
||||||
}): Promise<{ doBundlesEmailTemplate: boolean }> {
|
|
||||||
const { reactAppBuildDirPath, keycloakThemeBuildingDirPath, keycloakThemeEmailDirPath, keycloakVersion, buildOptions } = params;
|
|
||||||
|
|
||||||
const logger = getLogger({ isSilent: buildOptions.isSilent });
|
|
||||||
|
|
||||||
const getThemeDirPath = (themeType: ThemeType | "email") =>
|
|
||||||
pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", buildOptions.themeName, themeType);
|
|
||||||
|
|
||||||
let allCssGlobalsToDefine: Record<string, string> = {};
|
|
||||||
|
|
||||||
let generateFtlFilesCode_glob: ReturnType<typeof generateFtlFilesCodeFactory>["generateFtlFilesCode"] | undefined = undefined;
|
|
||||||
|
|
||||||
for (const themeType of themeTypes) {
|
|
||||||
if (themeType === "account") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const themeDirPath = getThemeDirPath(themeType);
|
|
||||||
|
|
||||||
copy_app_resources_to_theme_path: {
|
|
||||||
const isFirstPass = themeType.indexOf(themeType) === 0;
|
|
||||||
|
|
||||||
if (!isFirstPass && !buildOptions.isStandalone) {
|
|
||||||
break copy_app_resources_to_theme_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
});
|
|
||||||
|
|
||||||
register_css_variables: {
|
|
||||||
if (!isFirstPass) {
|
|
||||||
break register_css_variables;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const generateFtlFilesCode = (() => {
|
|
||||||
if (generateFtlFilesCode_glob !== undefined) {
|
|
||||||
return generateFtlFilesCode_glob;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
|
|
||||||
"indexHtmlCode": fs.readFileSync(pathJoin(reactAppBuildDirPath, "index.html")).toString("utf8"),
|
|
||||||
"cssGlobalsToDefine": allCssGlobalsToDefine,
|
|
||||||
"buildOptions": buildOptions
|
|
||||||
});
|
|
||||||
|
|
||||||
return generateFtlFilesCode;
|
|
||||||
})();
|
|
||||||
|
|
||||||
[...loginThemePageIds, ...(buildOptions.extraLoginPages ?? [])].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");
|
|
||||||
|
|
||||||
await 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"));
|
|
||||||
fs.rmSync(tmpDirPath, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
pathJoin(themeDirPath, "theme.properties"),
|
|
||||||
Buffer.from(["parent=keycloak", ...(buildOptions.extraThemeProperties ?? [])].join("\n\n"), "utf8")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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": getThemeDirPath("email")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return { doBundlesEmailTemplate };
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
import * as fs from "fs";
|
|
||||||
import { join as pathJoin } from "path";
|
|
||||||
import { assert } from "tsafe/assert";
|
|
||||||
import { Reflect } from "tsafe/Reflect";
|
|
||||||
import type { BuildOptions } from "./BuildOptions";
|
|
||||||
|
|
||||||
export type BuildOptionsLike = {
|
|
||||||
themeName: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
const buildOptions = Reflect<BuildOptions>();
|
|
||||||
|
|
||||||
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
|
|
||||||
}
|
|
||||||
|
|
||||||
generateStartKeycloakTestingContainer.basename = "start_keycloak_testing_container.sh";
|
|
||||||
|
|
||||||
const containerName = "keycloak-testing-container";
|
|
||||||
|
|
||||||
/** Files for being able to run a hot reload keycloak container */
|
|
||||||
export function generateStartKeycloakTestingContainer(params: {
|
|
||||||
keycloakVersion: string;
|
|
||||||
keycloakThemeBuildingDirPath: string;
|
|
||||||
buildOptions: BuildOptionsLike;
|
|
||||||
}) {
|
|
||||||
const {
|
|
||||||
keycloakThemeBuildingDirPath,
|
|
||||||
keycloakVersion,
|
|
||||||
buildOptions: { themeName }
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
const keycloakThemePath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName).replace(/\\/g, "/");
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
pathJoin(keycloakThemeBuildingDirPath, generateStartKeycloakTestingContainer.basename),
|
|
||||||
|
|
||||||
Buffer.from(
|
|
||||||
[
|
|
||||||
"#!/usr/bin/env bash",
|
|
||||||
"",
|
|
||||||
`docker rm ${containerName} || true`,
|
|
||||||
"",
|
|
||||||
`cd "${keycloakThemeBuildingDirPath.replace(/\\/g, "/")}"`,
|
|
||||||
"",
|
|
||||||
"docker run \\",
|
|
||||||
" -p 8080:8080 \\",
|
|
||||||
` --name ${containerName} \\`,
|
|
||||||
" -e KEYCLOAK_ADMIN=admin \\",
|
|
||||||
" -e KEYCLOAK_ADMIN_PASSWORD=admin \\",
|
|
||||||
" -e JAVA_OPTS=-Dkeycloak.profile=preview \\",
|
|
||||||
` -v "${keycloakThemePath}":"/opt/keycloak/themes/${themeName}":rw \\`,
|
|
||||||
` -it quay.io/keycloak/keycloak:${keycloakVersion} \\`,
|
|
||||||
` start-dev`,
|
|
||||||
""
|
|
||||||
].join("\n"),
|
|
||||||
"utf8"
|
|
||||||
),
|
|
||||||
{ "mode": 0o755 }
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
export * from "./keycloakify";
|
|
||||||
import { main } from "./keycloakify";
|
|
||||||
|
|
||||||
if (require.main === module) {
|
|
||||||
main().catch(e => console.error(e));
|
|
||||||
}
|
|
@ -1,139 +0,0 @@
|
|||||||
import { generateKeycloakThemeResources } from "./generateKeycloakThemeResources";
|
|
||||||
import { generateJavaStackFiles } from "./generateJavaStackFiles";
|
|
||||||
import { join as pathJoin, relative as pathRelative, basename as pathBasename } from "path";
|
|
||||||
import * as child_process from "child_process";
|
|
||||||
import { generateStartKeycloakTestingContainer } from "./generateStartKeycloakTestingContainer";
|
|
||||||
import * as fs from "fs";
|
|
||||||
import { readBuildOptions } from "./BuildOptions";
|
|
||||||
import { getLogger } from "../tools/logger";
|
|
||||||
import { getCliOptions } from "../tools/cliOptions";
|
|
||||||
import jar from "../tools/jar";
|
|
||||||
import { assert } from "tsafe/assert";
|
|
||||||
import type { Equals } from "tsafe";
|
|
||||||
|
|
||||||
const reactProjectDirPath = process.cwd();
|
|
||||||
|
|
||||||
export const keycloakThemeBuildingDirPath = pathJoin(reactProjectDirPath, "build_keycloak");
|
|
||||||
export const keycloakThemeEmailDirPath = pathJoin(keycloakThemeBuildingDirPath, "..", "keycloak_email");
|
|
||||||
|
|
||||||
export async function main() {
|
|
||||||
const { isSilent, hasExternalAssets } = getCliOptions(process.argv.slice(2));
|
|
||||||
const logger = getLogger({ isSilent });
|
|
||||||
logger.log("🔏 Building the keycloak theme...⌚");
|
|
||||||
|
|
||||||
const buildOptions = readBuildOptions({
|
|
||||||
"packageJson": fs.readFileSync(pathJoin(reactProjectDirPath, "package.json")).toString("utf8"),
|
|
||||||
"CNAME": (() => {
|
|
||||||
const cnameFilePath = pathJoin(reactProjectDirPath, "public", "CNAME");
|
|
||||||
|
|
||||||
if (!fs.existsSync(cnameFilePath)) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fs.readFileSync(cnameFilePath).toString("utf8");
|
|
||||||
})(),
|
|
||||||
"isExternalAssetsCliParamProvided": hasExternalAssets,
|
|
||||||
"isSilent": isSilent
|
|
||||||
});
|
|
||||||
|
|
||||||
const { doBundlesEmailTemplate } = await generateKeycloakThemeResources({
|
|
||||||
keycloakThemeBuildingDirPath,
|
|
||||||
keycloakThemeEmailDirPath,
|
|
||||||
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
|
|
||||||
buildOptions,
|
|
||||||
//We have to leave it at that otherwise we break our default theme.
|
|
||||||
//Problem is that we can't guarantee that the the old resources
|
|
||||||
//will still be available on the newer keycloak version.
|
|
||||||
"keycloakVersion": "11.0.3"
|
|
||||||
});
|
|
||||||
|
|
||||||
const { jarFilePath } = generateJavaStackFiles({
|
|
||||||
keycloakThemeBuildingDirPath,
|
|
||||||
doBundlesEmailTemplate,
|
|
||||||
buildOptions
|
|
||||||
});
|
|
||||||
|
|
||||||
switch (buildOptions.bundler) {
|
|
||||||
case "none":
|
|
||||||
logger.log("😱 Skipping bundling step, there will be no jar");
|
|
||||||
break;
|
|
||||||
case "keycloakify":
|
|
||||||
logger.log("🫶 Let keycloakify do its thang");
|
|
||||||
await jar({
|
|
||||||
"rootPath": pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources"),
|
|
||||||
"version": buildOptions.version,
|
|
||||||
"groupId": buildOptions.groupId,
|
|
||||||
"artifactId": buildOptions.artifactId,
|
|
||||||
"targetPath": jarFilePath
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "mvn":
|
|
||||||
logger.log("🫙 Run maven to deliver a jar");
|
|
||||||
child_process.execSync("mvn package", { "cwd": keycloakThemeBuildingDirPath });
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert<Equals<typeof buildOptions.bundler, never>>(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We want, however, to test in a container running the latest Keycloak version
|
|
||||||
const containerKeycloakVersion = "20.0.1";
|
|
||||||
|
|
||||||
generateStartKeycloakTestingContainer({
|
|
||||||
keycloakThemeBuildingDirPath,
|
|
||||||
"keycloakVersion": containerKeycloakVersion,
|
|
||||||
buildOptions
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.log(
|
|
||||||
[
|
|
||||||
"",
|
|
||||||
`✅ Your keycloak theme has been generated and bundled into ./${pathRelative(reactProjectDirPath, jarFilePath)} 🚀`,
|
|
||||||
`It is to be placed in "/opt/keycloak/providers" in the container running a quay.io/keycloak/keycloak Docker image.`,
|
|
||||||
"",
|
|
||||||
//TODO: Restore when we find a good Helm chart for Keycloak.
|
|
||||||
//"Using Helm (https://github.com/codecentric/helm-charts), edit to reflect:",
|
|
||||||
"",
|
|
||||||
"value.yaml: ",
|
|
||||||
" extraInitContainers: |",
|
|
||||||
" - name: realm-ext-provider",
|
|
||||||
" image: curlimages/curl",
|
|
||||||
" imagePullPolicy: IfNotPresent",
|
|
||||||
" command:",
|
|
||||||
" - sh",
|
|
||||||
" args:",
|
|
||||||
" - -c",
|
|
||||||
` - curl -L -f -S -o /extensions/${pathBasename(jarFilePath)} https://AN.URL.FOR/${pathBasename(jarFilePath)}`,
|
|
||||||
" volumeMounts:",
|
|
||||||
" - name: extensions",
|
|
||||||
" mountPath: /extensions",
|
|
||||||
" ",
|
|
||||||
" extraVolumeMounts: |",
|
|
||||||
" - name: extensions",
|
|
||||||
" mountPath: /opt/keycloak/providers",
|
|
||||||
" extraEnv: |",
|
|
||||||
" - name: KEYCLOAK_USER",
|
|
||||||
" value: admin",
|
|
||||||
" - name: KEYCLOAK_PASSWORD",
|
|
||||||
" value: xxxxxxxxx",
|
|
||||||
" - name: JAVA_OPTS",
|
|
||||||
" value: -Dkeycloak.profile=preview",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
`To test your theme locally you can spin up a Keycloak ${containerKeycloakVersion} container image with the theme pre loaded by running:`,
|
|
||||||
"",
|
|
||||||
`👉 $ ./${pathRelative(reactProjectDirPath, pathJoin(keycloakThemeBuildingDirPath, generateStartKeycloakTestingContainer.basename))} 👈`,
|
|
||||||
"",
|
|
||||||
"Test with different Keycloak versions by editing the .sh file. see available versions here: https://quay.io/repository/keycloak/keycloak?tab=tags",
|
|
||||||
"",
|
|
||||||
"Once your container is up and running: ",
|
|
||||||
"- Log into the admin console 👉 http://localhost:8080/admin username: admin, password: admin 👈",
|
|
||||||
'- Create a realm named "myrealm"',
|
|
||||||
'- Create a client with ID: "myclient", "Root URL": "https://www.keycloak.org/app/" and "Valid redirect URIs": "https://www.keycloak.org/app/*"',
|
|
||||||
`- Select Login Theme: ${buildOptions.themeName} (don't forget to save at the bottom of the page)`,
|
|
||||||
`- Go to 👉 https://www.keycloak.org/app/ 👈 Click "Save" then "Sign in". You should see your login page`,
|
|
||||||
"",
|
|
||||||
"Video demoing this process: https://youtu.be/N3wlBoH4hKg",
|
|
||||||
""
|
|
||||||
].join("\n")
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
|
|
||||||
import type { BuildOptions } from "../BuildOptions";
|
|
||||||
import { assert } from "tsafe/assert";
|
|
||||||
import { is } from "tsafe/is";
|
|
||||||
import { Reflect } from "tsafe/Reflect";
|
|
||||||
|
|
||||||
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
|
|
||||||
|
|
||||||
export namespace BuildOptionsLike {
|
|
||||||
export type Standalone = {
|
|
||||||
isStandalone: true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ExternalAssets = {
|
|
||||||
isStandalone: false;
|
|
||||||
urlOrigin: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const buildOptions = Reflect<BuildOptions>();
|
|
||||||
|
|
||||||
assert(!is<BuildOptions.ExternalAssets.CommonExternalAssets>(buildOptions));
|
|
||||||
|
|
||||||
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function replaceImportsFromStaticInJsCode(params: { jsCode: string; buildOptions: BuildOptionsLike }): { fixedJsCode: string } {
|
|
||||||
/*
|
|
||||||
NOTE:
|
|
||||||
|
|
||||||
When we have urlOrigin defined it means that
|
|
||||||
we are building with --external-assets
|
|
||||||
so we have to make sur that the fixed js code will run
|
|
||||||
inside and outside keycloak.
|
|
||||||
|
|
||||||
When urlOrigin isn't defined we can assume the fixedJsCode
|
|
||||||
will always run in keycloak context.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const { jsCode, buildOptions } = params;
|
|
||||||
|
|
||||||
const getReplaceArgs = (language: "js" | "css"): Parameters<typeof String.prototype.replace> => [
|
|
||||||
new RegExp(`([a-zA-Z_]+)\\.([a-zA-Z]+)=function\\(([a-zA-Z]+)\\){return"static\\/${language}\\/"`, "g"),
|
|
||||||
(...[, n, u, e]) => `
|
|
||||||
${n}[(function(){
|
|
||||||
var pd= Object.getOwnPropertyDescriptor(${n}, "p");
|
|
||||||
if( pd === undefined || pd.configurable ){
|
|
||||||
${
|
|
||||||
buildOptions.isStandalone
|
|
||||||
? `
|
|
||||||
Object.defineProperty(${n}, "p", {
|
|
||||||
get: function() { return window.${ftlValuesGlobalName}.url.resourcesPath; },
|
|
||||||
set: function (){}
|
|
||||||
});
|
|
||||||
`
|
|
||||||
: `
|
|
||||||
var p= "";
|
|
||||||
Object.defineProperty(${n}, "p", {
|
|
||||||
get: function() { return "${ftlValuesGlobalName}" in window ? "${buildOptions.urlOrigin}/" : p; },
|
|
||||||
set: function (value){ p = value;}
|
|
||||||
});
|
|
||||||
`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "${u}";
|
|
||||||
})()] = function(${e}) { return "${buildOptions.isStandalone ? "/build/" : ""}static/${language}/"`
|
|
||||||
];
|
|
||||||
|
|
||||||
const fixedJsCode = jsCode
|
|
||||||
.replace(...getReplaceArgs("js"))
|
|
||||||
.replace(...getReplaceArgs("css"))
|
|
||||||
.replace(/([a-zA-Z]+\.[a-zA-Z]+)\+"static\//g, (...[, group]) =>
|
|
||||||
buildOptions.isStandalone
|
|
||||||
? `window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/`
|
|
||||||
: `("${ftlValuesGlobalName}" in window ? "${buildOptions.urlOrigin}/" : ${group}) + "static/`
|
|
||||||
)
|
|
||||||
//TODO: Write a test case for this
|
|
||||||
.replace(/".chunk.css",([a-zA-Z])+=([a-zA-Z]+\.[a-zA-Z]+)\+([a-zA-Z]+),/, (...[, group1, group2, group3]) =>
|
|
||||||
buildOptions.isStandalone
|
|
||||||
? `".chunk.css",${group1} = window.${ftlValuesGlobalName}.url.resourcesPath + "/build/" + ${group3},`
|
|
||||||
: `".chunk.css",${group1} = ("${ftlValuesGlobalName}" in window ? "${buildOptions.urlOrigin}/" : ${group2}) + ${group3},`
|
|
||||||
);
|
|
||||||
|
|
||||||
return { fixedJsCode };
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
import * as crypto from "crypto";
|
|
||||||
import type { BuildOptions } from "../BuildOptions";
|
|
||||||
import { assert } from "tsafe/assert";
|
|
||||||
import { is } from "tsafe/is";
|
|
||||||
import { Reflect } from "tsafe/Reflect";
|
|
||||||
|
|
||||||
export type BuildOptionsLike = {
|
|
||||||
urlPathname: string | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
const buildOptions = Reflect<BuildOptions>();
|
|
||||||
|
|
||||||
assert(!is<BuildOptions.ExternalAssets.CommonExternalAssets>(buildOptions));
|
|
||||||
|
|
||||||
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function replaceImportsInCssCode(params: { cssCode: string }): {
|
|
||||||
fixedCssCode: string;
|
|
||||||
cssGlobalsToDefine: Record<string, string>;
|
|
||||||
} {
|
|
||||||
const { cssCode } = params;
|
|
||||||
|
|
||||||
const cssGlobalsToDefine: Record<string, string> = {};
|
|
||||||
|
|
||||||
new Set(cssCode.match(/url\(["']?\/[^/][^)"']+["']?\)[^;}]*/g) ?? []).forEach(
|
|
||||||
match => (cssGlobalsToDefine["url" + crypto.createHash("sha256").update(match).digest("hex").substring(0, 15)] = match)
|
|
||||||
);
|
|
||||||
|
|
||||||
let fixedCssCode = cssCode;
|
|
||||||
|
|
||||||
Object.keys(cssGlobalsToDefine).forEach(
|
|
||||||
cssVariableName =>
|
|
||||||
//NOTE: split/join pattern ~ replace all
|
|
||||||
(fixedCssCode = fixedCssCode.split(cssGlobalsToDefine[cssVariableName]).join(`var(--${cssVariableName})`))
|
|
||||||
);
|
|
||||||
|
|
||||||
return { fixedCssCode, cssGlobalsToDefine };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateCssCodeToDefineGlobals(params: { cssGlobalsToDefine: Record<string, string>; buildOptions: BuildOptionsLike }): {
|
|
||||||
cssCodeToPrependInHead: string;
|
|
||||||
} {
|
|
||||||
const { cssGlobalsToDefine, buildOptions } = params;
|
|
||||||
|
|
||||||
return {
|
|
||||||
"cssCodeToPrependInHead": [
|
|
||||||
":root {",
|
|
||||||
...Object.keys(cssGlobalsToDefine)
|
|
||||||
.map(cssVariableName =>
|
|
||||||
[
|
|
||||||
`--${cssVariableName}:`,
|
|
||||||
cssGlobalsToDefine[cssVariableName].replace(
|
|
||||||
new RegExp(`url\\(${(buildOptions.urlPathname ?? "/").replace(/\//g, "\\/")}`, "g"),
|
|
||||||
"url(${url.resourcesPath}/build/"
|
|
||||||
)
|
|
||||||
].join(" ")
|
|
||||||
)
|
|
||||||
.map(line => ` ${line};`),
|
|
||||||
"}"
|
|
||||||
].join("\n")
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
import type { BuildOptions } from "../BuildOptions";
|
|
||||||
import { assert } from "tsafe/assert";
|
|
||||||
import { is } from "tsafe/is";
|
|
||||||
import { Reflect } from "tsafe/Reflect";
|
|
||||||
|
|
||||||
export type BuildOptionsLike = BuildOptionsLike.Standalone | BuildOptionsLike.ExternalAssets;
|
|
||||||
|
|
||||||
export namespace BuildOptionsLike {
|
|
||||||
export type Common = {
|
|
||||||
urlPathname: string | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Standalone = Common & {
|
|
||||||
isStandalone: true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ExternalAssets = Common & {
|
|
||||||
isStandalone: false;
|
|
||||||
urlOrigin: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const buildOptions = Reflect<BuildOptions>();
|
|
||||||
|
|
||||||
assert(!is<BuildOptions.ExternalAssets.CommonExternalAssets>(buildOptions));
|
|
||||||
|
|
||||||
assert<typeof buildOptions extends BuildOptionsLike ? true : false>();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function replaceImportsInInlineCssCode(params: { cssCode: string; buildOptions: BuildOptionsLike }): {
|
|
||||||
fixedCssCode: string;
|
|
||||||
} {
|
|
||||||
const { cssCode, buildOptions } = params;
|
|
||||||
|
|
||||||
const fixedCssCode = cssCode.replace(
|
|
||||||
buildOptions.urlPathname === undefined
|
|
||||||
? /url\(["']?\/([^/][^)"']+)["']?\)/g
|
|
||||||
: new RegExp(`url\\(["']?${buildOptions.urlPathname}([^)"']+)["']?\\)`, "g"),
|
|
||||||
(...[, group]) =>
|
|
||||||
`url(${
|
|
||||||
buildOptions.isStandalone ? "${url.resourcesPath}/build/" + group : buildOptions.urlOrigin + (buildOptions.urlPathname ?? "/") + group
|
|
||||||
})`
|
|
||||||
);
|
|
||||||
|
|
||||||
return { fixedCssCode };
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
import { pathJoin } from "./tools/pathJoin";
|
|
||||||
|
|
||||||
export const mockTestingSubDirOfPublicDirBasename = "keycloak_static";
|
|
||||||
export const mockTestingResourcesPath = pathJoin(mockTestingSubDirOfPublicDirBasename, "resources");
|
|
||||||
export const mockTestingResourcesCommonPath = pathJoin(mockTestingResourcesPath, "resources_common");
|
|
@ -1,47 +0,0 @@
|
|||||||
import { getLatestsSemVersionedTagFactory } from "./tools/octokit-addons/getLatestsSemVersionedTag";
|
|
||||||
import { Octokit } from "@octokit/rest";
|
|
||||||
import cliSelect from "cli-select";
|
|
||||||
|
|
||||||
export async function promptKeycloakVersion() {
|
|
||||||
const { getLatestsSemVersionedTag } = (() => {
|
|
||||||
const { octokit } = (() => {
|
|
||||||
const githubToken = process.env.GITHUB_TOKEN;
|
|
||||||
|
|
||||||
const octokit = new Octokit(githubToken === undefined ? undefined : { "auth": githubToken });
|
|
||||||
|
|
||||||
return { octokit };
|
|
||||||
})();
|
|
||||||
|
|
||||||
const { getLatestsSemVersionedTag } = getLatestsSemVersionedTagFactory({ octokit });
|
|
||||||
|
|
||||||
return { getLatestsSemVersionedTag };
|
|
||||||
})();
|
|
||||||
|
|
||||||
console.log("Initialize the directory with email template from which keycloak version?");
|
|
||||||
|
|
||||||
const tags = [
|
|
||||||
...(await getLatestsSemVersionedTag({
|
|
||||||
"count": 10,
|
|
||||||
"doIgnoreBeta": true,
|
|
||||||
"owner": "keycloak",
|
|
||||||
"repo": "keycloak"
|
|
||||||
}).then(arr => arr.map(({ tag }) => tag))),
|
|
||||||
"11.0.3"
|
|
||||||
];
|
|
||||||
|
|
||||||
if (process.env["GITHUB_ACTIONS"] === "true") {
|
|
||||||
return { "keycloakVersion": tags[0] };
|
|
||||||
}
|
|
||||||
|
|
||||||
const { value: keycloakVersion } = await cliSelect<string>({
|
|
||||||
"values": tags
|
|
||||||
}).catch(() => {
|
|
||||||
console.log("Aborting");
|
|
||||||
|
|
||||||
process.exit(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(keycloakVersion);
|
|
||||||
|
|
||||||
return { keycloakVersion };
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
export type NpmModuleVersion = {
|
|
||||||
major: number;
|
|
||||||
minor: number;
|
|
||||||
patch: number;
|
|
||||||
betaPreRelease?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export namespace NpmModuleVersion {
|
|
||||||
export function parse(versionStr: string): NpmModuleVersion {
|
|
||||||
const match = versionStr.match(/^([0-9]+)\.([0-9]+)\.([0-9]+)(?:-beta.([0-9]+))?/);
|
|
||||||
|
|
||||||
if (!match) {
|
|
||||||
throw new Error(`${versionStr} is not a valid NPM version`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
"major": parseInt(match[1]),
|
|
||||||
"minor": parseInt(match[2]),
|
|
||||||
"patch": parseInt(match[3]),
|
|
||||||
...(() => {
|
|
||||||
const str = match[4];
|
|
||||||
return str === undefined ? {} : { "betaPreRelease": parseInt(str) };
|
|
||||||
})()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function stringify(v: NpmModuleVersion) {
|
|
||||||
return `${v.major}.${v.minor}.${v.patch}${v.betaPreRelease === undefined ? "" : `-beta.${v.betaPreRelease}`}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* v1 < v2 => -1
|
|
||||||
* v1 === v2 => 0
|
|
||||||
* v1 > v2 => 1
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export function compare(v1: NpmModuleVersion, v2: NpmModuleVersion): -1 | 0 | 1 {
|
|
||||||
const sign = (diff: number): -1 | 0 | 1 => (diff === 0 ? 0 : diff < 0 ? -1 : 1);
|
|
||||||
const noUndefined = (n: number | undefined) => n ?? Infinity;
|
|
||||||
|
|
||||||
for (const level of ["major", "minor", "patch", "betaPreRelease"] as const) {
|
|
||||||
if (noUndefined(v1[level]) !== noUndefined(v2[level])) {
|
|
||||||
return sign(noUndefined(v1[level]) - noUndefined(v2[level]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
console.log(compare(parse("3.0.0-beta.3"), parse("3.0.0")) === -1 )
|
|
||||||
console.log(compare(parse("3.0.0-beta.3"), parse("3.0.0-beta.4")) === -1 )
|
|
||||||
console.log(compare(parse("3.0.0-beta.3"), parse("4.0.0")) === -1 )
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function bumpType(params: { versionBehindStr: string; versionAheadStr: string }): "major" | "minor" | "patch" | "betaPreRelease" | "same" {
|
|
||||||
const versionAhead = parse(params.versionAheadStr);
|
|
||||||
const versionBehind = parse(params.versionBehindStr);
|
|
||||||
|
|
||||||
if (compare(versionBehind, versionAhead) === 1) {
|
|
||||||
throw new Error(`Version regression ${versionBehind} -> ${versionAhead}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const level of ["major", "minor", "patch", "betaPreRelease"] as const) {
|
|
||||||
if (versionBehind[level] !== versionAhead[level]) {
|
|
||||||
return level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "same";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
import parseArgv from "minimist";
|
|
||||||
|
|
||||||
export type CliOptions = {
|
|
||||||
isSilent: boolean;
|
|
||||||
hasExternalAssets: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getCliOptions = (processArgv: string[]): CliOptions => {
|
|
||||||
const argv = parseArgv(processArgv);
|
|
||||||
|
|
||||||
return {
|
|
||||||
isSilent: typeof argv["silent"] === "boolean" ? argv["silent"] : false,
|
|
||||||
hasExternalAssets: typeof argv["external-assets"] === "boolean" ? argv["external-assets"] : false
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,54 +0,0 @@
|
|||||||
import { Readable } from "stream";
|
|
||||||
|
|
||||||
const crc32tab = [
|
|
||||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
|
||||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
|
||||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
|
||||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
|
||||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
|
||||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
|
||||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
|
||||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
|
||||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
|
||||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
|
||||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
|
||||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
|
||||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
|
||||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
|
||||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
|
||||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
|
||||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
|
||||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
|
||||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
|
||||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
|
||||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
|
||||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param input either a byte stream, a string or a buffer, you want to have the checksum for
|
|
||||||
* @returns a promise for a checksum (uint32)
|
|
||||||
*/
|
|
||||||
export function crc32(input: Readable | String | Buffer): Promise<number> {
|
|
||||||
if (typeof input === "string") {
|
|
||||||
let crc = ~0;
|
|
||||||
for (let i = 0; i < input.length; i++) crc = (crc >>> 8) ^ crc32tab[(crc ^ input.charCodeAt(i)) & 0xff];
|
|
||||||
return Promise.resolve((crc ^ -1) >>> 0);
|
|
||||||
} else if (input instanceof Buffer) {
|
|
||||||
let crc = ~0;
|
|
||||||
for (let i = 0; i < input.length; i++) crc = (crc >>> 8) ^ crc32tab[(crc ^ input[i]) & 0xff];
|
|
||||||
return Promise.resolve((crc ^ -1) >>> 0);
|
|
||||||
} else if (input instanceof Readable) {
|
|
||||||
return new Promise<number>((resolve, reject) => {
|
|
||||||
let crc = ~0;
|
|
||||||
input.on("end", () => resolve((crc ^ -1) >>> 0));
|
|
||||||
input.on("error", e => reject(e));
|
|
||||||
input.on("data", (chunk: Buffer) => {
|
|
||||||
for (let i = 0; i < chunk.length; i++) crc = (crc >>> 8) ^ crc32tab[(crc ^ chunk[i]) & 0xff];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new Error("Unsupported input " + typeof input);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
import { PassThrough, Readable, TransformCallback, Writable } from "stream";
|
|
||||||
import { pipeline } from "stream/promises";
|
|
||||||
import { deflateRaw as deflateRawCb, createDeflateRaw } from "zlib";
|
|
||||||
import { promisify } from "util";
|
|
||||||
|
|
||||||
import { crc32 } from "./crc32";
|
|
||||||
import tee from "./tee";
|
|
||||||
|
|
||||||
const deflateRaw = promisify(deflateRawCb);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A stream transformer that records the number of bytes
|
|
||||||
* passed in its `size` property.
|
|
||||||
*/
|
|
||||||
class ByteCounter extends PassThrough {
|
|
||||||
size: number = 0;
|
|
||||||
_transform(chunk: any, encoding: BufferEncoding, callback: TransformCallback) {
|
|
||||||
if ("length" in chunk) this.size += chunk.length;
|
|
||||||
super._transform(chunk, encoding, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param data buffer containing the data to be compressed
|
|
||||||
* @returns a buffer containing the compressed/deflated data and the crc32 checksum
|
|
||||||
* of the source data
|
|
||||||
*/
|
|
||||||
export async function deflateBuffer(data: Buffer) {
|
|
||||||
const [deflated, checksum] = await Promise.all([deflateRaw(data), crc32(data)]);
|
|
||||||
return { deflated, crc32: checksum };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param input a byte stream, containing data to be compressed
|
|
||||||
* @param sink a method that will accept chunks of compressed data; We don't pass
|
|
||||||
* a writable here, since we don't want the writablestream to be closed after
|
|
||||||
* a single file
|
|
||||||
* @returns a promise, which will resolve with the crc32 checksum and the
|
|
||||||
* compressed size
|
|
||||||
*/
|
|
||||||
export async function deflateStream(input: Readable, sink: (chunk: Buffer) => void) {
|
|
||||||
const deflateWriter = new Writable({
|
|
||||||
write(chunk, _, callback) {
|
|
||||||
sink(chunk);
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// tee the input stream, so we can compress and calc crc32 in parallel
|
|
||||||
const [rs1, rs2] = tee(input);
|
|
||||||
const byteCounter = new ByteCounter();
|
|
||||||
const [_, crc] = await Promise.all([
|
|
||||||
// pipe input into zip compressor, count the bytes
|
|
||||||
// returned and pass compressed data to the sink
|
|
||||||
pipeline(rs1, createDeflateRaw(), byteCounter, deflateWriter),
|
|
||||||
// calc checksum
|
|
||||||
crc32(rs2)
|
|
||||||
]);
|
|
||||||
|
|
||||||
return { crc32: crc, compressedSize: byteCounter.size };
|
|
||||||
}
|
|
@ -1,277 +0,0 @@
|
|||||||
import { dirname as pathDirname, basename as pathBasename, join as pathJoin, join } from "path";
|
|
||||||
import { createReadStream, createWriteStream } from "fs";
|
|
||||||
import { stat, mkdir, unlink, writeFile } from "fs/promises";
|
|
||||||
import { transformCodebase } from "./transformCodebase";
|
|
||||||
import { createHash } from "crypto";
|
|
||||||
import fetch from "make-fetch-happen";
|
|
||||||
import { createInflateRaw } from "zlib";
|
|
||||||
import type { Readable } from "stream";
|
|
||||||
import { homedir } from "os";
|
|
||||||
import { FetchOptions } from "make-fetch-happen";
|
|
||||||
import { exec as execCallback } from "child_process";
|
|
||||||
import { promisify } from "util";
|
|
||||||
|
|
||||||
const exec = promisify(execCallback);
|
|
||||||
|
|
||||||
function hash(s: string) {
|
|
||||||
return createHash("sha256").update(s).digest("hex");
|
|
||||||
}
|
|
||||||
|
|
||||||
async function maybeStat(path: string) {
|
|
||||||
try {
|
|
||||||
return await stat(path);
|
|
||||||
} catch (error) {
|
|
||||||
if ((error as Error & { code: string }).code === "ENOENT") return undefined;
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an npm configuration value as string, undefined if not set.
|
|
||||||
*
|
|
||||||
* @param key
|
|
||||||
* @returns string or undefined
|
|
||||||
*/
|
|
||||||
async function getNmpConfig(key: string): Promise<string | undefined> {
|
|
||||||
const { stdout } = await exec(`npm config get ${key}`);
|
|
||||||
const value = stdout.trim();
|
|
||||||
return value && value !== "null" ? value : undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get proxy configuration from npm config files. Note that we don't care about
|
|
||||||
* proxy config in env vars, because make-fetch-happen will do that for us.
|
|
||||||
*
|
|
||||||
* @returns proxy configuration
|
|
||||||
*/
|
|
||||||
async function getNpmProxyConfig(): Promise<Pick<FetchOptions, "proxy" | "noProxy">> {
|
|
||||||
const proxy = (await getNmpConfig("https-proxy")) ?? (await getNmpConfig("proxy"));
|
|
||||||
const noProxy = (await getNmpConfig("noproxy")) ?? (await getNmpConfig("no-proxy"));
|
|
||||||
|
|
||||||
return { proxy, noProxy };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Download a file from `url` to `dir`. Will try to avoid downloading existing
|
|
||||||
* files by using the cache directory ~/.keycloakify/cache
|
|
||||||
*
|
|
||||||
* If the target directory does not exist, it will be created.
|
|
||||||
*
|
|
||||||
* If the target file exists, it will be overwritten.
|
|
||||||
*
|
|
||||||
* We use make-fetch-happen's internal file cache here, so we don't need to
|
|
||||||
* worry about redownloading the same file over and over. Unfortunately, that
|
|
||||||
* cache does not have a single file per entry, but bundles and indexes them,
|
|
||||||
* so we still need to write the contents to the target directory (possibly
|
|
||||||
* over and over), cause the current unzip implementation wants random access.
|
|
||||||
*
|
|
||||||
* @param url download url
|
|
||||||
* @param dir target directory
|
|
||||||
* @param filename target filename
|
|
||||||
* @returns promise for the full path of the downloaded file
|
|
||||||
*/
|
|
||||||
async function download(url: string, dir: string, filename: string): Promise<string> {
|
|
||||||
const proxyOpts = await getNpmProxyConfig();
|
|
||||||
const cacheRoot = process.env.XDG_CACHE_HOME ?? homedir();
|
|
||||||
const cachePath = join(cacheRoot, ".keycloakify/cache");
|
|
||||||
const opts: FetchOptions = { cachePath, ...proxyOpts };
|
|
||||||
const response = await fetch(url, opts);
|
|
||||||
const filepath = pathJoin(dir, filename);
|
|
||||||
await mkdir(dir, { recursive: true });
|
|
||||||
await writeFile(filepath, response.body);
|
|
||||||
return filepath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef
|
|
||||||
* @type MultiError = Error & { cause: Error[] }
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract the archive `zipFile` into the directory `dir`. If `archiveDir` is given,
|
|
||||||
* only that directory will be extracted, stripping the given path components.
|
|
||||||
*
|
|
||||||
* If dir does not exist, it will be created.
|
|
||||||
*
|
|
||||||
* If any archive file exists, it will be overwritten.
|
|
||||||
*
|
|
||||||
* Will unzip using all available nodejs worker threads.
|
|
||||||
*
|
|
||||||
* Will try to clean up extracted files on failure.
|
|
||||||
*
|
|
||||||
* If unpacking fails, will either throw an regular error, or
|
|
||||||
* possibly an `MultiError`, which contains a `cause` field with
|
|
||||||
* a number of root cause errors.
|
|
||||||
*
|
|
||||||
* Warning this method is not optimized for continuous reading of the zip
|
|
||||||
* archive, but is a trade-off between simplicity and allowing extraction
|
|
||||||
* of a single directory from the archive.
|
|
||||||
*
|
|
||||||
* @param zipFile the file to unzip
|
|
||||||
* @param dir the target directory
|
|
||||||
* @param archiveDir if given, unpack only files from this archive directory
|
|
||||||
* @throws {MultiError} error
|
|
||||||
* @returns Promise for a list of full file paths pointing to actually extracted files
|
|
||||||
*/
|
|
||||||
async function unzip(zipFile: string, dir: string, archiveDir?: string): Promise<string[]> {
|
|
||||||
await mkdir(dir, { recursive: true });
|
|
||||||
const promises: Promise<string>[] = [];
|
|
||||||
|
|
||||||
// Iterate over all files in the zip, skip files which are not in archiveDir,
|
|
||||||
// if given.
|
|
||||||
for await (const record of iterateZipArchive(zipFile)) {
|
|
||||||
const { path: recordPath, createReadStream: createRecordReadStream } = record;
|
|
||||||
const filePath = pathJoin(dir, recordPath);
|
|
||||||
const parent = pathDirname(filePath);
|
|
||||||
if (archiveDir && !recordPath.startsWith(archiveDir)) continue;
|
|
||||||
promises.push(
|
|
||||||
new Promise<string>(async (resolve, reject) => {
|
|
||||||
await mkdir(parent, { recursive: true });
|
|
||||||
// Pull the file out of the archive, write it to the target directory
|
|
||||||
const input = createRecordReadStream();
|
|
||||||
const output = createWriteStream(filePath);
|
|
||||||
output.setMaxListeners(Infinity);
|
|
||||||
output.on("error", e => reject(Object.assign(e, { filePath })));
|
|
||||||
output.on("finish", () => resolve(filePath));
|
|
||||||
input.pipe(output);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait until _all_ files are either extracted or failed
|
|
||||||
const results = await Promise.allSettled(promises);
|
|
||||||
const success = results.filter(r => r.status === "fulfilled").map(r => (r as PromiseFulfilledResult<string>).value);
|
|
||||||
const failure = results.filter(r => r.status === "rejected").map(r => (r as PromiseRejectedResult).reason);
|
|
||||||
|
|
||||||
// If any extraction failed, try to clean up, then throw a MultiError,
|
|
||||||
// which has a `cause` field, containing a list of root cause errors.
|
|
||||||
if (failure.length) {
|
|
||||||
await Promise.all(success.map(path => unlink(path)));
|
|
||||||
await Promise.all(failure.map(e => e && e.path && unlink(e.path as string)));
|
|
||||||
const e = new Error("Failed to extract: " + failure.map(e => e.message).join(";"));
|
|
||||||
(e as any).cause = failure;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param file file to read
|
|
||||||
* @param start first byte to read
|
|
||||||
* @param end last byte to read
|
|
||||||
* @returns Promise of a buffer of read bytes
|
|
||||||
*/
|
|
||||||
async function readFileChunk(file: string, start: number, end: number): Promise<Buffer> {
|
|
||||||
const chunks: Buffer[] = [];
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const stream = createReadStream(file, { start, end });
|
|
||||||
stream.setMaxListeners(Infinity);
|
|
||||||
stream.on("error", e => reject(e));
|
|
||||||
stream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
||||||
stream.on("data", chunk => chunks.push(chunk as Buffer));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
type ZipRecord = {
|
|
||||||
path: string;
|
|
||||||
createReadStream: () => Readable;
|
|
||||||
compressionMethod: "deflate" | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
type ZipRecordGenerator = AsyncGenerator<ZipRecord, void, unknown>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterate over all records of a zipfile, and yield a ZipRecord.
|
|
||||||
* Use `record.createReadStream()` to actually read the file.
|
|
||||||
*
|
|
||||||
* Warning this method will only work with single-disk zip files.
|
|
||||||
* Warning this method may fail if the zip archive has an crazy amount
|
|
||||||
* of files and the central directory is not fully contained within the
|
|
||||||
* last 65k bytes of the zip file.
|
|
||||||
*
|
|
||||||
* @param zipFile
|
|
||||||
* @returns AsyncGenerator which will yield ZipRecords
|
|
||||||
*/
|
|
||||||
async function* iterateZipArchive(zipFile: string): ZipRecordGenerator {
|
|
||||||
// Need to know zip file size before we can do anything else
|
|
||||||
const { size } = await stat(zipFile);
|
|
||||||
const chunkSize = 65_535 + 22 + 1; // max comment size + end header size + wiggle
|
|
||||||
// Read last ~65k bytes. Zip files have an comment up to 65_535 bytes at the very end,
|
|
||||||
// before that comes the zip central directory end header.
|
|
||||||
let chunk = await readFileChunk(zipFile, size - chunkSize, size);
|
|
||||||
const unread = size - chunk.length;
|
|
||||||
let i = chunk.length - 4;
|
|
||||||
let found = false;
|
|
||||||
// Find central directory end header, reading backwards from the end
|
|
||||||
while (!found && i-- > 0) if (chunk[i] === 0x50 && chunk.readUInt32LE(i) === 0x06054b50) found = true;
|
|
||||||
if (!found) throw new Error("Not a zip file");
|
|
||||||
// This method will fail on a multi-disk zip, so bail early.
|
|
||||||
if (chunk.readUInt16LE(i + 4) !== 0) throw new Error("Multi-disk zip not supported");
|
|
||||||
let nFiles = chunk.readUint16LE(i + 10);
|
|
||||||
// Get the position of the central directory
|
|
||||||
const directorySize = chunk.readUint32LE(i + 12);
|
|
||||||
const directoryOffset = chunk.readUint32LE(i + 16);
|
|
||||||
if (directoryOffset === 0xffff_ffff) throw new Error("zip64 not supported");
|
|
||||||
if (directoryOffset > size) throw new Error(`Central directory offset ${directoryOffset} is outside file`);
|
|
||||||
i = directoryOffset - unread;
|
|
||||||
// If i < 0, it means that the central directory is not contained within `chunk`
|
|
||||||
if (i < 0) {
|
|
||||||
chunk = await readFileChunk(zipFile, directoryOffset, directoryOffset + directorySize);
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
// Now iterate the central directory records, yield an `ZipRecord` for every entry
|
|
||||||
while (nFiles-- > 0) {
|
|
||||||
// Check for marker bytes
|
|
||||||
if (chunk.readUInt32LE(i) !== 0x02014b50) throw new Error("No central directory record at position " + (unread + i));
|
|
||||||
const compressionMethod = ({ 8: "deflate" } as const)[chunk.readUint16LE(i + 10)];
|
|
||||||
const compressedFileSize = chunk.readUint32LE(i + 20);
|
|
||||||
const filenameLength = chunk.readUint16LE(i + 28);
|
|
||||||
const extraLength = chunk.readUint16LE(i + 30);
|
|
||||||
const commentLength = chunk.readUint16LE(i + 32);
|
|
||||||
// Start of the actual content byte stream is after the 'local' record header,
|
|
||||||
// which is 30 bytes long plus filename and extra field
|
|
||||||
const start = chunk.readUint32LE(i + 42) + 30 + filenameLength + extraLength;
|
|
||||||
const end = start + compressedFileSize;
|
|
||||||
const filename = chunk.slice(i + 46, i + 46 + filenameLength).toString("utf-8");
|
|
||||||
const createRecordReadStream = () => {
|
|
||||||
const input = createReadStream(zipFile, { start, end });
|
|
||||||
if (compressionMethod === "deflate") {
|
|
||||||
const inflate = createInflateRaw();
|
|
||||||
input.pipe(inflate);
|
|
||||||
return inflate;
|
|
||||||
}
|
|
||||||
return input;
|
|
||||||
};
|
|
||||||
if (end > start) yield { path: filename, createReadStream: createRecordReadStream, compressionMethod };
|
|
||||||
// advance pointer to next central directory entry
|
|
||||||
i += 46 + filenameLength + extraLength + commentLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function downloadAndUnzip({
|
|
||||||
url,
|
|
||||||
destDirPath,
|
|
||||||
pathOfDirToExtractInArchive,
|
|
||||||
cacheDirPath
|
|
||||||
}: {
|
|
||||||
isSilent: boolean;
|
|
||||||
url: string;
|
|
||||||
destDirPath: string;
|
|
||||||
pathOfDirToExtractInArchive?: string;
|
|
||||||
cacheDirPath: string;
|
|
||||||
}) {
|
|
||||||
const downloadHash = hash(JSON.stringify({ url, pathOfDirToExtractInArchive })).substring(0, 15);
|
|
||||||
const extractDirPath = pathJoin(cacheDirPath, `_${downloadHash}`);
|
|
||||||
|
|
||||||
const filename = pathBasename(url);
|
|
||||||
const zipFilepath = await download(url, cacheDirPath, filename);
|
|
||||||
const zipMtime = (await stat(zipFilepath)).mtimeMs;
|
|
||||||
const unzipMtime = (await maybeStat(extractDirPath))?.mtimeMs;
|
|
||||||
|
|
||||||
if (!unzipMtime || zipMtime > unzipMtime) await unzip(zipFilepath, extractDirPath, pathOfDirToExtractInArchive);
|
|
||||||
|
|
||||||
const srcDirPath = pathOfDirToExtractInArchive === undefined ? extractDirPath : pathJoin(extractDirPath, pathOfDirToExtractInArchive);
|
|
||||||
transformCodebase({ srcDirPath, destDirPath });
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
import * as fs from "fs";
|
|
||||||
import * as path from "path";
|
|
||||||
|
|
||||||
function getProjectRootRec(dirPath: string): string {
|
|
||||||
if (fs.existsSync(path.join(dirPath, "package.json"))) {
|
|
||||||
return dirPath;
|
|
||||||
}
|
|
||||||
return getProjectRootRec(path.join(dirPath, ".."));
|
|
||||||
}
|
|
||||||
|
|
||||||
let result: string | undefined = undefined;
|
|
||||||
|
|
||||||
export function getProjectRoot(): string {
|
|
||||||
if (result !== undefined) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (result = getProjectRootRec(__dirname));
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
import { getProjectRoot } from "./getProjectRoot";
|
|
||||||
import { join as pathJoin } from "path";
|
|
||||||
import { constants } from "fs";
|
|
||||||
import { chmod, stat } from "fs/promises";
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
const { bin } = await import(pathJoin(getProjectRoot(), "package.json"));
|
|
||||||
|
|
||||||
const promises = Object.values<string>(bin).map(async scriptPath => {
|
|
||||||
const fullPath = pathJoin(getProjectRoot(), scriptPath);
|
|
||||||
const oldMode = (await stat(fullPath)).mode;
|
|
||||||
const newMode = oldMode | constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH;
|
|
||||||
await chmod(fullPath, newMode);
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
|
||||||
})();
|
|
@ -1,7 +0,0 @@
|
|||||||
import { relative as pathRelative } from "path";
|
|
||||||
|
|
||||||
export function isInside(params: { dirPath: string; filePath: string }) {
|
|
||||||
const { dirPath, filePath } = params;
|
|
||||||
|
|
||||||
return !pathRelative(dirPath, filePath).startsWith("..");
|
|
||||||
}
|
|
@ -1,102 +0,0 @@
|
|||||||
import { Readable, Transform } from "stream";
|
|
||||||
import { dirname, relative, sep } from "path";
|
|
||||||
import { createWriteStream } from "fs";
|
|
||||||
|
|
||||||
import walk from "./walk";
|
|
||||||
import type { ZipSource } from "./zip";
|
|
||||||
import zip from "./zip";
|
|
||||||
import { mkdir } from "fs/promises";
|
|
||||||
|
|
||||||
/** Trim leading whitespace from every line */
|
|
||||||
const trimIndent = (s: string) => s.replace(/(\n)\s+/g, "$1");
|
|
||||||
|
|
||||||
type JarArgs = {
|
|
||||||
rootPath: string;
|
|
||||||
targetPath: string;
|
|
||||||
groupId: string;
|
|
||||||
artifactId: string;
|
|
||||||
version: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a jar archive, using the resources found at `rootPath` (a directory) and write the
|
|
||||||
* archive to `targetPath` (a file). Use `groupId`, `artifactId` and `version` to define
|
|
||||||
* the contents of the pom.properties file which is going to be added to the archive.
|
|
||||||
*/
|
|
||||||
export default async function jar({ groupId, artifactId, version, rootPath, targetPath }: JarArgs) {
|
|
||||||
const manifest: ZipSource = {
|
|
||||||
path: "META-INF/MANIFEST.MF",
|
|
||||||
data: Buffer.from(
|
|
||||||
trimIndent(
|
|
||||||
`Manifest-Version: 1.0
|
|
||||||
Archiver-Version: Plexus Archiver
|
|
||||||
Created-By: Keycloakify
|
|
||||||
Built-By: unknown
|
|
||||||
Build-Jdk: 19.0.0`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
const pomProps: ZipSource = {
|
|
||||||
path: `META-INF/maven/${groupId}/${artifactId}/pom.properties`,
|
|
||||||
data: Buffer.from(
|
|
||||||
trimIndent(
|
|
||||||
`# Generated by keycloakify
|
|
||||||
# ${new Date()}
|
|
||||||
artifactId=${artifactId}
|
|
||||||
groupId=${groupId}
|
|
||||||
version=${version}`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert every path entry to a ZipSource record, and when all records are
|
|
||||||
* processed, append records for MANIFEST.mf and pom.properties
|
|
||||||
*/
|
|
||||||
const pathToRecord = () =>
|
|
||||||
new Transform({
|
|
||||||
objectMode: true,
|
|
||||||
transform: function (fsPath, _, cb) {
|
|
||||||
const path = relative(rootPath, fsPath).split(sep).join("/");
|
|
||||||
this.push({ path, fsPath });
|
|
||||||
cb();
|
|
||||||
},
|
|
||||||
final: function () {
|
|
||||||
this.push(manifest);
|
|
||||||
this.push(pomProps);
|
|
||||||
this.push(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await mkdir(dirname(targetPath), { recursive: true });
|
|
||||||
|
|
||||||
// Create an async pipeline, wait until everything is fully processed
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
|
||||||
// walk all files in `rootPath` recursively
|
|
||||||
Readable.from(walk(rootPath))
|
|
||||||
// transform every path into a ZipSource object
|
|
||||||
.pipe(pathToRecord())
|
|
||||||
// let the zip lib convert all ZipSource objects into a byte stream
|
|
||||||
.pipe(zip())
|
|
||||||
// write that byte stream to targetPath
|
|
||||||
.pipe(createWriteStream(targetPath, { encoding: "binary" }))
|
|
||||||
.on("finish", () => resolve())
|
|
||||||
.on("error", e => reject(e));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Standalone usage, call e.g. `ts-node jar.ts dirWithSources some-jar.jar`
|
|
||||||
*/
|
|
||||||
if (require.main === module) {
|
|
||||||
const main = () =>
|
|
||||||
jar({
|
|
||||||
rootPath: process.argv[2],
|
|
||||||
targetPath: process.argv[3],
|
|
||||||
artifactId: process.env.ARTIFACT_ID ?? "artifact",
|
|
||||||
groupId: process.env.GROUP_ID ?? "group",
|
|
||||||
version: process.env.VERSION ?? "1.0.0"
|
|
||||||
});
|
|
||||||
main().catch(e => console.error(e));
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
import { capitalize } from "tsafe/capitalize";
|
|
||||||
|
|
||||||
export function kebabCaseToCamelCase(kebabCaseString: string): string {
|
|
||||||
const [first, ...rest] = kebabCaseString.split("-");
|
|
||||||
|
|
||||||
return [first, ...rest.map(capitalize)].join("");
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
type LoggerOpts = {
|
|
||||||
force?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Logger = {
|
|
||||||
log: (message: string, opts?: LoggerOpts) => void;
|
|
||||||
warn: (message: string) => void;
|
|
||||||
error: (message: string) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getLogger = ({ isSilent }: { isSilent?: boolean } = {}): Logger => {
|
|
||||||
return {
|
|
||||||
log: (message, { force } = {}) => {
|
|
||||||
if (isSilent && !force) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(message);
|
|
||||||
},
|
|
||||||
warn: message => {
|
|
||||||
console.warn(message);
|
|
||||||
},
|
|
||||||
error: message => {
|
|
||||||
console.error(message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,40 +0,0 @@
|
|||||||
import { listTagsFactory } from "./listTags";
|
|
||||||
import type { Octokit } from "@octokit/rest";
|
|
||||||
import { NpmModuleVersion } from "../NpmModuleVersion";
|
|
||||||
|
|
||||||
export function getLatestsSemVersionedTagFactory(params: { octokit: Octokit }) {
|
|
||||||
const { octokit } = params;
|
|
||||||
|
|
||||||
async function getLatestsSemVersionedTag(params: { owner: string; repo: string; doIgnoreBeta: boolean; count: number }): Promise<
|
|
||||||
{
|
|
||||||
tag: string;
|
|
||||||
version: NpmModuleVersion;
|
|
||||||
}[]
|
|
||||||
> {
|
|
||||||
const { owner, repo, doIgnoreBeta, count } = params;
|
|
||||||
|
|
||||||
const semVersionedTags: { tag: string; version: NpmModuleVersion }[] = [];
|
|
||||||
|
|
||||||
const { listTags } = listTagsFactory({ octokit });
|
|
||||||
|
|
||||||
for await (const tag of listTags({ owner, repo })) {
|
|
||||||
let version: NpmModuleVersion;
|
|
||||||
|
|
||||||
try {
|
|
||||||
version = NpmModuleVersion.parse(tag.replace(/^[vV]?/, ""));
|
|
||||||
} catch {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doIgnoreBeta && version.betaPreRelease !== undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
semVersionedTags.push({ tag, version });
|
|
||||||
}
|
|
||||||
|
|
||||||
return semVersionedTags.sort(({ version: vX }, { version: vY }) => NpmModuleVersion.compare(vY, vX)).slice(0, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { getLatestsSemVersionedTag };
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
import type { Octokit } from "@octokit/rest";
|
|
||||||
|
|
||||||
const per_page = 99;
|
|
||||||
|
|
||||||
export function listTagsFactory(params: { octokit: Octokit }) {
|
|
||||||
const { octokit } = params;
|
|
||||||
|
|
||||||
const octokit_repo_listTags = async (params: { owner: string; repo: string; per_page: number; page: number }) => {
|
|
||||||
return octokit.repos.listTags(params);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function* listTags(params: { owner: string; repo: string }): AsyncGenerator<string> {
|
|
||||||
const { owner, repo } = params;
|
|
||||||
|
|
||||||
let page = 1;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const resp = await octokit_repo_listTags({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
per_page,
|
|
||||||
"page": page++
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const branch of resp.data.map(({ name }) => name)) {
|
|
||||||
yield branch;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resp.data.length < 99) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the same "latest" tag as deno.land/x, not actually the latest though */
|
|
||||||
async function getLatestTag(params: { owner: string; repo: string }): Promise<string | undefined> {
|
|
||||||
const { owner, repo } = params;
|
|
||||||
|
|
||||||
const itRes = await listTags({ owner, repo }).next();
|
|
||||||
|
|
||||||
if (itRes.done) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return itRes.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { listTags, getLatestTag };
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
export function pathJoin(...path: string[]): string {
|
|
||||||
return path
|
|
||||||
.map((part, i) => (i === 0 ? part : part.replace(/^\/+/, "")))
|
|
||||||
.map((part, i) => (i === path.length - 1 ? part : part.replace(/\/+$/, "")))
|
|
||||||
.join("/");
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
import { PassThrough, Readable } from "stream";
|
|
||||||
|
|
||||||
export default function tee(input: Readable) {
|
|
||||||
const a = new PassThrough();
|
|
||||||
const b = new PassThrough();
|
|
||||||
|
|
||||||
let aFull = false;
|
|
||||||
let bFull = false;
|
|
||||||
|
|
||||||
a.on("drain", () => {
|
|
||||||
aFull = false;
|
|
||||||
if (!aFull && !bFull) input.resume();
|
|
||||||
});
|
|
||||||
b.on("drain", () => {
|
|
||||||
bFull = false;
|
|
||||||
if (!aFull && !bFull) input.resume();
|
|
||||||
});
|
|
||||||
|
|
||||||
input.on("error", e => {
|
|
||||||
a.emit("error", e);
|
|
||||||
b.emit("error", e);
|
|
||||||
});
|
|
||||||
|
|
||||||
input.on("data", chunk => {
|
|
||||||
aFull = !a.write(chunk);
|
|
||||||
bFull = !b.write(chunk);
|
|
||||||
|
|
||||||
if (aFull || bFull) input.pause();
|
|
||||||
});
|
|
||||||
|
|
||||||
input.on("end", () => {
|
|
||||||
a.end();
|
|
||||||
b.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
return [a, b] as const;
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
import * as fs from "fs";
|
|
||||||
import * as path from "path";
|
|
||||||
import { crawl } from "./crawl";
|
|
||||||
import { id } from "tsafe/id";
|
|
||||||
|
|
||||||
type TransformSourceCode = (params: { sourceCode: Buffer; filePath: string }) =>
|
|
||||||
| {
|
|
||||||
modifiedSourceCode: Buffer;
|
|
||||||
newFileName?: string;
|
|
||||||
}
|
|
||||||
| undefined;
|
|
||||||
|
|
||||||
/** Apply a transformation function to every file of directory */
|
|
||||||
export function transformCodebase(params: { srcDirPath: string; destDirPath: string; transformSourceCode?: TransformSourceCode }) {
|
|
||||||
const {
|
|
||||||
srcDirPath,
|
|
||||||
destDirPath,
|
|
||||||
transformSourceCode = id<TransformSourceCode>(({ sourceCode }) => ({
|
|
||||||
"modifiedSourceCode": sourceCode
|
|
||||||
}))
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
for (const file_relative_path of crawl(srcDirPath)) {
|
|
||||||
const filePath = path.join(srcDirPath, file_relative_path);
|
|
||||||
|
|
||||||
const transformSourceCodeResult = transformSourceCode({
|
|
||||||
"sourceCode": fs.readFileSync(filePath),
|
|
||||||
"filePath": path.join(srcDirPath, file_relative_path)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (transformSourceCodeResult === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.mkdirSync(path.dirname(path.join(destDirPath, file_relative_path)), {
|
|
||||||
"recursive": true
|
|
||||||
});
|
|
||||||
|
|
||||||
const { newFileName, modifiedSourceCode } = transformSourceCodeResult;
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(path.dirname(path.join(destDirPath, file_relative_path)), newFileName ?? path.basename(file_relative_path)),
|
|
||||||
modifiedSourceCode
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
import { readdir } from "fs/promises";
|
|
||||||
import { resolve } from "path";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronously and recursively walk a directory tree, yielding every file and directory
|
|
||||||
* found
|
|
||||||
*
|
|
||||||
* @param root the starting directory
|
|
||||||
* @returns AsyncGenerator
|
|
||||||
*/
|
|
||||||
export default async function* walk(root: string): AsyncGenerator<string, void, void> {
|
|
||||||
for (const entry of await readdir(root, { withFileTypes: true })) {
|
|
||||||
const absolutePath = resolve(root, entry.name);
|
|
||||||
if (entry.isDirectory()) {
|
|
||||||
yield absolutePath;
|
|
||||||
yield* walk(absolutePath);
|
|
||||||
} else yield absolutePath;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,246 +0,0 @@
|
|||||||
import { Transform, TransformOptions } from "stream";
|
|
||||||
import { createReadStream } from "fs";
|
|
||||||
import { stat } from "fs/promises";
|
|
||||||
import { Blob } from "buffer";
|
|
||||||
|
|
||||||
import { deflateBuffer, deflateStream } from "./deflate";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Zip source
|
|
||||||
* @property filename the name of the entry in the archie
|
|
||||||
* @property path of the source file, if the source is an actual file
|
|
||||||
* @property data the actual data buffer, if the source is constructed in-memory
|
|
||||||
*/
|
|
||||||
export type ZipSource = { path: string } & ({ fsPath: string } | { data: Buffer });
|
|
||||||
|
|
||||||
export type ZipRecord = {
|
|
||||||
path: string;
|
|
||||||
compression: "deflate" | undefined;
|
|
||||||
uncompressedSize: number;
|
|
||||||
compressedSize?: number;
|
|
||||||
crc32?: number;
|
|
||||||
offset?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns the actual byte size of an string
|
|
||||||
*/
|
|
||||||
function utf8size(s: string) {
|
|
||||||
return new Blob([s]).size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param record
|
|
||||||
* @returns a buffer representing a Zip local header
|
|
||||||
* @link https://en.wikipedia.org/wiki/ZIP_(file_format)#Local_file_header
|
|
||||||
*/
|
|
||||||
function localHeader(record: ZipRecord) {
|
|
||||||
const { path, compression, uncompressedSize } = record;
|
|
||||||
const filenameSize = utf8size(path);
|
|
||||||
const buf = Buffer.alloc(30 + filenameSize);
|
|
||||||
|
|
||||||
buf.writeUInt32LE(0x04_03_4b_50, 0); // local header signature
|
|
||||||
buf.writeUInt16LE(10, 4); // min version
|
|
||||||
// we write 0x08 because crc and compressed size are unknown at
|
|
||||||
buf.writeUInt16LE(0x08, 6); // general purpose bit flag
|
|
||||||
buf.writeUInt16LE(compression ? ({ "deflate": 8 } as const)[compression] : 0, 8);
|
|
||||||
buf.writeUInt16LE(0, 10); // modified time
|
|
||||||
buf.writeUInt16LE(0, 12); // modified date
|
|
||||||
buf.writeUInt32LE(0, 14); // crc unknown
|
|
||||||
buf.writeUInt32LE(0, 18); // compressed size unknown
|
|
||||||
buf.writeUInt32LE(uncompressedSize, 22);
|
|
||||||
buf.writeUInt16LE(filenameSize, 26);
|
|
||||||
buf.writeUInt16LE(0, 28); // extra field length
|
|
||||||
buf.write(path, 30, "utf-8");
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param record
|
|
||||||
* @returns a buffer representing a Zip central header
|
|
||||||
* @link https://en.wikipedia.org/wiki/ZIP_(file_format)#Central_directory_file_header
|
|
||||||
*/
|
|
||||||
function centralHeader(record: ZipRecord) {
|
|
||||||
const { path, compression, crc32, compressedSize, uncompressedSize, offset } = record;
|
|
||||||
const filenameSize = utf8size(path);
|
|
||||||
const buf = Buffer.alloc(46 + filenameSize);
|
|
||||||
const isFile = !path.endsWith("/");
|
|
||||||
|
|
||||||
if (typeof offset === "undefined") throw new Error("Illegal argument");
|
|
||||||
|
|
||||||
// we don't want to deal with possibly messed up file or directory
|
|
||||||
// permissions, so we ignore the original permissions
|
|
||||||
const externalAttr = isFile ? 0x81a40000 : 0x41ed0000;
|
|
||||||
|
|
||||||
buf.writeUInt32LE(0x0201_4b50, 0); // central header signature
|
|
||||||
buf.writeUInt16LE(10, 4); // version
|
|
||||||
buf.writeUInt16LE(10, 6); // min version
|
|
||||||
buf.writeUInt16LE(0, 8); // general purpose bit flag
|
|
||||||
buf.writeUInt16LE(compression ? ({ "deflate": 8 } as const)[compression] : 0, 10);
|
|
||||||
buf.writeUInt16LE(0, 12); // modified time
|
|
||||||
buf.writeUInt16LE(0, 14); // modified date
|
|
||||||
buf.writeUInt32LE(crc32 || 0, 16);
|
|
||||||
buf.writeUInt32LE(compressedSize || 0, 20);
|
|
||||||
buf.writeUInt32LE(uncompressedSize, 24);
|
|
||||||
buf.writeUInt16LE(filenameSize, 28);
|
|
||||||
buf.writeUInt16LE(0, 30); // extra field length
|
|
||||||
buf.writeUInt16LE(0, 32); // comment field length
|
|
||||||
buf.writeUInt16LE(0, 34); // disk number
|
|
||||||
buf.writeUInt16LE(0, 36); // internal
|
|
||||||
buf.writeUInt32LE(externalAttr, 38); // external
|
|
||||||
buf.writeUInt32LE(offset, 42); // offset where file starts
|
|
||||||
buf.write(path, 46, "utf-8");
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns a buffer representing an Zip End-Of-Central-Directory block
|
|
||||||
* @link https://en.wikipedia.org/wiki/ZIP_(file_format)#End_of_central_directory_record_(EOCD)
|
|
||||||
*/
|
|
||||||
function eocd({ offset, cdSize, nRecords }: { offset: number; cdSize: number; nRecords: number }) {
|
|
||||||
const buf = Buffer.alloc(22);
|
|
||||||
buf.writeUint32LE(0x06054b50, 0); // eocd signature
|
|
||||||
buf.writeUInt16LE(0, 4); // disc number
|
|
||||||
buf.writeUint16LE(0, 6); // disc where central directory starts
|
|
||||||
buf.writeUint16LE(nRecords, 8); // records on this disc
|
|
||||||
buf.writeUInt16LE(nRecords, 10); // records total
|
|
||||||
buf.writeUInt32LE(cdSize, 12); // byte size of cd
|
|
||||||
buf.writeUInt32LE(offset, 16); // cd offset
|
|
||||||
buf.writeUint16LE(0, 20); // comment length
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns a stream Transform, which reads a stream of ZipRecords and
|
|
||||||
* writes a bytestream
|
|
||||||
*/
|
|
||||||
export default function zip() {
|
|
||||||
/**
|
|
||||||
* This is called when the input stream of ZipSource items is finished.
|
|
||||||
* Will write central directory and end-of-central-direcotry blocks.
|
|
||||||
*/
|
|
||||||
const final = () => {
|
|
||||||
// write central directory
|
|
||||||
let cdSize = 0;
|
|
||||||
for (const record of records) {
|
|
||||||
const head = centralHeader(record);
|
|
||||||
zipTransform.push(head);
|
|
||||||
cdSize += head.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// write end-of-central-directory
|
|
||||||
zipTransform.push(eocd({ offset, cdSize, nRecords: records.length }));
|
|
||||||
// signal stream end
|
|
||||||
zipTransform.push(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a directory entry to the archive
|
|
||||||
* @param path
|
|
||||||
*/
|
|
||||||
const writeDir = async (path: string) => {
|
|
||||||
const record: ZipRecord = {
|
|
||||||
path: path + "/",
|
|
||||||
offset,
|
|
||||||
compression: undefined,
|
|
||||||
uncompressedSize: 0
|
|
||||||
};
|
|
||||||
const head = localHeader(record);
|
|
||||||
zipTransform.push(head);
|
|
||||||
records.push(record);
|
|
||||||
offset += head.length;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write a file entry to the archive
|
|
||||||
* @param archivePath path of the file in archive
|
|
||||||
* @param fsPath path to file on filesystem
|
|
||||||
* @param size of the actual, uncompressed, file
|
|
||||||
*/
|
|
||||||
const writeFile = async (archivePath: string, fsPath: string, size: number) => {
|
|
||||||
const record: ZipRecord = {
|
|
||||||
path: archivePath,
|
|
||||||
offset,
|
|
||||||
compression: "deflate",
|
|
||||||
uncompressedSize: size
|
|
||||||
};
|
|
||||||
const head = localHeader(record);
|
|
||||||
zipTransform.push(head);
|
|
||||||
|
|
||||||
const { crc32, compressedSize } = await deflateStream(createReadStream(fsPath), chunk => zipTransform.push(chunk));
|
|
||||||
|
|
||||||
record.crc32 = crc32;
|
|
||||||
record.compressedSize = compressedSize;
|
|
||||||
records.push(record);
|
|
||||||
offset += head.length + compressedSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write archive record based on filesystem file or directory
|
|
||||||
* @param archivePath path of item in archive
|
|
||||||
* @param fsPath path to item on filesystem
|
|
||||||
*/
|
|
||||||
const writeFromPath = async (archivePath: string, fsPath: string) => {
|
|
||||||
const fileStats = await stat(fsPath);
|
|
||||||
fileStats.isDirectory() ? await writeDir(archivePath) /**/ : await writeFile(archivePath, fsPath, fileStats.size) /**/;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write archive record based on data in a buffer
|
|
||||||
* @param path
|
|
||||||
* @param data
|
|
||||||
*/
|
|
||||||
const writeFromBuffer = async (path: string, data: Buffer) => {
|
|
||||||
const { deflated, crc32 } = await deflateBuffer(data);
|
|
||||||
const record: ZipRecord = {
|
|
||||||
path,
|
|
||||||
compression: "deflate",
|
|
||||||
crc32,
|
|
||||||
uncompressedSize: data.length,
|
|
||||||
compressedSize: deflated.length,
|
|
||||||
offset
|
|
||||||
};
|
|
||||||
const head = localHeader(record);
|
|
||||||
zipTransform.push(head);
|
|
||||||
zipTransform.push(deflated);
|
|
||||||
records.push(record);
|
|
||||||
offset += head.length + deflated.length;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write an archive record
|
|
||||||
* @param source
|
|
||||||
*/
|
|
||||||
const writeRecord = async (source: ZipSource) => {
|
|
||||||
if ("fsPath" in source) await writeFromPath(source.path, source.fsPath);
|
|
||||||
else if ("data" in source) await writeFromBuffer(source.path, source.data);
|
|
||||||
else throw new Error("Illegal argument " + typeof source + " " + JSON.stringify(source));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The actual stream transform function
|
|
||||||
* @param source
|
|
||||||
* @param _ encoding, ignored
|
|
||||||
* @param cb
|
|
||||||
*/
|
|
||||||
const transform: TransformOptions["transform"] = async (source: ZipSource, _, cb) => {
|
|
||||||
await writeRecord(source);
|
|
||||||
cb();
|
|
||||||
};
|
|
||||||
|
|
||||||
/** offset and records keep local state during processing */
|
|
||||||
let offset = 0;
|
|
||||||
const records: ZipRecord[] = [];
|
|
||||||
|
|
||||||
const zipTransform = new Transform({
|
|
||||||
readableObjectMode: false,
|
|
||||||
writableObjectMode: true,
|
|
||||||
transform,
|
|
||||||
final
|
|
||||||
});
|
|
||||||
|
|
||||||
return zipTransform;
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../../tsproject.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "CommonJS",
|
|
||||||
"target": "ES5",
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"lib": ["es2015", "DOM", "ES2019.Object"],
|
|
||||||
"outDir": "../../dist/bin",
|
|
||||||
"rootDir": "."
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,137 +0,0 @@
|
|||||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
|
||||||
//PLEASE DO NOT EDIT MANUALLY
|
|
||||||
|
|
||||||
/* spell-checker: disable */
|
|
||||||
const messages = {
|
|
||||||
"doSave": "Desa",
|
|
||||||
"doCancel": "Cancel·la",
|
|
||||||
"doLogOutAllSessions": "Desconnecta de totes les sessions",
|
|
||||||
"doRemove": "Elimina",
|
|
||||||
"doAdd": "Afegeix",
|
|
||||||
"doSignOut": "Desconnectar",
|
|
||||||
"editAccountHtmlTitle": "Edita compte",
|
|
||||||
"federatedIdentitiesHtmlTitle": "Identitats federades",
|
|
||||||
"accountLogHtmlTitle": "Registre del compte",
|
|
||||||
"changePasswordHtmlTitle": "Canvia contrasenya",
|
|
||||||
"sessionsHtmlTitle": "Sessions",
|
|
||||||
"accountManagementTitle": "Gestió de Compte Keycloak",
|
|
||||||
"authenticatorTitle": "Autenticador",
|
|
||||||
"applicationsHtmlTitle": "Aplicacions",
|
|
||||||
"authenticatorCode": "Codi d'un sol ús",
|
|
||||||
"email": "Email",
|
|
||||||
"firstName": "Nom",
|
|
||||||
"givenName": "Nom de pila",
|
|
||||||
"fullName": "Nom complet",
|
|
||||||
"lastName": "Cognoms",
|
|
||||||
"familyName": "Cognom",
|
|
||||||
"password": "Contrasenya",
|
|
||||||
"passwordConfirm": "Confirma la contrasenya",
|
|
||||||
"passwordNew": "Nova contrasenya",
|
|
||||||
"username": "Usuari",
|
|
||||||
"address": "Adreça",
|
|
||||||
"street": "Carrer",
|
|
||||||
"locality": "Ciutat o Municipi",
|
|
||||||
"region": "Estat, Província, o Regió",
|
|
||||||
"postal_code": "Postal code",
|
|
||||||
"country": "País",
|
|
||||||
"emailVerified": "Email verificat",
|
|
||||||
"gssDelegationCredential": "GSS Delegation Credential",
|
|
||||||
"role_admin": "Administrador",
|
|
||||||
"role_realm-admin": "Administrador del domini",
|
|
||||||
"role_create-realm": "Crear domini",
|
|
||||||
"role_view-realm": "Veure domini",
|
|
||||||
"role_view-users": "Veure usuaris",
|
|
||||||
"role_view-applications": "Veure aplicacions",
|
|
||||||
"role_view-clients": "Veure clients",
|
|
||||||
"role_view-events": "Veure events",
|
|
||||||
"role_view-identity-providers": "Veure proveïdors d'identitat",
|
|
||||||
"role_manage-realm": "Gestionar domini",
|
|
||||||
"role_manage-users": "Gestinar usuaris",
|
|
||||||
"role_manage-applications": "Gestionar aplicacions",
|
|
||||||
"role_manage-identity-providers": "Gestionar proveïdors d'identitat",
|
|
||||||
"role_manage-clients": "Gestionar clients",
|
|
||||||
"role_manage-events": "Gestionar events",
|
|
||||||
"role_view-profile": "Veure perfil",
|
|
||||||
"role_manage-account": "Gestionar compte",
|
|
||||||
"role_read-token": "Llegir token",
|
|
||||||
"role_offline-access": "Accés sense connexió",
|
|
||||||
"client_account": "Compte",
|
|
||||||
"client_security-admin-console": "Consola d'Administració de Seguretat",
|
|
||||||
"client_realm-management": "Gestió de domini",
|
|
||||||
"client_broker": "Broker",
|
|
||||||
"requiredFields": "Camps obligatoris",
|
|
||||||
"allFieldsRequired": "Tots els camps obligatoris",
|
|
||||||
"backToApplication": "« Torna a l'aplicació",
|
|
||||||
"backTo": "Torna a {0}",
|
|
||||||
"date": "Data",
|
|
||||||
"event": "Event",
|
|
||||||
"ip": "IP",
|
|
||||||
"client": "Client",
|
|
||||||
"clients": "Clients",
|
|
||||||
"details": "Detalls",
|
|
||||||
"started": "Iniciat",
|
|
||||||
"lastAccess": "Últim accés",
|
|
||||||
"expires": "Expira",
|
|
||||||
"applications": "Aplicacions",
|
|
||||||
"account": "Compte",
|
|
||||||
"federatedIdentity": "Identitat federada",
|
|
||||||
"authenticator": "Autenticador",
|
|
||||||
"sessions": "Sessions",
|
|
||||||
"log": "Registre",
|
|
||||||
"application": "Aplicació",
|
|
||||||
"availablePermissions": "Permisos disponibles",
|
|
||||||
"grantedPermissions": "Permisos concedits",
|
|
||||||
"grantedPersonalInfo": "Informació personal concedida",
|
|
||||||
"additionalGrants": "Permisos addicionals",
|
|
||||||
"action": "Acció",
|
|
||||||
"inResource": "a",
|
|
||||||
"fullAccess": "Accés total",
|
|
||||||
"offlineToken": "Codi d'autorització offline",
|
|
||||||
"revoke": "Revocar permís",
|
|
||||||
"configureAuthenticators": "Autenticadors configurats",
|
|
||||||
"mobile": "Mòbil",
|
|
||||||
"totpStep1":
|
|
||||||
'Instal·la <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> o Google Authenticator al teu telèfon mòbil. Les dues aplicacions estan disponibles a <a href="https://play.google.com">Google Play</a> i en l\'App Store d\'Apple.',
|
|
||||||
"totpStep2": "Obre l'aplicació i escaneja el codi o introdueix la clau.",
|
|
||||||
"totpStep3": "Introdueix el codi únic que et mostra l'aplicació d'autenticació i fes clic a Envia per finalitzar la configuració",
|
|
||||||
"missingUsernameMessage": "Si us plau indica el teu usuari.",
|
|
||||||
"missingFirstNameMessage": "Si us plau indica el nom.",
|
|
||||||
"invalidEmailMessage": "Email no vàlid",
|
|
||||||
"missingLastNameMessage": "Si us plau indica els teus cognoms.",
|
|
||||||
"missingEmailMessage": "Si us plau indica l'email.",
|
|
||||||
"missingPasswordMessage": "Si us plau indica la contrasenya.",
|
|
||||||
"notMatchPasswordMessage": "Les contrasenyes no coincideixen.",
|
|
||||||
"missingTotpMessage": "Si us plau indica el teu codi d'autenticació",
|
|
||||||
"invalidPasswordExistingMessage": "La contrasenya actual no és correcta.",
|
|
||||||
"invalidPasswordConfirmMessage": "La confirmació de contrasenya no coincideix.",
|
|
||||||
"invalidTotpMessage": "El código de autenticación no es válido.",
|
|
||||||
"usernameExistsMessage": "L'usuari ja existeix",
|
|
||||||
"emailExistsMessage": "L'email ja existeix",
|
|
||||||
"readOnlyUserMessage": "No pots actualitzar el teu usuari perquè el teu compte és de només lectura.",
|
|
||||||
"readOnlyPasswordMessage": "No pots actualitzar la contrasenya perquè el teu compte és de només lectura.",
|
|
||||||
"successTotpMessage": "Aplicació d'autenticació mòbil configurada.",
|
|
||||||
"successTotpRemovedMessage": "Aplicació d'autenticació mòbil eliminada.",
|
|
||||||
"successGrantRevokedMessage": "Permís revocat correctament",
|
|
||||||
"accountUpdatedMessage": "El teu compte s'ha actualitzat.",
|
|
||||||
"accountPasswordUpdatedMessage": "La contrasenya s'ha actualitzat.",
|
|
||||||
"missingIdentityProviderMessage": "Proveïdor d'identitat no indicat.",
|
|
||||||
"invalidFederatedIdentityActionMessage": "Acció no vàlida o no indicada.",
|
|
||||||
"identityProviderNotFoundMessage": "No s'ha trobat un proveïdor d'identitat.",
|
|
||||||
"federatedIdentityLinkNotActiveMessage": "Aquesta identitat ja no està activa",
|
|
||||||
"federatedIdentityRemovingLastProviderMessage": "No pots eliminar l'última identitat federada perquè no tens fixada una contrasenya.",
|
|
||||||
"identityProviderRedirectErrorMessage": "Error en la redirecció al proveïdor d'identitat",
|
|
||||||
"identityProviderRemovedMessage": "Proveïdor d'identitat esborrat correctament.",
|
|
||||||
"accountDisabledMessage": "El compte està desactivada, contacteu amb l'administrador.",
|
|
||||||
"accountTemporarilyDisabledMessage": "El compte està temporalment desactivat, contacta amb l'administrador o intenta-ho de nou més tard.",
|
|
||||||
"invalidPasswordMinLengthMessage": "Contrasenya incorrecta: longitud mínima {0}.",
|
|
||||||
"invalidPasswordMinLowerCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres minúscules.",
|
|
||||||
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
|
|
||||||
"invalidPasswordMinUpperCaseCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} lletres majúscules.",
|
|
||||||
"invalidPasswordMinSpecialCharsMessage": "Contrasenya incorrecta: ha de contenir almenys {0} caràcters especials.",
|
|
||||||
"invalidPasswordNotUsernameMessage": "Contrasenya incorrecta: no pot ser igual al nom d'usuari.",
|
|
||||||
"invalidPasswordRegexPatternMessage": "Contrasenya incorrecta: no compleix l'expressió regular.",
|
|
||||||
"invalidPasswordHistoryMessage": "Contrasenya incorrecta: no pot ser igual a cap de les últimes {0} contrasenyes."
|
|
||||||
};
|
|
||||||
|
|
||||||
export default messages;
|
|
||||||
/* spell-checker: enable */
|
|
@ -1,156 +0,0 @@
|
|||||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
|
||||||
//PLEASE DO NOT EDIT MANUALLY
|
|
||||||
|
|
||||||
/* spell-checker: disable */
|
|
||||||
const messages = {
|
|
||||||
"doSave": "Uložit",
|
|
||||||
"doCancel": "Zrušit",
|
|
||||||
"doLogOutAllSessions": "Odhlásit všechny relace",
|
|
||||||
"doRemove": "Odstranit",
|
|
||||||
"doAdd": "Přidat",
|
|
||||||
"doSignOut": "Odhlásit se",
|
|
||||||
"editAccountHtmlTitle": "Upravit účet",
|
|
||||||
"federatedIdentitiesHtmlTitle": "Propojené identity",
|
|
||||||
"accountLogHtmlTitle": "Log účtu",
|
|
||||||
"changePasswordHtmlTitle": "Změnit heslo",
|
|
||||||
"sessionsHtmlTitle": "Relace",
|
|
||||||
"accountManagementTitle": "Správa účtů Keycloak",
|
|
||||||
"authenticatorTitle": "Autentizátor",
|
|
||||||
"applicationsHtmlTitle": "Aplikace",
|
|
||||||
"authenticatorCode": "Jednorázový kód",
|
|
||||||
"email": "E-mail",
|
|
||||||
"firstName": "První křestní jméno",
|
|
||||||
"givenName": "Křestní jména",
|
|
||||||
"fullName": "Celé jméno",
|
|
||||||
"lastName": "Příjmení",
|
|
||||||
"familyName": "Rodinné jméno",
|
|
||||||
"password": "Heslo",
|
|
||||||
"passwordConfirm": "Nové heslo (znovu)",
|
|
||||||
"passwordNew": "Nové heslo",
|
|
||||||
"username": "Uživatelské jméno",
|
|
||||||
"address": "Adresa",
|
|
||||||
"street": "Ulice",
|
|
||||||
"locality": "Město nebo lokalita",
|
|
||||||
"region": "Kraj",
|
|
||||||
"postal_code": "PSČ",
|
|
||||||
"country": "Stát",
|
|
||||||
"emailVerified": "E-mail ověřen",
|
|
||||||
"gssDelegationCredential": "GSS delegované oprávnění",
|
|
||||||
"role_admin": "Správce",
|
|
||||||
"role_realm-admin": "Správce realmu",
|
|
||||||
"role_create-realm": "Vytvořit realm",
|
|
||||||
"role_view-realm": "Zobrazit realm",
|
|
||||||
"role_view-users": "Zobrazit uživatele",
|
|
||||||
"role_view-applications": "Zobrazit aplikace",
|
|
||||||
"role_view-clients": "Zobrazit klienty",
|
|
||||||
"role_view-events": "Zobrazit události",
|
|
||||||
"role_view-identity-providers": "Zobrazit poskytovatele identity",
|
|
||||||
"role_manage-realm": "Spravovat realm",
|
|
||||||
"role_manage-users": "Spravovat uživatele",
|
|
||||||
"role_manage-applications": "Spravovat aplikace",
|
|
||||||
"role_manage-identity-providers": "Spravovat poskytovatele identity",
|
|
||||||
"role_manage-clients": "Spravovat klienty",
|
|
||||||
"role_manage-events": "Spravovat události",
|
|
||||||
"role_view-profile": "Zobrazit profil",
|
|
||||||
"role_manage-account": "Spravovat účet",
|
|
||||||
"role_manage-account-links": "Spravovat odkazy na účet",
|
|
||||||
"role_read-token": "Číst token",
|
|
||||||
"role_offline-access": "Přístup offline",
|
|
||||||
"role_uma_authorization": "Získání oprávnění",
|
|
||||||
"client_account": "Účet",
|
|
||||||
"client_security-admin-console": "Administrátorská bezpečnostní konzole",
|
|
||||||
"client_admin-cli": "Administrátorské CLI",
|
|
||||||
"client_realm-management": "Správa realmů",
|
|
||||||
"client_broker": "Broker",
|
|
||||||
"requiredFields": "Požadovaná pole",
|
|
||||||
"allFieldsRequired": "Všechna pole vyžadovaná",
|
|
||||||
"backToApplication": "« Zpět na aplikaci",
|
|
||||||
"backTo": "Zpět na {0}",
|
|
||||||
"date": "Datum",
|
|
||||||
"event": "Událost",
|
|
||||||
"ip": "IP",
|
|
||||||
"client": "Klient",
|
|
||||||
"clients": "Klienti",
|
|
||||||
"details": "Podrobnosti",
|
|
||||||
"started": "Zahájeno",
|
|
||||||
"lastAccess": "Poslední přístup",
|
|
||||||
"expires": "Vyprší",
|
|
||||||
"applications": "Aplikace",
|
|
||||||
"account": "Účet",
|
|
||||||
"federatedIdentity": "Propojená identita",
|
|
||||||
"authenticator": "Autentizátor",
|
|
||||||
"sessions": "Relace",
|
|
||||||
"log": "Log",
|
|
||||||
"application": "Aplikace",
|
|
||||||
"availablePermissions": "Dostupná oprávnění",
|
|
||||||
"grantedPermissions": "Udělené oprávnění",
|
|
||||||
"grantedPersonalInfo": "Poskytnuté osobní informace",
|
|
||||||
"additionalGrants": "Dodatečné oprávnění",
|
|
||||||
"action": "Akce",
|
|
||||||
"inResource": "v",
|
|
||||||
"fullAccess": "Úplný přístup",
|
|
||||||
"offlineToken": "Offline Token",
|
|
||||||
"revoke": "Zrušit oprávnění",
|
|
||||||
"configureAuthenticators": "Konfigurované autentizátory",
|
|
||||||
"mobile": "Mobilní",
|
|
||||||
"totpStep1": "Nainstalujte jednu z následujících aplikací",
|
|
||||||
"totpStep2": "Otevřete aplikaci a naskenujte čárový kód",
|
|
||||||
"totpStep3": "Zadejte jednorázový kód poskytnutý aplikací a klepnutím na tlačítko Uložit dokončete nastavení.",
|
|
||||||
"totpManualStep2": "Otevřete aplikaci a zadejte klíč",
|
|
||||||
"totpManualStep3": "Použijte následující hodnoty konfigurace, pokud aplikace umožňuje jejich nastavení",
|
|
||||||
"totpUnableToScan": "Nelze skenovat?",
|
|
||||||
"totpScanBarcode": "Skenovat čárový kód?",
|
|
||||||
"totp.totp": "Založeno na čase",
|
|
||||||
"totp.hotp": "Založeno na čítači",
|
|
||||||
"totpType": "Typ",
|
|
||||||
"totpAlgorithm": "Algoritmus",
|
|
||||||
"totpDigits": "Číslice",
|
|
||||||
"totpInterval": "Interval",
|
|
||||||
"totpCounter": "Čítač",
|
|
||||||
"missingUsernameMessage": "Zadejte uživatelské jméno.",
|
|
||||||
"missingFirstNameMessage": "Zadejte prosím křestní jméno.",
|
|
||||||
"invalidEmailMessage": "Neplatná e-mailová adresa.",
|
|
||||||
"missingLastNameMessage": "Zadejte prosím příjmení.",
|
|
||||||
"missingEmailMessage": "Zadejte prosím e-mail.",
|
|
||||||
"missingPasswordMessage": "Zadejte prosím heslo.",
|
|
||||||
"notMatchPasswordMessage": "Hesla se neshodují.",
|
|
||||||
"missingTotpMessage": "Zadejte prosím kód autentizátoru.",
|
|
||||||
"invalidPasswordExistingMessage": "Neplatné stávající heslo.",
|
|
||||||
"invalidPasswordConfirmMessage": "Nová hesla se neshodují.",
|
|
||||||
"invalidTotpMessage": "Neplatný kód autentizátoru.",
|
|
||||||
"usernameExistsMessage": "Uživatelské jméno již existuje.",
|
|
||||||
"emailExistsMessage": "E-mail již existuje.",
|
|
||||||
"readOnlyUserMessage": "Nemůžete svůj účet aktualizovat, protože je pouze pro čtení.",
|
|
||||||
"readOnlyUsernameMessage": "Nemůžete aktualizovat své uživatelské jméno, protože je pouze pro čtení.",
|
|
||||||
"readOnlyPasswordMessage": "Nemůžete aktualizovat své heslo, protože váš účet je jen pro čtení.",
|
|
||||||
"successTotpMessage": "Ověření pomocí OTP úspěšně konfigurováno.",
|
|
||||||
"successTotpRemovedMessage": "Ověření pomocí OTP úspěšně odstraněno.",
|
|
||||||
"successGrantRevokedMessage": "Oprávnění bylo úspěšně zrušeno.",
|
|
||||||
"accountUpdatedMessage": "Váš účet byl aktualizován.",
|
|
||||||
"accountPasswordUpdatedMessage": "Vaše heslo bylo aktualizováno.",
|
|
||||||
"missingIdentityProviderMessage": "Chybějící poskytovatel identity.",
|
|
||||||
"invalidFederatedIdentityActionMessage": "Neplatná nebo chybějící akce.",
|
|
||||||
"identityProviderNotFoundMessage": "Poskytovatel identity nenalezen.",
|
|
||||||
"federatedIdentityLinkNotActiveMessage": "Tato identita již není aktivní.",
|
|
||||||
"federatedIdentityRemovingLastProviderMessage": "Nemůžete odstranit poslední propojenou identitu, protože nemáte heslo.",
|
|
||||||
"identityProviderRedirectErrorMessage": "Nepodařilo se přesměrovat na poskytovatele identity.",
|
|
||||||
"identityProviderRemovedMessage": "Poskytovatel identity byl úspěšně odstraněn.",
|
|
||||||
"identityProviderAlreadyLinkedMessage": "Propojená identita vrácená uživatelem {0} je již propojena s jiným uživatelem.",
|
|
||||||
"staleCodeAccountMessage": "Platnost vypršela. Zkuste to ještě jednou.",
|
|
||||||
"consentDenied": "Souhlas byl zamítnut.",
|
|
||||||
"accountDisabledMessage": "Účet je zakázán, kontaktujte správce.",
|
|
||||||
"accountTemporarilyDisabledMessage": "Účet je dočasně zakázán, kontaktujte správce nebo zkuste to později.",
|
|
||||||
"invalidPasswordMinLengthMessage": "Neplatné heslo: musí obsahovat minimálně {0} malých znaků.",
|
|
||||||
"invalidPasswordMinLowerCaseCharsMessage": "Neplatné heslo: musí obsahovat minimálně {0} malé znaky.",
|
|
||||||
"invalidPasswordMinDigitsMessage": "Neplatné heslo: musí obsahovat nejméně {0} číslic.",
|
|
||||||
"invalidPasswordMinUpperCaseCharsMessage": "Neplatné heslo: musí obsahovat nejméně {0} velkých písmenen.",
|
|
||||||
"invalidPasswordMinSpecialCharsMessage": "Neplatné heslo: musí obsahovat nejméně {0} speciálních znaků.",
|
|
||||||
"invalidPasswordNotUsernameMessage": "Neplatné heslo: nesmí být totožné s uživatelským jménem.",
|
|
||||||
"invalidPasswordRegexPatternMessage": "Neplatné heslo: neshoduje se zadaným regulárním výrazem.",
|
|
||||||
"invalidPasswordHistoryMessage": "Neplatné heslo: Nesmí se opakovat žádné z posledních {0} hesel.",
|
|
||||||
"invalidPasswordBlacklistedMessage": "Neplatné heslo: heslo je na černé listině.",
|
|
||||||
"invalidPasswordGenericMessage": "Neplatné heslo: nové heslo neodpovídá pravidlům hesla."
|
|
||||||
};
|
|
||||||
|
|
||||||
export default messages;
|
|
||||||
/* spell-checker: enable */
|
|
@ -1,156 +0,0 @@
|
|||||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
|
||||||
//PLEASE DO NOT EDIT MANUALLY
|
|
||||||
|
|
||||||
/* spell-checker: disable */
|
|
||||||
const messages = {
|
|
||||||
"doSave": "Speichern",
|
|
||||||
"doCancel": "Abbrechen",
|
|
||||||
"doLogOutAllSessions": "Alle Sitzungen abmelden",
|
|
||||||
"doRemove": "Entfernen",
|
|
||||||
"doAdd": "Hinzufügen",
|
|
||||||
"doSignOut": "Abmelden",
|
|
||||||
"editAccountHtmlTitle": "Benutzerkonto bearbeiten",
|
|
||||||
"federatedIdentitiesHtmlTitle": "Föderierte Identitäten",
|
|
||||||
"accountLogHtmlTitle": "Benutzerkonto Log",
|
|
||||||
"changePasswordHtmlTitle": "Passwort Ändern",
|
|
||||||
"sessionsHtmlTitle": "Sitzungen",
|
|
||||||
"accountManagementTitle": "Keycloak Benutzerkontoverwaltung",
|
|
||||||
"authenticatorTitle": "Mehrfachauthentifizierung",
|
|
||||||
"applicationsHtmlTitle": "Applikationen",
|
|
||||||
"authenticatorCode": "One-time Code",
|
|
||||||
"email": "E-Mail",
|
|
||||||
"firstName": "Vorname",
|
|
||||||
"givenName": "Vorname",
|
|
||||||
"fullName": "Voller Name",
|
|
||||||
"lastName": "Nachname",
|
|
||||||
"familyName": "Nachname",
|
|
||||||
"password": "Passwort",
|
|
||||||
"passwordConfirm": "Passwort bestätigen",
|
|
||||||
"passwordNew": "Neues Passwort",
|
|
||||||
"username": "Benutzername",
|
|
||||||
"address": "Adresse",
|
|
||||||
"street": "Straße",
|
|
||||||
"region": "Staat, Provinz, Region",
|
|
||||||
"postal_code": "PLZ",
|
|
||||||
"locality": "Stadt oder Ortschaft",
|
|
||||||
"country": "Land",
|
|
||||||
"emailVerified": "E-Mail verifiziert",
|
|
||||||
"gssDelegationCredential": "GSS delegierte Berechtigung",
|
|
||||||
"role_admin": "Admin",
|
|
||||||
"role_realm-admin": "Realm Admin",
|
|
||||||
"role_create-realm": "Realm erstellen",
|
|
||||||
"role_view-realm": "Realm ansehen",
|
|
||||||
"role_view-users": "Benutzer ansehen",
|
|
||||||
"role_view-applications": "Applikationen ansehen",
|
|
||||||
"role_view-clients": "Clients ansehen",
|
|
||||||
"role_view-events": "Events ansehen",
|
|
||||||
"role_view-identity-providers": "Identity Provider ansehen",
|
|
||||||
"role_manage-realm": "Realm verwalten",
|
|
||||||
"role_manage-users": "Benutzer verwalten",
|
|
||||||
"role_manage-applications": "Applikationen verwalten",
|
|
||||||
"role_manage-identity-providers": "Identity Provider verwalten",
|
|
||||||
"role_manage-clients": "Clients verwalten",
|
|
||||||
"role_manage-events": "Events verwalten",
|
|
||||||
"role_view-profile": "Profile ansehen",
|
|
||||||
"role_manage-account": "Profile verwalten",
|
|
||||||
"role_manage-account-links": "Profil-Links verwalten",
|
|
||||||
"role_read-token": "Token lesen",
|
|
||||||
"role_offline-access": "Offline-Zugriff",
|
|
||||||
"role_uma_authorization": "Berechtigungen einholen",
|
|
||||||
"client_account": "Clientkonto",
|
|
||||||
"client_security-admin-console": "Security Adminkonsole",
|
|
||||||
"client_realm-management": "Realm-Management",
|
|
||||||
"client_broker": "Broker",
|
|
||||||
"requiredFields": "Erforderliche Felder",
|
|
||||||
"allFieldsRequired": "Alle Felder sind erforderlich",
|
|
||||||
"backToApplication": "« Zurück zur Applikation",
|
|
||||||
"backTo": "Zurück zu {0}",
|
|
||||||
"date": "Datum",
|
|
||||||
"event": "Ereignis",
|
|
||||||
"ip": "IP",
|
|
||||||
"client": "Client",
|
|
||||||
"clients": "Clients",
|
|
||||||
"details": "Details",
|
|
||||||
"started": "Startdatum",
|
|
||||||
"lastAccess": "Letzter Zugriff",
|
|
||||||
"expires": "Ablaufdatum",
|
|
||||||
"applications": "Applikationen",
|
|
||||||
"account": "Benutzerkonto",
|
|
||||||
"federatedIdentity": "Föderierte Identität",
|
|
||||||
"authenticator": "Mehrfachauthentifizierung",
|
|
||||||
"sessions": "Sitzungen",
|
|
||||||
"log": "Log",
|
|
||||||
"application": "Applikation",
|
|
||||||
"availablePermissions": "verfügbare Berechtigungen",
|
|
||||||
"grantedPermissions": "gewährte Berechtigungen",
|
|
||||||
"grantedPersonalInfo": "gewährte persönliche Informationen",
|
|
||||||
"additionalGrants": "zusätzliche Berechtigungen",
|
|
||||||
"action": "Aktion",
|
|
||||||
"inResource": "in",
|
|
||||||
"fullAccess": "Vollzugriff",
|
|
||||||
"offlineToken": "Offline-Token",
|
|
||||||
"revoke": "Berechtigung widerrufen",
|
|
||||||
"configureAuthenticators": "Mehrfachauthentifizierung konfigurieren",
|
|
||||||
"mobile": "Mobil",
|
|
||||||
"totpStep1": "Installieren Sie eine der folgenden Applikationen auf Ihrem Smartphone:",
|
|
||||||
"totpStep2": "Öffnen Sie die Applikation und scannen Sie den Barcode.",
|
|
||||||
"totpStep3": "Geben Sie den von der Applikation generierten One-time Code ein und klicken Sie auf Speichern.",
|
|
||||||
"totpManualStep2": "Öffnen Sie die Applikation und geben Sie den folgenden Schlüssel ein.",
|
|
||||||
"totpManualStep3": "Verwenden Sie die folgenden Konfigurationswerte, falls Sie diese für die Applikation anpassen können:",
|
|
||||||
"totpUnableToScan": "Sie können den Barcode nicht scannen?",
|
|
||||||
"totpScanBarcode": "Barcode scannen?",
|
|
||||||
"totp.totp": "zeitbasiert (time-based)",
|
|
||||||
"totp.hotp": "zählerbasiert (counter-based)",
|
|
||||||
"totpType": "Typ",
|
|
||||||
"totpAlgorithm": "Algorithmus",
|
|
||||||
"totpDigits": "Ziffern",
|
|
||||||
"totpInterval": "Intervall",
|
|
||||||
"totpCounter": "Zähler",
|
|
||||||
"missingUsernameMessage": "Bitte geben Sie einen Benutzernamen ein.",
|
|
||||||
"missingFirstNameMessage": "Bitte geben Sie einen Vornamen ein.",
|
|
||||||
"invalidEmailMessage": "Ungültige E-Mail Adresse.",
|
|
||||||
"missingLastNameMessage": "Bitte geben Sie einen Nachnamen ein.",
|
|
||||||
"missingEmailMessage": "Bitte geben Sie eine E-Mail Adresse ein.",
|
|
||||||
"missingPasswordMessage": "Bitte geben Sie ein Passwort ein.",
|
|
||||||
"notMatchPasswordMessage": "Die Passwörter sind nicht identisch.",
|
|
||||||
"missingTotpMessage": "Bitte geben Sie den One-time Code ein.",
|
|
||||||
"invalidPasswordExistingMessage": "Das aktuelle Passwort ist ungültig.",
|
|
||||||
"invalidPasswordConfirmMessage": "Die Passwortbestätigung ist nicht identisch.",
|
|
||||||
"invalidTotpMessage": "Ungültiger One-time Code.",
|
|
||||||
"usernameExistsMessage": "Der Benutzername existiert bereits.",
|
|
||||||
"emailExistsMessage": "Die E-Mail-Adresse existiert bereits.",
|
|
||||||
"readOnlyUserMessage": "Sie können Ihr Benutzerkonto nicht ändern, da es schreibgeschützt ist.",
|
|
||||||
"readOnlyUsernameMessage": "Sie können Ihren Benutzernamen nicht ändern, da er schreibgeschützt ist.",
|
|
||||||
"readOnlyPasswordMessage": "Sie können Ihr Passwort nicht ändern, da es schreibgeschützt ist.",
|
|
||||||
"successTotpMessage": "Mehrfachauthentifizierung erfolgreich konfiguriert.",
|
|
||||||
"successTotpRemovedMessage": "Mehrfachauthentifizierung erfolgreich entfernt.",
|
|
||||||
"successGrantRevokedMessage": "Berechtigung erfolgreich widerrufen.",
|
|
||||||
"accountUpdatedMessage": "Ihr Benutzerkonto wurde aktualisiert.",
|
|
||||||
"accountPasswordUpdatedMessage": "Ihr Passwort wurde aktualisiert.",
|
|
||||||
"missingIdentityProviderMessage": "Identity Provider nicht angegeben.",
|
|
||||||
"invalidFederatedIdentityActionMessage": "Ungültige oder fehlende Aktion.",
|
|
||||||
"identityProviderNotFoundMessage": "Angegebener Identity Provider nicht gefunden.",
|
|
||||||
"federatedIdentityLinkNotActiveMessage": "Diese Identität ist nicht mehr aktiv.",
|
|
||||||
"federatedIdentityRemovingLastProviderMessage": "Sie können den letzten Eintrag nicht entfernen, da Sie kein Passwort haben.",
|
|
||||||
"identityProviderRedirectErrorMessage": "Fehler bei der Weiterleitung zum Identity Provider.",
|
|
||||||
"identityProviderRemovedMessage": "Identity Provider erfolgreich entfernt.",
|
|
||||||
"identityProviderAlreadyLinkedMessage": "Die föderierte Identität von {0} ist bereits einem anderen Benutzer zugewiesen.",
|
|
||||||
"staleCodeAccountMessage": "Diese Seite ist nicht mehr gültig, bitte versuchen Sie es noch einmal.",
|
|
||||||
"consentDenied": "Einverständnis verweigert.",
|
|
||||||
"accountDisabledMessage": "Ihr Benutzerkonto ist gesperrt, bitte kontaktieren Sie den Admin.",
|
|
||||||
"accountTemporarilyDisabledMessage":
|
|
||||||
"Ihr Benutzerkonto ist temporär gesperrt, bitte kontaktieren Sie den Admin oder versuchen Sie es später noch einmal.",
|
|
||||||
"invalidPasswordMinLengthMessage": "Ungültiges Passwort: Es muss mindestens {0} Zeichen lang sein.",
|
|
||||||
"invalidPasswordMinLowerCaseCharsMessage": "Ungültiges Passwort: Es muss mindestens {0} Kleinbuchstaben beinhalten.",
|
|
||||||
"invalidPasswordMinDigitsMessage": "Ungültiges Passwort: Es muss mindestens {0} Zahl(en) beinhalten.",
|
|
||||||
"invalidPasswordMinUpperCaseCharsMessage": "Ungültiges Passwort: Es muss mindestens {0} Großbuchstaben beinhalten.",
|
|
||||||
"invalidPasswordMinSpecialCharsMessage": "Ungültiges Passwort: Es muss mindestens {0} Sonderzeichen beinhalten.",
|
|
||||||
"invalidPasswordNotUsernameMessage": "Ungültiges Passwort: Es darf nicht gleich sein wie der Benutzername.",
|
|
||||||
"invalidPasswordRegexPatternMessage": "Ungültiges Passwort: Es entspricht nicht dem Regex-Muster.",
|
|
||||||
"invalidPasswordHistoryMessage": "Ungültiges Passwort: Es darf nicht einem der letzten {0} Passwörter entsprechen.",
|
|
||||||
"invalidPasswordBlacklistedMessage": "Ungültiges Passwort: Das Passwort steht auf der Blocklist (schwarzen Liste).",
|
|
||||||
"invalidPasswordGenericMessge": "Ungültiges Passwort: Das neue Passwort verletzt die Passwort-Richtlinien."
|
|
||||||
};
|
|
||||||
|
|
||||||
export default messages;
|
|
||||||
/* spell-checker: enable */
|
|
@ -1,325 +0,0 @@
|
|||||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
|
||||||
//PLEASE DO NOT EDIT MANUALLY
|
|
||||||
|
|
||||||
/* spell-checker: disable */
|
|
||||||
const messages = {
|
|
||||||
"doSave": "Save",
|
|
||||||
"doCancel": "Cancel",
|
|
||||||
"doLogOutAllSessions": "Log out all sessions",
|
|
||||||
"doRemove": "Remove",
|
|
||||||
"doAdd": "Add",
|
|
||||||
"doSignOut": "Sign Out",
|
|
||||||
"doLogIn": "Log In",
|
|
||||||
"doLink": "Link",
|
|
||||||
"editAccountHtmlTitle": "Edit Account",
|
|
||||||
"personalInfoHtmlTitle": "Personal Info",
|
|
||||||
"federatedIdentitiesHtmlTitle": "Federated Identities",
|
|
||||||
"accountLogHtmlTitle": "Account Log",
|
|
||||||
"changePasswordHtmlTitle": "Change Password",
|
|
||||||
"deviceActivityHtmlTitle": "Device Activity",
|
|
||||||
"sessionsHtmlTitle": "Sessions",
|
|
||||||
"accountManagementTitle": "Keycloak Account Management",
|
|
||||||
"authenticatorTitle": "Authenticator",
|
|
||||||
"applicationsHtmlTitle": "Applications",
|
|
||||||
"linkedAccountsHtmlTitle": "Linked Accounts",
|
|
||||||
"accountManagementWelcomeMessage": "Welcome to Keycloak Account Management",
|
|
||||||
"personalInfoIntroMessage": "Manage your basic information",
|
|
||||||
"accountSecurityTitle": "Account Security",
|
|
||||||
"accountSecurityIntroMessage": "Control your password and account access",
|
|
||||||
"applicationsIntroMessage": "Track and manage your app permission to access your account",
|
|
||||||
"resourceIntroMessage": "Share your resources among team members",
|
|
||||||
"passwordLastUpdateMessage": "Your password was updated at",
|
|
||||||
"updatePasswordTitle": "Update Password",
|
|
||||||
"updatePasswordMessageTitle": "Make sure you choose a strong password",
|
|
||||||
"updatePasswordMessage":
|
|
||||||
"A strong password contains a mix of numbers, letters, and symbols. It is hard to guess, does not resemble a real word, and is only used for this account.",
|
|
||||||
"personalSubTitle": "Your Personal Info",
|
|
||||||
"personalSubMessage": "Manage this basic information: your first name, last name and email",
|
|
||||||
"authenticatorCode": "One-time code",
|
|
||||||
"email": "Email",
|
|
||||||
"firstName": "First name",
|
|
||||||
"givenName": "Given name",
|
|
||||||
"fullName": "Full name",
|
|
||||||
"lastName": "Last name",
|
|
||||||
"familyName": "Family name",
|
|
||||||
"password": "Password",
|
|
||||||
"currentPassword": "Current Password",
|
|
||||||
"passwordConfirm": "Confirmation",
|
|
||||||
"passwordNew": "New Password",
|
|
||||||
"username": "Username",
|
|
||||||
"address": "Address",
|
|
||||||
"street": "Street",
|
|
||||||
"locality": "City or Locality",
|
|
||||||
"region": "State, Province, or Region",
|
|
||||||
"postal_code": "Zip or Postal code",
|
|
||||||
"country": "Country",
|
|
||||||
"emailVerified": "Email verified",
|
|
||||||
"gssDelegationCredential": "GSS Delegation Credential",
|
|
||||||
"profileScopeConsentText": "User profile",
|
|
||||||
"emailScopeConsentText": "Email address",
|
|
||||||
"addressScopeConsentText": "Address",
|
|
||||||
"phoneScopeConsentText": "Phone number",
|
|
||||||
"offlineAccessScopeConsentText": "Offline Access",
|
|
||||||
"samlRoleListScopeConsentText": "My Roles",
|
|
||||||
"rolesScopeConsentText": "User roles",
|
|
||||||
"role_admin": "Admin",
|
|
||||||
"role_realm-admin": "Realm Admin",
|
|
||||||
"role_create-realm": "Create realm",
|
|
||||||
"role_view-realm": "View realm",
|
|
||||||
"role_view-users": "View users",
|
|
||||||
"role_view-applications": "View applications",
|
|
||||||
"role_view-clients": "View clients",
|
|
||||||
"role_view-events": "View events",
|
|
||||||
"role_view-identity-providers": "View identity providers",
|
|
||||||
"role_view-consent": "View consents",
|
|
||||||
"role_manage-realm": "Manage realm",
|
|
||||||
"role_manage-users": "Manage users",
|
|
||||||
"role_manage-applications": "Manage applications",
|
|
||||||
"role_manage-identity-providers": "Manage identity providers",
|
|
||||||
"role_manage-clients": "Manage clients",
|
|
||||||
"role_manage-events": "Manage events",
|
|
||||||
"role_view-profile": "View profile",
|
|
||||||
"role_manage-account": "Manage account",
|
|
||||||
"role_manage-account-links": "Manage account links",
|
|
||||||
"role_manage-consent": "Manage consents",
|
|
||||||
"role_read-token": "Read token",
|
|
||||||
"role_offline-access": "Offline access",
|
|
||||||
"role_uma_authorization": "Obtain permissions",
|
|
||||||
"client_account": "Account",
|
|
||||||
"client_account-console": "Account Console",
|
|
||||||
"client_security-admin-console": "Security Admin Console",
|
|
||||||
"client_admin-cli": "Admin CLI",
|
|
||||||
"client_realm-management": "Realm Management",
|
|
||||||
"client_broker": "Broker",
|
|
||||||
"requiredFields": "Required fields",
|
|
||||||
"allFieldsRequired": "All fields required",
|
|
||||||
"backToApplication": "« Back to application",
|
|
||||||
"backTo": "Back to {0}",
|
|
||||||
"date": "Date",
|
|
||||||
"event": "Event",
|
|
||||||
"ip": "IP",
|
|
||||||
"client": "Client",
|
|
||||||
"clients": "Clients",
|
|
||||||
"details": "Details",
|
|
||||||
"started": "Started",
|
|
||||||
"lastAccess": "Last Access",
|
|
||||||
"expires": "Expires",
|
|
||||||
"applications": "Applications",
|
|
||||||
"account": "Account",
|
|
||||||
"federatedIdentity": "Federated Identity",
|
|
||||||
"authenticator": "Authenticator",
|
|
||||||
"device-activity": "Device Activity",
|
|
||||||
"sessions": "Sessions",
|
|
||||||
"log": "Log",
|
|
||||||
"application": "Application",
|
|
||||||
"availableRoles": "Available Roles",
|
|
||||||
"grantedPermissions": "Granted Permissions",
|
|
||||||
"grantedPersonalInfo": "Granted Personal Info",
|
|
||||||
"additionalGrants": "Additional Grants",
|
|
||||||
"action": "Action",
|
|
||||||
"inResource": "in",
|
|
||||||
"fullAccess": "Full Access",
|
|
||||||
"offlineToken": "Offline Token",
|
|
||||||
"revoke": "Revoke Grant",
|
|
||||||
"configureAuthenticators": "Configured Authenticators",
|
|
||||||
"mobile": "Mobile",
|
|
||||||
"totpStep1": "Install one of the following applications on your mobile:",
|
|
||||||
"totpStep2": "Open the application and scan the barcode:",
|
|
||||||
"totpStep3": "Enter the one-time code provided by the application and click Save to finish the setup.",
|
|
||||||
"totpStep3DeviceName": "Provide a Device Name to help you manage your OTP devices.",
|
|
||||||
"totpManualStep2": "Open the application and enter the key:",
|
|
||||||
"totpManualStep3": "Use the following configuration values if the application allows setting them:",
|
|
||||||
"totpUnableToScan": "Unable to scan?",
|
|
||||||
"totpScanBarcode": "Scan barcode?",
|
|
||||||
"totp.totp": "Time-based",
|
|
||||||
"totp.hotp": "Counter-based",
|
|
||||||
"totpType": "Type",
|
|
||||||
"totpAlgorithm": "Algorithm",
|
|
||||||
"totpDigits": "Digits",
|
|
||||||
"totpInterval": "Interval",
|
|
||||||
"totpCounter": "Counter",
|
|
||||||
"totpDeviceName": "Device Name",
|
|
||||||
"missingUsernameMessage": "Please specify username.",
|
|
||||||
"missingFirstNameMessage": "Please specify first name.",
|
|
||||||
"invalidEmailMessage": "Invalid email address.",
|
|
||||||
"missingLastNameMessage": "Please specify last name.",
|
|
||||||
"missingEmailMessage": "Please specify email.",
|
|
||||||
"missingPasswordMessage": "Please specify password.",
|
|
||||||
"notMatchPasswordMessage": "Passwords don't match.",
|
|
||||||
"invalidUserMessage": "Invalid user",
|
|
||||||
"missingTotpMessage": "Please specify authenticator code.",
|
|
||||||
"missingTotpDeviceNameMessage": "Please specify device name.",
|
|
||||||
"invalidPasswordExistingMessage": "Invalid existing password.",
|
|
||||||
"invalidPasswordConfirmMessage": "Password confirmation doesn't match.",
|
|
||||||
"invalidTotpMessage": "Invalid authenticator code.",
|
|
||||||
"usernameExistsMessage": "Username already exists.",
|
|
||||||
"emailExistsMessage": "Email already exists.",
|
|
||||||
"readOnlyUserMessage": "You can't update your account as it is read-only.",
|
|
||||||
"readOnlyUsernameMessage": "You can't update your username as it is read-only.",
|
|
||||||
"readOnlyPasswordMessage": "You can't update your password as your account is read-only.",
|
|
||||||
"successTotpMessage": "Mobile authenticator configured.",
|
|
||||||
"successTotpRemovedMessage": "Mobile authenticator removed.",
|
|
||||||
"successGrantRevokedMessage": "Grant revoked successfully.",
|
|
||||||
"accountUpdatedMessage": "Your account has been updated.",
|
|
||||||
"accountPasswordUpdatedMessage": "Your password has been updated.",
|
|
||||||
"missingIdentityProviderMessage": "Identity provider not specified.",
|
|
||||||
"invalidFederatedIdentityActionMessage": "Invalid or missing action.",
|
|
||||||
"identityProviderNotFoundMessage": "Specified identity provider not found.",
|
|
||||||
"federatedIdentityLinkNotActiveMessage": "This identity is not active anymore.",
|
|
||||||
"federatedIdentityRemovingLastProviderMessage": "You can't remove last federated identity as you don't have a password.",
|
|
||||||
"identityProviderRedirectErrorMessage": "Failed to redirect to identity provider.",
|
|
||||||
"identityProviderRemovedMessage": "Identity provider removed successfully.",
|
|
||||||
"identityProviderAlreadyLinkedMessage": "Federated identity returned by {0} is already linked to another user.",
|
|
||||||
"staleCodeAccountMessage": "The page expired. Please try one more time.",
|
|
||||||
"consentDenied": "Consent denied.",
|
|
||||||
"accountDisabledMessage": "Account is disabled, contact your administrator.",
|
|
||||||
"accountTemporarilyDisabledMessage": "Account is temporarily disabled, contact your administrator or try again later.",
|
|
||||||
"invalidPasswordMinLengthMessage": "Invalid password: minimum length {0}.",
|
|
||||||
"invalidPasswordMinLowerCaseCharsMessage": "Invalid password: must contain at least {0} lower case characters.",
|
|
||||||
"invalidPasswordMinDigitsMessage": "Invalid password: must contain at least {0} numerical digits.",
|
|
||||||
"invalidPasswordMinUpperCaseCharsMessage": "Invalid password: must contain at least {0} upper case characters.",
|
|
||||||
"invalidPasswordMinSpecialCharsMessage": "Invalid password: must contain at least {0} special characters.",
|
|
||||||
"invalidPasswordNotUsernameMessage": "Invalid password: must not be equal to the username.",
|
|
||||||
"invalidPasswordRegexPatternMessage": "Invalid password: fails to match regex pattern(s).",
|
|
||||||
"invalidPasswordHistoryMessage": "Invalid password: must not be equal to any of last {0} passwords.",
|
|
||||||
"invalidPasswordBlacklistedMessage": "Invalid password: password is blacklisted.",
|
|
||||||
"invalidPasswordGenericMessage": "Invalid password: new password doesn't match password policies.",
|
|
||||||
"myResources": "My Resources",
|
|
||||||
"myResourcesSub": "My resources",
|
|
||||||
"doDeny": "Deny",
|
|
||||||
"doRevoke": "Revoke",
|
|
||||||
"doApprove": "Approve",
|
|
||||||
"doRemoveSharing": "Remove Sharing",
|
|
||||||
"doRemoveRequest": "Remove Request",
|
|
||||||
"peopleAccessResource": "People with access to this resource",
|
|
||||||
"resourceManagedPolicies": "Permissions granting access to this resource",
|
|
||||||
"resourceNoPermissionsGrantingAccess": "No permissions granting access to this resource",
|
|
||||||
"anyAction": "Any action",
|
|
||||||
"description": "Description",
|
|
||||||
"name": "Name",
|
|
||||||
"scopes": "Scopes",
|
|
||||||
"resource": "Resource",
|
|
||||||
"user": "User",
|
|
||||||
"peopleSharingThisResource": "People sharing this resource",
|
|
||||||
"shareWithOthers": "Share with others",
|
|
||||||
"needMyApproval": "Need my approval",
|
|
||||||
"requestsWaitingApproval": "Your requests waiting approval",
|
|
||||||
"icon": "Icon",
|
|
||||||
"requestor": "Requestor",
|
|
||||||
"owner": "Owner",
|
|
||||||
"resourcesSharedWithMe": "Resources shared with me",
|
|
||||||
"permissionRequestion": "Permission Requestion",
|
|
||||||
"permission": "Permission",
|
|
||||||
"shares": "share(s)",
|
|
||||||
"notBeingShared": "This resource is not being shared.",
|
|
||||||
"notHaveAnyResource": "You don't have any resources",
|
|
||||||
"noResourcesSharedWithYou": "There are no resources shared with you",
|
|
||||||
"havePermissionRequestsWaitingForApproval": "You have {0} permission request(s) waiting for approval.",
|
|
||||||
"clickHereForDetails": "Click here for details.",
|
|
||||||
"resourceIsNotBeingShared": "The resource is not being shared",
|
|
||||||
"locale_ca": "Català",
|
|
||||||
"locale_cs": "Čeština",
|
|
||||||
"locale_de": "Deutsch",
|
|
||||||
"locale_en": "English",
|
|
||||||
"locale_es": "Español",
|
|
||||||
"locale_fr": "Français",
|
|
||||||
"locale_it": "Italian",
|
|
||||||
"locale_ja": "日本語",
|
|
||||||
"locale_nl": "Nederlands",
|
|
||||||
"locale_no": "Norsk",
|
|
||||||
"locale_lt": "Lietuvių",
|
|
||||||
"locale_pt-BR": "Português (Brasil)",
|
|
||||||
"locale_ru": "Русский",
|
|
||||||
"locale_sk": "Slovenčina",
|
|
||||||
"locale_sv": "Svenska",
|
|
||||||
"locale_tr": "Turkish",
|
|
||||||
"locale_zh-CN": "中文简体",
|
|
||||||
"applicaitonName": "Name",
|
|
||||||
"applicationType": "Application Type",
|
|
||||||
"applicationInUse": "In-use app only",
|
|
||||||
"clearAllFilter": "Clear all filters",
|
|
||||||
"activeFilters": "Active filters",
|
|
||||||
"filterByName": "Filter By Name ...",
|
|
||||||
"allApps": "All applications",
|
|
||||||
"internalApps": "Internal applications",
|
|
||||||
"thirdpartyApps": "Third-Party applications",
|
|
||||||
"appResults": "Results",
|
|
||||||
"clientNotFoundMessage": "Client not found.",
|
|
||||||
"authorizedProvider": "Authorized Provider",
|
|
||||||
"authorizedProviderMessage": "Authorized Providers linked with your account",
|
|
||||||
"identityProvider": "Identity Provider",
|
|
||||||
"identityProviderMessage": "To link your account with identity providers you have configured",
|
|
||||||
"socialLogin": "Social Login",
|
|
||||||
"userDefined": "User Defined",
|
|
||||||
"removeAccess": "Remove Access",
|
|
||||||
"removeAccessMessage": "You will need to grant access again, if you want to use this app account.",
|
|
||||||
"authenticatorStatusMessage": "Two-factor authentication is currently",
|
|
||||||
"authenticatorFinishSetUpTitle": "Your Two-Factor Authentication",
|
|
||||||
"authenticatorFinishSetUpMessage":
|
|
||||||
"Each time you sign in to your Keycloak account, you will be asked to provide a two-factor authentication code.",
|
|
||||||
"authenticatorSubTitle": "Set Up Two-Factor Authentication",
|
|
||||||
"authenticatorSubMessage": "To enhance the security of your account, enable at least one of the available two-factor authentication methods.",
|
|
||||||
"authenticatorMobileTitle": "Mobile Authenticator",
|
|
||||||
"authenticatorMobileMessage": "Use mobile Authenticator to get Verification codes as the two-factor authentication.",
|
|
||||||
"authenticatorMobileFinishSetUpMessage": "The authenticator has been bound to your phone.",
|
|
||||||
"authenticatorActionSetup": "Set up",
|
|
||||||
"authenticatorSMSTitle": "SMS Code",
|
|
||||||
"authenticatorSMSMessage": "Keycloak will send the Verification code to your phone as the two-factor authentication.",
|
|
||||||
"authenticatorSMSFinishSetUpMessage": "Text messages are sent to",
|
|
||||||
"authenticatorDefaultStatus": "Default",
|
|
||||||
"authenticatorChangePhone": "Change Phone Number",
|
|
||||||
"authenticatorBackupCodesTitle": "Backup Codes",
|
|
||||||
"authenticatorBackupCodesMessage": "Get your 8-digit backup codes",
|
|
||||||
"authenticatorBackupCodesFinishSetUpMessage": "12 backup codes were generated at this time. Each one can be used once.",
|
|
||||||
"authenticatorMobileSetupTitle": "Mobile Authenticator Setup",
|
|
||||||
"smscodeIntroMessage": "Enter your phone number and a verification code will be sent to your phone.",
|
|
||||||
"mobileSetupStep1": "Install an authenticator application on your phone. The applications listed here are supported.",
|
|
||||||
"mobileSetupStep2": "Open the application and scan the barcode:",
|
|
||||||
"mobileSetupStep3": "Enter the one-time code provided by the application and click Save to finish the setup.",
|
|
||||||
"scanBarCode": "Want to scan the barcode?",
|
|
||||||
"enterBarCode": "Enter the one-time code",
|
|
||||||
"doCopy": "Copy",
|
|
||||||
"doFinish": "Finish",
|
|
||||||
"authenticatorSMSCodeSetupTitle": "SMS Code Setup",
|
|
||||||
"chooseYourCountry": "Choose your country",
|
|
||||||
"enterYourPhoneNumber": "Enter your phone number",
|
|
||||||
"sendVerficationCode": "Send Verification Code",
|
|
||||||
"enterYourVerficationCode": "Enter your verification code",
|
|
||||||
"authenticatorBackupCodesSetupTitle": "Backup Codes Setup",
|
|
||||||
"backupcodesIntroMessage":
|
|
||||||
"If you lose access to your phone, you can still log into your account through backup codes. Keep them somewhere safe and accessible.",
|
|
||||||
"realmName": "Realm",
|
|
||||||
"doDownload": "Download",
|
|
||||||
"doPrint": "Print",
|
|
||||||
"backupCodesTips-1": "Each backup code can be used once.",
|
|
||||||
"backupCodesTips-2": "These codes were generated on",
|
|
||||||
"generateNewBackupCodes": "Generate New Backup Codes",
|
|
||||||
"backupCodesTips-3": "When you generate new backup codes, the current codes will not work anymore.",
|
|
||||||
"backtoAuthenticatorPage": "Back to Authenticator Page",
|
|
||||||
"resources": "Resources",
|
|
||||||
"sharedwithMe": "Shared with Me",
|
|
||||||
"share": "Share",
|
|
||||||
"sharedwith": "Shared with",
|
|
||||||
"accessPermissions": "Access Permissions",
|
|
||||||
"permissionRequests": "Permission Requests",
|
|
||||||
"approve": "Approve",
|
|
||||||
"approveAll": "Approve all",
|
|
||||||
"people": "people",
|
|
||||||
"perPage": "per page",
|
|
||||||
"currentPage": "Current Page",
|
|
||||||
"sharetheResource": "Share the resource",
|
|
||||||
"group": "Group",
|
|
||||||
"selectPermission": "Select Permission",
|
|
||||||
"addPeople": "Add people to share your resource with",
|
|
||||||
"addTeam": "Add team to share your resource with",
|
|
||||||
"myPermissions": "My Permissions",
|
|
||||||
"waitingforApproval": "Waiting for approval",
|
|
||||||
"anyPermission": "Any Permission",
|
|
||||||
"openshift.scope.user_info": "User information",
|
|
||||||
"openshift.scope.user_check-access": "User access information",
|
|
||||||
"openshift.scope.user_full": "Full Access",
|
|
||||||
"openshift.scope.list-projects": "List projects"
|
|
||||||
};
|
|
||||||
|
|
||||||
export default messages;
|
|
||||||
/* spell-checker: enable */
|
|
@ -1,137 +0,0 @@
|
|||||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
|
||||||
//PLEASE DO NOT EDIT MANUALLY
|
|
||||||
|
|
||||||
/* spell-checker: disable */
|
|
||||||
const messages = {
|
|
||||||
"doSave": "Guardar",
|
|
||||||
"doCancel": "Cancelar",
|
|
||||||
"doLogOutAllSessions": "Desconectar de todas las sesiones",
|
|
||||||
"doRemove": "Eliminar",
|
|
||||||
"doAdd": "Añadir",
|
|
||||||
"doSignOut": "Desconectar",
|
|
||||||
"editAccountHtmlTitle": "Editar cuenta",
|
|
||||||
"federatedIdentitiesHtmlTitle": "Identidades federadas",
|
|
||||||
"accountLogHtmlTitle": "Registro de la cuenta",
|
|
||||||
"changePasswordHtmlTitle": "Cambiar contraseña",
|
|
||||||
"sessionsHtmlTitle": "Sesiones",
|
|
||||||
"accountManagementTitle": "Gestión de Cuenta Keycloak",
|
|
||||||
"authenticatorTitle": "Autenticador",
|
|
||||||
"applicationsHtmlTitle": "Aplicaciones",
|
|
||||||
"authenticatorCode": "Código de un solo uso",
|
|
||||||
"email": "Email",
|
|
||||||
"firstName": "Nombre",
|
|
||||||
"givenName": "Nombre de pila",
|
|
||||||
"fullName": "Nombre completo",
|
|
||||||
"lastName": "Apellidos",
|
|
||||||
"familyName": "Apellido",
|
|
||||||
"password": "Contraseña",
|
|
||||||
"passwordConfirm": "Confirma la contraseña",
|
|
||||||
"passwordNew": "Nueva contraseña",
|
|
||||||
"username": "Usuario",
|
|
||||||
"address": "Dirección",
|
|
||||||
"street": "Calle",
|
|
||||||
"locality": "Ciudad o Municipio",
|
|
||||||
"region": "Estado, Provincia, o Región",
|
|
||||||
"postal_code": "Código Postal",
|
|
||||||
"country": "País",
|
|
||||||
"emailVerified": "Email verificado",
|
|
||||||
"gssDelegationCredential": "GSS Delegation Credential",
|
|
||||||
"role_admin": "Administrador",
|
|
||||||
"role_realm-admin": "Administrador del dominio",
|
|
||||||
"role_create-realm": "Crear dominio",
|
|
||||||
"role_view-realm": "Ver dominio",
|
|
||||||
"role_view-users": "Ver usuarios",
|
|
||||||
"role_view-applications": "Ver aplicaciones",
|
|
||||||
"role_view-clients": "Ver clientes",
|
|
||||||
"role_view-events": "Ver eventos",
|
|
||||||
"role_view-identity-providers": "Ver proveedores de identidad",
|
|
||||||
"role_manage-realm": "Gestionar dominio",
|
|
||||||
"role_manage-users": "Gestionar usuarios",
|
|
||||||
"role_manage-applications": "Gestionar aplicaciones",
|
|
||||||
"role_manage-identity-providers": "Gestionar proveedores de identidad",
|
|
||||||
"role_manage-clients": "Gestionar clientes",
|
|
||||||
"role_manage-events": "Gestionar eventos",
|
|
||||||
"role_view-profile": "Ver perfil",
|
|
||||||
"role_manage-account": "Gestionar cuenta",
|
|
||||||
"role_read-token": "Leer token",
|
|
||||||
"role_offline-access": "Acceso sin conexión",
|
|
||||||
"client_account": "Cuenta",
|
|
||||||
"client_security-admin-console": "Consola de Administración de Seguridad",
|
|
||||||
"client_realm-management": "Gestión de dominio",
|
|
||||||
"client_broker": "Broker",
|
|
||||||
"requiredFields": "Campos obligatorios",
|
|
||||||
"allFieldsRequired": "Todos los campos obligatorios",
|
|
||||||
"backToApplication": "« Volver a la aplicación",
|
|
||||||
"backTo": "Volver a {0}",
|
|
||||||
"date": "Fecha",
|
|
||||||
"event": "Evento",
|
|
||||||
"ip": "IP",
|
|
||||||
"client": "Cliente",
|
|
||||||
"clients": "Clientes",
|
|
||||||
"details": "Detalles",
|
|
||||||
"started": "Iniciado",
|
|
||||||
"lastAccess": "Último acceso",
|
|
||||||
"expires": "Expira",
|
|
||||||
"applications": "Aplicaciones",
|
|
||||||
"account": "Cuenta",
|
|
||||||
"federatedIdentity": "Identidad federada",
|
|
||||||
"authenticator": "Autenticador",
|
|
||||||
"sessions": "Sesiones",
|
|
||||||
"log": "Regisro",
|
|
||||||
"application": "Aplicación",
|
|
||||||
"availablePermissions": "Permisos disponibles",
|
|
||||||
"grantedPermissions": "Permisos concedidos",
|
|
||||||
"grantedPersonalInfo": "Información personal concedida",
|
|
||||||
"additionalGrants": "Permisos adicionales",
|
|
||||||
"action": "Acción",
|
|
||||||
"inResource": "en",
|
|
||||||
"fullAccess": "Acceso total",
|
|
||||||
"offlineToken": "Código de autorización offline",
|
|
||||||
"revoke": "Revocar permiso",
|
|
||||||
"configureAuthenticators": "Autenticadores configurados",
|
|
||||||
"mobile": "Móvil",
|
|
||||||
"totpStep1":
|
|
||||||
'Instala <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> o Google Authenticator en tu teléfono móvil. Ambas aplicaciones están disponibles en <a href="https://play.google.com">Google Play</a> y en la App Store de Apple.',
|
|
||||||
"totpStep2": "Abre la aplicación y escanea el código o introduce la clave.",
|
|
||||||
"totpStep3": "Introduce el código único que te muestra la aplicación de autenticación y haz clic en Enviar para finalizar la configuración",
|
|
||||||
"missingUsernameMessage": "Por favor indica tu usuario.",
|
|
||||||
"missingFirstNameMessage": "Por favor indica el nombre.",
|
|
||||||
"invalidEmailMessage": "Email no válido",
|
|
||||||
"missingLastNameMessage": "Por favor indica tus apellidos.",
|
|
||||||
"missingEmailMessage": "Por favor indica el email.",
|
|
||||||
"missingPasswordMessage": "Por favor indica tu contraseña.",
|
|
||||||
"notMatchPasswordMessage": "Las contraseñas no coinciden.",
|
|
||||||
"missingTotpMessage": "Por favor indica tu código de autenticación",
|
|
||||||
"invalidPasswordExistingMessage": "La contraseña actual no es correcta.",
|
|
||||||
"invalidPasswordConfirmMessage": "La confirmación de contraseña no coincide.",
|
|
||||||
"invalidTotpMessage": "El código de autenticación no es válido.",
|
|
||||||
"usernameExistsMessage": "El usuario ya existe",
|
|
||||||
"emailExistsMessage": "El email ya existe",
|
|
||||||
"readOnlyUserMessage": "No puedes actualizar tu usuario porque tu cuenta es de solo lectura.",
|
|
||||||
"readOnlyPasswordMessage": "No puedes actualizar tu contraseña porque tu cuenta es de solo lectura.",
|
|
||||||
"successTotpMessage": "Aplicación de autenticación móvil configurada.",
|
|
||||||
"successTotpRemovedMessage": "Aplicación de autenticación móvil eliminada.",
|
|
||||||
"successGrantRevokedMessage": "Permiso revocado correctamente",
|
|
||||||
"accountUpdatedMessage": "Tu cuenta se ha actualizado.",
|
|
||||||
"accountPasswordUpdatedMessage": "Tu contraseña se ha actualizado.",
|
|
||||||
"missingIdentityProviderMessage": "Proveedor de identidad no indicado.",
|
|
||||||
"invalidFederatedIdentityActionMessage": "Acción no válida o no indicada.",
|
|
||||||
"identityProviderNotFoundMessage": "No se encontró un proveedor de identidad.",
|
|
||||||
"federatedIdentityLinkNotActiveMessage": "Esta identidad ya no está activa",
|
|
||||||
"federatedIdentityRemovingLastProviderMessage": "No puedes eliminar la última identidad federada porque no tienes fijada una contraseña.",
|
|
||||||
"identityProviderRedirectErrorMessage": "Error en la redirección al proveedor de identidad",
|
|
||||||
"identityProviderRemovedMessage": "Proveedor de identidad borrado correctamente.",
|
|
||||||
"accountDisabledMessage": "La cuenta está desactivada, contacta con el administrador.",
|
|
||||||
"accountTemporarilyDisabledMessage": "La cuenta está temporalmente desactivada, contacta con el administrador o inténtalo de nuevo más tarde.",
|
|
||||||
"invalidPasswordMinLengthMessage": "Contraseña incorrecta: longitud mínima {0}.",
|
|
||||||
"invalidPasswordMinLowerCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras minúsculas.",
|
|
||||||
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
|
|
||||||
"invalidPasswordMinUpperCaseCharsMessage": "Contraseña incorrecta: debe contener al menos {0} letras mayúsculas.",
|
|
||||||
"invalidPasswordMinSpecialCharsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres especiales.",
|
|
||||||
"invalidPasswordNotUsernameMessage": "Contraseña incorrecta: no puede ser igual al nombre de usuario.",
|
|
||||||
"invalidPasswordRegexPatternMessage": "Contraseña incorrecta: no cumple la expresión regular.",
|
|
||||||
"invalidPasswordHistoryMessage": "Contraseña incorrecta: no puede ser igual a ninguna de las últimas {0} contraseñas."
|
|
||||||
};
|
|
||||||
|
|
||||||
export default messages;
|
|
||||||
/* spell-checker: enable */
|
|
@ -1,150 +0,0 @@
|
|||||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
|
||||||
//PLEASE DO NOT EDIT MANUALLY
|
|
||||||
|
|
||||||
/* spell-checker: disable */
|
|
||||||
const messages = {
|
|
||||||
"doSave": "Sauvegarder",
|
|
||||||
"doCancel": "Annuler",
|
|
||||||
"doLogOutAllSessions": "Déconnexion de toutes les sessions",
|
|
||||||
"doRemove": "Supprimer",
|
|
||||||
"doAdd": "Ajouter",
|
|
||||||
"doSignOut": "Déconnexion",
|
|
||||||
"editAccountHtmlTitle": "Édition du compte",
|
|
||||||
"federatedIdentitiesHtmlTitle": "Identités fédérées",
|
|
||||||
"accountLogHtmlTitle": "Accès au compte",
|
|
||||||
"changePasswordHtmlTitle": "Changer de mot de passe",
|
|
||||||
"sessionsHtmlTitle": "Sessions",
|
|
||||||
"accountManagementTitle": "Gestion du compte Keycloak",
|
|
||||||
"authenticatorTitle": "Authentification",
|
|
||||||
"applicationsHtmlTitle": "Applications",
|
|
||||||
"authenticatorCode": "Mot de passe unique",
|
|
||||||
"email": "Courriel",
|
|
||||||
"firstName": "Prénom",
|
|
||||||
"givenName": "Prénom",
|
|
||||||
"fullName": "Nom complet",
|
|
||||||
"lastName": "Nom",
|
|
||||||
"familyName": "Nom de famille",
|
|
||||||
"password": "Mot de passe",
|
|
||||||
"passwordConfirm": "Confirmation",
|
|
||||||
"passwordNew": "Nouveau mot de passe",
|
|
||||||
"username": "Compte",
|
|
||||||
"address": "Adresse",
|
|
||||||
"street": "Rue",
|
|
||||||
"locality": "Ville ou Localité",
|
|
||||||
"region": "État, Province ou Région",
|
|
||||||
"postal_code": "Code Postal",
|
|
||||||
"country": "Pays",
|
|
||||||
"emailVerified": "Courriel vérifié",
|
|
||||||
"gssDelegationCredential": "Accréditation de délégation GSS",
|
|
||||||
"role_admin": "Administrateur",
|
|
||||||
"role_realm-admin": "Administrateur du domaine",
|
|
||||||
"role_create-realm": "Créer un domaine",
|
|
||||||
"role_view-realm": "Voir un domaine",
|
|
||||||
"role_view-users": "Voir les utilisateurs",
|
|
||||||
"role_view-applications": "Voir les applications",
|
|
||||||
"role_view-clients": "Voir les clients",
|
|
||||||
"role_view-events": "Voir les événements",
|
|
||||||
"role_view-identity-providers": "Voir les fournisseurs d'identités",
|
|
||||||
"role_manage-realm": "Gérer le domaine",
|
|
||||||
"role_manage-users": "Gérer les utilisateurs",
|
|
||||||
"role_manage-applications": "Gérer les applications",
|
|
||||||
"role_manage-identity-providers": "Gérer les fournisseurs d'identités",
|
|
||||||
"role_manage-clients": "Gérer les clients",
|
|
||||||
"role_manage-events": "Gérer les événements",
|
|
||||||
"role_view-profile": "Voir le profil",
|
|
||||||
"role_manage-account": "Gérer le compte",
|
|
||||||
"role_read-token": "Lire le jeton d'authentification",
|
|
||||||
"role_offline-access": "Accès hors-ligne",
|
|
||||||
"client_account": "Compte",
|
|
||||||
"client_security-admin-console": "Console d'administration de la sécurité",
|
|
||||||
"client_admin-cli": "Admin CLI",
|
|
||||||
"client_realm-management": "Gestion du domaine",
|
|
||||||
"client_broker": "Broker",
|
|
||||||
"requiredFields": "Champs obligatoires",
|
|
||||||
"allFieldsRequired": "Tous les champs sont obligatoires",
|
|
||||||
"backToApplication": "« Revenir à l'application",
|
|
||||||
"backTo": "Revenir à {0}",
|
|
||||||
"date": "Date",
|
|
||||||
"event": "Evénement",
|
|
||||||
"ip": "IP",
|
|
||||||
"client": "Client",
|
|
||||||
"clients": "Clients",
|
|
||||||
"details": "Détails",
|
|
||||||
"started": "Début",
|
|
||||||
"lastAccess": "Dernier accès",
|
|
||||||
"expires": "Expiration",
|
|
||||||
"applications": "Applications",
|
|
||||||
"account": "Compte",
|
|
||||||
"federatedIdentity": "Identité fédérée",
|
|
||||||
"authenticator": "Authentification",
|
|
||||||
"sessions": "Sessions",
|
|
||||||
"log": "Connexion",
|
|
||||||
"application": "Application",
|
|
||||||
"availablePermissions": "Permissions disponibles",
|
|
||||||
"grantedPermissions": "Permissions accordées",
|
|
||||||
"grantedPersonalInfo": "Informations personnelles accordées",
|
|
||||||
"additionalGrants": "Droits additionnels",
|
|
||||||
"action": "Action",
|
|
||||||
"inResource": "dans",
|
|
||||||
"fullAccess": "Accès complet",
|
|
||||||
"offlineToken": "Jeton d'authentification hors-ligne",
|
|
||||||
"revoke": "Révoquer un droit",
|
|
||||||
"configureAuthenticators": "Authentifications configurées.",
|
|
||||||
"mobile": "Téléphone mobile",
|
|
||||||
"totpStep1": "Installez une des applications suivantes sur votre mobile",
|
|
||||||
"totpStep2": "Ouvrez l'application et scannez le code-barres ou entrez la clef.",
|
|
||||||
"totpStep3": "Entrez le code à usage unique fourni par l'application et cliquez sur Sauvegarder pour terminer.",
|
|
||||||
"totpManualStep2": "Ouvrez l'application et entrez la clef",
|
|
||||||
"totpManualStep3": "Utilisez les valeurs de configuration suivante si l'application les autorise",
|
|
||||||
"totpUnableToScan": "Impossible de scanner ?",
|
|
||||||
"totpScanBarcode": "Scanner le code-barres ?",
|
|
||||||
"totp.totp": "Basé sur le temps",
|
|
||||||
"totp.hotp": "Basé sur un compteur",
|
|
||||||
"totpType": "Type",
|
|
||||||
"totpAlgorithm": "Algorithme",
|
|
||||||
"totpDigits": "Chiffres",
|
|
||||||
"totpInterval": "Intervalle",
|
|
||||||
"totpCounter": "Compteur",
|
|
||||||
"missingUsernameMessage": "Veuillez entrer votre nom d'utilisateur.",
|
|
||||||
"missingFirstNameMessage": "Veuillez entrer votre prénom.",
|
|
||||||
"invalidEmailMessage": "Courriel invalide.",
|
|
||||||
"missingLastNameMessage": "Veuillez entrer votre nom.",
|
|
||||||
"missingEmailMessage": "Veuillez entrer votre courriel.",
|
|
||||||
"missingPasswordMessage": "Veuillez entrer votre mot de passe.",
|
|
||||||
"notMatchPasswordMessage": "Les mots de passe ne sont pas identiques",
|
|
||||||
"missingTotpMessage": "Veuillez entrer le code d'authentification.",
|
|
||||||
"invalidPasswordExistingMessage": "Mot de passe existant invalide.",
|
|
||||||
"invalidPasswordConfirmMessage": "Le mot de passe de confirmation ne correspond pas.",
|
|
||||||
"invalidTotpMessage": "Le code d'authentification est invalide.",
|
|
||||||
"usernameExistsMessage": "Le nom d'utilisateur existe déjà.",
|
|
||||||
"emailExistsMessage": "Le courriel existe déjà.",
|
|
||||||
"readOnlyUserMessage": "Vous ne pouvez pas mettre à jour votre compte car il est en lecture seule.",
|
|
||||||
"readOnlyPasswordMessage": "Vous ne pouvez pas mettre à jour votre mot de passe car votre compte est en lecture seule.",
|
|
||||||
"successTotpMessage": "L'authentification via téléphone mobile est configurée.",
|
|
||||||
"successTotpRemovedMessage": "L'authentification via téléphone mobile est supprimée.",
|
|
||||||
"successGrantRevokedMessage": "Droit révoqué avec succès.",
|
|
||||||
"accountUpdatedMessage": "Votre compte a été mis à jour.",
|
|
||||||
"accountPasswordUpdatedMessage": "Votre mot de passe a été mis à jour.",
|
|
||||||
"missingIdentityProviderMessage": "Le fournisseur d'identité n'est pas spécifié.",
|
|
||||||
"invalidFederatedIdentityActionMessage": "Action manquante ou invalide.",
|
|
||||||
"identityProviderNotFoundMessage": "Le fournisseur d'identité spécifié n'est pas trouvé.",
|
|
||||||
"federatedIdentityLinkNotActiveMessage": "Cette identité n'est plus active dorénavant.",
|
|
||||||
"federatedIdentityRemovingLastProviderMessage":
|
|
||||||
"Vous ne pouvez pas supprimer votre dernière fédération d'identité sans avoir de mot de passe spécifié.",
|
|
||||||
"identityProviderRedirectErrorMessage": "Erreur de redirection vers le fournisseur d'identité.",
|
|
||||||
"identityProviderRemovedMessage": "Le fournisseur d'identité a été supprimé correctement.",
|
|
||||||
"identityProviderAlreadyLinkedMessage": "Le fournisseur d'identité retourné par {0} est déjà lié à un autre utilisateur.",
|
|
||||||
"accountDisabledMessage": "Ce compte est désactivé, veuillez contacter votre administrateur.",
|
|
||||||
"accountTemporarilyDisabledMessage": "Ce compte est temporairement désactivé, veuillez contacter votre administrateur ou réessayez plus tard.",
|
|
||||||
"invalidPasswordMinLengthMessage": "Mot de passe invalide: longueur minimale {0}.",
|
|
||||||
"invalidPasswordMinLowerCaseCharsMessage": "Mot de passe invalide: doit contenir au moins {0} lettre(s) en minuscule.",
|
|
||||||
"invalidPasswordMinDigitsMessage": "Mot de passe invalide: doit contenir au moins {0} chiffre(s).",
|
|
||||||
"invalidPasswordMinUpperCaseCharsMessage": "Mot de passe invalide: doit contenir au moins {0} lettre(s) en majuscule.",
|
|
||||||
"invalidPasswordMinSpecialCharsMessage": "Mot de passe invalide: doit contenir au moins {0} caractère(s) spéciaux.",
|
|
||||||
"invalidPasswordNotUsernameMessage": "Mot de passe invalide: ne doit pas être identique au nom d'utilisateur.",
|
|
||||||
"invalidPasswordRegexPatternMessage": "Mot de passe invalide: ne valide pas l'expression rationnelle.",
|
|
||||||
"invalidPasswordHistoryMessage": "Mot de passe invalide: ne doit pas être égal aux {0} derniers mots de passe."
|
|
||||||
};
|
|
||||||
|
|
||||||
export default messages;
|
|
||||||
/* spell-checker: enable */
|
|
@ -1,310 +0,0 @@
|
|||||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
|
||||||
//PLEASE DO NOT EDIT MANUALLY
|
|
||||||
|
|
||||||
/* spell-checker: disable */
|
|
||||||
const messages = {
|
|
||||||
"doSave": "Salva",
|
|
||||||
"doCancel": "Annulla",
|
|
||||||
"doLogOutAllSessions": "Effettua il logout da tutte le sessioni",
|
|
||||||
"doRemove": "Elimina",
|
|
||||||
"doAdd": "Aggiungi",
|
|
||||||
"doSignOut": "Esci",
|
|
||||||
"doLogIn": "Log In",
|
|
||||||
"doLink": "Link",
|
|
||||||
"editAccountHtmlTitle": "Modifica Account",
|
|
||||||
"personalInfoHtmlTitle": "Informazioni personali",
|
|
||||||
"federatedIdentitiesHtmlTitle": "Identità federate",
|
|
||||||
"accountLogHtmlTitle": "Log dell'account",
|
|
||||||
"changePasswordHtmlTitle": "Cambia password",
|
|
||||||
"deviceActivityHtmlTitle": "Attività dei dispositivi",
|
|
||||||
"sessionsHtmlTitle": "Sessioni",
|
|
||||||
"accountManagementTitle": "Gestione degli account di Keycloak",
|
|
||||||
"authenticatorTitle": "Autenticatore",
|
|
||||||
"applicationsHtmlTitle": "Applicazioni",
|
|
||||||
"linkedAccountsHtmlTitle": "Account collegati",
|
|
||||||
"accountManagementWelcomeMessage": "Benvenuto nella gestione degli account di Keycloak",
|
|
||||||
"personalInfoIntroMessage": "Gestisci le tue informazioni di base",
|
|
||||||
"accountSecurityTitle": "Sicurezza dell'account",
|
|
||||||
"accountSecurityIntroMessage": "Controlla la tua password e gli accessi dell'account",
|
|
||||||
"applicationsIntroMessage": "Traccia e gestisci i permessi delle applicazioni nell'accesso al tuo account",
|
|
||||||
"resourceIntroMessage": "Condividi le tue risorse tra i membri del team",
|
|
||||||
"passwordLastUpdateMessage": "La tua password è stata aggiornata il",
|
|
||||||
"updatePasswordTitle": "Aggiornamento password",
|
|
||||||
"updatePasswordMessageTitle": "Assicurati di scegliere una password robusta",
|
|
||||||
"updatePasswordMessage":
|
|
||||||
"Una password robusta contiene un misto di numeri, lettere, e simboli. È difficile da indovinare, non assomiglia a una parola reale, ed è utilizzata solo per questo account.",
|
|
||||||
"personalSubTitle": "Le tue informazioni personali",
|
|
||||||
"personalSubMessage": "Gestisce queste informazioni di base: il tuo nome, cognome, e indirizzo email",
|
|
||||||
"authenticatorCode": "Codice monouso",
|
|
||||||
"email": "Email",
|
|
||||||
"firstName": "Nome",
|
|
||||||
"givenName": "Nome",
|
|
||||||
"fullName": "Nome completo",
|
|
||||||
"lastName": "Cognome",
|
|
||||||
"familyName": "Cognome",
|
|
||||||
"password": "Password",
|
|
||||||
"currentPassword": "Password attuale",
|
|
||||||
"passwordConfirm": "Conferma password",
|
|
||||||
"passwordNew": "Nuova password",
|
|
||||||
"username": "Username",
|
|
||||||
"address": "Indirizzo",
|
|
||||||
"street": "Via",
|
|
||||||
"locality": "Città o località",
|
|
||||||
"region": "Stato, Provincia, o Regione",
|
|
||||||
"postal_code": "CAP",
|
|
||||||
"country": "Paese",
|
|
||||||
"emailVerified": "Email verificata",
|
|
||||||
"gssDelegationCredential": "Credenziali delega GSS",
|
|
||||||
"profileScopeConsentText": "Profilo utente",
|
|
||||||
"emailScopeConsentText": "Indirizzo email",
|
|
||||||
"addressScopeConsentText": "Indirizzo",
|
|
||||||
"phoneScopeConsentText": "Numero di telefono",
|
|
||||||
"offlineAccessScopeConsentText": "Accesso offline",
|
|
||||||
"samlRoleListScopeConsentText": "I miei ruoli",
|
|
||||||
"rolesScopeConsentText": "Ruoli utente",
|
|
||||||
"role_admin": "Admin",
|
|
||||||
"role_realm-admin": "Realm admin",
|
|
||||||
"role_create-realm": "Crea realm",
|
|
||||||
"role_view-realm": "Visualizza realm",
|
|
||||||
"role_view-users": "Visualizza utenti",
|
|
||||||
"role_view-applications": "Visualizza applicazioni",
|
|
||||||
"role_view-clients": "Visualizza client",
|
|
||||||
"role_view-events": "Visualizza eventi",
|
|
||||||
"role_view-identity-providers": "Visualizza identity provider",
|
|
||||||
"role_view-consent": "Visualizza consensi",
|
|
||||||
"role_manage-realm": "Gestisci realm",
|
|
||||||
"role_manage-users": "Gestisci utenti",
|
|
||||||
"role_manage-applications": "Gestisci applicazioni",
|
|
||||||
"role_manage-identity-providers": "Gestisci identity provider",
|
|
||||||
"role_manage-clients": "Gestisci client",
|
|
||||||
"role_manage-events": "Gestisci eventi",
|
|
||||||
"role_view-profile": "Visualizza profilo",
|
|
||||||
"role_manage-account": "Gestisci account",
|
|
||||||
"role_manage-account-links": "Gestisci i link dell'account",
|
|
||||||
"role_manage-consent": "Gestisci consensi",
|
|
||||||
"role_read-token": "Leggi token",
|
|
||||||
"role_offline-access": "Accesso offline",
|
|
||||||
"role_uma_authorization": "Ottieni permessi",
|
|
||||||
"client_account": "Account",
|
|
||||||
"client_account-console": "Console account",
|
|
||||||
"client_security-admin-console": "Console di amministrazione di sicurezza",
|
|
||||||
"client_admin-cli": "Admin CLI",
|
|
||||||
"client_realm-management": "Gestione realm",
|
|
||||||
"client_broker": "Broker",
|
|
||||||
"requiredFields": "Campi obbligatori",
|
|
||||||
"allFieldsRequired": "Tutti campi obbligatori",
|
|
||||||
"backToApplication": "« Torna all'applicazione",
|
|
||||||
"backTo": "Torna a {0}",
|
|
||||||
"date": "Data",
|
|
||||||
"event": "Evento",
|
|
||||||
"ip": "IP",
|
|
||||||
"client": "Client",
|
|
||||||
"clients": "Client",
|
|
||||||
"details": "Dettagli",
|
|
||||||
"started": "Iniziato",
|
|
||||||
"lastAccess": "Ultimo accesso",
|
|
||||||
"expires": "Scade",
|
|
||||||
"applications": "Applicazioni",
|
|
||||||
"account": "Account",
|
|
||||||
"federatedIdentity": "Identità federate",
|
|
||||||
"authenticator": "Autenticatore",
|
|
||||||
"device-activity": "Attività dei dispositivi",
|
|
||||||
"sessions": "Sessioni",
|
|
||||||
"log": "Log",
|
|
||||||
"application": "Applicazione",
|
|
||||||
"availablePermissions": "Autorizzazioni disponibili",
|
|
||||||
"grantedPermissions": "Autorizzazioni concesse",
|
|
||||||
"grantedPersonalInfo": "Informazioni personali concesse",
|
|
||||||
"additionalGrants": "Ulteriori concessioni",
|
|
||||||
"action": "Azione",
|
|
||||||
"inResource": "in",
|
|
||||||
"fullAccess": "Accesso completo",
|
|
||||||
"offlineToken": "Token offline",
|
|
||||||
"revoke": "Revoca concessione",
|
|
||||||
"configureAuthenticators": "Autenticatori configurati",
|
|
||||||
"mobile": "Dispositivo mobile",
|
|
||||||
"totpStep1": "Installa una delle seguenti applicazioni sul tuo dispositivo mobile",
|
|
||||||
"totpStep2": "Apri l'applicazione e scansiona il codice QR",
|
|
||||||
"totpStep3": "Scrivi il codice monouso fornito dall'applicazione e clicca Salva per completare il setup.",
|
|
||||||
"totpStep3DeviceName": "Fornisci il nome del dispositivo per aiutarti a gestire i dispositivi di autenticazione.",
|
|
||||||
"totpManualStep2": "Apri l'applicazione e scrivi la chiave",
|
|
||||||
"totpManualStep3": "Usa le seguenti impostazioni se l'applicazione lo consente",
|
|
||||||
"totpUnableToScan": "Non riesci a scansionare il codice QR?",
|
|
||||||
"totpScanBarcode": "Vuoi scansionare il codice QR?",
|
|
||||||
"totp.totp": "Basato sull'ora",
|
|
||||||
"totp.hotp": "Basato sul contatore",
|
|
||||||
"totpType": "Tipo",
|
|
||||||
"totpAlgorithm": "Algoritmo",
|
|
||||||
"totpDigits": "Cifre",
|
|
||||||
"totpInterval": "Intervallo",
|
|
||||||
"totpCounter": "Contatore",
|
|
||||||
"totpDeviceName": "Nome dispositivo",
|
|
||||||
"missingUsernameMessage": "Inserisci lo username.",
|
|
||||||
"missingFirstNameMessage": "Inserisci il nome.",
|
|
||||||
"invalidEmailMessage": "Indirizzo email non valido.",
|
|
||||||
"missingLastNameMessage": "Inserisci il cognome.",
|
|
||||||
"missingEmailMessage": "Inserisci l'indirizzo email.",
|
|
||||||
"missingPasswordMessage": "Inserisci la password.",
|
|
||||||
"notMatchPasswordMessage": "Le password non coincidono.",
|
|
||||||
"invalidUserMessage": "Utente non valido",
|
|
||||||
"missingTotpMessage": "Inserisci il codice di autenticazione.",
|
|
||||||
"missingTotpDeviceNameMessage": "Inserisci il nome del dispositivo di autenticazione.",
|
|
||||||
"invalidPasswordExistingMessage": "Password esistente non valida.",
|
|
||||||
"invalidPasswordConfirmMessage": "La password di conferma non coincide.",
|
|
||||||
"invalidTotpMessage": "Codice di autenticazione non valido.",
|
|
||||||
"usernameExistsMessage": "Username già esistente.",
|
|
||||||
"emailExistsMessage": "Email già esistente.",
|
|
||||||
"readOnlyUserMessage": "Non puoi aggiornare il tuo account poiché è in modalità sola lettura.",
|
|
||||||
"readOnlyUsernameMessage": "Non puoi aggiornare il tuo nome utente poiché è in modalità sola lettura.",
|
|
||||||
"readOnlyPasswordMessage": "Non puoi aggiornare il tuo account poiché è in modalità sola lettura.",
|
|
||||||
"successTotpMessage": "Autenticatore mobile configurato.",
|
|
||||||
"successTotpRemovedMessage": "Autenticatore mobile eliminato.",
|
|
||||||
"successGrantRevokedMessage": "Concessione revocata con successo.",
|
|
||||||
"accountUpdatedMessage": "Il tuo account è stato aggiornato.",
|
|
||||||
"accountPasswordUpdatedMessage": "La tua password è stata aggiornata.",
|
|
||||||
"missingIdentityProviderMessage": "Identity provider non specificato.",
|
|
||||||
"invalidFederatedIdentityActionMessage": "Azione non valida o mancante.",
|
|
||||||
"identityProviderNotFoundMessage": "L'identity provider specificato non è stato trovato.",
|
|
||||||
"federatedIdentityLinkNotActiveMessage": "Questo identity non è più attivo.",
|
|
||||||
"federatedIdentityRemovingLastProviderMessage": "Non puoi rimuovere l'ultima identità federata poiché non hai più la password.",
|
|
||||||
"identityProviderRedirectErrorMessage": "Il reindirizzamento all'identity provider è fallito.",
|
|
||||||
"identityProviderRemovedMessage": "Identity provider eliminato correttamente.",
|
|
||||||
"identityProviderAlreadyLinkedMessage": "L'identità federata restituita da {0} è già collegata ad un altro utente.",
|
|
||||||
"staleCodeAccountMessage": "La pagina è scaduta. Prova di nuovo.",
|
|
||||||
"consentDenied": "Consenso negato.",
|
|
||||||
"accountDisabledMessage": "Account disabilitato, contatta l'amministratore.",
|
|
||||||
"accountTemporarilyDisabledMessage": "L'account è temporaneamente disabilitato, contatta l'amministratore o riprova più tardi.",
|
|
||||||
"invalidPasswordMinLengthMessage": "Password non valida: lunghezza minima {0}.",
|
|
||||||
"invalidPasswordMinLowerCaseCharsMessage": "Password non valida: deve contenere almeno {0} caratteri minuscoli.",
|
|
||||||
"invalidPasswordMinDigitsMessage": "Password non valida: deve contenere almeno {0} numeri.",
|
|
||||||
"invalidPasswordMinUpperCaseCharsMessage": "Password non valida: deve contenere almeno {0} caratteri maiuscoli.",
|
|
||||||
"invalidPasswordMinSpecialCharsMessage": "Password non valida: deve contenere almeno {0} caratteri speciali.",
|
|
||||||
"invalidPasswordNotUsernameMessage": "Password non valida: non deve essere uguale allo username.",
|
|
||||||
"invalidPasswordRegexPatternMessage": "Password non valida: fallito il match con una o più espressioni regolari.",
|
|
||||||
"invalidPasswordHistoryMessage": "Password non valida: non deve essere uguale a una delle ultime {0} password.",
|
|
||||||
"invalidPasswordBlacklistedMessage": "Password non valida: la password non è consentita.",
|
|
||||||
"invalidPasswordGenericMessage": "Password non valida: la nuova password non rispetta le indicazioni previste.",
|
|
||||||
"myResources": "Le mie risorse",
|
|
||||||
"myResourcesSub": "Le mie risorse",
|
|
||||||
"doDeny": "Nega",
|
|
||||||
"doRevoke": "Revoca",
|
|
||||||
"doApprove": "Approva",
|
|
||||||
"doRemoveSharing": "Rimuovi condivisione",
|
|
||||||
"doRemoveRequest": "Rimuovi richiesta",
|
|
||||||
"peopleAccessResource": "Persone che hanno accesso a questa risorsa",
|
|
||||||
"resourceManagedPolicies": "Permessi che danno accesso a questa risorsa",
|
|
||||||
"resourceNoPermissionsGrantingAccess": "Nessun permesso dà accesso a questa risorsa",
|
|
||||||
"anyAction": "Qualsiasi azione",
|
|
||||||
"description": "Descrizione",
|
|
||||||
"name": "Nome",
|
|
||||||
"scopes": "Ambito",
|
|
||||||
"resource": "Risorsa",
|
|
||||||
"user": "Utente",
|
|
||||||
"peopleSharingThisResource": "Persone che condividono questa risorsa",
|
|
||||||
"shareWithOthers": "Condividi con altri",
|
|
||||||
"needMyApproval": "Richiede la mia approvazione",
|
|
||||||
"requestsWaitingApproval": "La tua richiesta è in attesa di approvazione",
|
|
||||||
"icon": "Icona",
|
|
||||||
"requestor": "Richiedente",
|
|
||||||
"owner": "Proprietario",
|
|
||||||
"resourcesSharedWithMe": "Risorse condivise con me",
|
|
||||||
"permissionRequestion": "Richiesta di permesso",
|
|
||||||
"permission": "Permesso",
|
|
||||||
"shares": "condivisioni",
|
|
||||||
"notBeingShared": "Questa risorsa non è in condivisione.",
|
|
||||||
"notHaveAnyResource": "Non hai nessuna risorsa",
|
|
||||||
"noResourcesSharedWithYou": "Non ci sono risorse condivise con te",
|
|
||||||
"havePermissionRequestsWaitingForApproval": "Hai {0} richiesta(e) di permesso in attesa di approvazione.",
|
|
||||||
"clickHereForDetails": "Clicca qui per i dettagli.",
|
|
||||||
"resourceIsNotBeingShared": "La risorsa non è in condivisione",
|
|
||||||
"locale_it": "Italiano",
|
|
||||||
"applicaitonName": "Nome",
|
|
||||||
"applicationType": "Tipo applicazione",
|
|
||||||
"applicationInUse": "In-use app only",
|
|
||||||
"clearAllFilter": "Azzera tutti i filtri",
|
|
||||||
"activeFilters": "Filtri attivi",
|
|
||||||
"filterByName": "Filtra per nome ...",
|
|
||||||
"allApps": "Tutte le applicazioni",
|
|
||||||
"internalApps": "Applicazioni interne",
|
|
||||||
"thirdpartyApps": "Applicazioni di terze parti",
|
|
||||||
"appResults": "Risultati",
|
|
||||||
"clientNotFoundMessage": "Client non trovato.",
|
|
||||||
"authorizedProvider": "Provider autorizzato",
|
|
||||||
"authorizedProviderMessage": "Provider autorizzati collegati al tuo account",
|
|
||||||
"identityProvider": "Identity provider",
|
|
||||||
"identityProviderMessage": "Collegare il tuo account con gli identity provider che hai configurato",
|
|
||||||
"socialLogin": "Social Login",
|
|
||||||
"userDefined": "Definito dall'utente",
|
|
||||||
"removeAccess": "Rimuovi accesso",
|
|
||||||
"removeAccessMessage": "Devi concedere di nuovo l'accesso, se vuoi utilizzare l'account di questa applicazione.",
|
|
||||||
"authenticatorStatusMessage": "L'autenticazione a due fattori è attualmente",
|
|
||||||
"authenticatorFinishSetUpTitle": "La tua autenticazione a due fattori",
|
|
||||||
"authenticatorFinishSetUpMessage":
|
|
||||||
"Ogni volta che effettui l'accesso al tuo account Keycloak, ti verrà richiesto di fornire il tuo codice di autenticazione a due fattori.",
|
|
||||||
"authenticatorSubTitle": "Imposta l'autenticazione a due fattori",
|
|
||||||
"authenticatorSubMessage":
|
|
||||||
"Per incrementare la sicurezza del tuo account, attiva almeno uno dei metodi disponibili per l'autenticazione a due fattori.",
|
|
||||||
"authenticatorMobileTitle": "Autenticatore mobile",
|
|
||||||
"authenticatorMobileMessage": "Utilizza l'autenticatore mobile per ottenere i codici di verifica per l'autenticazione a due fattori.",
|
|
||||||
"authenticatorMobileFinishSetUpMessage": "L'autenticatore è stato collegato al tuo telefono.",
|
|
||||||
"authenticatorActionSetup": "Set up",
|
|
||||||
"authenticatorSMSTitle": "Codice SMS",
|
|
||||||
"authenticatorSMSMessage": "Keycloak invierà il codice di verifica al tuo telefono per l'autenticazione a due fattori.",
|
|
||||||
"authenticatorSMSFinishSetUpMessage": "I messaggi di testo vengono inviati a",
|
|
||||||
"authenticatorDefaultStatus": "Default",
|
|
||||||
"authenticatorChangePhone": "Cambia numero di telefono",
|
|
||||||
"authenticatorBackupCodesTitle": "Codici di backup",
|
|
||||||
"authenticatorBackupCodesMessage": "Ottieni i tuoi codici di backup a otto cifre",
|
|
||||||
"authenticatorBackupCodesFinishSetUpMessage": "Sono stati generati dodici codici di backup. Ognuno può essere usato una sola volta.",
|
|
||||||
"authenticatorMobileSetupTitle": "Setup autenticatore mobile",
|
|
||||||
"smscodeIntroMessage": "Inserisci il tuo numero di telefono e ti verrà inviato un codice di verifica.",
|
|
||||||
"mobileSetupStep1": "Installa un'applicazione di autenticazione sul tuo telefono. Sono supportate le applicazioni qui elencate.",
|
|
||||||
"mobileSetupStep2": "Apri l'applicazione e scansiona il codice QR:",
|
|
||||||
"mobileSetupStep3": "Inserisci il codice monouso fornito dall'applicazione e clicca Salva per completare il setup.",
|
|
||||||
"scanBarCode": "Vuoi scansionare il codice QR?",
|
|
||||||
"enterBarCode": "Inserisci il codice monouso",
|
|
||||||
"doCopy": "Copia",
|
|
||||||
"doFinish": "Termina",
|
|
||||||
"authenticatorSMSCodeSetupTitle": "Setup codice SMS",
|
|
||||||
"chooseYourCountry": "Scegli la tua nazione",
|
|
||||||
"enterYourPhoneNumber": "Inserisci il tuo numero di telefono",
|
|
||||||
"sendVerficationCode": "Invia il codice di verifica",
|
|
||||||
"enterYourVerficationCode": "Inserisci il codice di verifica",
|
|
||||||
"authenticatorBackupCodesSetupTitle": "Setup backup codici",
|
|
||||||
"backupcodesIntroMessage":
|
|
||||||
"Se non disponi più del tuo telefono, puoi comunque accedere al tuo account attraverso i codici di backup. Conservali in un posto sicuro e accessibile.",
|
|
||||||
"realmName": "Realm",
|
|
||||||
"doDownload": "Download",
|
|
||||||
"doPrint": "Stampa",
|
|
||||||
"backupCodesTips-1": "Ogni codice di backup può essere usato una sola volta.",
|
|
||||||
"backupCodesTips-2": "Questi codici sono stati generati il",
|
|
||||||
"generateNewBackupCodes": "Genera dei nuovi codici di backup",
|
|
||||||
"backupCodesTips-3": "Quando generi dei nuovi codici di backup, quelli attuali non funzioneranno più.",
|
|
||||||
"backtoAuthenticatorPage": "Torna alla pagina dell'autenticatore",
|
|
||||||
"resources": "Risorse",
|
|
||||||
"sharedwithMe": "Condiviso con me",
|
|
||||||
"share": "Condiviso",
|
|
||||||
"sharedwith": "Condiviso con",
|
|
||||||
"accessPermissions": "Permessi di accesso",
|
|
||||||
"permissionRequests": "Richieste di permesso",
|
|
||||||
"approve": "Approva",
|
|
||||||
"approveAll": "Approva tutti",
|
|
||||||
"people": "persone",
|
|
||||||
"perPage": "per pagina",
|
|
||||||
"currentPage": "Pagina corrente",
|
|
||||||
"sharetheResource": "Condividi la risorsa",
|
|
||||||
"group": "Gruppo",
|
|
||||||
"selectPermission": "Seleziona permessi",
|
|
||||||
"addPeople": "Aggiungi persone con le quali condividere la tua risorsa",
|
|
||||||
"addTeam": "Aggiungi gruppi con i quali condividere la tua risorsa",
|
|
||||||
"myPermissions": "Miei permessi",
|
|
||||||
"waitingforApproval": "Attesa dell'approvazione",
|
|
||||||
"anyPermission": "Qualsiasi permesso",
|
|
||||||
"openshift.scope.user_info": "Informazioni utente",
|
|
||||||
"openshift.scope.user_check-access": "Informazioni per l'accesso dell'utente",
|
|
||||||
"openshift.scope.user_full": "Accesso completo",
|
|
||||||
"openshift.scope.list-projects": "Elenca progetti"
|
|
||||||
};
|
|
||||||
|
|
||||||
export default messages;
|
|
||||||
/* spell-checker: enable */
|
|
@ -1,324 +0,0 @@
|
|||||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
|
||||||
//PLEASE DO NOT EDIT MANUALLY
|
|
||||||
|
|
||||||
/* spell-checker: disable */
|
|
||||||
const messages = {
|
|
||||||
"doSave": "保存",
|
|
||||||
"doCancel": "キャンセル",
|
|
||||||
"doLogOutAllSessions": "全セッションからログアウト",
|
|
||||||
"doRemove": "削除",
|
|
||||||
"doAdd": "追加",
|
|
||||||
"doSignOut": "サインアウト",
|
|
||||||
"doLogIn": "ログイン",
|
|
||||||
"doLink": "リンク",
|
|
||||||
"editAccountHtmlTitle": "アカウントの編集",
|
|
||||||
"personalInfoHtmlTitle": "個人情報",
|
|
||||||
"federatedIdentitiesHtmlTitle": "連携済みアイデンティティー",
|
|
||||||
"accountLogHtmlTitle": "アカウントログ",
|
|
||||||
"changePasswordHtmlTitle": "パスワード変更",
|
|
||||||
"deviceActivityHtmlTitle": "デバイス・アクティビティー",
|
|
||||||
"sessionsHtmlTitle": "セッション",
|
|
||||||
"accountManagementTitle": "Keycloakアカウント管理",
|
|
||||||
"authenticatorTitle": "オーセンティケーター",
|
|
||||||
"applicationsHtmlTitle": "アプリケーション",
|
|
||||||
"linkedAccountsHtmlTitle": "リンクされたアカウント",
|
|
||||||
"accountManagementWelcomeMessage": "Keycloakアカウント管理へようこそ",
|
|
||||||
"personalInfoIntroMessage": "基本情報を管理する",
|
|
||||||
"accountSecurityTitle": "アカウント・セキュリティー",
|
|
||||||
"accountSecurityIntroMessage": "パスワードとアカウント・アクセスを制御する",
|
|
||||||
"applicationsIntroMessage": "アカウントへアクセスするためにアプリのパーミッションを追跡して管理する",
|
|
||||||
"resourceIntroMessage": "チームメンバー間でリソースを共有する",
|
|
||||||
"passwordLastUpdateMessage": "パスワードは更新されました",
|
|
||||||
"updatePasswordTitle": "パスワードの更新",
|
|
||||||
"updatePasswordMessageTitle": "強力なパスワードを選択してください",
|
|
||||||
"updatePasswordMessage":
|
|
||||||
"強力なパスワードは、数字、文字、記号を含みます。推測が難しく、実在する言葉に似ておらず、このアカウントだけで使用されています。",
|
|
||||||
"personalSubTitle": "個人情報",
|
|
||||||
"personalSubMessage": "この基本情報を管理してください:名、姓、メール",
|
|
||||||
"authenticatorCode": "ワンタイムコード",
|
|
||||||
"email": "Eメール",
|
|
||||||
"firstName": "名",
|
|
||||||
"givenName": "名",
|
|
||||||
"fullName": "氏名",
|
|
||||||
"lastName": "姓",
|
|
||||||
"familyName": "姓",
|
|
||||||
"password": "パスワード",
|
|
||||||
"currentPassword": "現在のパスワード",
|
|
||||||
"passwordConfirm": "新しいパスワード(確認)",
|
|
||||||
"passwordNew": "新しいパスワード",
|
|
||||||
"username": "ユーザー名",
|
|
||||||
"address": "住所",
|
|
||||||
"street": "番地",
|
|
||||||
"locality": "市区町村",
|
|
||||||
"region": "都道府県",
|
|
||||||
"postal_code": "郵便番号",
|
|
||||||
"country": "国",
|
|
||||||
"emailVerified": "確認済みEメール",
|
|
||||||
"gssDelegationCredential": "GSS委譲クレデンシャル",
|
|
||||||
"profileScopeConsentText": "ユーザー・プロファイル",
|
|
||||||
"emailScopeConsentText": "メールアドレス",
|
|
||||||
"addressScopeConsentText": "アドレス",
|
|
||||||
"phoneScopeConsentText": "電話番号",
|
|
||||||
"offlineAccessScopeConsentText": "オフライン・アクセス",
|
|
||||||
"samlRoleListScopeConsentText": "ロール",
|
|
||||||
"rolesScopeConsentText": "ユーザーロール",
|
|
||||||
"role_admin": "管理者",
|
|
||||||
"role_realm-admin": "レルム管理者",
|
|
||||||
"role_create-realm": "レルムの作成",
|
|
||||||
"role_view-realm": "レルムの参照",
|
|
||||||
"role_view-users": "ユーザーの参照",
|
|
||||||
"role_view-applications": "アプリケーションの参照",
|
|
||||||
"role_view-clients": "クライアントの参照",
|
|
||||||
"role_view-events": "イベントの参照",
|
|
||||||
"role_view-identity-providers": "アイデンティティー・プロバイダーの参照",
|
|
||||||
"role_view-consent": "同意の参照",
|
|
||||||
"role_manage-realm": "レルムの管理",
|
|
||||||
"role_manage-users": "ユーザーの管理",
|
|
||||||
"role_manage-applications": "アプリケーションの管理",
|
|
||||||
"role_manage-identity-providers": "アイデンティティー・プロバイダーの管理",
|
|
||||||
"role_manage-clients": "クライアントの管理",
|
|
||||||
"role_manage-events": "イベントの管理",
|
|
||||||
"role_view-profile": "プロファイルの参照",
|
|
||||||
"role_manage-account": "アカウントの管理",
|
|
||||||
"role_manage-account-links": "アカウントリンクの管理",
|
|
||||||
"role_manage-consent": "同意の管理",
|
|
||||||
"role_read-token": "トークンの参照",
|
|
||||||
"role_offline-access": "オフライン・アクセス",
|
|
||||||
"role_uma_authorization": "パーミッションの取得",
|
|
||||||
"client_account": "アカウント",
|
|
||||||
"client_account-console": "アカウント・コンソール",
|
|
||||||
"client_security-admin-console": "セキュリティー管理コンソール",
|
|
||||||
"client_admin-cli": "管理CLI",
|
|
||||||
"client_realm-management": "レルム管理",
|
|
||||||
"client_broker": "ブローカー",
|
|
||||||
"requiredFields": "必須",
|
|
||||||
"allFieldsRequired": "全ての入力項目が必須",
|
|
||||||
"backToApplication": "« アプリケーションに戻る",
|
|
||||||
"backTo": "{0}に戻る",
|
|
||||||
"date": "日付",
|
|
||||||
"event": "イベント",
|
|
||||||
"ip": "IP",
|
|
||||||
"client": "クライアント",
|
|
||||||
"clients": "クライアント",
|
|
||||||
"details": "詳細",
|
|
||||||
"started": "開始",
|
|
||||||
"lastAccess": "最終アクセス",
|
|
||||||
"expires": "有効期限",
|
|
||||||
"applications": "アプリケーション",
|
|
||||||
"account": "アカウント",
|
|
||||||
"federatedIdentity": "連携済みアイデンティティー",
|
|
||||||
"authenticator": "オーセンティケーター",
|
|
||||||
"device-activity": "デバイス・アクティビティー",
|
|
||||||
"sessions": "セッション",
|
|
||||||
"log": "ログ",
|
|
||||||
"application": "アプリケーション",
|
|
||||||
"availableRoles": "利用可能なロール",
|
|
||||||
"grantedPermissions": "許可されたパーミッション",
|
|
||||||
"grantedPersonalInfo": "許可された個人情報",
|
|
||||||
"additionalGrants": "追加の許可",
|
|
||||||
"action": "アクション",
|
|
||||||
"inResource": "in",
|
|
||||||
"fullAccess": "フルアクセス",
|
|
||||||
"offlineToken": "オフライン・トークン",
|
|
||||||
"revoke": "許可の取り消し",
|
|
||||||
"configureAuthenticators": "設定済みのオーセンティケーター",
|
|
||||||
"mobile": "モバイル",
|
|
||||||
"totpStep1": "モバイルに以下のアプリケーションのいずれかをインストールしてください。",
|
|
||||||
"totpStep2": "アプリケーションを開き、バーコードをスキャンしてください。",
|
|
||||||
"totpStep3": "アプリケーションで提供されたワンタイムコードを入力して保存をクリックし、セットアップを完了してください。",
|
|
||||||
"totpStep3DeviceName": "OTPデバイスの管理に役立つようなデバイス名を指定してください。",
|
|
||||||
"totpManualStep2": "アプリケーションを開き、キーを入力してください。",
|
|
||||||
"totpManualStep3": "アプリケーションが設定できる場合は、次の設定値を使用してください。",
|
|
||||||
"totpUnableToScan": "スキャンできませんか?",
|
|
||||||
"totpScanBarcode": "バーコードをスキャンしますか?",
|
|
||||||
"totp.totp": "時間ベース",
|
|
||||||
"totp.hotp": "カウンターベース",
|
|
||||||
"totpType": "タイプ",
|
|
||||||
"totpAlgorithm": "アルゴリズム",
|
|
||||||
"totpDigits": "数字",
|
|
||||||
"totpInterval": "間隔",
|
|
||||||
"totpCounter": "カウンター",
|
|
||||||
"totpDeviceName": "デバイス名",
|
|
||||||
"missingUsernameMessage": "ユーザー名を入力してください。",
|
|
||||||
"missingFirstNameMessage": "名を入力してください。",
|
|
||||||
"invalidEmailMessage": "無効なメールアドレスです。",
|
|
||||||
"missingLastNameMessage": "姓を入力してください。",
|
|
||||||
"missingEmailMessage": "Eメールを入力してください。",
|
|
||||||
"missingPasswordMessage": "パスワードを入力してください。",
|
|
||||||
"notMatchPasswordMessage": "パスワードが一致していません。",
|
|
||||||
"invalidUserMessage": "無効なユーザーです。",
|
|
||||||
"missingTotpMessage": "オーセンティケーター・コードを入力してください。",
|
|
||||||
"missingTotpDeviceNameMessage": "デバイス名を指定してください。",
|
|
||||||
"invalidPasswordExistingMessage": "既存のパスワードが不正です。",
|
|
||||||
"invalidPasswordConfirmMessage": "新しいパスワード(確認)と一致していません。",
|
|
||||||
"invalidTotpMessage": "無効なオーセンティケーター・コードです。",
|
|
||||||
"usernameExistsMessage": "既に存在するユーザー名です。",
|
|
||||||
"emailExistsMessage": "既に存在するEメールです。",
|
|
||||||
"readOnlyUserMessage": "読み取り専用のため、アカウントを更新することはできません。",
|
|
||||||
"readOnlyUsernameMessage": "読み取り専用のため、ユーザー名を更新することはできません。",
|
|
||||||
"readOnlyPasswordMessage": "読み取り専用のため、パスワードを更新することはできません。",
|
|
||||||
"successTotpMessage": "モバイル・オーセンティケーターが設定されました。",
|
|
||||||
"successTotpRemovedMessage": "モバイル・オーセンティケーターが削除されました。",
|
|
||||||
"successGrantRevokedMessage": "許可が正常に取り消しされました。",
|
|
||||||
"accountUpdatedMessage": "アカウントが更新されました。",
|
|
||||||
"accountPasswordUpdatedMessage": "パスワードが更新されました。",
|
|
||||||
"missingIdentityProviderMessage": "アイデンティティー・プロバイダーが指定されていません。",
|
|
||||||
"invalidFederatedIdentityActionMessage": "無効または存在しないアクションです。",
|
|
||||||
"identityProviderNotFoundMessage": "指定されたアイデンティティー・プロバイダーが見つかりません。",
|
|
||||||
"federatedIdentityLinkNotActiveMessage": "このアイデンティティーは有効ではありません。",
|
|
||||||
"federatedIdentityRemovingLastProviderMessage": "パスワードがないため、最後の連携済みアイデンティティーが削除できません。",
|
|
||||||
"identityProviderRedirectErrorMessage": "アイデンティティー・プロバイダーへのリダイレクトに失敗しました。",
|
|
||||||
"identityProviderRemovedMessage": "アイデンティティー・プロバイダーが正常に削除されました。",
|
|
||||||
"identityProviderAlreadyLinkedMessage": "{0}から返された連携済みアイデンティティーは既に他のユーザーに関連付けされています。",
|
|
||||||
"staleCodeAccountMessage": "有効期限切れです。再度お試しください。",
|
|
||||||
"consentDenied": "同意が拒否されました。",
|
|
||||||
"accountDisabledMessage": "アカウントが無効です。管理者に連絡してください。",
|
|
||||||
"accountTemporarilyDisabledMessage": "アカウントが一時的に無効です。管理者に連絡するか、しばらく時間をおいてから再度お試しください。",
|
|
||||||
"invalidPasswordMinLengthMessage": "無効なパスワード: 最小{0}の長さが必要です。",
|
|
||||||
"invalidPasswordMinLowerCaseCharsMessage": "無効なパスワード: 少なくとも{0}文字の小文字を含む必要があります。",
|
|
||||||
"invalidPasswordMinDigitsMessage": "無効なパスワード: 少なくとも{0}文字の数字を含む必要があります。",
|
|
||||||
"invalidPasswordMinUpperCaseCharsMessage": "無効なパスワード:少なくとも{0}文字の大文字を含む必要があります。",
|
|
||||||
"invalidPasswordMinSpecialCharsMessage": "無効なパスワード: 少なくとも{0}文字の特殊文字を含む必要があります。",
|
|
||||||
"invalidPasswordNotUsernameMessage": "無効なパスワード: ユーザー名と同じパスワードは禁止されています。",
|
|
||||||
"invalidPasswordRegexPatternMessage": "無効なパスワード: 正規表現パターンと一致しません。",
|
|
||||||
"invalidPasswordHistoryMessage": "無効なパスワード: 最近の{0}パスワードのいずれかと同じパスワードは禁止されています。",
|
|
||||||
"invalidPasswordBlacklistedMessage": "無効なパスワード: パスワードがブラックリストに含まれています。",
|
|
||||||
"invalidPasswordGenericMessage": "無効なパスワード: 新しいパスワードはパスワード・ポリシーと一致しません。",
|
|
||||||
"myResources": "マイリソース",
|
|
||||||
"myResourcesSub": "マイリソース",
|
|
||||||
"doDeny": "拒否",
|
|
||||||
"doRevoke": "取り消し",
|
|
||||||
"doApprove": "承認",
|
|
||||||
"doRemoveSharing": "共有の削除",
|
|
||||||
"doRemoveRequest": "要求の削除",
|
|
||||||
"peopleAccessResource": "このリソースにアクセスできる人",
|
|
||||||
"resourceManagedPolicies": "このリソースへのアクセスを許可するパーミッション",
|
|
||||||
"resourceNoPermissionsGrantingAccess": "このリソースへのアクセスを許可する権限はありません",
|
|
||||||
"anyAction": "任意のアクション",
|
|
||||||
"description": "説明",
|
|
||||||
"name": "名前",
|
|
||||||
"scopes": "スコープ",
|
|
||||||
"resource": "リソース",
|
|
||||||
"user": "ユーザー",
|
|
||||||
"peopleSharingThisResource": "このリソースを共有している人",
|
|
||||||
"shareWithOthers": "他人と共有",
|
|
||||||
"needMyApproval": "承認が必要",
|
|
||||||
"requestsWaitingApproval": "承認待ちの要求",
|
|
||||||
"icon": "アイコン",
|
|
||||||
"requestor": "要求者",
|
|
||||||
"owner": "オーナー",
|
|
||||||
"resourcesSharedWithMe": "共有しているリソース",
|
|
||||||
"permissionRequestion": "パーミッションの要求",
|
|
||||||
"permission": "パーミッション",
|
|
||||||
"shares": "共有(複数)",
|
|
||||||
"notBeingShared": "このリソースは共有されていません。",
|
|
||||||
"notHaveAnyResource": "リソースがありません。",
|
|
||||||
"noResourcesSharedWithYou": "共有しているリソースはありません",
|
|
||||||
"havePermissionRequestsWaitingForApproval": "承認を待っている{0}個のパーミッションの要求があります。",
|
|
||||||
"clickHereForDetails": "詳細はこちらをクリックしてください。",
|
|
||||||
"resourceIsNotBeingShared": "リソースは共有されていません。",
|
|
||||||
"locale_ca": "Català",
|
|
||||||
"locale_de": "Deutsch",
|
|
||||||
"locale_en": "English",
|
|
||||||
"locale_es": "Español",
|
|
||||||
"locale_fr": "Français",
|
|
||||||
"locale_it": "Italian",
|
|
||||||
"locale_ja": "日本語",
|
|
||||||
"locale_nl": "Nederlands",
|
|
||||||
"locale_no": "Norsk",
|
|
||||||
"locale_lt": "Lietuvių",
|
|
||||||
"locale_pt-BR": "Português (Brasil)",
|
|
||||||
"locale_ru": "Русский",
|
|
||||||
"locale_sk": "Slovenčina",
|
|
||||||
"locale_sv": "Svenska",
|
|
||||||
"locale_tr": "Turkish",
|
|
||||||
"locale_zh-CN": "中文简体",
|
|
||||||
"applicaitonName": "名前",
|
|
||||||
"applicationType": "アプリケーション・タイプ",
|
|
||||||
"applicationInUse": "使用中のアプリケーションのみ",
|
|
||||||
"clearAllFilter": "すべてのフィルターをクリア",
|
|
||||||
"activeFilters": "アクティブなフィルター",
|
|
||||||
"filterByName": "名前でフィルタリング...",
|
|
||||||
"allApps": "すべてのアプリケーション",
|
|
||||||
"internalApps": "内部アプリケーション",
|
|
||||||
"thirdpartyApps": "サードパーティーのアプリケーション",
|
|
||||||
"appResults": "結果",
|
|
||||||
"clientNotFoundMessage": "クライアントが見つかりません。",
|
|
||||||
"authorizedProvider": "認可済みプロバイダー",
|
|
||||||
"authorizedProviderMessage": "アカウントにリンクされた認可済みプロバイダー",
|
|
||||||
"identityProvider": "アイデンティティー・プロバイダー",
|
|
||||||
"identityProviderMessage": "アカウントと設定したアイデンティティー・プロバイダーをリンクするには",
|
|
||||||
"socialLogin": "ソーシャル・ログイン",
|
|
||||||
"userDefined": "ユーザー定義",
|
|
||||||
"removeAccess": "アクセス権の削除",
|
|
||||||
"removeAccessMessage": "このアプリ・アカウントを使用する場合は、アクセス権を再度付与する必要があります。",
|
|
||||||
"authenticatorStatusMessage": "2要素認証は現在",
|
|
||||||
"authenticatorFinishSetUpTitle": "あなたの2要素認証",
|
|
||||||
"authenticatorFinishSetUpMessage": "Keycloakアカウントにサインインするたびに、2要素認証コードを入力するように求められます。",
|
|
||||||
"authenticatorSubTitle": "2要素認証を設定する",
|
|
||||||
"authenticatorSubMessage": "アカウントのセキュリティーを強化するには、利用可能な2要素認証の方式のうち少なくとも1つを有効にします。",
|
|
||||||
"authenticatorMobileTitle": "モバイル・オーセンティケーター",
|
|
||||||
"authenticatorMobileMessage": "モバイル・オーセンティケーターを使用して、2要素認証として確認コードを取得します。",
|
|
||||||
"authenticatorMobileFinishSetUpMessage": "オーセンティケーターはあなたの携帯電話にバインドされています。",
|
|
||||||
"authenticatorActionSetup": "セットアップ",
|
|
||||||
"authenticatorSMSTitle": "SMSコード",
|
|
||||||
"authenticatorSMSMessage": "Keycloakは、2要素認証として確認コードを携帯電話に送信します。",
|
|
||||||
"authenticatorSMSFinishSetUpMessage": "テキスト・メッセージが次の電話番号宛に送信されます:",
|
|
||||||
"authenticatorDefaultStatus": "デフォルト",
|
|
||||||
"authenticatorChangePhone": "電話番号の変更",
|
|
||||||
"authenticatorBackupCodesTitle": "バックアップ・コード",
|
|
||||||
"authenticatorBackupCodesMessage": "8桁のバックアップ・コードの入手",
|
|
||||||
"authenticatorBackupCodesFinishSetUpMessage": "この時点で12個のバックアップ・コードが生成されました。それぞれ一度だけ使用できます。",
|
|
||||||
"authenticatorMobileSetupTitle": "モバイル・オーセンティケーターのセットアップ",
|
|
||||||
"smscodeIntroMessage": "電話番号を入力すると、確認コードがあなたの電話に送信されます。",
|
|
||||||
"mobileSetupStep1":
|
|
||||||
"携帯電話にオーセンティケーター・アプリケーションをインストールします。ここにリストされているアプリケーションがサポートされています。",
|
|
||||||
"mobileSetupStep2": "アプリケーションを開き、バーコードをスキャンしてください。",
|
|
||||||
"mobileSetupStep3": "アプリケーションから提供されたワンタイムコードを入力し、保存をクリックしてセットアップを終了します。",
|
|
||||||
"scanBarCode": "バーコードをスキャンしますか?",
|
|
||||||
"enterBarCode": "ワンタイムコードを入力してください",
|
|
||||||
"doCopy": "コピー",
|
|
||||||
"doFinish": "終了",
|
|
||||||
"authenticatorSMSCodeSetupTitle": "SMSコードのセットアップ",
|
|
||||||
"chooseYourCountry": "国を選んでください",
|
|
||||||
"enterYourPhoneNumber": "電話番号を入力してください",
|
|
||||||
"sendVerficationCode": "確認コードの送信",
|
|
||||||
"enterYourVerficationCode": "確認コードを入力してください",
|
|
||||||
"authenticatorBackupCodesSetupTitle": "バックアップ・コードのセットアップ",
|
|
||||||
"backupcodesIntroMessage":
|
|
||||||
"携帯電話にアクセスできない場合でも、バックアップ・コードを使用してアカウントにログインできます。どこか安全でアクセス可能な場所に保管してください。",
|
|
||||||
"realmName": "レルム",
|
|
||||||
"doDownload": "ダウンロード",
|
|
||||||
"doPrint": "印刷",
|
|
||||||
"backupCodesTips-1": "各バックアップ・コードは1回使用できます。",
|
|
||||||
"backupCodesTips-2": "これらのコードはこの日に生成されました:",
|
|
||||||
"generateNewBackupCodes": "新しいバックアップ・コードを生成する",
|
|
||||||
"backupCodesTips-3": "新しいバックアップ・コードを生成すると、現在のコードは機能しなくなります。",
|
|
||||||
"backtoAuthenticatorPage": "オーセンティケーター・ページに戻る",
|
|
||||||
"resources": "リソース",
|
|
||||||
"sharedwithMe": "私と共有",
|
|
||||||
"share": "共有",
|
|
||||||
"sharedwith": "共有",
|
|
||||||
"accessPermissions": "アクセス・パーミッション",
|
|
||||||
"permissionRequests": "パーミッションの要求",
|
|
||||||
"approve": "承認",
|
|
||||||
"approveAll": "すべて承認",
|
|
||||||
"people": "人",
|
|
||||||
"perPage": "1ページあたり",
|
|
||||||
"currentPage": "現在のページ",
|
|
||||||
"sharetheResource": "リソースの共有",
|
|
||||||
"group": "グループ",
|
|
||||||
"selectPermission": "パーミッションを選択",
|
|
||||||
"addPeople": "あなたのリソースを共有する人を追加",
|
|
||||||
"addTeam": "あなたのリソースを共有するチームを追加",
|
|
||||||
"myPermissions": "私のパーミッション",
|
|
||||||
"waitingforApproval": "承認待ち",
|
|
||||||
"anyPermission": "任意のパーミッション",
|
|
||||||
"openshift.scope.user_info": "ユーザー情報",
|
|
||||||
"openshift.scope.user_check-access": "ユーザーアクセス情報",
|
|
||||||
"openshift.scope.user_full": "フルアクセス",
|
|
||||||
"openshift.scope.list-projects": "プロジェクトの一覧表示"
|
|
||||||
};
|
|
||||||
|
|
||||||
export default messages;
|
|
||||||
/* spell-checker: enable */
|
|
@ -1,143 +0,0 @@
|
|||||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
|
||||||
//PLEASE DO NOT EDIT MANUALLY
|
|
||||||
|
|
||||||
/* spell-checker: disable */
|
|
||||||
const messages = {
|
|
||||||
"doSave": "Saugoti",
|
|
||||||
"doCancel": "Atšaukti",
|
|
||||||
"doLogOutAllSessions": "Atjungti visas sesijas",
|
|
||||||
"doRemove": "Šalinti",
|
|
||||||
"doAdd": "Pridėti",
|
|
||||||
"doSignOut": "Atsijungti",
|
|
||||||
"editAccountHtmlTitle": "Redaguoti paskyrą",
|
|
||||||
"federatedIdentitiesHtmlTitle": "Susietos paskyros",
|
|
||||||
"accountLogHtmlTitle": "Paskyros žurnalas",
|
|
||||||
"changePasswordHtmlTitle": "Keisti slaptažodį",
|
|
||||||
"sessionsHtmlTitle": "Prisijungimo sesijos",
|
|
||||||
"accountManagementTitle": "Keycloak Naudotojų Administravimas",
|
|
||||||
"authenticatorTitle": "Autentifikatorius",
|
|
||||||
"applicationsHtmlTitle": "Programos",
|
|
||||||
"authenticatorCode": "Vienkartinis kodas",
|
|
||||||
"email": "El. paštas",
|
|
||||||
"firstName": "Vardas",
|
|
||||||
"givenName": "Pavardė",
|
|
||||||
"fullName": "Pilnas vardas",
|
|
||||||
"lastName": "Pavardė",
|
|
||||||
"familyName": "Pavardė",
|
|
||||||
"password": "Slaptažodis",
|
|
||||||
"passwordConfirm": "Pakartotas slaptažodis",
|
|
||||||
"passwordNew": "Naujas slaptažodis",
|
|
||||||
"username": "Naudotojo vardas",
|
|
||||||
"address": "Adresas",
|
|
||||||
"street": "Gatvė",
|
|
||||||
"locality": "Miestas arba vietovė",
|
|
||||||
"region": "Rajonas",
|
|
||||||
"postal_code": "Pašto kodas",
|
|
||||||
"country": "Šalis",
|
|
||||||
"emailVerified": "El. pašto adresas patvirtintas",
|
|
||||||
"gssDelegationCredential": "GSS prisijungimo duomenų delegavimas",
|
|
||||||
"role_admin": "Administratorius",
|
|
||||||
"role_realm-admin": "Srities administravimas",
|
|
||||||
"role_create-realm": "Kurti sritį",
|
|
||||||
"role_view-realm": "Peržiūrėti sritį",
|
|
||||||
"role_view-users": "Peržiūrėti naudotojus",
|
|
||||||
"role_view-applications": "Peržiūrėti programas",
|
|
||||||
"role_view-clients": "Peržiūrėti klientines programas",
|
|
||||||
"role_view-events": "Peržiūrėti įvykių žurnalą",
|
|
||||||
"role_view-identity-providers": "Peržiūrėti tapatybės teikėjus",
|
|
||||||
"role_manage-realm": "Valdyti sritis",
|
|
||||||
"role_manage-users": "Valdyti naudotojus",
|
|
||||||
"role_manage-applications": "Valdyti programas",
|
|
||||||
"role_manage-identity-providers": "Valdyti tapatybės teikėjus",
|
|
||||||
"role_manage-clients": "Valdyti programas",
|
|
||||||
"role_manage-events": "Valdyti įvykius",
|
|
||||||
"role_view-profile": "Peržiūrėti paskyrą",
|
|
||||||
"role_manage-account": "Valdyti paskyrą",
|
|
||||||
"role_read-token": "Skaityti prieigos rakšą",
|
|
||||||
"role_offline-access": "Darbas neprisijungus",
|
|
||||||
"role_uma_authorization": "Įgauti UMA autorizavimo teises",
|
|
||||||
"client_account": "Paskyra",
|
|
||||||
"client_security-admin-console": "Saugumo administravimo konsolė",
|
|
||||||
"client_admin-cli": "Administravimo CLI",
|
|
||||||
"client_realm-management": "Srities valdymas",
|
|
||||||
"client_broker": "Tarpininkas",
|
|
||||||
"requiredFields": "Privalomi laukai",
|
|
||||||
"allFieldsRequired": "Visi laukai yra privalomi",
|
|
||||||
"backToApplication": "« Grįžti į programą",
|
|
||||||
"backTo": "Atgal į {0}",
|
|
||||||
"date": "Data",
|
|
||||||
"event": "Įvykis",
|
|
||||||
"ip": "IP",
|
|
||||||
"client": "Klientas",
|
|
||||||
"clients": "Klientai",
|
|
||||||
"details": "Detaliau",
|
|
||||||
"started": "Sukūrimo laikas",
|
|
||||||
"lastAccess": "Vėliausia prieiga",
|
|
||||||
"expires": "Galioja iki",
|
|
||||||
"applications": "Programos",
|
|
||||||
"account": "Paskyra",
|
|
||||||
"federatedIdentity": "Susieta tapatybė",
|
|
||||||
"authenticator": "Autentifikatorius",
|
|
||||||
"sessions": "Sesijos",
|
|
||||||
"log": "Įvykiai",
|
|
||||||
"application": "Programa",
|
|
||||||
"availablePermissions": "Galimos teisės",
|
|
||||||
"grantedPermissions": "Įgalintos teisės",
|
|
||||||
"grantedPersonalInfo": "Įgalinta asmeninė informacija",
|
|
||||||
"additionalGrants": "Papildomi įgaliojimai",
|
|
||||||
"action": "Veiksmas",
|
|
||||||
"inResource": "yra",
|
|
||||||
"fullAccess": "Pilna prieiga",
|
|
||||||
"offlineToken": "Režimo neprisijungus raktas (token)",
|
|
||||||
"revoke": "Atšaukti įgaliojimą",
|
|
||||||
"configureAuthenticators": "Sukonfigūruotas autentifikatorius",
|
|
||||||
"mobile": "Mobilus",
|
|
||||||
"totpStep1":
|
|
||||||
'Įdiekite <a href="https://freeotp.github.io/" target="_blank">FreeOTP</a> arba Google Authenticator savo įrenginyje. Programėlės prieinamos <a href="https://play.google.com">Google Play</a> ir Apple App Store.',
|
|
||||||
"totpStep2": "Atidarykite programėlę ir nuskenuokite barkodą arba įveskite kodą.",
|
|
||||||
"totpStep3": "Įveskite programėlėje sugeneruotą vieną kartą galiojantį kodą ir paspauskite Saugoti norėdami prisijungti.",
|
|
||||||
"missingUsernameMessage": "Prašome įvesti naudotojo vardą.",
|
|
||||||
"missingFirstNameMessage": "Prašome įvesti vardą.",
|
|
||||||
"invalidEmailMessage": "Neteisingas el. pašto adresas.",
|
|
||||||
"missingLastNameMessage": "Prašome įvesti pavardę.",
|
|
||||||
"missingEmailMessage": "Prašome įvesti el. pašto adresą.",
|
|
||||||
"missingPasswordMessage": "Prašome įvesti slaptažodį.",
|
|
||||||
"notMatchPasswordMessage": "Slaptažodžiai nesutampa.",
|
|
||||||
"missingTotpMessage": "Prašome įvesti autentifikacijos kodą.",
|
|
||||||
"invalidPasswordExistingMessage": "Neteisingas dabartinis slaptažodis.",
|
|
||||||
"invalidPasswordConfirmMessage": "Pakartotas slaptažodis nesutampa.",
|
|
||||||
"invalidTotpMessage": "Neteisingas autentifikacijos kodas.",
|
|
||||||
"usernameExistsMessage": "Toks naudotojas jau egzistuoja.",
|
|
||||||
"emailExistsMessage": "El. pašto adresas jau egzistuoja.",
|
|
||||||
"readOnlyUserMessage": "Tik skaitymui sukonfigūruotos paskyros duomenų atnaujinti neleidžiama.",
|
|
||||||
"readOnlyPasswordMessage": "Tik skaitymui sukonfigūruotos paskyros slaptažodžio atnaujinti neleidžiama.",
|
|
||||||
"successTotpMessage": "Mobilus autentifikatorius sukonfigūruotas.",
|
|
||||||
"successTotpRemovedMessage": "Mobilus autentifikatorius pašalintas.",
|
|
||||||
"successGrantRevokedMessage": "Įgalinimas pašalintas sėkmingai.",
|
|
||||||
"accountUpdatedMessage": "Jūsų paskyros duomenys sėkmingai atnaujinti.",
|
|
||||||
"accountPasswordUpdatedMessage": "Jūsų paskyros slaptažodis pakeistas.",
|
|
||||||
"missingIdentityProviderMessage": "Nenurodytas tapatybės teikėjas.",
|
|
||||||
"invalidFederatedIdentityActionMessage": "Neteisingas arba nežinomas veiksmas.",
|
|
||||||
"identityProviderNotFoundMessage": "Nurodytas tapatybės teikėjas nerastas.",
|
|
||||||
"federatedIdentityLinkNotActiveMessage": "Nurodyta susieta tapatybė neaktyvi.",
|
|
||||||
"federatedIdentityRemovingLastProviderMessage":
|
|
||||||
"Jūs negalite pašalinti paskutinio tapatybės teikėjo sąsajos, nes Jūs neturite nusistatę paskyros slaptažodžio.",
|
|
||||||
"identityProviderRedirectErrorMessage": "Klaida nukreipiant į tapatybės teikėjo puslapį.",
|
|
||||||
"identityProviderRemovedMessage": "Tapatybės teikėjas sėkmingai pašalintas.",
|
|
||||||
"identityProviderAlreadyLinkedMessage": "Susieta tapatybė iš {0} jau susieta su kita paskyra.",
|
|
||||||
"staleCodeAccountMessage": "Puslapio galiojimas baigėsi. Bandykite dar kartą.",
|
|
||||||
"consentDenied": "Prieiga draudžiama.",
|
|
||||||
"accountDisabledMessage": "Paskyros galiojimas sustabdytas, kreipkitės į administratorių.",
|
|
||||||
"accountTemporarilyDisabledMessage": "Paskyros galiojimas laikinai sustabdytas. Kreipkitės į administratorių arba pabandykite vėliau.",
|
|
||||||
"invalidPasswordMinLengthMessage": "Per trumpas slaptažodis: mažiausias ilgis {0}.",
|
|
||||||
"invalidPasswordMinLowerCaseCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} mažąją raidę.",
|
|
||||||
"invalidPasswordMinDigitsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} skaitmenį.",
|
|
||||||
"invalidPasswordMinUpperCaseCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} didžiąją raidę.",
|
|
||||||
"invalidPasswordMinSpecialCharsMessage": "Neteisingas slaptažodis: privaloma įvesti {0} specialų simbolį.",
|
|
||||||
"invalidPasswordNotUsernameMessage": "Neteisingas slaptažodis: slaptažodis negali sutapti su naudotojo vardu.",
|
|
||||||
"invalidPasswordRegexPatternMessage": "Neteisingas slaptažodis: slaptažodis netenkina regex taisyklės(ių).",
|
|
||||||
"invalidPasswordHistoryMessage": "Neteisingas slaptažodis: slaptažodis negali sutapti su prieš tai buvusiais {0} slaptažodžiais."
|
|
||||||
};
|
|
||||||
|
|
||||||
export default messages;
|
|
||||||
/* spell-checker: enable */
|
|
@ -1,143 +0,0 @@
|
|||||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
|
||||||
//PLEASE DO NOT EDIT MANUALLY
|
|
||||||
|
|
||||||
/* spell-checker: disable */
|
|
||||||
const messages = {
|
|
||||||
"doSave": "Opslaan",
|
|
||||||
"doCancel": "Annuleer",
|
|
||||||
"doLogOutAllSessions": "Alle sessies uitloggen",
|
|
||||||
"doRemove": "Verwijder",
|
|
||||||
"doAdd": "Voeg toe",
|
|
||||||
"doSignOut": "Afmelden",
|
|
||||||
"editAccountHtmlTitle": "Bewerk account",
|
|
||||||
"federatedIdentitiesHtmlTitle": "Federated Identities",
|
|
||||||
"accountLogHtmlTitle": "Account log",
|
|
||||||
"changePasswordHtmlTitle": "Verander wachtwoord",
|
|
||||||
"sessionsHtmlTitle": "Sessies",
|
|
||||||
"accountManagementTitle": "Keycloak Accountbeheer",
|
|
||||||
"authenticatorTitle": "Authenticator",
|
|
||||||
"applicationsHtmlTitle": "Toepassingen",
|
|
||||||
"authenticatorCode": "Eenmalige code",
|
|
||||||
"email": "E-mailadres",
|
|
||||||
"firstName": "Voornaam",
|
|
||||||
"givenName": "Voornaam",
|
|
||||||
"fullName": "Volledige naam",
|
|
||||||
"lastName": "Achternaam",
|
|
||||||
"familyName": "Achternaam",
|
|
||||||
"password": "Wachtwoord",
|
|
||||||
"passwordConfirm": "Bevestiging",
|
|
||||||
"passwordNew": "Nieuw Wachtwoord",
|
|
||||||
"username": "Gebruikersnaam",
|
|
||||||
"address": "Adres",
|
|
||||||
"street": "Straat",
|
|
||||||
"locality": "Stad of plaats",
|
|
||||||
"region": "Staat, provincie of regio",
|
|
||||||
"postal_code": "Postcode",
|
|
||||||
"country": "Land",
|
|
||||||
"emailVerified": "E-mailadres geverifieerd",
|
|
||||||
"gssDelegationCredential": "GSS gedelegeerde aanmeldgegevens",
|
|
||||||
"role_admin": "Beheer",
|
|
||||||
"role_realm-admin": "Realmbeheer",
|
|
||||||
"role_create-realm": "Creëer realm",
|
|
||||||
"role_view-realm": "Bekijk realm",
|
|
||||||
"role_view-users": "Bekijk gebruikers",
|
|
||||||
"role_view-applications": "Bekijk toepassingen",
|
|
||||||
"role_view-clients": "Bekijk clients",
|
|
||||||
"role_view-events": "Bekijk gebeurtenissen",
|
|
||||||
"role_view-identity-providers": "Bekijk identity providers",
|
|
||||||
"role_manage-realm": "Beheer realm",
|
|
||||||
"role_manage-users": "Beheer gebruikers",
|
|
||||||
"role_manage-applications": "Beheer toepassingen",
|
|
||||||
"role_manage-identity-providers": "Beheer identity providers",
|
|
||||||
"role_manage-clients": "Beheer clients",
|
|
||||||
"role_manage-events": "Beheer gebeurtenissen",
|
|
||||||
"role_view-profile": "Bekijk profiel",
|
|
||||||
"role_manage-account": "Beheer account",
|
|
||||||
"role_manage-account-links": "Beheer accountkoppelingen",
|
|
||||||
"role_read-token": "Lees token",
|
|
||||||
"role_offline-access": "Offline toegang",
|
|
||||||
"role_uma_authorization": "Verkrijg UMA rechten",
|
|
||||||
"client_account": "Account",
|
|
||||||
"client_security-admin-console": "Console Veligheidsbeheer",
|
|
||||||
"client_admin-cli": "Beheer CLI",
|
|
||||||
"client_realm-management": "Realmbeheer",
|
|
||||||
"client_broker": "Broker",
|
|
||||||
"requiredFields": "Verplichte velden",
|
|
||||||
"allFieldsRequired": "Alle velden verplicht",
|
|
||||||
"backToApplication": "« Terug naar toepassing",
|
|
||||||
"backTo": "Terug naar {0}",
|
|
||||||
"date": "Datum",
|
|
||||||
"event": "Gebeurtenis",
|
|
||||||
"ip": "IP",
|
|
||||||
"client": "Client",
|
|
||||||
"clients": "Clients",
|
|
||||||
"details": "Details",
|
|
||||||
"started": "Gestart",
|
|
||||||
"lastAccess": "Laatste toegang",
|
|
||||||
"expires": "Vervalt",
|
|
||||||
"applications": "Toepassingen",
|
|
||||||
"account": "Account",
|
|
||||||
"federatedIdentity": "Federated Identity",
|
|
||||||
"authenticator": "Authenticator",
|
|
||||||
"sessions": "Sessies",
|
|
||||||
"log": "Log",
|
|
||||||
"application": "Toepassing",
|
|
||||||
"availablePermissions": "Beschikbare rechten",
|
|
||||||
"grantedPermissions": "Gegunde rechten",
|
|
||||||
"grantedPersonalInfo": "Gegunde Persoonsgegevens",
|
|
||||||
"additionalGrants": "Verdere vergunningen",
|
|
||||||
"action": "Actie",
|
|
||||||
"inResource": "in",
|
|
||||||
"fullAccess": "Volledige toegang",
|
|
||||||
"offlineToken": "Offline Token",
|
|
||||||
"revoke": "Vergunning intrekken",
|
|
||||||
"configureAuthenticators": "Ingestelde authenticators",
|
|
||||||
"mobile": "Mobiel nummer",
|
|
||||||
"totpStep1": "Installeer een van de onderstaande applicaties op uw mobiele apparaat:",
|
|
||||||
"totpStep2": "Open de toepassing en scan de QR-code of voer de sleutel in.",
|
|
||||||
"totpStep3": "Voer de door de toepassing gegeven eenmalige code in en klik op Opslaan om de configuratie af te ronden.",
|
|
||||||
"missingUsernameMessage": "Gebruikersnaam ontbreekt.",
|
|
||||||
"missingFirstNameMessage": "Voornaam onbreekt.",
|
|
||||||
"invalidEmailMessage": "Ongeldig e-mailadres.",
|
|
||||||
"missingLastNameMessage": "Achternaam ontbreekt.",
|
|
||||||
"missingEmailMessage": "E-mailadres ontbreekt.",
|
|
||||||
"missingPasswordMessage": "Wachtwoord ontbreekt.",
|
|
||||||
"notMatchPasswordMessage": "Wachtwoorden komen niet overeen.",
|
|
||||||
"missingTotpMessage": "Authenticatiecode ontbreekt.",
|
|
||||||
"invalidPasswordExistingMessage": "Ongeldig bestaand wachtwoord.",
|
|
||||||
"invalidPasswordConfirmMessage": "Wachtwoordbevestiging komt niet overeen.",
|
|
||||||
"invalidTotpMessage": "Ongeldige authenticatiecode.",
|
|
||||||
"emailExistsMessage": "E-mailadres bestaat reeds.",
|
|
||||||
"readOnlyUserMessage": "U kunt uw account niet bijwerken aangezien het account alleen-lezen is.",
|
|
||||||
"readOnlyPasswordMessage": "U kunt uw wachtwoord niet wijzigen omdat uw account alleen-lezen is.",
|
|
||||||
"successTotpMessage": "Mobiele authenticator geconfigureerd.",
|
|
||||||
"successTotpRemovedMessage": "Mobiele authenticator verwijderd.",
|
|
||||||
"successGrantRevokedMessage": "Vergunning succesvol ingetrokken",
|
|
||||||
"accountUpdatedMessage": "Uw account is gewijzigd.",
|
|
||||||
"accountPasswordUpdatedMessage": "Uw wachtwoord is gewijzigd.",
|
|
||||||
"missingIdentityProviderMessage": "Geen identity provider aangegeven.",
|
|
||||||
"invalidFederatedIdentityActionMessage": "Ongeldige of ontbrekende actie op federated identity.",
|
|
||||||
"identityProviderNotFoundMessage": "Gespecificeerde identity provider niet gevonden.",
|
|
||||||
"federatedIdentityLinkNotActiveMessage": "Deze federated identity is niet langer geldig.",
|
|
||||||
"federatedIdentityRemovingLastProviderMessage":
|
|
||||||
"U kunt de laatste federated identity provider niet verwijderen aangezien u dan niet langer zou kunnen inloggen.",
|
|
||||||
"identityProviderRedirectErrorMessage": "Kon niet herverwijzen naar identity provider.",
|
|
||||||
"identityProviderRemovedMessage": "Identity provider met succes verwijderd.",
|
|
||||||
"identityProviderAlreadyLinkedMessage": "Door {0} teruggegeven federated identity is al gekoppeld aan een andere gebruiker.",
|
|
||||||
"staleCodeAccountMessage": "De pagina is verlopen. Probeer het nogmaals.",
|
|
||||||
"consentDenied": "Toestemming geweigerd",
|
|
||||||
"accountDisabledMessage": "Account is gedeactiveerd. Contacteer de beheerder.",
|
|
||||||
"accountTemporarilyDisabledMessage": "Account is tijdelijk deactiveerd, neem contact op met de beheerder of probeer het later opnieuw.",
|
|
||||||
"invalidPasswordMinLengthMessage": "Ongeldig wachtwoord: de minimale lengte is {0} karakters.",
|
|
||||||
"invalidPasswordMinLowerCaseCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} kleine letters bevatten.",
|
|
||||||
"invalidPasswordMinDigitsMessage": "Ongeldig wachtwoord: het moet minstens {0} getallen bevatten.",
|
|
||||||
"invalidPasswordMinUpperCaseCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} hoofdletters bevatten.",
|
|
||||||
"invalidPasswordMinSpecialCharsMessage": "Ongeldig wachtwoord: het moet minstens {0} speciale karakters bevatten.",
|
|
||||||
"invalidPasswordNotUsernameMessage": "Ongeldig wachtwoord: het mag niet overeenkomen met de gebruikersnaam.",
|
|
||||||
"invalidPasswordRegexPatternMessage": "Ongeldig wachtwoord: het voldoet niet aan het door de beheerder ingestelde patroon.",
|
|
||||||
"invalidPasswordHistoryMessage": "Ongeldig wachtwoord: het mag niet overeen komen met een van de laatste {0} wachtwoorden.",
|
|
||||||
"invalidPasswordGenericMessage": "Ongeldig wachtwoord: het nieuwe wachtwoord voldoet niet aan het wachtwoordbeleid."
|
|
||||||
};
|
|
||||||
|
|
||||||
export default messages;
|
|
||||||
/* spell-checker: enable */
|
|
@ -1,153 +0,0 @@
|
|||||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
|
||||||
//PLEASE DO NOT EDIT MANUALLY
|
|
||||||
|
|
||||||
/* spell-checker: disable */
|
|
||||||
const messages = {
|
|
||||||
"doSave": "Lagre",
|
|
||||||
"doCancel": "Avbryt",
|
|
||||||
"doLogOutAllSessions": "Logg ut av alle sesjoner",
|
|
||||||
"doRemove": "Fjern",
|
|
||||||
"doAdd": "Legg til",
|
|
||||||
"doSignOut": "Logg ut",
|
|
||||||
"editAccountHtmlTitle": "Rediger konto",
|
|
||||||
"federatedIdentitiesHtmlTitle": "Federerte identiteter",
|
|
||||||
"accountLogHtmlTitle": "Kontologg",
|
|
||||||
"changePasswordHtmlTitle": "Endre passord",
|
|
||||||
"sessionsHtmlTitle": "Sesjoner",
|
|
||||||
"accountManagementTitle": "Keycloak kontoadministrasjon",
|
|
||||||
"authenticatorTitle": "Autentikator",
|
|
||||||
"applicationsHtmlTitle": "Applikasjoner",
|
|
||||||
"authenticatorCode": "Engangskode",
|
|
||||||
"email": "E-post",
|
|
||||||
"firstName": "Fornavn",
|
|
||||||
"givenName": "Fornavn",
|
|
||||||
"fullName": "Fullt navn",
|
|
||||||
"lastName": "Etternavn",
|
|
||||||
"familyName": "Etternavn",
|
|
||||||
"password": "Passord",
|
|
||||||
"passwordConfirm": "Bekreftelse",
|
|
||||||
"passwordNew": "Nytt passord",
|
|
||||||
"username": "Brukernavn",
|
|
||||||
"address": "Adresse",
|
|
||||||
"street": "Gate-/veinavn + husnummer",
|
|
||||||
"locality": "By",
|
|
||||||
"region": "Fylke",
|
|
||||||
"postal_code": "Postnummer",
|
|
||||||
"country": "Land",
|
|
||||||
"emailVerified": "E-post bekreftet",
|
|
||||||
"gssDelegationCredential": "GSS legitimasjonsdelegering",
|
|
||||||
"role_admin": "Administrator",
|
|
||||||
"role_realm-admin": "Administrator for sikkerhetsdomene",
|
|
||||||
"role_create-realm": "Opprette sikkerhetsdomene",
|
|
||||||
"role_view-realm": "Se sikkerhetsdomene",
|
|
||||||
"role_view-users": "Se brukere",
|
|
||||||
"role_view-applications": "Se applikasjoner",
|
|
||||||
"role_view-clients": "Se klienter",
|
|
||||||
"role_view-events": "Se hendelser",
|
|
||||||
"role_view-identity-providers": "Se identitetsleverandører",
|
|
||||||
"role_manage-realm": "Administrere sikkerhetsdomene",
|
|
||||||
"role_manage-users": "Administrere brukere",
|
|
||||||
"role_manage-applications": "Administrere applikasjoner",
|
|
||||||
"role_manage-identity-providers": "Administrere identitetsleverandører",
|
|
||||||
"role_manage-clients": "Administrere klienter",
|
|
||||||
"role_manage-events": "Administrere hendelser",
|
|
||||||
"role_view-profile": "Se profil",
|
|
||||||
"role_manage-account": "Administrere konto",
|
|
||||||
"role_read-token": "Lese token",
|
|
||||||
"role_offline-access": "Frakoblet tilgang",
|
|
||||||
"role_uma_authorization": "Skaffe tillatelser",
|
|
||||||
"client_account": "Konto",
|
|
||||||
"client_security-admin-console": "Sikkerhetsadministrasjonskonsoll",
|
|
||||||
"client_admin-cli": "Kommandolinje-grensesnitt for administrator",
|
|
||||||
"client_realm-management": "Sikkerhetsdomene-administrasjon",
|
|
||||||
"client_broker": "Broker",
|
|
||||||
"requiredFields": "Obligatoriske felt",
|
|
||||||
"allFieldsRequired": "Alle felt må fylles ut",
|
|
||||||
"backToApplication": "« Tilbake til applikasjonen",
|
|
||||||
"backTo": "Tilbake til {0}",
|
|
||||||
"date": "Dato",
|
|
||||||
"event": "Hendelse",
|
|
||||||
"ip": "IP",
|
|
||||||
"client": "Klient",
|
|
||||||
"clients": "Klienter",
|
|
||||||
"details": "Detaljer",
|
|
||||||
"started": "Startet",
|
|
||||||
"lastAccess": "Sist benyttet",
|
|
||||||
"expires": "Utløper",
|
|
||||||
"applications": "Applikasjoner",
|
|
||||||
"account": "Konto",
|
|
||||||
"federatedIdentity": "Federert identitet",
|
|
||||||
"authenticator": "Autentikator",
|
|
||||||
"sessions": "Sesjoner",
|
|
||||||
"log": "Logg",
|
|
||||||
"application": "Applikasjon",
|
|
||||||
"availablePermissions": "Tilgjengelige rettigheter",
|
|
||||||
"grantedPermissions": "Innvilgede rettigheter",
|
|
||||||
"grantedPersonalInfo": "Innvilget personlig informasjon",
|
|
||||||
"additionalGrants": "Ekstra rettigheter",
|
|
||||||
"action": "Handling",
|
|
||||||
"inResource": "i",
|
|
||||||
"fullAccess": "Full tilgang",
|
|
||||||
"offlineToken": "Offline token",
|
|
||||||
"revoke": "Opphev rettighet",
|
|
||||||
"configureAuthenticators": "Konfigurerte autentikatorer",
|
|
||||||
"mobile": "Mobiltelefon",
|
|
||||||
"totpStep1": "Installer ett av følgende programmer på mobilen din.",
|
|
||||||
"totpStep2": "Åpne applikasjonen og skann strekkoden eller skriv inn koden.",
|
|
||||||
"totpStep3": "Skriv inn engangskoden gitt av applikasjonen og klikk Lagre for å fullføre.",
|
|
||||||
"missingUsernameMessage": "Vennligst oppgi brukernavn.",
|
|
||||||
"missingFirstNameMessage": "Vennligst oppgi fornavn.",
|
|
||||||
"invalidEmailMessage": "Ugyldig e-postadresse.",
|
|
||||||
"missingLastNameMessage": "Vennligst oppgi etternavn.",
|
|
||||||
"missingEmailMessage": "Vennligst oppgi e-postadresse.",
|
|
||||||
"missingPasswordMessage": "Vennligst oppgi passord.",
|
|
||||||
"notMatchPasswordMessage": "Passordene er ikke like.",
|
|
||||||
"missingTotpMessage": "Vennligst oppgi engangskode.",
|
|
||||||
"invalidPasswordExistingMessage": "Ugyldig eksisterende passord.",
|
|
||||||
"invalidPasswordConfirmMessage": "Passordene er ikke like.",
|
|
||||||
"invalidTotpMessage": "Ugyldig engangskode.",
|
|
||||||
"usernameExistsMessage": "Brukernavnet finnes allerede.",
|
|
||||||
"emailExistsMessage": "E-postadressen finnes allerede.",
|
|
||||||
"readOnlyUserMessage": "Du kan ikke oppdatere kontoen din ettersom den er skrivebeskyttet.",
|
|
||||||
"readOnlyPasswordMessage": "Du kan ikke oppdatere passordet ditt ettersom kontoen din er skrivebeskyttet.",
|
|
||||||
"successTotpMessage": "Autentikator for mobiltelefon er konfigurert.",
|
|
||||||
"successTotpRemovedMessage": "Autentikator for mobiltelefon er fjernet.",
|
|
||||||
"successGrantRevokedMessage": "Vellykket oppheving av rettighet.",
|
|
||||||
"accountUpdatedMessage": "Kontoen din har blitt oppdatert.",
|
|
||||||
"accountPasswordUpdatedMessage": "Ditt passord har blitt oppdatert.",
|
|
||||||
"missingIdentityProviderMessage": "Identitetsleverandør er ikke spesifisert.",
|
|
||||||
"invalidFederatedIdentityActionMessage": "Ugyldig eller manglende handling.",
|
|
||||||
"identityProviderNotFoundMessage": "Spesifisert identitetsleverandør ikke funnet.",
|
|
||||||
"federatedIdentityLinkNotActiveMessage": "Denne identiteten er ikke lenger aktiv.",
|
|
||||||
"federatedIdentityRemovingLastProviderMessage": "Du kan ikke fjerne siste federerte identitet ettersom du ikke har et passord.",
|
|
||||||
"identityProviderRedirectErrorMessage": "Redirect til identitetsleverandør feilet.",
|
|
||||||
"identityProviderRemovedMessage": "Fjerning av identitetsleverandør var vellykket.",
|
|
||||||
"identityProviderAlreadyLinkedMessage": "Federert identitet returnert av {0} er allerede koblet til en annen bruker.",
|
|
||||||
"staleCodeAccountMessage": "Siden har utløpt. Vennligst prøv en gang til.",
|
|
||||||
"consentDenied": "Samtykke avslått.",
|
|
||||||
"accountDisabledMessage": "Konto er deaktivert, kontakt administrator.",
|
|
||||||
"accountTemporarilyDisabledMessage": "Konto er midlertidig deaktivert, kontakt administrator eller prøv igjen senere.",
|
|
||||||
"invalidPasswordMinLengthMessage": "Ugyldig passord: minimum lengde {0}.",
|
|
||||||
"invalidPasswordMinLowerCaseCharsMessage": "Ugyldig passord: må inneholde minimum {0} små bokstaver.",
|
|
||||||
"invalidPasswordMinDigitsMessage": "Ugyldig passord: må inneholde minimum {0} sifre.",
|
|
||||||
"invalidPasswordMinUpperCaseCharsMessage": "Ugyldig passord: må inneholde minimum {0} store bokstaver.",
|
|
||||||
"invalidPasswordMinSpecialCharsMessage": "Ugyldig passord: må inneholde minimum {0} spesialtegn.",
|
|
||||||
"invalidPasswordNotUsernameMessage": "Ugyldig passord: kan ikke være likt brukernavn.",
|
|
||||||
"invalidPasswordRegexPatternMessage": "Ugyldig passord: tilfredsstiller ikke kravene for passord-mønster.",
|
|
||||||
"invalidPasswordHistoryMessage": "Ugyldig passord: kan ikke være likt noen av de {0} foregående passordene.",
|
|
||||||
"locale_ca": "Català",
|
|
||||||
"locale_de": "Deutsch",
|
|
||||||
"locale_en": "English",
|
|
||||||
"locale_es": "Español",
|
|
||||||
"locale_fr": "Français",
|
|
||||||
"locale_it": "Italian",
|
|
||||||
"locale_ja": "日本語",
|
|
||||||
"locale_no": "Norsk",
|
|
||||||
"locale_nl": "Nederlands",
|
|
||||||
"locale_pt-BR": "Português (Brasil)",
|
|
||||||
"locale_ru": "Русский",
|
|
||||||
"locale_zh-CN": "中文简体"
|
|
||||||
};
|
|
||||||
|
|
||||||
export default messages;
|
|
||||||
/* spell-checker: enable */
|
|
@ -1,8 +0,0 @@
|
|||||||
//This code was automatically generated by running dist/bin/generate-i18n-messages.js
|
|
||||||
//PLEASE DO NOT EDIT MANUALLY
|
|
||||||
|
|
||||||
/* spell-checker: disable */
|
|
||||||
const messages = {};
|
|
||||||
|
|
||||||
export default messages;
|
|
||||||
/* spell-checker: enable */
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user