Compare commits

...

19 Commits

Author SHA1 Message Date
7d8ae040fd Bundle JAR WPI 2024-08-24 23:13:16 +02:00
c6cf564842 Release candidate 2024-08-23 19:01:56 +02:00
380b739017 Don't pin the patch version in the docker tag 2024-08-23 19:01:37 +02:00
c3f3c55303 Release candidate 2024-08-23 18:45:56 +02:00
2c01018529 2024-08-23 18:36:40 +02:00
dd2edf3013 Merge pull request from keycloakify/all-contributors/add-oliviergoulet5
docs: add oliviergoulet5 as a contributor for code
2024-08-22 00:56:15 +02:00
7f3cdf9fac Release candidate 2024-08-22 00:55:39 +02:00
f75a91fbc1 docs: update .all-contributorsrc [skip ci] 2024-08-21 22:55:00 +00:00
f151086bb1 docs: update README.md [skip ci] 2024-08-21 22:54:59 +00:00
7c833e6f10 Merge pull request from oliviergoulet5/fix-array-operations
Fix array comparison and improve type check
2024-08-22 00:53:48 +02:00
885e8314e8 Fix array comparison and type check 2024-08-21 17:13:06 -04:00
3bdd955ab6 Release candidate 2024-08-19 02:11:31 +02:00
9499587bad Fix formating bug of Docker command being run 2024-08-19 02:10:59 +02:00
0879ddba7c Release candidate 2024-08-19 00:25:54 +02:00
106a1dd4c7 Support parsing of the KC_HTTP_RELATIVE_PATH option 2024-08-19 00:25:41 +02:00
5580248bcd Release candidate 2024-08-19 00:00:22 +02:00
c9c10b8fba Fix issue with the port in the start-keycloak command 2024-08-19 00:00:08 +02:00
ed254922e9 Relase candidate 2024-08-18 23:46:12 +02:00
4b7d1e2cec Fix bug in docker command 2024-08-18 23:45:58 +02:00
12 changed files with 280 additions and 80 deletions

@ -240,6 +240,15 @@
"contributions": [ "contributions": [
"code" "code"
] ]
},
{
"login": "oliviergoulet5",
"name": "Olivier Goulet",
"avatar_url": "https://avatars.githubusercontent.com/u/17685861?v=4",
"profile": "https://github.com/oliviergoulet5",
"contributions": [
"code"
]
} }
], ],
"contributorsPerLine": 7, "contributorsPerLine": 7,

@ -132,6 +132,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/law108000"><img src="https://avatars.githubusercontent.com/u/8112024?v=4?s=100" width="100px;" alt="Rlok"/><br /><sub><b>Rlok</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=law108000" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/law108000"><img src="https://avatars.githubusercontent.com/u/8112024?v=4?s=100" width="100px;" alt="Rlok"/><br /><sub><b>Rlok</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=law108000" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Moulyy"><img src="https://avatars.githubusercontent.com/u/115405804?v=4?s=100" width="100px;" alt="Moulyy"/><br /><sub><b>Moulyy</b></sub></a><br /><a href="https://github.com/keycloakify/keycloakify/commits?author=Moulyy" title="Code">💻</a></td> <td align="center" valign="top" width="14.28%"><a href="https://github.com/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/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>
</tr> </tr>
</tbody> </tbody>
</table> </table>

