keycloakify 11.4.5 → 11.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/375.index.js +4089 -0
- package/bin/{20.index.js → 490.index.js} +378 -56
- package/bin/{36.index.js → 503.index.js} +53 -2
- package/bin/{450.index.js → 525.index.js} +2 -4085
- package/bin/653.index.js +108 -110
- package/bin/682.index.js +1885 -0
- package/bin/735.index.js +107 -109
- package/bin/921.index.js +1 -1
- package/bin/main.js +8 -2
- package/bin/shared/constants.d.ts +3 -0
- package/bin/shared/constants.js +3 -0
- package/bin/shared/constants.js.map +1 -1
- package/bin/start-keycloak/getSupportedDockerImageTags.d.ts +8 -0
- package/bin/start-keycloak/realmConfig/ParsedRealmJson.d.ts +38 -0
- package/bin/start-keycloak/realmConfig/defaultConfig/defaultConfig.d.ts +8 -0
- package/bin/start-keycloak/realmConfig/defaultConfig/index.d.ts +1 -0
- package/bin/start-keycloak/realmConfig/dumpContainerConfig.d.ts +9 -0
- package/bin/start-keycloak/realmConfig/index.d.ts +1 -0
- package/bin/start-keycloak/realmConfig/prepareRealmConfig.d.ts +15 -0
- package/bin/start-keycloak/realmConfig/realmConfig.d.ts +16 -0
- package/package.json +31 -14
- package/src/bin/shared/constants.ts +6 -0
- package/src/bin/start-keycloak/getSupportedDockerImageTags.ts +230 -0
- package/src/bin/start-keycloak/keycloakify-logging-1.0.3.jar +0 -0
- package/src/bin/start-keycloak/realmConfig/ParsedRealmJson.ts +118 -0
- package/src/bin/start-keycloak/realmConfig/defaultConfig/defaultConfig.ts +75 -0
- package/src/bin/start-keycloak/realmConfig/defaultConfig/index.ts +1 -0
- package/src/bin/start-keycloak/{myrealm-realm-18.json → realmConfig/defaultConfig/realm-kc-18.json} +123 -60
- package/src/bin/start-keycloak/{myrealm-realm-19.json → realmConfig/defaultConfig/realm-kc-19.json} +81 -41
- package/src/bin/start-keycloak/{myrealm-realm-20.json → realmConfig/defaultConfig/realm-kc-20.json} +83 -42
- package/src/bin/start-keycloak/{myrealm-realm-21.json → realmConfig/defaultConfig/realm-kc-21.json} +58 -17
- package/src/bin/start-keycloak/{myrealm-realm-23.json → realmConfig/defaultConfig/realm-kc-23.json} +64 -20
- package/src/bin/start-keycloak/{myrealm-realm-24.json → realmConfig/defaultConfig/realm-kc-24.json} +63 -19
- package/src/bin/start-keycloak/{myrealm-realm-25.json → realmConfig/defaultConfig/realm-kc-25.json} +75 -20
- package/src/bin/start-keycloak/{myrealm-realm-26.json → realmConfig/defaultConfig/realm-kc-26.json} +86 -20
- package/src/bin/start-keycloak/realmConfig/dumpContainerConfig.ts +147 -0
- package/src/bin/start-keycloak/realmConfig/index.ts +1 -0
- package/src/bin/start-keycloak/realmConfig/prepareRealmConfig.ts +302 -0
- package/src/bin/start-keycloak/realmConfig/realmConfig.ts +151 -0
- package/src/bin/start-keycloak/start-keycloak.ts +160 -184
- package/src/bin/start-keycloak/startViteDevServer.ts +1 -0
- package/vite-plugin/index.js +6 -0
- package/bin/392.index.js +0 -740
- package/bin/932.index.js +0 -327
@@ -0,0 +1,147 @@
|
|
1
|
+
import { CONTAINER_NAME } from "../../shared/constants";
|
2
|
+
import child_process from "child_process";
|
3
|
+
import { join as pathJoin } from "path";
|
4
|
+
import chalk from "chalk";
|
5
|
+
import { Deferred } from "evt/tools/Deferred";
|
6
|
+
import { assert, is } from "tsafe/assert";
|
7
|
+
import type { BuildContext } from "../../shared/buildContext";
|
8
|
+
import { type ParsedRealmJson, readRealmJsonFile } from "./ParsedRealmJson";
|
9
|
+
|
10
|
+
export type BuildContextLike = {
|
11
|
+
cacheDirPath: string;
|
12
|
+
};
|
13
|
+
|
14
|
+
assert<BuildContext extends BuildContextLike ? true : false>();
|
15
|
+
|
16
|
+
export async function dumpContainerConfig(params: {
|
17
|
+
realmName: string;
|
18
|
+
keycloakMajorVersionNumber: number;
|
19
|
+
buildContext: BuildContextLike;
|
20
|
+
}): Promise<ParsedRealmJson> {
|
21
|
+
const { realmName, keycloakMajorVersionNumber, buildContext } = params;
|
22
|
+
|
23
|
+
{
|
24
|
+
// https://github.com/keycloak/keycloak/issues/33800
|
25
|
+
const doesUseLockedH2Database = keycloakMajorVersionNumber >= 25;
|
26
|
+
|
27
|
+
if (doesUseLockedH2Database) {
|
28
|
+
child_process.execSync(
|
29
|
+
`docker exec ${CONTAINER_NAME} sh -c "cp -rp /opt/keycloak/data/h2 /tmp"`
|
30
|
+
);
|
31
|
+
}
|
32
|
+
|
33
|
+
const dCompleted = new Deferred<void>();
|
34
|
+
|
35
|
+
const child = child_process.spawn(
|
36
|
+
"docker",
|
37
|
+
[
|
38
|
+
...["exec", CONTAINER_NAME],
|
39
|
+
...["/opt/keycloak/bin/kc.sh", "export"],
|
40
|
+
...["--dir", "/tmp"],
|
41
|
+
...["--realm", realmName],
|
42
|
+
...["--users", "realm_file"],
|
43
|
+
...(!doesUseLockedH2Database
|
44
|
+
? []
|
45
|
+
: [
|
46
|
+
...["--db", "dev-file"],
|
47
|
+
...[
|
48
|
+
"--db-url",
|
49
|
+
"'jdbc:h2:file:/tmp/h2/keycloakdb;NON_KEYWORDS=VALUE'"
|
50
|
+
]
|
51
|
+
])
|
52
|
+
],
|
53
|
+
{ shell: true }
|
54
|
+
);
|
55
|
+
|
56
|
+
let output = "";
|
57
|
+
|
58
|
+
const onExit = (code: number | null) => {
|
59
|
+
dCompleted.reject(new Error(`Exited with code ${code}`));
|
60
|
+
};
|
61
|
+
|
62
|
+
child.once("exit", onExit);
|
63
|
+
|
64
|
+
child.stdout.on("data", data => {
|
65
|
+
const outputStr = data.toString("utf8");
|
66
|
+
|
67
|
+
if (outputStr.includes("Export finished successfully")) {
|
68
|
+
child.removeListener("exit", onExit);
|
69
|
+
|
70
|
+
// NOTE: On older Keycloak versions the process keeps running after the export is done.
|
71
|
+
const timer = setTimeout(() => {
|
72
|
+
child.removeListener("exit", onExit2);
|
73
|
+
child.kill();
|
74
|
+
dCompleted.resolve();
|
75
|
+
}, 1500);
|
76
|
+
|
77
|
+
const onExit2 = () => {
|
78
|
+
clearTimeout(timer);
|
79
|
+
dCompleted.resolve();
|
80
|
+
};
|
81
|
+
|
82
|
+
child.once("exit", onExit2);
|
83
|
+
}
|
84
|
+
|
85
|
+
output += outputStr;
|
86
|
+
});
|
87
|
+
|
88
|
+
child.stderr.on("data", data => (output += chalk.red(data.toString("utf8"))));
|
89
|
+
|
90
|
+
try {
|
91
|
+
await dCompleted.pr;
|
92
|
+
} catch (error) {
|
93
|
+
assert(is<Error>(error));
|
94
|
+
|
95
|
+
console.log(chalk.red(error.message));
|
96
|
+
|
97
|
+
console.log(output);
|
98
|
+
|
99
|
+
process.exit(1);
|
100
|
+
}
|
101
|
+
|
102
|
+
if (doesUseLockedH2Database) {
|
103
|
+
const dCompleted = new Deferred<void>();
|
104
|
+
|
105
|
+
child_process.exec(
|
106
|
+
`docker exec ${CONTAINER_NAME} sh -c "rm -rf /tmp/h2"`,
|
107
|
+
error => {
|
108
|
+
if (error !== null) {
|
109
|
+
dCompleted.reject(error);
|
110
|
+
return;
|
111
|
+
}
|
112
|
+
|
113
|
+
dCompleted.resolve();
|
114
|
+
}
|
115
|
+
);
|
116
|
+
|
117
|
+
await dCompleted.pr;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
const targetRealmConfigJsonFilePath_tmp = pathJoin(
|
122
|
+
buildContext.cacheDirPath,
|
123
|
+
"realm.json"
|
124
|
+
);
|
125
|
+
|
126
|
+
{
|
127
|
+
const dCompleted = new Deferred<void>();
|
128
|
+
|
129
|
+
child_process.exec(
|
130
|
+
`docker cp ${CONTAINER_NAME}:/tmp/${realmName}-realm.json ${targetRealmConfigJsonFilePath_tmp}`,
|
131
|
+
error => {
|
132
|
+
if (error !== null) {
|
133
|
+
dCompleted.reject(error);
|
134
|
+
return;
|
135
|
+
}
|
136
|
+
|
137
|
+
dCompleted.resolve();
|
138
|
+
}
|
139
|
+
);
|
140
|
+
|
141
|
+
await dCompleted.pr;
|
142
|
+
}
|
143
|
+
|
144
|
+
return readRealmJsonFile({
|
145
|
+
realmJsonFilePath: targetRealmConfigJsonFilePath_tmp
|
146
|
+
});
|
147
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from "./realmConfig";
|
@@ -0,0 +1,302 @@
|
|
1
|
+
import { assert } from "tsafe/assert";
|
2
|
+
import type { ParsedRealmJson } from "./ParsedRealmJson";
|
3
|
+
import { getDefaultConfig } from "./defaultConfig";
|
4
|
+
import type { BuildContext } from "../../shared/buildContext";
|
5
|
+
import { objectKeys } from "tsafe/objectKeys";
|
6
|
+
import { TEST_APP_URL } from "../../shared/constants";
|
7
|
+
import { sameFactory } from "evt/tools/inDepth/same";
|
8
|
+
|
9
|
+
export type BuildContextLike = {
|
10
|
+
themeNames: BuildContext["themeNames"];
|
11
|
+
implementedThemeTypes: BuildContext["implementedThemeTypes"];
|
12
|
+
};
|
13
|
+
|
14
|
+
assert<BuildContext extends BuildContextLike ? true : false>;
|
15
|
+
|
16
|
+
export function prepareRealmConfig(params: {
|
17
|
+
parsedRealmJson: ParsedRealmJson;
|
18
|
+
keycloakMajorVersionNumber: number;
|
19
|
+
buildContext: BuildContextLike;
|
20
|
+
}): {
|
21
|
+
realmName: string;
|
22
|
+
clientName: string;
|
23
|
+
username: string;
|
24
|
+
} {
|
25
|
+
const { parsedRealmJson, keycloakMajorVersionNumber, buildContext } = params;
|
26
|
+
|
27
|
+
const { username } = addOrEditTestUser({
|
28
|
+
parsedRealmJson,
|
29
|
+
keycloakMajorVersionNumber
|
30
|
+
});
|
31
|
+
|
32
|
+
const { clientId } = addOrEditClient({
|
33
|
+
parsedRealmJson,
|
34
|
+
keycloakMajorVersionNumber
|
35
|
+
});
|
36
|
+
|
37
|
+
editAccountConsoleAndSecurityAdminConsole({ parsedRealmJson });
|
38
|
+
|
39
|
+
enableCustomThemes({
|
40
|
+
parsedRealmJson,
|
41
|
+
themeName: buildContext.themeNames[0],
|
42
|
+
implementedThemeTypes: buildContext.implementedThemeTypes
|
43
|
+
});
|
44
|
+
|
45
|
+
enable_custom_events_listeners: {
|
46
|
+
const name = "keycloakify-logging";
|
47
|
+
|
48
|
+
if (parsedRealmJson.eventsListeners.includes(name)) {
|
49
|
+
break enable_custom_events_listeners;
|
50
|
+
}
|
51
|
+
|
52
|
+
parsedRealmJson.eventsListeners.push(name);
|
53
|
+
|
54
|
+
parsedRealmJson.eventsListeners.sort();
|
55
|
+
}
|
56
|
+
|
57
|
+
return {
|
58
|
+
realmName: parsedRealmJson.realm,
|
59
|
+
clientName: clientId,
|
60
|
+
username
|
61
|
+
};
|
62
|
+
}
|
63
|
+
|
64
|
+
function enableCustomThemes(params: {
|
65
|
+
parsedRealmJson: ParsedRealmJson;
|
66
|
+
themeName: string;
|
67
|
+
implementedThemeTypes: BuildContextLike["implementedThemeTypes"];
|
68
|
+
}) {
|
69
|
+
const { parsedRealmJson, themeName, implementedThemeTypes } = params;
|
70
|
+
|
71
|
+
for (const themeType of objectKeys(implementedThemeTypes)) {
|
72
|
+
if (!implementedThemeTypes[themeType].isImplemented) {
|
73
|
+
continue;
|
74
|
+
}
|
75
|
+
|
76
|
+
parsedRealmJson[`${themeType}Theme` as const] = themeName;
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
function addOrEditTestUser(params: {
|
81
|
+
parsedRealmJson: ParsedRealmJson;
|
82
|
+
keycloakMajorVersionNumber: number;
|
83
|
+
}): { username: string } {
|
84
|
+
const { parsedRealmJson, keycloakMajorVersionNumber } = params;
|
85
|
+
|
86
|
+
const parsedRealmJson_default = getDefaultConfig({ keycloakMajorVersionNumber });
|
87
|
+
|
88
|
+
const [defaultUser_default] = parsedRealmJson_default.users;
|
89
|
+
|
90
|
+
assert(defaultUser_default !== undefined);
|
91
|
+
|
92
|
+
const defaultUser_preexisting = parsedRealmJson.users.find(
|
93
|
+
user => user.username === defaultUser_default.username
|
94
|
+
);
|
95
|
+
|
96
|
+
const newUser = structuredClone(
|
97
|
+
defaultUser_preexisting ??
|
98
|
+
(() => {
|
99
|
+
const firstUser = parsedRealmJson.users[0];
|
100
|
+
|
101
|
+
if (firstUser === undefined) {
|
102
|
+
return undefined;
|
103
|
+
}
|
104
|
+
|
105
|
+
const firstUserCopy = structuredClone(firstUser);
|
106
|
+
|
107
|
+
firstUserCopy.id = defaultUser_default.id;
|
108
|
+
|
109
|
+
return firstUserCopy;
|
110
|
+
})() ??
|
111
|
+
defaultUser_default
|
112
|
+
);
|
113
|
+
|
114
|
+
newUser.username = defaultUser_default.username;
|
115
|
+
newUser.email = defaultUser_default.email;
|
116
|
+
|
117
|
+
delete_existing_password_credential_if_any: {
|
118
|
+
const i = newUser.credentials.findIndex(
|
119
|
+
credential => credential.type === "password"
|
120
|
+
);
|
121
|
+
|
122
|
+
if (i === -1) {
|
123
|
+
break delete_existing_password_credential_if_any;
|
124
|
+
}
|
125
|
+
|
126
|
+
newUser.credentials.splice(i, 1);
|
127
|
+
}
|
128
|
+
|
129
|
+
{
|
130
|
+
const credential = defaultUser_default.credentials.find(
|
131
|
+
credential => credential.type === "password"
|
132
|
+
);
|
133
|
+
|
134
|
+
assert(credential !== undefined);
|
135
|
+
|
136
|
+
newUser.credentials.push(credential);
|
137
|
+
}
|
138
|
+
|
139
|
+
{
|
140
|
+
const nameByClientId = Object.fromEntries(
|
141
|
+
parsedRealmJson.clients.map(client => [client.id, client.clientId] as const)
|
142
|
+
);
|
143
|
+
|
144
|
+
const newClientRoles: NonNullable<
|
145
|
+
ParsedRealmJson["users"][number]["clientRoles"]
|
146
|
+
> = {};
|
147
|
+
|
148
|
+
for (const clientRole of Object.values(parsedRealmJson.roles.client).flat()) {
|
149
|
+
const clientName = nameByClientId[clientRole.containerId];
|
150
|
+
|
151
|
+
assert(clientName !== undefined);
|
152
|
+
|
153
|
+
(newClientRoles[clientName] ??= []).push(clientRole.name);
|
154
|
+
}
|
155
|
+
|
156
|
+
const { same: sameSet } = sameFactory({
|
157
|
+
takeIntoAccountArraysOrdering: false
|
158
|
+
});
|
159
|
+
|
160
|
+
for (const [clientName, roles] of Object.entries(newClientRoles)) {
|
161
|
+
keep_previous_ordering_if_possible: {
|
162
|
+
const roles_previous = newUser.clientRoles?.[clientName];
|
163
|
+
|
164
|
+
if (roles_previous === undefined) {
|
165
|
+
break keep_previous_ordering_if_possible;
|
166
|
+
}
|
167
|
+
|
168
|
+
if (!sameSet(roles_previous, roles)) {
|
169
|
+
break keep_previous_ordering_if_possible;
|
170
|
+
}
|
171
|
+
|
172
|
+
continue;
|
173
|
+
}
|
174
|
+
|
175
|
+
(newUser.clientRoles ??= {})[clientName] = roles;
|
176
|
+
}
|
177
|
+
}
|
178
|
+
|
179
|
+
if (defaultUser_preexisting === undefined) {
|
180
|
+
parsedRealmJson.users.push(newUser);
|
181
|
+
} else {
|
182
|
+
const i = parsedRealmJson.users.indexOf(defaultUser_preexisting);
|
183
|
+
assert(i !== -1);
|
184
|
+
parsedRealmJson.users[i] = newUser;
|
185
|
+
}
|
186
|
+
|
187
|
+
return { username: newUser.username };
|
188
|
+
}
|
189
|
+
|
190
|
+
function addOrEditClient(params: {
|
191
|
+
parsedRealmJson: ParsedRealmJson;
|
192
|
+
keycloakMajorVersionNumber: number;
|
193
|
+
}): { clientId: string } {
|
194
|
+
const { parsedRealmJson, keycloakMajorVersionNumber } = params;
|
195
|
+
|
196
|
+
const parsedRealmJson_default = getDefaultConfig({ keycloakMajorVersionNumber });
|
197
|
+
|
198
|
+
const testClient_default = (() => {
|
199
|
+
const clients = parsedRealmJson_default.clients.filter(client => {
|
200
|
+
return JSON.stringify(client).includes(TEST_APP_URL);
|
201
|
+
});
|
202
|
+
|
203
|
+
assert(clients.length === 1);
|
204
|
+
|
205
|
+
return clients[0];
|
206
|
+
})();
|
207
|
+
|
208
|
+
const clientIds_builtIn = parsedRealmJson_default.clients
|
209
|
+
.map(client => client.clientId)
|
210
|
+
.filter(clientId => clientId !== testClient_default.clientId);
|
211
|
+
|
212
|
+
const testClient_preexisting = (() => {
|
213
|
+
const clients = parsedRealmJson.clients
|
214
|
+
.filter(client => !clientIds_builtIn.includes(client.clientId))
|
215
|
+
.filter(client => client.protocol === "openid-connect");
|
216
|
+
|
217
|
+
{
|
218
|
+
const client = clients.find(
|
219
|
+
client => client.clientId === testClient_default.clientId
|
220
|
+
);
|
221
|
+
|
222
|
+
if (client !== undefined) {
|
223
|
+
return client;
|
224
|
+
}
|
225
|
+
}
|
226
|
+
|
227
|
+
{
|
228
|
+
const client = clients.find(
|
229
|
+
client =>
|
230
|
+
client.redirectUris?.find(redirectUri =>
|
231
|
+
redirectUri.startsWith(TEST_APP_URL)
|
232
|
+
) !== undefined
|
233
|
+
);
|
234
|
+
|
235
|
+
if (client !== undefined) {
|
236
|
+
return client;
|
237
|
+
}
|
238
|
+
}
|
239
|
+
|
240
|
+
const [client] = clients;
|
241
|
+
|
242
|
+
if (client === undefined) {
|
243
|
+
return undefined;
|
244
|
+
}
|
245
|
+
|
246
|
+
return client;
|
247
|
+
})();
|
248
|
+
|
249
|
+
let testClient: typeof testClient_default;
|
250
|
+
|
251
|
+
if (testClient_preexisting !== undefined) {
|
252
|
+
testClient = testClient_preexisting;
|
253
|
+
} else {
|
254
|
+
testClient = structuredClone(testClient_default);
|
255
|
+
delete testClient.protocolMappers;
|
256
|
+
parsedRealmJson.clients.push(testClient);
|
257
|
+
}
|
258
|
+
|
259
|
+
testClient.redirectUris = [
|
260
|
+
`${TEST_APP_URL}/*`,
|
261
|
+
"http://localhost*",
|
262
|
+
"http://127.0.0.1*"
|
263
|
+
]
|
264
|
+
.sort()
|
265
|
+
.reverse();
|
266
|
+
|
267
|
+
(testClient.attributes ??= {})["post.logout.redirect.uris"] = "+";
|
268
|
+
|
269
|
+
testClient.webOrigins = ["*"];
|
270
|
+
|
271
|
+
return { clientId: testClient.clientId };
|
272
|
+
}
|
273
|
+
|
274
|
+
function editAccountConsoleAndSecurityAdminConsole(params: {
|
275
|
+
parsedRealmJson: ParsedRealmJson;
|
276
|
+
}) {
|
277
|
+
const { parsedRealmJson } = params;
|
278
|
+
|
279
|
+
for (const clientId of ["account-console", "security-admin-console"]) {
|
280
|
+
const client = parsedRealmJson.clients.find(
|
281
|
+
client => client.clientId === clientId
|
282
|
+
);
|
283
|
+
|
284
|
+
assert(client !== undefined);
|
285
|
+
|
286
|
+
{
|
287
|
+
const arr = (client.redirectUris ??= []);
|
288
|
+
|
289
|
+
for (const value of ["http://localhost*", "http://127.0.0.1*"]) {
|
290
|
+
if (!arr.includes(value)) {
|
291
|
+
arr.push(value);
|
292
|
+
}
|
293
|
+
}
|
294
|
+
|
295
|
+
client.redirectUris?.sort().reverse();
|
296
|
+
}
|
297
|
+
|
298
|
+
(client.attributes ??= {})["post.logout.redirect.uris"] = "+";
|
299
|
+
|
300
|
+
client.webOrigins = ["*"];
|
301
|
+
}
|
302
|
+
}
|
@@ -0,0 +1,151 @@
|
|
1
|
+
import type { BuildContext } from "../../shared/buildContext";
|
2
|
+
import { assert } from "tsafe/assert";
|
3
|
+
import { runPrettier, getIsPrettierAvailable } from "../../tools/runPrettier";
|
4
|
+
import { getDefaultConfig } from "./defaultConfig";
|
5
|
+
import {
|
6
|
+
prepareRealmConfig,
|
7
|
+
type BuildContextLike as BuildContextLike_prepareRealmConfig
|
8
|
+
} from "./prepareRealmConfig";
|
9
|
+
import * as fs from "fs";
|
10
|
+
import {
|
11
|
+
join as pathJoin,
|
12
|
+
dirname as pathDirname,
|
13
|
+
relative as pathRelative,
|
14
|
+
sep as pathSep
|
15
|
+
} from "path";
|
16
|
+
import { existsAsync } from "../../tools/fs.existsAsync";
|
17
|
+
import { readRealmJsonFile, type ParsedRealmJson } from "./ParsedRealmJson";
|
18
|
+
import {
|
19
|
+
dumpContainerConfig,
|
20
|
+
type BuildContextLike as BuildContextLike_dumpContainerConfig
|
21
|
+
} from "./dumpContainerConfig";
|
22
|
+
import * as runExclusive from "run-exclusive";
|
23
|
+
import { waitForDebounceFactory } from "powerhooks/tools/waitForDebounce";
|
24
|
+
import chalk from "chalk";
|
25
|
+
|
26
|
+
export type BuildContextLike = BuildContextLike_dumpContainerConfig &
|
27
|
+
BuildContextLike_prepareRealmConfig & {
|
28
|
+
projectDirPath: string;
|
29
|
+
};
|
30
|
+
|
31
|
+
assert<BuildContext extends BuildContextLike ? true : false>;
|
32
|
+
|
33
|
+
export async function getRealmConfig(params: {
|
34
|
+
keycloakMajorVersionNumber: number;
|
35
|
+
realmJsonFilePath_userProvided: string | undefined;
|
36
|
+
buildContext: BuildContextLike;
|
37
|
+
}): Promise<{
|
38
|
+
realmJsonFilePath: string;
|
39
|
+
clientName: string;
|
40
|
+
realmName: string;
|
41
|
+
username: string;
|
42
|
+
onRealmConfigChange: () => Promise<void>;
|
43
|
+
}> {
|
44
|
+
const { keycloakMajorVersionNumber, realmJsonFilePath_userProvided, buildContext } =
|
45
|
+
params;
|
46
|
+
|
47
|
+
const realmJsonFilePath = pathJoin(
|
48
|
+
buildContext.projectDirPath,
|
49
|
+
".keycloakify",
|
50
|
+
`realm-kc-${keycloakMajorVersionNumber}.json`
|
51
|
+
);
|
52
|
+
|
53
|
+
const parsedRealmJson = await (async () => {
|
54
|
+
if (realmJsonFilePath_userProvided !== undefined) {
|
55
|
+
return readRealmJsonFile({
|
56
|
+
realmJsonFilePath: realmJsonFilePath_userProvided
|
57
|
+
});
|
58
|
+
}
|
59
|
+
|
60
|
+
if (await existsAsync(realmJsonFilePath)) {
|
61
|
+
return readRealmJsonFile({
|
62
|
+
realmJsonFilePath
|
63
|
+
});
|
64
|
+
}
|
65
|
+
|
66
|
+
return getDefaultConfig({ keycloakMajorVersionNumber });
|
67
|
+
})();
|
68
|
+
|
69
|
+
const { clientName, realmName, username } = prepareRealmConfig({
|
70
|
+
parsedRealmJson,
|
71
|
+
buildContext,
|
72
|
+
keycloakMajorVersionNumber
|
73
|
+
});
|
74
|
+
|
75
|
+
{
|
76
|
+
const dirPath = pathDirname(realmJsonFilePath);
|
77
|
+
|
78
|
+
if (!(await existsAsync(dirPath))) {
|
79
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
const writeRealmJsonFile = async (params: { parsedRealmJson: ParsedRealmJson }) => {
|
84
|
+
const { parsedRealmJson } = params;
|
85
|
+
|
86
|
+
let sourceCode = JSON.stringify(parsedRealmJson, null, 2);
|
87
|
+
|
88
|
+
if (await getIsPrettierAvailable()) {
|
89
|
+
sourceCode = await runPrettier({
|
90
|
+
sourceCode,
|
91
|
+
filePath: realmJsonFilePath
|
92
|
+
});
|
93
|
+
}
|
94
|
+
|
95
|
+
fs.writeFileSync(realmJsonFilePath, sourceCode);
|
96
|
+
};
|
97
|
+
|
98
|
+
await writeRealmJsonFile({ parsedRealmJson });
|
99
|
+
|
100
|
+
const { onRealmConfigChange } = (() => {
|
101
|
+
const run = runExclusive.build(async () => {
|
102
|
+
const start = Date.now();
|
103
|
+
|
104
|
+
console.log(
|
105
|
+
chalk.grey(`Changes detected to the '${realmName}' config, backing up...`)
|
106
|
+
);
|
107
|
+
|
108
|
+
const parsedRealmJson = await dumpContainerConfig({
|
109
|
+
buildContext,
|
110
|
+
realmName,
|
111
|
+
keycloakMajorVersionNumber
|
112
|
+
});
|
113
|
+
|
114
|
+
await writeRealmJsonFile({ parsedRealmJson });
|
115
|
+
|
116
|
+
console.log(
|
117
|
+
[
|
118
|
+
chalk.grey(
|
119
|
+
`Save changed to \`.${pathSep}${pathRelative(buildContext.projectDirPath, realmJsonFilePath)}\``
|
120
|
+
),
|
121
|
+
chalk.grey(
|
122
|
+
`Next time you'll be running \`keycloakify start-keycloak\`, the realm '${realmName}' will be restored to this state.`
|
123
|
+
),
|
124
|
+
chalk.green(
|
125
|
+
`✓ '${realmName}' config backed up completed in ${Date.now() - start}ms`
|
126
|
+
)
|
127
|
+
].join("\n")
|
128
|
+
);
|
129
|
+
});
|
130
|
+
|
131
|
+
const { waitForDebounce } = waitForDebounceFactory({
|
132
|
+
delay: 1_000
|
133
|
+
});
|
134
|
+
|
135
|
+
async function onRealmConfigChange() {
|
136
|
+
await waitForDebounce();
|
137
|
+
|
138
|
+
run();
|
139
|
+
}
|
140
|
+
|
141
|
+
return { onRealmConfigChange };
|
142
|
+
})();
|
143
|
+
|
144
|
+
return {
|
145
|
+
realmJsonFilePath,
|
146
|
+
clientName,
|
147
|
+
realmName,
|
148
|
+
username,
|
149
|
+
onRealmConfigChange
|
150
|
+
};
|
151
|
+
}
|