@appsemble/cli 0.27.11 → 0.28.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/README.md +3 -3
- package/commands/app/delete.js +2 -10
- package/commands/app/export.js +2 -3
- package/commands/block/delete.js +2 -3
- package/commands/serve.js +20 -1
- package/commands/team/create.js +1 -2
- package/commands/team/delete.js +1 -2
- package/lib/app.d.ts +11 -2
- package/lib/app.js +199 -4
- package/lib/app.test.js +767 -22
- package/lib/asset.test.d.ts +1 -0
- package/lib/asset.test.js +101 -0
- package/lib/block.d.ts +2 -1
- package/lib/block.js +3 -2
- package/lib/block.test.js +52 -1
- package/lib/organization.js +5 -3
- package/lib/organization.test.d.ts +1 -0
- package/lib/organization.test.js +222 -0
- package/lib/resource.test.d.ts +1 -0
- package/lib/resource.test.js +391 -0
- package/lib/team.d.ts +6 -2
- package/lib/team.js +6 -4
- package/lib/team.test.d.ts +1 -0
- package/lib/team.test.js +606 -0
- package/lib/testUtils.d.ts +2 -0
- package/lib/testUtils.js +11 -0
- package/package.json +6 -6
- package/server/options/getAppVariables.d.ts +3 -0
- package/server/options/getAppVariables.js +4 -0
- package/server/options/index.d.ts +1 -0
- package/server/options/index.js +1 -0
- package/server/options/options.js +2 -1
- package/server/options/parseQuery.test.js +2 -0
- package/templates/apps/empty/app-definition.yaml +2 -2
- package/templates/blocks/mini-jsx/package.json +1 -1
- package/templates/blocks/preact/package.json +2 -2
- package/templates/blocks/vanilla/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
#  Appsemble CLI
|
|
2
2
|
|
|
3
3
|
> Manage apps and blocks from the command line.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@appsemble/cli)
|
|
6
|
-
[](https://gitlab.com/appsemble/appsemble/-/releases/0.28.0)
|
|
7
7
|
[](https://prettier.io)
|
|
8
8
|
|
|
9
9
|
## Table of Contents
|
|
@@ -323,5 +323,5 @@ appsemble run-cronjobs --interval 30
|
|
|
323
323
|
|
|
324
324
|
## License
|
|
325
325
|
|
|
326
|
-
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.
|
|
326
|
+
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.28.0/LICENSE.md) ©
|
|
327
327
|
[Appsemble](https://appsemble.com)
|
package/commands/app/delete.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import axios from 'axios';
|
|
1
|
+
import { deleteApp } from '../../lib/app.js';
|
|
3
2
|
export const command = 'delete';
|
|
4
3
|
export const description = 'Delete an app using the app id.';
|
|
5
4
|
export function builder(yargs) {
|
|
@@ -9,13 +8,6 @@ export function builder(yargs) {
|
|
|
9
8
|
});
|
|
10
9
|
}
|
|
11
10
|
export async function handler({ clientCredentials, id, remote, }) {
|
|
12
|
-
await
|
|
13
|
-
const response = await axios.get(`/api/apps/${id}`);
|
|
14
|
-
const { name } = response.data;
|
|
15
|
-
logger.warn(`Deleting app: ${name}`);
|
|
16
|
-
axios.delete(`/api/apps/${id}`, {
|
|
17
|
-
baseURL: remote,
|
|
18
|
-
});
|
|
19
|
-
logger.info(`Successfully deleted ${name} app.`);
|
|
11
|
+
await deleteApp({ id, remote, clientCredentials });
|
|
20
12
|
}
|
|
21
13
|
//# sourceMappingURL=delete.js.map
|
package/commands/app/export.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { logger } from '@appsemble/node-utils';
|
|
3
3
|
import { exportAppAsZip } from '../../lib/app.js';
|
|
4
4
|
export const command = 'export';
|
|
5
5
|
export const description = `Export an app as a zip file using app id.
|
|
@@ -24,9 +24,8 @@ export function builder(yargs) {
|
|
|
24
24
|
export async function handler(args) {
|
|
25
25
|
const { assets = false, clientCredentials, id: appId, path: outputDirectory, remote, resources = false, } = args;
|
|
26
26
|
const defaultOutputDirectory = join(process.cwd(), 'apps');
|
|
27
|
-
await authenticate(remote, 'apps:export', clientCredentials);
|
|
28
27
|
logger.info(`Exporting app with id: ${appId}`);
|
|
29
|
-
await exportAppAsZip(appId, assets, resources, outputDirectory || defaultOutputDirectory, remote);
|
|
28
|
+
await exportAppAsZip(clientCredentials, appId, assets, resources, outputDirectory || defaultOutputDirectory, remote);
|
|
30
29
|
logger.info('Export completed successfully');
|
|
31
30
|
}
|
|
32
31
|
//# sourceMappingURL=export.js.map
|
package/commands/block/delete.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { logger } from '@appsemble/node-utils';
|
|
2
2
|
import { deleteBlock } from '../../lib/block.js';
|
|
3
3
|
export const command = 'delete <block>';
|
|
4
4
|
export const description = 'Delete a specific block version. Using this outside local development is discouraged.';
|
|
@@ -13,9 +13,8 @@ export function builder(yargs) {
|
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
export async function handler({ block, clientCredentials, organization, remote, }) {
|
|
16
|
-
await authenticate(remote, 'blocks:delete', clientCredentials);
|
|
17
16
|
const [blockName, blockVersion] = block.split(':');
|
|
18
17
|
logger.info(`Deleting Block ${block}`);
|
|
19
|
-
await deleteBlock({ organization, blockName, blockVersion, remote });
|
|
18
|
+
await deleteBlock({ organization, blockName, blockVersion, remote, clientCredentials });
|
|
20
19
|
}
|
|
21
20
|
//# sourceMappingURL=delete.js.map
|
package/commands/serve.js
CHANGED
|
@@ -9,7 +9,7 @@ import axios from 'axios';
|
|
|
9
9
|
import csvToJson from 'csvtojson';
|
|
10
10
|
import FormData from 'form-data';
|
|
11
11
|
import globalCacheDir from 'global-cache-dir';
|
|
12
|
-
import { traverseAppDirectory } from '../lib/app.js';
|
|
12
|
+
import { parseValues, traverseAppDirectory } from '../lib/app.js';
|
|
13
13
|
import { buildBlock } from '../lib/block.js';
|
|
14
14
|
import { getProjectBuildConfig, getProjectWebpackConfig } from '../lib/config.js';
|
|
15
15
|
import { createApiServer, createStaticServer } from '../lib/createServers.js';
|
|
@@ -207,6 +207,24 @@ export async function handler(argv) {
|
|
|
207
207
|
const { name } = parse(stat.name);
|
|
208
208
|
await Resource.bulkCreate(resources, name, true);
|
|
209
209
|
}, { allowMissing: true });
|
|
210
|
+
const appVariables = [];
|
|
211
|
+
const configPath = join(appPath, 'config');
|
|
212
|
+
if (existsSync(configPath)) {
|
|
213
|
+
const variablesPath = join(configPath, 'variables.json');
|
|
214
|
+
if (existsSync(variablesPath)) {
|
|
215
|
+
const [readAppVariables] = (await readData(variablesPath));
|
|
216
|
+
if (readAppVariables.length) {
|
|
217
|
+
for (const appVariable of readAppVariables) {
|
|
218
|
+
const { name, value } = appVariable;
|
|
219
|
+
const [parsedValues, missingValues] = parseValues('variable', name, [{ value }]);
|
|
220
|
+
if (missingValues) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
appVariables.push({ name, ...parsedValues });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
210
228
|
const stubbedApp = {
|
|
211
229
|
...appsembleApp,
|
|
212
230
|
id: appId,
|
|
@@ -221,6 +239,7 @@ export async function handler(argv) {
|
|
|
221
239
|
appsembleApp: stubbedApp,
|
|
222
240
|
appBlocks,
|
|
223
241
|
appMessages,
|
|
242
|
+
appVariables,
|
|
224
243
|
...(appSecurity
|
|
225
244
|
? {
|
|
226
245
|
appMembers,
|
package/commands/team/create.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { authenticate } from '@appsemble/node-utils';
|
|
2
1
|
import { resolveAppIdAndRemote } from '../../lib/app.js';
|
|
3
2
|
import { createTeam } from '../../lib/team.js';
|
|
4
3
|
export const command = 'create <name>';
|
|
@@ -28,12 +27,12 @@ export function builder(yargs) {
|
|
|
28
27
|
}
|
|
29
28
|
export async function handler({ annotation, app, appId, clientCredentials, context, name, remote, }) {
|
|
30
29
|
const [resolvedAppId, resolvedRemote] = await resolveAppIdAndRemote(app, context, remote, appId);
|
|
31
|
-
await authenticate(resolvedRemote, 'teams:write', clientCredentials);
|
|
32
30
|
await createTeam({
|
|
33
31
|
name,
|
|
34
32
|
appId: resolvedAppId,
|
|
35
33
|
annotations: annotation,
|
|
36
34
|
remote: resolvedRemote,
|
|
35
|
+
clientCredentials,
|
|
37
36
|
});
|
|
38
37
|
}
|
|
39
38
|
//# sourceMappingURL=create.js.map
|
package/commands/team/delete.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { authenticate } from '@appsemble/node-utils';
|
|
2
1
|
import { resolveAppIdAndRemote } from '../../lib/app.js';
|
|
3
2
|
import { deleteTeam } from '../../lib/team.js';
|
|
4
3
|
export const command = 'delete';
|
|
@@ -24,11 +23,11 @@ export function builder(yargs) {
|
|
|
24
23
|
}
|
|
25
24
|
export async function handler({ app, appId, clientCredentials, context, id, remote, }) {
|
|
26
25
|
const [resolvedAppId, resolvedRemote] = await resolveAppIdAndRemote(app, context, remote, appId);
|
|
27
|
-
await authenticate(resolvedRemote, 'teams:write', clientCredentials);
|
|
28
26
|
await deleteTeam({
|
|
29
27
|
id,
|
|
30
28
|
appId: resolvedAppId,
|
|
31
29
|
remote: resolvedRemote,
|
|
30
|
+
clientCredentials,
|
|
32
31
|
});
|
|
33
32
|
}
|
|
34
33
|
//# sourceMappingURL=delete.js.map
|
package/lib/app.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
2
|
/// <reference types="node" resolution-mode="require"/>
|
|
3
3
|
import { type ReadStream } from 'node:fs';
|
|
4
|
-
import { type App, type AppLock, type AppsembleContext, type AppsembleRC, type AppVisibility } from '@appsemble/types';
|
|
4
|
+
import { type App, type AppLock, type AppsembleContext, type AppsembleRC, type AppVisibility, type ValueFromDefinition, type ValueFromProcess } from '@appsemble/types';
|
|
5
5
|
import FormData from 'form-data';
|
|
6
6
|
/**
|
|
7
7
|
* Traverses an app directory and appends the files it finds to the given FormData object.
|
|
@@ -15,13 +15,14 @@ export declare function traverseAppDirectory(path: string, context: string, form
|
|
|
15
15
|
/**
|
|
16
16
|
* Export an app as a zip file.
|
|
17
17
|
*
|
|
18
|
+
* @param clientCredentials The OAuth2 client credentials to use
|
|
18
19
|
* @param appId Id of the app to be exported.
|
|
19
20
|
* @param assets Boolean representing whether to include assets in the exported zip file.
|
|
20
21
|
* @param resources Boolean representing whether to include resources in the zip file.
|
|
21
22
|
* @param path Path of the folder where you want to put your downloaded file.
|
|
22
23
|
* @param remote The remote to fetch the app from.
|
|
23
24
|
*/
|
|
24
|
-
export declare function exportAppAsZip(appId: number, assets: boolean, resources: boolean, path: string, remote: string): Promise<void>;
|
|
25
|
+
export declare function exportAppAsZip(clientCredentials: string, appId: number, assets: boolean, resources: boolean, path: string, remote: string): Promise<void>;
|
|
25
26
|
/**
|
|
26
27
|
* Upload messages for an app.
|
|
27
28
|
*
|
|
@@ -31,8 +32,16 @@ export declare function exportAppAsZip(appId: number, assets: boolean, resources
|
|
|
31
32
|
* @param force Whether or not to force the update for locked apps.
|
|
32
33
|
*/
|
|
33
34
|
export declare function uploadMessages(path: string, appId: number, remote: string, force: boolean): Promise<void>;
|
|
35
|
+
interface DeleteAppArgs {
|
|
36
|
+
remote: string;
|
|
37
|
+
id: number;
|
|
38
|
+
clientCredentials: string;
|
|
39
|
+
}
|
|
40
|
+
export declare function deleteApp({ clientCredentials, id, remote }: DeleteAppArgs): Promise<void>;
|
|
34
41
|
export declare function publishSeedResources(path: string, app: App, remote: string): Promise<void>;
|
|
35
42
|
export declare function publishSeedAssets(path: string, app: App, remote: string, assetsClonable: boolean): Promise<void>;
|
|
43
|
+
export declare function parseValues(type: string, name: string, valuesToParse: Record<string, ValueFromDefinition>[]): [Record<string, ValueFromProcess>, boolean];
|
|
44
|
+
export declare function publishAppConfig(path: string, app: App, remote: string): Promise<void>;
|
|
36
45
|
/**
|
|
37
46
|
* @param path The path to the app directory.
|
|
38
47
|
* @param languages A list of languages for which translations should be added in addition to the
|
package/lib/app.js
CHANGED
|
@@ -152,6 +152,13 @@ export async function traverseAppDirectory(path, context, formData) {
|
|
|
152
152
|
: maskableIconPath;
|
|
153
153
|
return [discoveredContext, rc, yaml, gatheredData];
|
|
154
154
|
}
|
|
155
|
+
function extractFilenameFromContentDisposition(contentDisposition) {
|
|
156
|
+
const match = contentDisposition.match(/filename="(.+?)"/);
|
|
157
|
+
if (match && match.length > 1) {
|
|
158
|
+
return match[1];
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
155
162
|
async function retrieveContext(path, context) {
|
|
156
163
|
let rc;
|
|
157
164
|
let discoveredContext;
|
|
@@ -190,20 +197,20 @@ async function retrieveContext(path, context) {
|
|
|
190
197
|
/**
|
|
191
198
|
* Export an app as a zip file.
|
|
192
199
|
*
|
|
200
|
+
* @param clientCredentials The OAuth2 client credentials to use
|
|
193
201
|
* @param appId Id of the app to be exported.
|
|
194
202
|
* @param assets Boolean representing whether to include assets in the exported zip file.
|
|
195
203
|
* @param resources Boolean representing whether to include resources in the zip file.
|
|
196
204
|
* @param path Path of the folder where you want to put your downloaded file.
|
|
197
205
|
* @param remote The remote to fetch the app from.
|
|
198
206
|
*/
|
|
199
|
-
export async function exportAppAsZip(appId, assets, resources, path, remote) {
|
|
200
|
-
|
|
201
|
-
const { name } = app.data.definition;
|
|
207
|
+
export async function exportAppAsZip(clientCredentials, appId, assets, resources, path, remote) {
|
|
208
|
+
await authenticate(remote, 'apps:export', clientCredentials);
|
|
202
209
|
const response = await axios.get(`/api/apps/${appId}/export?resources=${resources}&assets=${assets}`, {
|
|
203
210
|
baseURL: remote,
|
|
204
211
|
responseType: 'stream',
|
|
205
212
|
});
|
|
206
|
-
const zipFileName = join(path,
|
|
213
|
+
const zipFileName = join(path, String(extractFilenameFromContentDisposition(response.headers['content-disposition'])));
|
|
207
214
|
const writeStream = createWriteStream(zipFileName);
|
|
208
215
|
response.data.pipe(writeStream);
|
|
209
216
|
logger.info(`Successfully downloaded file: ${zipFileName}`);
|
|
@@ -236,6 +243,16 @@ export async function uploadMessages(path, appId, remote, force) {
|
|
|
236
243
|
logger.info(`Successfully uploaded messages for language “${language.language}” 🎉`);
|
|
237
244
|
}
|
|
238
245
|
}
|
|
246
|
+
export async function deleteApp({ clientCredentials, id, remote }) {
|
|
247
|
+
await authenticate(remote, 'apps:delete', clientCredentials);
|
|
248
|
+
const response = await axios.get(`/api/apps/${id}`);
|
|
249
|
+
const { name } = response.data;
|
|
250
|
+
logger.warn(`Deleting app: ${name}`);
|
|
251
|
+
await axios.delete(`/api/apps/${id}`, {
|
|
252
|
+
baseURL: remote,
|
|
253
|
+
});
|
|
254
|
+
logger.info(`Successfully deleted app with id ${id}`);
|
|
255
|
+
}
|
|
239
256
|
export async function publishSeedResources(path, app, remote) {
|
|
240
257
|
var _a, _b;
|
|
241
258
|
const resourcesPath = join(path, 'resources');
|
|
@@ -318,6 +335,182 @@ export async function publishSeedAssets(path, app, remote, assetsClonable) {
|
|
|
318
335
|
logger.warn(`Missing assets directory in ${path}. Skipping...`);
|
|
319
336
|
}
|
|
320
337
|
}
|
|
338
|
+
function parseValueFromDefinition(value) {
|
|
339
|
+
let parsed = value;
|
|
340
|
+
if (typeof parsed === 'string' && parsed.includes('{{') && parsed.includes('}}')) {
|
|
341
|
+
parsed = parsed.replaceAll(/{{\s*([^\s{}]+)\s*}}/g, (match, variable) => process.env[variable.trim()] || match);
|
|
342
|
+
}
|
|
343
|
+
return parsed;
|
|
344
|
+
}
|
|
345
|
+
export function parseValues(type, name, valuesToParse) {
|
|
346
|
+
const parsedValues = {};
|
|
347
|
+
let missingValues = false;
|
|
348
|
+
for (const valueToParse of valuesToParse) {
|
|
349
|
+
const [key, value] = Object.entries(valueToParse)[0];
|
|
350
|
+
parsedValues[key] = parseValueFromDefinition(value);
|
|
351
|
+
if (!parsedValues[key]) {
|
|
352
|
+
logger.error(`Missing ${key} value for ${type} ${name}`);
|
|
353
|
+
missingValues = true;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return [parsedValues, missingValues];
|
|
357
|
+
}
|
|
358
|
+
export async function publishAppConfig(path, app, remote) {
|
|
359
|
+
const configPath = join(path, 'config');
|
|
360
|
+
logger.info(`Publishing app config from ${configPath}`);
|
|
361
|
+
if (existsSync(configPath)) {
|
|
362
|
+
try {
|
|
363
|
+
logger.info(`Deleting existing variables from app ${app.id}`);
|
|
364
|
+
await axios.delete(`/api/apps/${app.id}/variables`, { baseURL: remote });
|
|
365
|
+
const appVariablesPath = join(configPath, 'variables.json');
|
|
366
|
+
if (existsSync(appVariablesPath)) {
|
|
367
|
+
const [appVariables] = (await readData(appVariablesPath));
|
|
368
|
+
if (appVariables.length) {
|
|
369
|
+
logger.info(`Publishing ${appVariables.length} app variable(s)`);
|
|
370
|
+
for (const appVariable of appVariables) {
|
|
371
|
+
const { name, value } = appVariable;
|
|
372
|
+
const [parsedValues, missingValues] = parseValues('variable', name, [{ value }]);
|
|
373
|
+
if (missingValues) {
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
await axios.post(`/api/apps/${app.id}/variables`, { name, ...parsedValues }, { baseURL: remote });
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
logger.error('Something went wrong when creating app variables:');
|
|
383
|
+
logger.error(error);
|
|
384
|
+
}
|
|
385
|
+
try {
|
|
386
|
+
logger.info(`Deleting existing service secrets from app ${app.id}`);
|
|
387
|
+
await axios.delete(`/api/apps/${app.id}/secrets/service`, { baseURL: remote });
|
|
388
|
+
const serviceSecretsPath = join(configPath, 'secrets', 'service.json');
|
|
389
|
+
if (existsSync(serviceSecretsPath)) {
|
|
390
|
+
const [serviceSecrets] = (await readData(serviceSecretsPath));
|
|
391
|
+
if (serviceSecrets.length) {
|
|
392
|
+
logger.info(`Publishing ${serviceSecrets.length} app service secret(s)`);
|
|
393
|
+
for (const serviceSecret of serviceSecrets) {
|
|
394
|
+
const { identifier, name, secret, urlPatterns, ...rest } = serviceSecret;
|
|
395
|
+
const [parsedValues, missingValues] = parseValues('service secret', name, [
|
|
396
|
+
{ secret },
|
|
397
|
+
{ identifier },
|
|
398
|
+
{ urlPatterns },
|
|
399
|
+
]);
|
|
400
|
+
if (missingValues) {
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
await axios.post(`/api/apps/${app.id}/secrets/service`, { name, ...parsedValues, ...rest }, { baseURL: remote });
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
logger.error('Something went wrong when creating app service secrets:');
|
|
410
|
+
logger.error(error);
|
|
411
|
+
}
|
|
412
|
+
try {
|
|
413
|
+
logger.info(`Deleting existing saml secrets from app ${app.id}`);
|
|
414
|
+
await axios.delete(`/api/apps/${app.id}/secrets/saml`, { baseURL: remote });
|
|
415
|
+
const samlSecretsPath = join(configPath, 'secrets', 'saml.json');
|
|
416
|
+
if (existsSync(samlSecretsPath)) {
|
|
417
|
+
const [samlSecrets] = (await readData(samlSecretsPath));
|
|
418
|
+
if (samlSecrets.length) {
|
|
419
|
+
logger.info(`Publishing ${samlSecrets.length} app saml secret(s)`);
|
|
420
|
+
for (const samlSecret of samlSecrets) {
|
|
421
|
+
const { entityId, idpCertificate, name, ssoUrl, ...rest } = samlSecret;
|
|
422
|
+
const [parsedValues, missingValues] = parseValues('saml secret', name, [
|
|
423
|
+
{ entityId },
|
|
424
|
+
{ idpCertificate },
|
|
425
|
+
{ ssoUrl },
|
|
426
|
+
]);
|
|
427
|
+
if (missingValues) {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
await axios.post(`/api/apps/${app.id}/secrets/saml`, { name, ...parsedValues, ...rest }, { baseURL: remote });
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
logger.error('Something went wrong when creating app saml secrets:');
|
|
437
|
+
logger.error(error);
|
|
438
|
+
}
|
|
439
|
+
try {
|
|
440
|
+
logger.info(`Deleting existing oauth2 secrets from app ${app.id}`);
|
|
441
|
+
await axios.delete(`/api/apps/${app.id}/secrets/oauth2`, { baseURL: remote });
|
|
442
|
+
const oauth2SecretsPath = join(configPath, 'secrets', 'oauth2.json');
|
|
443
|
+
if (existsSync(oauth2SecretsPath)) {
|
|
444
|
+
const [oauth2Secrets] = (await readData(oauth2SecretsPath));
|
|
445
|
+
if (oauth2Secrets.length) {
|
|
446
|
+
logger.info(`Publishing ${oauth2Secrets.length} app oauth2 secrets`);
|
|
447
|
+
for (const oauth2Secret of oauth2Secrets) {
|
|
448
|
+
const { authorizationUrl, clientId, clientSecret, name, tokenUrl, userInfoUrl, ...rest } = oauth2Secret;
|
|
449
|
+
const [parsedValues, missingValues] = parseValues('oauth2 secret', name, [
|
|
450
|
+
{ authorizationUrl },
|
|
451
|
+
{ tokenUrl },
|
|
452
|
+
{ userInfoUrl },
|
|
453
|
+
{ clientId },
|
|
454
|
+
{ clientSecret },
|
|
455
|
+
]);
|
|
456
|
+
if (missingValues) {
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
await axios.post(`/api/apps/${app.id}/secrets/oauth2`, { name, ...parsedValues, ...rest }, { baseURL: remote });
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
logger.error('Something went wrong when creating app oauth2 secrets:');
|
|
466
|
+
logger.error(error);
|
|
467
|
+
}
|
|
468
|
+
try {
|
|
469
|
+
const sslSecretPath = join(configPath, 'secrets', 'ssl.json');
|
|
470
|
+
if (existsSync(sslSecretPath)) {
|
|
471
|
+
const [sslSecret] = (await readData(sslSecretPath));
|
|
472
|
+
if (sslSecret) {
|
|
473
|
+
logger.info('Publishing app ssl secret');
|
|
474
|
+
const { certificate, key } = sslSecret;
|
|
475
|
+
const [parsedValues, missingValues] = parseValues('ssl secret', '', [
|
|
476
|
+
{ certificate },
|
|
477
|
+
{ key },
|
|
478
|
+
]);
|
|
479
|
+
if (missingValues) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
await axios.put(`/api/apps/${app.id}/secrets/ssl`, { ...parsedValues }, { baseURL: remote });
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
logger.error('Something went wrong when creating app ssl secret:');
|
|
488
|
+
logger.error(error);
|
|
489
|
+
}
|
|
490
|
+
try {
|
|
491
|
+
const scimSecretPath = join(configPath, 'secrets', 'scim.json');
|
|
492
|
+
if (existsSync(scimSecretPath)) {
|
|
493
|
+
const [scimSecret] = (await readData(scimSecretPath));
|
|
494
|
+
if (scimSecret) {
|
|
495
|
+
logger.info('Publishing app scim secret');
|
|
496
|
+
const { enabled, token } = scimSecret;
|
|
497
|
+
const [parsedValues, missingValues] = parseValues('scim secret', '', [{ token }]);
|
|
498
|
+
if (missingValues) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
await axios.patch(`/api/apps/${app.id}/secrets/scim`, { enabled, ...parsedValues }, { baseURL: remote });
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
logger.error('Something went wrong when creating app scim secret:');
|
|
507
|
+
logger.error(error);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
logger.warn(`Missing config directory in ${path}. Skipping...`);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
321
514
|
/**
|
|
322
515
|
* @param path The path to the app directory.
|
|
323
516
|
* @param languages A list of languages for which translations should be added in addition to the
|
|
@@ -616,6 +809,7 @@ export async function publishApp({ clientCredentials, context, dryRun, modifyCon
|
|
|
616
809
|
// After uploading the app, upload block styles, messages if they are available
|
|
617
810
|
await traverseBlockThemes(appVariantPath, data.id, remote, false);
|
|
618
811
|
await uploadMessages(appVariantPath, data.id, remote, false);
|
|
812
|
+
await publishAppConfig(appVariantPath, data, remote);
|
|
619
813
|
// After uploading the app, publish seed resources and assets if they are available
|
|
620
814
|
if (assets) {
|
|
621
815
|
await publishSeedAssets(appVariantPath, data, remote, assetsClonable);
|
|
@@ -776,6 +970,7 @@ export async function updateApp({ clientCredentials, context, force, path, ...op
|
|
|
776
970
|
// After uploading the app, upload block styles and messages if they are available
|
|
777
971
|
await traverseBlockThemes(appVariantPath, data.id, remote, force);
|
|
778
972
|
await uploadMessages(appVariantPath, data.id, remote, force);
|
|
973
|
+
await publishAppConfig(appVariantPath, data, remote);
|
|
779
974
|
// After updating the app, publish seed resources and assets if they are available
|
|
780
975
|
if (assets && (data.locked !== 'fullLock' || force)) {
|
|
781
976
|
await publishSeedAssets(appVariantPath, data, remote, assetsClonable);
|