@ -1,6 +1,6 @@
{ {
"name": "keycloakify", "name": "keycloakify",
"version": "10.0.0-rc.140", "version": "10.0.0-rc.147",
"description": "Create Keycloak themes using React", "description": "Create Keycloak themes using React",
"repository": { "repository": {
"type": "git", "type": "git",

@ -17,15 +17,20 @@ import { isInside } from "../../tools/isInside";
import child_process from "child_process"; import child_process from "child_process";
import { rmSync } from "../../tools/fs.rmSync"; import { rmSync } from "../../tools/fs.rmSync";
import { writeMetaInfKeycloakThemes } from "../../shared/metaInfKeycloakThemes"; import { writeMetaInfKeycloakThemes } from "../../shared/metaInfKeycloakThemes";
import {
bundleExtensionsIntoJar,
type BuildContextLike as BuildContextLike_bundleExtensionsIntoJar
} from "./bundleExtensionsIntoJar";
export type BuildContextLike = BuildContextLike_generatePom & { export type BuildContextLike = BuildContextLike_generatePom &
keycloakifyBuildDirPath: string; BuildContextLike_bundleExtensionsIntoJar & {
themeNames: string[]; keycloakifyBuildDirPath: string;
artifactId: string; themeNames: string[];
themeVersion: string; artifactId: string;
cacheDirPath: string; themeVersion: string;
implementedThemeTypes: BuildContext["implementedThemeTypes"]; cacheDirPath: string;
}; implementedThemeTypes: BuildContext["implementedThemeTypes"];
};
assert<BuildContext extends BuildContextLike ? true : false>(); assert<BuildContext extends BuildContextLike ? true : false>();
@ -234,12 +239,19 @@ export async function buildJar(params: {
) )
); );
const jarFilePath_generatedByMaven = pathJoin(
keycloakifyBuildCacheDirPath,
"target",
`${buildContext.artifactId}-${buildContext.themeVersion}.jar`
);
await bundleExtensionsIntoJar({
buildContext,
jarFilePath: jarFilePath_generatedByMaven
});
await fs.rename( await fs.rename(
pathJoin( jarFilePath_generatedByMaven,
keycloakifyBuildCacheDirPath,
"target",
`${buildContext.artifactId}-${buildContext.themeVersion}.jar`
),
pathJoin(buildContext.keycloakifyBuildDirPath, jarFileBasename) pathJoin(buildContext.keycloakifyBuildDirPath, jarFileBasename)
); );
} }

@ -0,0 +1,137 @@
import { downloadAndExtractArchive } from "../../tools/downloadAndExtractArchive";
import { assert } from "tsafe/assert";
import type { BuildContext } from "../../shared/buildContext";
import { transformCodebase } from "../../tools/transformCodebase";
import { join as pathJoin, basename as pathBasename, sep as pathSep } from "path";
import { rm } from "../../tools/fs.rm";
import { extractArchive } from "../../tools/extractArchive";
import * as crypto from "crypto";
export type BuildContextLike = {
cacheDirPath: string;
fetchOptions: BuildContext["fetchOptions"];
extensionJars: BuildContext["extensionJars"];
};
assert<BuildContext extends BuildContextLike ? true : false>();
export async function bundleExtensionsIntoJar(params: {
jarFilePath: string;
buildContext: BuildContextLike;
}): Promise<void> {
const { jarFilePath, buildContext } = params;
if (buildContext.extensionJars.length === 0) {
return;
}
const mergeDirPath = pathJoin(
buildContext.cacheDirPath,
`merge_${pathBasename(jarFilePath).replace(/\.jar$/, "")}_${crypto
.createHash("sha256")
.update(jarFilePath)
.digest("hex")
.substring(0, 5)}`
);
await extractArchive({
archiveFilePath: jarFilePath,
onArchiveFile: async ({ relativeFilePathInArchive, writeFile }) =>
writeFile({
filePath: pathJoin(mergeDirPath, relativeFilePathInArchive)
})
});
for (const extensionJar of buildContext.extensionJars) {
const transformSourceCode = (params: {
fileRelativePath: string;
sourceCode: Buffer;
}): { modifiedSourceCode: Buffer } | undefined => {
const { fileRelativePath } = params;
if (!fileRelativePath.startsWith(`META-INF${pathSep}`)) {
for (const ext of [".DSA", ".SF", ".RSA"]) {
if (fileRelativePath.endsWith(ext)) {
return undefined;
}
}
}
return undefined;
};
switch (extensionJar.type) {
case "path":
await extractArchive({
archiveFilePath: extensionJar.path,
onArchiveFile: async ({
relativeFilePathInArchive,
writeFile,
readFile
}) => {
const transformResult = transformSourceCode({
fileRelativePath: relativeFilePathInArchive,
sourceCode: await readFile()
});
if (transformResult === undefined) {
return;
}
await writeFile({
filePath: pathJoin(mergeDirPath, relativeFilePathInArchive),
modifiedData: transformResult.modifiedSourceCode
});
}
});
break;
case "url": {
const { extractedDirPath } = await downloadAndExtractArchive({
url: extensionJar.url,
cacheDirPath: buildContext.cacheDirPath,
fetchOptions: buildContext.fetchOptions,
uniqueIdOfOnArchiveFile: "noOp",
onArchiveFile: async ({ fileRelativePath, writeFile }) =>
writeFile({ fileRelativePath })
});
transformCodebase({
srcDirPath: extractedDirPath,
destDirPath: mergeDirPath,
transformSourceCode
});
break;
}
}
/*
transformCodebase({
srcDirPath: extractedDirPath,
destDirPath: mergeDirPath,
transformSourceCode: ({ fileRelativePath, sourceCode }) => {
if (fileRelativePath === pathJoin("META-INF", "MANIFEST.MF")) {
const sourceCodeStr = sourceCode.toString("utf8");
const lines = sourceCodeStr.split(/\r?\n/);
console.log(lines);
return {
modifiedSourceCode: Buffer.concat([
sourceCode,
Buffer.from(
`Class-Path: ${pathBasename(userProvidedJarFilePathOrUrl)}\n`
)
])
};
}
}
});
*/
}
// TODO: Acctually build new jar
await rm(mergeDirPath, { recursive: true, force: true });
}

