Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
5502a74994 | |||
c5ef4c973b | |||
dbae909903 | |||
74317a1f3c | |||
7260589136 | |||
b2e9ddaa4f | |||
4338b3ecb7 | |||
0f81d9f146 | |||
9980b10a83 | |||
6bfd388827 | |||
8203ed687b | |||
f394e06e4d | |||
8db35a81da | |||
2e0ebfcf58 | |||
51d2ff85e0 | |||
8b54426b89 | |||
fa346c5b1f | |||
d87788980d | |||
1e4319498c |
@ -249,6 +249,16 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "liamlows",
|
||||
"name": "Liam Lowsley-Williams",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/1365914?v=4",
|
||||
"profile": "https://github.com/liamlows",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
@ -41,7 +41,7 @@
|
||||
<img width="400" src="https://github.com/user-attachments/assets/6bf3bef9-00b0-4460-97b9-0d2da8500798">
|
||||
</p>
|
||||
|
||||
Keycloakify is fully compatible with Keycloak 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, [~~22~~](https://github.com/keycloakify/keycloakify/issues/389#issuecomment-1822509763), 23, 24, 25...[and beyond](https://github.com/keycloakify/keycloakify/discussions/346#discussioncomment-5889791)
|
||||
Keycloakify is fully compatible with Keycloak from version 11 to 25...[and beyond](https://github.com/keycloakify/keycloakify/discussions/346#discussioncomment-5889791)
|
||||
|
||||
## Sponsors
|
||||
|
||||
@ -130,6 +130,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Moulyy"><img src="https://avatars.githubusercontent.com/u/115405804?v=4?s=100" width="100px;" alt="Moulyy"/><br /><sub><b>Moulyy</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=Moulyy" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/madmadson"><img src="https://avatars.githubusercontent.com/u/798831?v=4?s=100" width="100px;" alt="Tobias Matt"/><br /><sub><b>Tobias Matt</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=madmadson" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/oliviergoulet5"><img src="https://avatars.githubusercontent.com/u/17685861?v=4?s=100" width="100px;" alt="Olivier Goulet"/><br /><sub><b>Olivier Goulet</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=oliviergoulet5" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/liamlows"><img src="https://avatars.githubusercontent.com/u/1365914?v=4?s=100" width="100px;" alt="Liam Lowsley-Williams"/><br /><sub><b>Liam Lowsley-Williams</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=liamlows" title="Code">💻</a> <a href="https://github.com/keycloakify/keycloakify/commits?author=liamlows" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "keycloakify",
|
||||
"version": "10.0.0",
|
||||
"version": "10.0.6",
|
||||
"description": "Create Keycloak themes using React",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
getLatestsSemVersionedTag,
|
||||
type BuildContextLike as BuildContextLike_getLatestsSemVersionedTag
|
||||
} from "../shared/getLatestsSemVersionedTag";
|
||||
import { SemVer } from "../tools/SemVer";
|
||||
import fetch from "make-fetch-happen";
|
||||
import { z } from "zod";
|
||||
import { assert, type Equals } from "tsafe/assert";
|
||||
@ -68,7 +69,9 @@ export async function initializeAccountTheme_singlePage(params: {
|
||||
})()
|
||||
);
|
||||
|
||||
dependencies.dependencies["@keycloakify/keycloak-account-ui"] = semVersionedTag.tag;
|
||||
dependencies.dependencies["@keycloakify/keycloak-account-ui"] = SemVer.stringify(
|
||||
semVersionedTag.version
|
||||
);
|
||||
|
||||
const parsedPackageJson = (() => {
|
||||
type ParsedPackageJson = {
|
||||
|
@ -60,14 +60,14 @@ export function updateAccountThemeImplementationInConfig(params: {
|
||||
{
|
||||
const parsedPackageJson = (() => {
|
||||
type ParsedPackageJson = {
|
||||
keycloakify: Record<string, string>;
|
||||
keycloakify: Record<string, unknown>;
|
||||
};
|
||||
|
||||
const zParsedPackageJson = (() => {
|
||||
type TargetType = ParsedPackageJson;
|
||||
|
||||
const zTargetType = z.object({
|
||||
keycloakify: z.record(z.string())
|
||||
keycloakify: z.record(z.unknown())
|
||||
});
|
||||
|
||||
assert<Equals<z.infer<typeof zTargetType>, TargetType>>();
|
||||
@ -75,17 +75,22 @@ export function updateAccountThemeImplementationInConfig(params: {
|
||||
return id<z.ZodType<TargetType>>(zTargetType);
|
||||
})();
|
||||
|
||||
return zParsedPackageJson.parse(
|
||||
JSON.parse(
|
||||
fs
|
||||
.readFileSync(buildContext.packageJsonFilePath)
|
||||
.toString("utf8")
|
||||
)
|
||||
const parsedPackageJson = JSON.parse(
|
||||
fs.readFileSync(buildContext.packageJsonFilePath).toString("utf8")
|
||||
);
|
||||
|
||||
zParsedPackageJson.parse(parsedPackageJson);
|
||||
|
||||
return parsedPackageJson;
|
||||
})();
|
||||
|
||||
parsedPackageJson.keycloakify.accountThemeImplementation =
|
||||
accountThemeType;
|
||||
|
||||
fs.writeFileSync(
|
||||
buildContext.packageJsonFilePath,
|
||||
Buffer.from(JSON.stringify(parsedPackageJson, undefined, 4), "utf8")
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
||||
// NOTE: This is arbitrary
|
||||
startingFromMajor: 17,
|
||||
excludeMajorVersions: [],
|
||||
doOmitPatch: false,
|
||||
buildContext
|
||||
});
|
||||
|
||||
|
@ -139,25 +139,6 @@ export async function buildJar(params: {
|
||||
break route_legacy_pages;
|
||||
}
|
||||
|
||||
// NOTE: If there's no account theme there is no special target for keycloak 24 and up so we create
|
||||
// the pages anyway. If there is an account pages, since we know that account-v1 is only support keycloak
|
||||
// 24 in version 0.4 and up, we can safely break the route for legacy pages.
|
||||
const doBreak: boolean = (() => {
|
||||
switch (keycloakAccountV1Version) {
|
||||
case null:
|
||||
return false;
|
||||
case "0.3":
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
})();
|
||||
|
||||
// TODO: Remove this optimization, it's a bit hacky.
|
||||
if (doBreak) {
|
||||
break route_legacy_pages;
|
||||
}
|
||||
|
||||
(["register.ftl", "login-update-profile.ftl"] as const).forEach(pageId =>
|
||||
buildContext.themeNames.map(themeName => {
|
||||
const ftlFilePath = pathJoin(
|
||||
|
@ -4,8 +4,7 @@ import {
|
||||
join as pathJoin,
|
||||
resolve as pathResolve,
|
||||
relative as pathRelative,
|
||||
dirname as pathDirname,
|
||||
basename as pathBasename
|
||||
dirname as pathDirname
|
||||
} from "path";
|
||||
import { replaceImportsInJsCode } from "../replacers/replaceImportsInJsCode";
|
||||
import { replaceImportsInCssCode } from "../replacers/replaceImportsInCssCode";
|
||||
@ -43,7 +42,7 @@ import {
|
||||
} from "../../shared/metaInfKeycloakThemes";
|
||||
import { objectEntries } from "tsafe/objectEntries";
|
||||
import { escapeStringForPropertiesFile } from "../../tools/escapeStringForPropertiesFile";
|
||||
import { downloadAndExtractArchive } from "../../tools/downloadAndExtractArchive";
|
||||
import * as child_process from "child_process";
|
||||
|
||||
export type BuildContextLike = BuildContextLike_kcContextExclusionsFtlCode &
|
||||
BuildContextLike_downloadKeycloakStaticResources &
|
||||
@ -56,6 +55,7 @@ export type BuildContextLike = BuildContextLike_kcContextExclusionsFtlCode &
|
||||
implementedThemeTypes: BuildContext["implementedThemeTypes"];
|
||||
themeSrcDirPath: string;
|
||||
bundler: "vite" | "webpack";
|
||||
packageJsonFilePath: string;
|
||||
};
|
||||
|
||||
assert<BuildContext extends BuildContextLike ? true : false>();
|
||||
@ -313,27 +313,23 @@ export async function generateResourcesForMainTheme(params: {
|
||||
break bring_in_account_v3_i18n_messages;
|
||||
}
|
||||
|
||||
const { extractedDirPath } = await downloadAndExtractArchive({
|
||||
url: "https://repo1.maven.org/maven2/org/keycloak/keycloak-account-ui/25.0.1/keycloak-account-ui-25.0.1.jar",
|
||||
cacheDirPath: buildContext.cacheDirPath,
|
||||
fetchOptions: buildContext.fetchOptions,
|
||||
uniqueIdOfOnArchiveFile: "bring_in_account_v3_i18n_messages",
|
||||
onArchiveFile: async ({ fileRelativePath, writeFile }) => {
|
||||
if (
|
||||
!fileRelativePath.startsWith(
|
||||
pathJoin("theme", "keycloak.v3", "account", "messages")
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
await writeFile({
|
||||
fileRelativePath: pathBasename(fileRelativePath)
|
||||
});
|
||||
}
|
||||
});
|
||||
const accountUiDirPath = child_process
|
||||
.execSync("npm list @keycloakify/keycloak-account-ui --parseable", {
|
||||
cwd: pathDirname(buildContext.packageJsonFilePath)
|
||||
})
|
||||
.toString("utf8")
|
||||
.trim();
|
||||
|
||||
const messagesDirPath = pathJoin(accountUiDirPath, "messages");
|
||||
|
||||
if (!fs.existsSync(messagesDirPath)) {
|
||||
throw new Error(
|
||||
`Please update @keycloakify/keycloak-account-ui to 25.0.4-rc.5 or later.`
|
||||
);
|
||||
}
|
||||
|
||||
transformCodebase({
|
||||
srcDirPath: extractedDirPath,
|
||||
srcDirPath: messagesDirPath,
|
||||
destDirPath: pathJoin(
|
||||
getThemeTypeDirPath({ themeType: "account" }),
|
||||
"messages"
|
||||
|
@ -12,6 +12,10 @@ import * as os from "os";
|
||||
import { rmSync } from "../tools/fs.rmSync";
|
||||
|
||||
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
||||
const { cliCommandOptions } = params;
|
||||
|
||||
const buildContext = getBuildContext({ cliCommandOptions });
|
||||
|
||||
exit_if_maven_not_installed: {
|
||||
let commandOutput: Buffer | undefined = undefined;
|
||||
|
||||
@ -25,31 +29,44 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
||||
break exit_if_maven_not_installed;
|
||||
}
|
||||
|
||||
const installationCommand = (() => {
|
||||
switch (os.platform()) {
|
||||
case "darwin":
|
||||
return "brew install mvn";
|
||||
case "win32":
|
||||
return "choco install mvn";
|
||||
case "linux":
|
||||
default:
|
||||
return "sudo apt-get install mvn";
|
||||
}
|
||||
})();
|
||||
if (
|
||||
fs
|
||||
.readFileSync(buildContext.packageJsonFilePath)
|
||||
.toString("utf8")
|
||||
.includes(`"mvn"`)
|
||||
) {
|
||||
console.log(
|
||||
chalk.red(
|
||||
[
|
||||
"Please remove the 'mvn' package from your package.json'dependencies list,",
|
||||
"reinstall your dependencies and try again.",
|
||||
"We need the Apache Maven CLI, not this: https://www.npmjs.com/package/mvn"
|
||||
].join(" ")
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const installationCommand = (() => {
|
||||
switch (os.platform()) {
|
||||
case "darwin":
|
||||
return "brew install mvn";
|
||||
case "win32":
|
||||
return "choco install mvn";
|
||||
case "linux":
|
||||
default:
|
||||
return "sudo apt-get install mvn";
|
||||
}
|
||||
})();
|
||||
|
||||
console.log(
|
||||
`${chalk.red("Apache Maven required.")} Install it with \`${chalk.bold(
|
||||
installationCommand
|
||||
)}\` (for example)`
|
||||
);
|
||||
console.log(
|
||||
`${chalk.red("Apache Maven required.")} Install it with \`${chalk.bold(
|
||||
installationCommand
|
||||
)}\` (for example)`
|
||||
);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const { cliCommandOptions } = params;
|
||||
|
||||
const buildContext = getBuildContext({ cliCommandOptions });
|
||||
|
||||
console.log(
|
||||
[
|
||||
chalk.cyan(`keycloakify v${readThisNpmPackageVersion()}`),
|
||||
|
@ -14,9 +14,10 @@ assert<BuildContext extends BuildContextLike ? true : false>();
|
||||
export async function promptKeycloakVersion(params: {
|
||||
startingFromMajor: number | undefined;
|
||||
excludeMajorVersions: number[];
|
||||
doOmitPatch: boolean;
|
||||
buildContext: BuildContextLike;
|
||||
}) {
|
||||
const { startingFromMajor, excludeMajorVersions, buildContext } = params;
|
||||
const { startingFromMajor, excludeMajorVersions, doOmitPatch, buildContext } = params;
|
||||
|
||||
const semVersionedTagByMajor = new Map<number, { tag: string; version: SemVer }>();
|
||||
|
||||
@ -55,7 +56,8 @@ export async function promptKeycloakVersion(params: {
|
||||
});
|
||||
|
||||
const lastMajorVersions = Array.from(semVersionedTagByMajor.values()).map(
|
||||
({ version }) => `${version.major}.${version.minor}`
|
||||
({ version }) =>
|
||||
`${version.major}.${version.minor}${doOmitPatch ? "" : `.${version.patch}`}`
|
||||
);
|
||||
|
||||
const { value } = await cliSelect<string>({
|
||||
|
@ -115,6 +115,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
|
||||
const { keycloakVersion } = await promptKeycloakVersion({
|
||||
startingFromMajor: 18,
|
||||
excludeMajorVersions: [22],
|
||||
doOmitPatch: true,
|
||||
buildContext
|
||||
});
|
||||
|
||||
|
@ -580,7 +580,7 @@ function TextareaTag(props: InputFieldByTypeProps) {
|
||||
function SelectTag(props: InputFieldByTypeProps) {
|
||||
const { attribute, dispatchFormAction, kcClsx, displayableErrors, i18n, valueOrValues } = props;
|
||||
|
||||
const { advancedMsg } = i18n;
|
||||
const { advancedMsgStr } = i18n;
|
||||
|
||||
const isMultiple = attribute.annotations.inputType === "multiselect";
|
||||
|
||||
@ -649,11 +649,11 @@ function SelectTag(props: InputFieldByTypeProps) {
|
||||
if (attribute.annotations.inputOptionLabels !== undefined) {
|
||||
const { inputOptionLabels } = attribute.annotations;
|
||||
|
||||
return advancedMsg(inputOptionLabels[option] ?? option);
|
||||
return advancedMsgStr(inputOptionLabels[option] ?? option);
|
||||
}
|
||||
|
||||
if (attribute.annotations.inputOptionLabelsI18nPrefix !== undefined) {
|
||||
return advancedMsg(`${attribute.annotations.inputOptionLabelsI18nPrefix}.${option}`);
|
||||
return advancedMsgStr(`${attribute.annotations.inputOptionLabelsI18nPrefix}.${option}`);
|
||||
}
|
||||
|
||||
return option;
|
||||
|
@ -43,7 +43,7 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
|
||||
}
|
||||
socialProvidersNode={
|
||||
<>
|
||||
{realm.password && social.providers?.length && (
|
||||
{realm.password && social.providers !== undefined && social.providers.length !== 0 && (
|
||||
<div id="kc-social-providers" className={kcClsx("kcFormSocialAccountSectionClass")}>
|
||||
<hr />
|
||||
<h2>{msg("identity-provider-login-label")}</h2>
|
||||
|
@ -40,7 +40,7 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
|
||||
headerNode={msg("doLogIn")}
|
||||
socialProvidersNode={
|
||||
<>
|
||||
{realm.password && social.providers?.length && (
|
||||
{realm.password && social.providers !== undefined && social.providers.length !== 0 && (
|
||||
<div id="kc-social-providers" className={kcClsx("kcFormSocialAccountSectionClass")}>
|
||||
<hr />
|
||||
<h2>{msg("identity-provider-login-label")}</h2>
|
||||
|
Reference in New Issue
Block a user