@ -314,7 +314,7 @@ export async function generateResourcesForMainTheme(params: {
} }
const { extractedDirPath } = await downloadAndExtractArchive({ const { extractedDirPath } = await downloadAndExtractArchive({
urlOrPath: "https://repo1.maven.org/maven2/org/keycloak/keycloak-account-ui/25.0.1/keycloak-account-ui-25.0.1.jar", url: "https://repo1.maven.org/maven2/org/keycloak/keycloak-account-ui/25.0.1/keycloak-account-ui-25.0.1.jar",
cacheDirPath: buildContext.cacheDirPath, cacheDirPath: buildContext.cacheDirPath,
fetchOptions: buildContext.fetchOptions, fetchOptions: buildContext.fetchOptions,
uniqueIdOfOnArchiveFile: "bring_in_account_v3_i18n_messages", uniqueIdOfOnArchiveFile: "bring_in_account_v3_i18n_messages",

@ -26,6 +26,8 @@ import { type ThemeType } from "./constants";
import { id } from "tsafe/id"; import { id } from "tsafe/id";
import chalk from "chalk"; import chalk from "chalk";
import { getProxyFetchOptions, type ProxyFetchOptions } from "../tools/fetchProxyOptions"; import { getProxyFetchOptions, type ProxyFetchOptions } from "../tools/fetchProxyOptions";
import { removeDuplicates } from "evt/tools/reducers/removeDuplicates";
import { same } from "evt/tools/inDepth/same";
export type BuildContext = { export type BuildContext = {
themeVersion: string; themeVersion: string;
@ -61,6 +63,7 @@ export type BuildContext = {
keycloakVersionRange: KeycloakVersionRange; keycloakVersionRange: KeycloakVersionRange;
jarFileBasename: string; jarFileBasename: string;
}[]; }[];
extensionJars: ({ type: "path"; path: string } | { type: "url"; url: string })[];
startKeycloakOptions: { startKeycloakOptions: {
dockerImage: dockerImage:
| { | {
@ -88,6 +91,7 @@ export type BuildOptions = {
loginThemeResourcesFromKeycloakVersion?: string; loginThemeResourcesFromKeycloakVersion?: string;
keycloakifyBuildDirPath?: string; keycloakifyBuildDirPath?: string;
kcContextExclusionsFtl?: string; kcContextExclusionsFtl?: string;
extensionJars?: string[];
startKeycloakOptions?: { startKeycloakOptions?: {
dockerImage?: string; dockerImage?: string;
dockerExtraArgs?: string[]; dockerExtraArgs?: string[];
@ -360,6 +364,7 @@ export function getBuildContext(params: {
loginThemeResourcesFromKeycloakVersion: z.string().optional(), loginThemeResourcesFromKeycloakVersion: z.string().optional(),
keycloakifyBuildDirPath: z.string().optional(), keycloakifyBuildDirPath: z.string().optional(),
kcContextExclusionsFtl: z.string().optional(), kcContextExclusionsFtl: z.string().optional(),
extensionJars: z.array(z.string()).optional(),
startKeycloakOptions: zStartKeycloakOptions.optional() startKeycloakOptions: zStartKeycloakOptions.optional()
}), }),
zAccountThemeImplAndKeycloakVersionTargets zAccountThemeImplAndKeycloakVersionTargets
@ -520,6 +525,36 @@ export function getBuildContext(params: {
return pathJoin(projectDirPath, resolvedViteConfig.buildDir); return pathJoin(projectDirPath, resolvedViteConfig.buildDir);
})(); })();
const buildForKeycloakMajorVersionNumber = (() => {
const envValue = process.env[BUILD_FOR_KEYCLOAK_MAJOR_VERSION_ENV_NAME];
if (envValue === undefined) {
return undefined;
}
const major = parseInt(envValue);
assert(!isNaN(major));
return major;
})();
function urlOrPathToDiscriminatingWrapper(
urlOrPath: string
): { type: "url"; url: string } | { type: "path"; path: string } {
if (/^https?:\/\//.test(urlOrPath)) {
return { type: "url", url: urlOrPath };
}
return {
type: "path",
path: getAbsoluteAndInOsFormatPath({
pathIsh: urlOrPath,
cwd: projectDirPath
})
};
}
return { return {
bundler, bundler,
packageJsonFilePath, packageJsonFilePath,
@ -717,21 +752,6 @@ export function getBuildContext(params: {
`keycloak-theme-for-kc-${range}.jar`; `keycloak-theme-for-kc-${range}.jar`;
build_for_specific_keycloak_major_version: { build_for_specific_keycloak_major_version: {
const buildForKeycloakMajorVersionNumber = (() => {
const envValue =
process.env[BUILD_FOR_KEYCLOAK_MAJOR_VERSION_ENV_NAME];
if (envValue === undefined) {
return undefined;
}
const major = parseInt(envValue);
assert(!isNaN(major));
return major;
})();
if (buildForKeycloakMajorVersionNumber === undefined) { if (buildForKeycloakMajorVersionNumber === undefined) {
break build_for_specific_keycloak_major_version; break build_for_specific_keycloak_major_version;
} }
@ -931,6 +951,10 @@ export function getBuildContext(params: {
return jarTargets; return jarTargets;
})(), })(),
extensionJars: (buildForKeycloakMajorVersionNumber !== undefined
? []
: buildOptions.extensionJars ?? []
).map(urlOrPath => urlOrPathToDiscriminatingWrapper(urlOrPath)),
startKeycloakOptions: { startKeycloakOptions: {
dockerImage: (() => { dockerImage: (() => {
if (buildOptions.startKeycloakOptions?.dockerImage === undefined) { if (buildOptions.startKeycloakOptions?.dockerImage === undefined) {
@ -949,21 +973,14 @@ export function getBuildContext(params: {
})(), })(),
dockerExtraArgs: buildOptions.startKeycloakOptions?.dockerExtraArgs ?? [], dockerExtraArgs: buildOptions.startKeycloakOptions?.dockerExtraArgs ?? [],
keycloakExtraArgs: buildOptions.startKeycloakOptions?.keycloakExtraArgs ?? [], keycloakExtraArgs: buildOptions.startKeycloakOptions?.keycloakExtraArgs ?? [],
extensionJars: (buildOptions.startKeycloakOptions?.extensionJars ?? []).map( extensionJars: [
urlOrPath => { ...(buildForKeycloakMajorVersionNumber !== undefined
if (/^https?:\/\//.test(urlOrPath)) { ? buildOptions.extensionJars ?? []
return { type: "url", url: urlOrPath }; : []),
} ...(buildOptions.startKeycloakOptions?.extensionJars ?? [])
]
return { .map(urlOrPath => urlOrPathToDiscriminatingWrapper(urlOrPath))
type: "path", .reduce(...removeDuplicates<BuildContext["extensionJars"][number]>(same)),
path: getAbsoluteAndInOsFormatPath({
pathIsh: urlOrPath,
cwd: projectDirPath
})
};
}
),
realmJsonFilePath: realmJsonFilePath:
buildOptions.startKeycloakOptions?.realmJsonFilePath === undefined buildOptions.startKeycloakOptions?.realmJsonFilePath === undefined
? undefined ? undefined

@ -21,7 +21,7 @@ export async function downloadKeycloakDefaultTheme(params: {
let kcNodeModulesKeepFilePaths_lastAccountV1: Set<string> | undefined = undefined; let kcNodeModulesKeepFilePaths_lastAccountV1: Set<string> | undefined = undefined;
const { extractedDirPath } = await downloadAndExtractArchive({ const { extractedDirPath } = await downloadAndExtractArchive({
urlOrPath: `https://repo1.maven.org/maven2/org/keycloak/keycloak-themes/${keycloakVersion}/keycloak-themes-${keycloakVersion}.jar`, url: `https://repo1.maven.org/maven2/org/keycloak/keycloak-themes/${keycloakVersion}/keycloak-themes-${keycloakVersion}.jar`,
cacheDirPath: buildContext.cacheDirPath, cacheDirPath: buildContext.cacheDirPath,
fetchOptions: buildContext.fetchOptions, fetchOptions: buildContext.fetchOptions,
uniqueIdOfOnArchiveFile: "downloadKeycloakDefaultTheme", uniqueIdOfOnArchiveFile: "downloadKeycloakDefaultTheme",

@ -55,7 +55,7 @@ export async function promptKeycloakVersion(params: {
}); });
const lastMajorVersions = Array.from(semVersionedTagByMajor.values()).map( const lastMajorVersions = Array.from(semVersionedTagByMajor.values()).map(
({ tag }) => tag ({ version }) => `${version.major}.${version.minor}`
); );
const { value } = await cliSelect<string>({ const { value } = await cliSelect<string>({

@ -200,7 +200,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
const { archiveFilePath } = await downloadAndExtractArchive({ const { archiveFilePath } = await downloadAndExtractArchive({
cacheDirPath: buildContext.cacheDirPath, cacheDirPath: buildContext.cacheDirPath,
fetchOptions: buildContext.fetchOptions, fetchOptions: buildContext.fetchOptions,
urlOrPath: extensionJar.url, url: extensionJar.url,
uniqueIdOfOnArchiveFile: "no extraction", uniqueIdOfOnArchiveFile: "no extraction",
onArchiveFile: async () => {} onArchiveFile: async () => {}
}); });
@ -334,19 +334,30 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
}); });
} catch {} } catch {}
const DEFAULT_PORT = 8080;
const port =
cliCommandOptions.port ?? buildContext.startKeycloakOptions.port ?? DEFAULT_PORT;
const SPACE_PLACEHOLDER = "SPACE_PLACEHOLDER_xKLmdPd"; const SPACE_PLACEHOLDER = "SPACE_PLACEHOLDER_xKLmdPd";
const dockerRunArgs: string[] = [ const dockerRunArgs: string[] = [
`-p${SPACE_PLACEHOLDER}${cliCommandOptions.port ?? buildContext.startKeycloakOptions.port ?? 8080}:8080`, `-p${SPACE_PLACEHOLDER}${port}:8080`,
`--name${SPACE_PLACEHOLDER}${CONTAINER_NAME}`, `--name${SPACE_PLACEHOLDER}${CONTAINER_NAME}`,
`-e${SPACE_PLACEHOLDER}KEYCLOAK_ADMIN=admin`, `-e${SPACE_PLACEHOLDER}KEYCLOAK_ADMIN=admin`,
`-e${SPACE_PLACEHOLDER}KEYCLOAK_ADMIN_PASSWORD=admin`, `-e${SPACE_PLACEHOLDER}KEYCLOAK_ADMIN_PASSWORD=admin`,
...(buildContext.startKeycloakOptions.dockerExtraArgs.length === 0
? []
: [
buildContext.startKeycloakOptions.dockerExtraArgs.join(
SPACE_PLACEHOLDER
)
]),
...(realmJsonFilePath === undefined ...(realmJsonFilePath === undefined
? [] ? []
: [ : [
`-v${SPACE_PLACEHOLDER}".${pathSep}${pathRelative(process.cwd(), realmJsonFilePath)}":/opt/keycloak/data/import/myrealm-realm.json` `-v${SPACE_PLACEHOLDER}".${pathSep}${pathRelative(process.cwd(), realmJsonFilePath)}":/opt/keycloak/data/import/myrealm-realm.json`
]), ]),
`-v${SPACE_PLACEHOLDER}"./${pathRelative(process.cwd(), jarFilePath_cacheDir)}":/opt/keycloak/providers/keycloak-theme.jar`, `-v${SPACE_PLACEHOLDER}".${pathSep}${pathRelative(process.cwd(), jarFilePath_cacheDir)}":/opt/keycloak/providers/keycloak-theme.jar`,
...extensionJarFilePaths.map( ...extensionJarFilePaths.map(
jarFilePath => jarFilePath =>
`-v${SPACE_PLACEHOLDER}".${pathSep}${pathRelative(process.cwd(), jarFilePath)}":/opt/keycloak/providers/${pathBasename(jarFilePath)}` `-v${SPACE_PLACEHOLDER}".${pathSep}${pathRelative(process.cwd(), jarFilePath)}":/opt/keycloak/providers/${pathBasename(jarFilePath)}`
@ -388,14 +399,19 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
({ name, envValue }) => ({ name, envValue }) =>
`--env${SPACE_PLACEHOLDER}${name}='${envValue.replace(/'/g, "'\\''")}'` `--env${SPACE_PLACEHOLDER}${name}='${envValue.replace(/'/g, "'\\''")}'`
), ),
...buildContext.startKeycloakOptions.dockerExtraArgs.join(SPACE_PLACEHOLDER),
`${buildContext.startKeycloakOptions.dockerImage?.reference ?? "quay.io/keycloak/keycloak"}:${dockerImageTag}`, `${buildContext.startKeycloakOptions.dockerImage?.reference ?? "quay.io/keycloak/keycloak"}:${dockerImageTag}`,
"start-dev", "start-dev",
...(21 <= keycloakMajorVersionNumber && keycloakMajorVersionNumber < 24 ...(21 <= keycloakMajorVersionNumber && keycloakMajorVersionNumber < 24
? ["--features=declarative-user-profile"] ? ["--features=declarative-user-profile"]
: []), : []),
...(realmJsonFilePath === undefined ? [] : ["--import-realm"]), ...(realmJsonFilePath === undefined ? [] : ["--import-realm"]),
...buildContext.startKeycloakOptions.keycloakExtraArgs.join(SPACE_PLACEHOLDER) ...(buildContext.startKeycloakOptions.keycloakExtraArgs.length === 0
? []
: [
buildContext.startKeycloakOptions.keycloakExtraArgs.join(
SPACE_PLACEHOLDER
)
])
]; ];
console.log( console.log(
@ -427,6 +443,18 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
const srcDirPath = pathJoin(buildContext.projectDirPath, "src"); const srcDirPath = pathJoin(buildContext.projectDirPath, "src");
{ {
const kcHttpRelativePath = (() => {
const match = buildContext.startKeycloakOptions.dockerExtraArgs
.join(" ")
.match(/KC_HTTP_RELATIVE_PATH=([^ ]+)/);
if (match === null) {
return undefined;
}
return match[1];
})();
const handler = async (data: Buffer) => { const handler = async (data: Buffer) => {
if (!data.toString("utf8").includes("Listening on: http://0.0.0.0:8080")) { if (!data.toString("utf8").includes("Listening on: http://0.0.0.0:8080")) {
return; return;
@ -444,7 +472,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
)} are mounted in the Keycloak container.`, )} are mounted in the Keycloak container.`,
"", "",
`Keycloak Admin console: ${chalk.cyan.bold( `Keycloak Admin console: ${chalk.cyan.bold(
`http://localhost:${cliCommandOptions.port}` `http://localhost:${port}${kcHttpRelativePath ?? ""}`
)}`, )}`,
`- user: ${chalk.cyan.bold("admin")}`, `- user: ${chalk.cyan.bold("admin")}`,
`- password: ${chalk.cyan.bold("admin")}`, `- password: ${chalk.cyan.bold("admin")}`,
@ -452,7 +480,21 @@ export async function command(params: { cliCommandOptions: CliCommandOptions })
"", "",
`${chalk.green("Your theme is accessible at:")}`, `${chalk.green("Your theme is accessible at:")}`,
`${chalk.green("➜")} ${chalk.cyan.bold( `${chalk.green("➜")} ${chalk.cyan.bold(
`https://my-theme.keycloakify.dev${cliCommandOptions.port === 8080 ? "" : `?port=${cliCommandOptions.port}`}` (() => {
const url = new URL("https://my-theme.keycloakify.dev");
if (port !== DEFAULT_PORT) {
url.searchParams.set("port", `${port}`);
}
if (kcHttpRelativePath !== undefined) {
url.searchParams.set(
"kcHttpRelativePath",
kcHttpRelativePath
);
}
return url.href;
})()
)}`, )}`,
"", "",
"You can login with the following credentials:", "You can login with the following credentials:",

@ -1,15 +1,14 @@
import fetch, { type FetchOptions } from "make-fetch-happen"; import fetch, { type FetchOptions } from "make-fetch-happen";
import { mkdir, unlink, writeFile, readdir, readFile } from "fs/promises"; import { mkdir, unlink, writeFile, readdir, readFile } from "fs/promises";
import { dirname as pathDirname, join as pathJoin, basename as pathBasename } from "path"; import { dirname as pathDirname, join as pathJoin } from "path";
import { assert } from "tsafe/assert"; import { assert } from "tsafe/assert";
import { extractArchive } from "./extractArchive"; import { extractArchive } from "./extractArchive";
import { existsAsync } from "./fs.existsAsync"; import { existsAsync } from "./fs.existsAsync";
import * as crypto from "crypto"; import * as crypto from "crypto";
import { rm } from "./fs.rm"; import { rm } from "./fs.rm";
import * as fsPr from "fs/promises";
export async function downloadAndExtractArchive(params: { export async function downloadAndExtractArchive(params: {
urlOrPath: string; url: string;
uniqueIdOfOnArchiveFile: string; uniqueIdOfOnArchiveFile: string;
onArchiveFile: (params: { onArchiveFile: (params: {
fileRelativePath: string; fileRelativePath: string;
@ -21,34 +20,17 @@ export async function downloadAndExtractArchive(params: {
}) => Promise<void>; }) => Promise<void>;
cacheDirPath: string; cacheDirPath: string;
fetchOptions: FetchOptions | undefined; fetchOptions: FetchOptions | undefined;
}): Promise<{ extractedDirPath: string; archiveFilePath: string; }> { }): Promise<{ extractedDirPath: string; archiveFilePath: string }> {
const { const { url, uniqueIdOfOnArchiveFile, onArchiveFile, cacheDirPath, fetchOptions } =
urlOrPath, params;
uniqueIdOfOnArchiveFile,
onArchiveFile,
cacheDirPath,
fetchOptions
} = params;
const isUrl = /^https?:\/\//.test(urlOrPath); const archiveFileBasename = url.split("?")[0].split("/").reverse()[0];
const archiveFileBasename = isUrl
? urlOrPath.split("?")[0].split("/").reverse()[0]
: pathBasename(urlOrPath);
const archiveFilePath = pathJoin(cacheDirPath, archiveFileBasename); const archiveFilePath = pathJoin(cacheDirPath, archiveFileBasename);
download: { download: {
await mkdir(pathDirname(archiveFilePath), { recursive: true }); await mkdir(pathDirname(archiveFilePath), { recursive: true });
if (!isUrl) {
await fsPr.copyFile(urlOrPath, archiveFilePath);
break download;
}
const url = urlOrPath;
if (await existsAsync(archiveFilePath)) { if (await existsAsync(archiveFilePath)) {
const isDownloaded = await SuccessTracker.getIsDownloaded({ const isDownloaded = await SuccessTracker.getIsDownloaded({
cacheDirPath, cacheDirPath,

@ -153,7 +153,7 @@ export default function WebauthnRegister(props: PageProps<Extract<KcContext, { p
function getPubKeyCredParams(signatureAlgorithmsList) { function getPubKeyCredParams(signatureAlgorithmsList) {
let pubKeyCredParams = []; let pubKeyCredParams = [];
if (signatureAlgorithmsList === []) { if (signatureAlgorithmsList.length === 0) {
pubKeyCredParams.push({type: "public-key", alg: -7}); pubKeyCredParams.push({type: "public-key", alg: -7});
return pubKeyCredParams; return pubKeyCredParams;
} }
@ -184,7 +184,7 @@ export default function WebauthnRegister(props: PageProps<Extract<KcContext, { p
} }
function getTransportsAsString(transportsList) { function getTransportsAsString(transportsList) {
if (transportsList === '' || transportsList.constructor !== Array) return ""; if (transportsList === '' || Array.isArray(transportsList)) return "";
let transportsString = ""; let transportsString = "";