@logto/cli 1.1.0 → 1.2.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/lib/commands/connector/add.js +4 -4
- package/lib/commands/connector/index.js +2 -0
- package/lib/commands/connector/link.d.ts +9 -0
- package/lib/commands/connector/link.js +46 -0
- package/lib/commands/connector/list.js +4 -3
- package/lib/commands/connector/remove.js +6 -6
- package/lib/commands/connector/utils.d.ts +2 -0
- package/lib/commands/connector/utils.js +22 -10
- package/lib/commands/database/alteration/index.js +8 -8
- package/lib/commands/database/alteration/version.js +4 -4
- package/lib/commands/database/config.js +11 -11
- package/lib/commands/database/seed/cloud.js +2 -2
- package/lib/commands/database/seed/index.js +4 -5
- package/lib/commands/database/seed/oidc-config.js +5 -5
- package/lib/commands/database/seed/tables.js +2 -2
- package/lib/commands/database/seed/tenant.js +2 -2
- package/lib/commands/database/system.js +5 -5
- package/lib/commands/database/utils.js +1 -1
- package/lib/commands/install/index.js +3 -3
- package/lib/commands/install/utils.js +12 -12
- package/lib/commands/translate/create.js +17 -54
- package/lib/commands/translate/index.js +2 -0
- package/lib/commands/translate/list-tags.js +2 -1
- package/lib/commands/translate/openai.d.ts +8 -1
- package/lib/commands/translate/openai.js +15 -11
- package/lib/commands/translate/prompts.d.ts +14 -0
- package/lib/commands/translate/prompts.js +39 -0
- package/lib/commands/translate/sync.d.ts +7 -0
- package/lib/commands/translate/sync.js +40 -0
- package/lib/commands/translate/utils.d.ts +11 -0
- package/lib/commands/translate/utils.js +111 -2
- package/lib/connector/factories.js +2 -1
- package/lib/connector/loader.js +4 -3
- package/lib/database.js +3 -3
- package/lib/index.js +2 -2
- package/lib/package-json.d.ts +4 -0
- package/lib/package-json.js +13 -9
- package/lib/utils.d.ts +2 -8
- package/lib/utils.js +13 -17
- package/package.json +14 -10
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { consoleLog } from '../../utils.js';
|
|
2
2
|
import { addConnectors, addOfficialConnectors, inquireInstancePath } from './utils.js';
|
|
3
3
|
const add = {
|
|
4
4
|
command: ['add [packages...]', 'a', 'install', 'i'],
|
|
@@ -26,7 +26,7 @@ const add = {
|
|
|
26
26
|
handler: async ({ packages: packageNames, path, official, cloud }) => {
|
|
27
27
|
const instancePath = await inquireInstancePath(path);
|
|
28
28
|
if (cloud && !official) {
|
|
29
|
-
|
|
29
|
+
consoleLog.fatal('--cloud option can only be used with --official option');
|
|
30
30
|
}
|
|
31
31
|
if (official) {
|
|
32
32
|
// Add demo connectors for Logto Cloud only
|
|
@@ -34,11 +34,11 @@ const add = {
|
|
|
34
34
|
}
|
|
35
35
|
else {
|
|
36
36
|
if (!packageNames?.length) {
|
|
37
|
-
|
|
37
|
+
consoleLog.fatal('No connector name provided');
|
|
38
38
|
}
|
|
39
39
|
await addConnectors(instancePath, packageNames);
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
consoleLog.info('Restart your Logto instance to get the changes reflected.');
|
|
42
42
|
},
|
|
43
43
|
};
|
|
44
44
|
export default add;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { noop } from '@silverhand/essentials';
|
|
2
2
|
import add from './add.js';
|
|
3
|
+
import link from './link.js';
|
|
3
4
|
import list from './list.js';
|
|
4
5
|
import remove from './remove.js';
|
|
5
6
|
const connector = {
|
|
@@ -14,6 +15,7 @@ const connector = {
|
|
|
14
15
|
.command(add)
|
|
15
16
|
.command(list)
|
|
16
17
|
.command(remove)
|
|
18
|
+
.command(link)
|
|
17
19
|
.demandCommand(1),
|
|
18
20
|
handler: noop,
|
|
19
21
|
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { consoleLog } from '../../utils.js';
|
|
4
|
+
import { getConnectorDirectory, getLocalConnectorPackages, inquireInstancePath } from './utils.js';
|
|
5
|
+
const link = {
|
|
6
|
+
command: ['link', 'ln'],
|
|
7
|
+
describe: 'Link all connectors in `packages/connectors`, useful for adding local connectors.',
|
|
8
|
+
builder: (yargs) => yargs
|
|
9
|
+
.option('cloud', {
|
|
10
|
+
describe: 'Add additional connectors for Logto Cloud',
|
|
11
|
+
type: 'boolean',
|
|
12
|
+
default: false,
|
|
13
|
+
hidden: true,
|
|
14
|
+
})
|
|
15
|
+
.option('mock', {
|
|
16
|
+
describe: 'Add mock connectors',
|
|
17
|
+
type: 'boolean',
|
|
18
|
+
default: false,
|
|
19
|
+
hidden: true,
|
|
20
|
+
}),
|
|
21
|
+
handler: async ({ path: inputPath, cloud, mock }) => {
|
|
22
|
+
const instancePath = await inquireInstancePath(inputPath);
|
|
23
|
+
const packages = await getLocalConnectorPackages(instancePath);
|
|
24
|
+
const connectorDirectory = getConnectorDirectory(instancePath);
|
|
25
|
+
await fs.mkdir(connectorDirectory, { recursive: true });
|
|
26
|
+
await Promise.all(packages.map(async ([packageName, packagePath]) => {
|
|
27
|
+
if (!cloud && packageName.startsWith('connector-logto-')) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (!mock && packageName.startsWith('connector-mock-')) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const targetPath = path.join(connectorDirectory, '@logto-' + packageName);
|
|
35
|
+
await fs.rm(targetPath, { recursive: true, force: true });
|
|
36
|
+
await fs.symlink(path.relative(connectorDirectory, packagePath), targetPath);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
consoleLog.warn(error);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
consoleLog.succeed('Linked', packagePath);
|
|
43
|
+
}));
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
export default link;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import { consoleLog } from '../../utils.js';
|
|
2
3
|
import { getConnectorPackagesFrom, isOfficialConnector } from './utils.js';
|
|
3
4
|
const logConnectorNames = (type, packages) => {
|
|
4
5
|
if (packages.length === 0) {
|
|
5
6
|
return;
|
|
6
7
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
consoleLog.plain();
|
|
9
|
+
consoleLog.plain(chalk.blue(type));
|
|
10
|
+
consoleLog.plain(packages.map(({ name }) => ' ' + name).join('\n'));
|
|
10
11
|
};
|
|
11
12
|
const list = {
|
|
12
13
|
command: ['list', 'l'],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import {
|
|
3
|
+
import { consoleLog } from '../../utils.js';
|
|
4
4
|
import { getConnectorPackagesFrom } from './utils.js';
|
|
5
5
|
const remove = {
|
|
6
6
|
command: ['remove [packages...]', 'rm', 'delete'],
|
|
@@ -13,12 +13,12 @@ const remove = {
|
|
|
13
13
|
}),
|
|
14
14
|
handler: async ({ path: inputPath, packages: packageNames }) => {
|
|
15
15
|
if (!packageNames?.length) {
|
|
16
|
-
|
|
16
|
+
consoleLog.fatal('No connector name provided');
|
|
17
17
|
}
|
|
18
18
|
const existingPackages = await getConnectorPackagesFrom(inputPath);
|
|
19
19
|
const notFoundPackageNames = packageNames.filter((current) => !existingPackages.some(({ name }) => current === name));
|
|
20
20
|
if (notFoundPackageNames.length > 0) {
|
|
21
|
-
|
|
21
|
+
consoleLog.fatal(`Cannot remove ${notFoundPackageNames
|
|
22
22
|
.map((name) => chalk.green(name))
|
|
23
23
|
.join(', ')}: not found in your Logto instance directory`);
|
|
24
24
|
}
|
|
@@ -30,13 +30,13 @@ const remove = {
|
|
|
30
30
|
return okSymbol;
|
|
31
31
|
}
|
|
32
32
|
catch (error) {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
consoleLog.warn(`Error while removing ${chalk.green(packageInfo?.name)}`);
|
|
34
|
+
consoleLog.warn(error);
|
|
35
35
|
return error;
|
|
36
36
|
}
|
|
37
37
|
}));
|
|
38
38
|
const errorCount = result.filter((value) => value !== okSymbol).length;
|
|
39
|
-
|
|
39
|
+
consoleLog.info(`Removed ${result.length - errorCount} connectors`);
|
|
40
40
|
},
|
|
41
41
|
};
|
|
42
42
|
export default remove;
|
|
@@ -14,7 +14,9 @@ export declare const npmPackResultGuard: z.ZodArray<z.ZodObject<{
|
|
|
14
14
|
}>, "many">;
|
|
15
15
|
export declare const inquireInstancePath: (initialPath?: string) => Promise<string>;
|
|
16
16
|
export declare const normalizePackageName: (name: string) => string;
|
|
17
|
+
export declare const getConnectorDirectory: (instancePath: string) => string;
|
|
17
18
|
export declare const isOfficialConnector: (packageName: string) => boolean;
|
|
18
19
|
export declare const getConnectorPackagesFrom: (instancePath?: string) => Promise<import("../../utils.js").ConnectorPackage[]>;
|
|
20
|
+
export declare const getLocalConnectorPackages: (instancePath: string) => Promise<(readonly [string, string])[]>;
|
|
19
21
|
export declare const addConnectors: (instancePath: string, packageNames: string[]) => Promise<void>;
|
|
20
22
|
export declare const addOfficialConnectors: (instancePath: string, includingCloudConnectors?: boolean) => Promise<void>;
|
|
@@ -12,7 +12,7 @@ import pRetry from 'p-retry';
|
|
|
12
12
|
import tar from 'tar';
|
|
13
13
|
import { z } from 'zod';
|
|
14
14
|
import { connectorDirectory } from '../../constants.js';
|
|
15
|
-
import { getConnectorPackagesFromDirectory, isTty,
|
|
15
|
+
import { consoleLog, getConnectorPackagesFromDirectory, isTty, oraPromise } from '../../utils.js';
|
|
16
16
|
import { defaultPath } from '../install/utils.js';
|
|
17
17
|
const coreDirectory = 'packages/core';
|
|
18
18
|
const execPromise = promisify(exec);
|
|
@@ -42,6 +42,9 @@ const validatePath = async (value) => {
|
|
|
42
42
|
};
|
|
43
43
|
export const inquireInstancePath = async (initialPath) => {
|
|
44
44
|
const inquire = async () => {
|
|
45
|
+
if (!initialPath && (await validatePath('.')) === true) {
|
|
46
|
+
return path.resolve('.');
|
|
47
|
+
}
|
|
45
48
|
if (!isTty()) {
|
|
46
49
|
assert(initialPath, new Error('Path is missing'));
|
|
47
50
|
return initialPath;
|
|
@@ -59,7 +62,7 @@ export const inquireInstancePath = async (initialPath) => {
|
|
|
59
62
|
const instancePath = await inquire();
|
|
60
63
|
const validated = await validatePath(instancePath);
|
|
61
64
|
if (validated !== true) {
|
|
62
|
-
|
|
65
|
+
consoleLog.fatal(validated);
|
|
63
66
|
}
|
|
64
67
|
return instancePath;
|
|
65
68
|
};
|
|
@@ -71,22 +74,31 @@ export const normalizePackageName = (name) => name
|
|
|
71
74
|
? packagePrefix + fragment
|
|
72
75
|
: fragment)
|
|
73
76
|
.join('/');
|
|
74
|
-
const getConnectorDirectory = (instancePath) => path.join(instancePath, coreDirectory, connectorDirectory);
|
|
77
|
+
export const getConnectorDirectory = (instancePath) => path.join(instancePath, coreDirectory, connectorDirectory);
|
|
75
78
|
export const isOfficialConnector = (packageName) => packageName.startsWith('@logto/connector-');
|
|
76
79
|
export const getConnectorPackagesFrom = async (instancePath) => {
|
|
77
80
|
const directory = getConnectorDirectory(await inquireInstancePath(instancePath));
|
|
78
81
|
const packages = await trySafe(getConnectorPackagesFromDirectory(directory));
|
|
79
82
|
if (!packages || packages.length === 0) {
|
|
80
|
-
throw new Error('No connector found');
|
|
83
|
+
throw new Error('No connector found in ' + directory);
|
|
81
84
|
}
|
|
82
85
|
return packages;
|
|
83
86
|
};
|
|
87
|
+
export const getLocalConnectorPackages = async (instancePath) => {
|
|
88
|
+
const directory = path.join(instancePath, 'packages', connectorDirectory);
|
|
89
|
+
const files = await fs.readdir(directory, { withFileTypes: true });
|
|
90
|
+
const packages = files.filter((entity) => entity.isDirectory() && entity.name.startsWith('connector-'));
|
|
91
|
+
if (packages.length === 0) {
|
|
92
|
+
throw new Error('No connector found in ' + directory);
|
|
93
|
+
}
|
|
94
|
+
return packages.map(({ name }) => [name, path.join(directory, name)]);
|
|
95
|
+
};
|
|
84
96
|
export const addConnectors = async (instancePath, packageNames) => {
|
|
85
97
|
const cwd = getConnectorDirectory(instancePath);
|
|
86
98
|
if (!existsSync(cwd)) {
|
|
87
99
|
await fs.mkdir(cwd, { recursive: true });
|
|
88
100
|
}
|
|
89
|
-
|
|
101
|
+
consoleLog.info('Fetch connector metadata');
|
|
90
102
|
const limit = pLimit(10);
|
|
91
103
|
const results = await Promise.all(packageNames
|
|
92
104
|
.map((name) => normalizePackageName(name))
|
|
@@ -105,25 +117,25 @@ export const addConnectors = async (instancePath, packageNames) => {
|
|
|
105
117
|
await fs.mkdir(packageDirectory, { recursive: true });
|
|
106
118
|
await tar.extract({ cwd: packageDirectory, file: tarPath, strip: 1 });
|
|
107
119
|
await fs.unlink(tarPath);
|
|
108
|
-
|
|
120
|
+
consoleLog.succeed(`Added ${chalk.green(name)} v${version}`);
|
|
109
121
|
};
|
|
110
122
|
return limit(async () => {
|
|
111
123
|
try {
|
|
112
124
|
await pRetry(run, { retries: 2 });
|
|
113
125
|
}
|
|
114
126
|
catch (error) {
|
|
115
|
-
|
|
127
|
+
consoleLog.error(`[${packageName}]`, error);
|
|
116
128
|
return packageName;
|
|
117
129
|
}
|
|
118
130
|
});
|
|
119
131
|
}));
|
|
120
132
|
const errorPackages = results.filter(Boolean);
|
|
121
133
|
const errorCount = errorPackages.length;
|
|
122
|
-
|
|
134
|
+
consoleLog.info(errorCount
|
|
123
135
|
? `Finished with ${errorCount} error${conditionalString(errorCount > 1 && 's')}.`
|
|
124
136
|
: 'Finished');
|
|
125
137
|
if (errorCount) {
|
|
126
|
-
|
|
138
|
+
consoleLog.warn('Failed to add ' + errorPackages.map((name) => chalk.green(name)).join(', '));
|
|
127
139
|
}
|
|
128
140
|
};
|
|
129
141
|
const officialConnectorPrefix = '@logto/connector-';
|
|
@@ -159,6 +171,6 @@ export const addOfficialConnectors = async (instancePath, includingCloudConnecto
|
|
|
159
171
|
text: 'Fetch official connector list',
|
|
160
172
|
prefixText: chalk.blue('[info]'),
|
|
161
173
|
});
|
|
162
|
-
|
|
174
|
+
consoleLog.info(`Found ${packages.length} official connectors`);
|
|
163
175
|
await addConnectors(instancePath, packages.map(({ name }) => name));
|
|
164
176
|
};
|
|
@@ -2,7 +2,7 @@ import { conditionalString } from '@silverhand/essentials';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { createPoolFromConfig } from '../../../database.js';
|
|
4
4
|
import { getCurrentDatabaseAlterationTimestamp, updateDatabaseTimestamp, } from '../../../queries/system.js';
|
|
5
|
-
import {
|
|
5
|
+
import { consoleLog } from '../../../utils.js';
|
|
6
6
|
import { getAlterationFiles, getTimestampFromFilename } from './utils.js';
|
|
7
7
|
import { chooseAlterationsByVersion, chooseRevertAlterationsByVersion } from './version.js';
|
|
8
8
|
const importAlterationScript = async (filePath) => {
|
|
@@ -44,13 +44,13 @@ const deployAlteration = async (pool, { path: filePath, filename }, action = 'up
|
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
catch (error) {
|
|
47
|
-
|
|
47
|
+
consoleLog.error(error);
|
|
48
48
|
await pool.end();
|
|
49
|
-
|
|
49
|
+
consoleLog.fatal(`Error ocurred during running alteration ${chalk.blue(filename)}.\n\n` +
|
|
50
50
|
" This alteration didn't change anything since it was in a transaction.\n" +
|
|
51
51
|
' Try to fix the error and deploy again.');
|
|
52
52
|
}
|
|
53
|
-
|
|
53
|
+
consoleLog.info(`Run alteration ${filename} \`${action}()\` function succeeded`);
|
|
54
54
|
};
|
|
55
55
|
const alteration = {
|
|
56
56
|
command: ['alteration <action> [target]', 'alt', 'alter'],
|
|
@@ -69,13 +69,13 @@ const alteration = {
|
|
|
69
69
|
if (action === 'list') {
|
|
70
70
|
const files = await getAlterationFiles();
|
|
71
71
|
for (const file of files) {
|
|
72
|
-
|
|
72
|
+
consoleLog.plain(file.filename);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
else if (action === 'deploy') {
|
|
76
76
|
const pool = await createPoolFromConfig();
|
|
77
77
|
const alterations = await chooseAlterationsByVersion(await getAvailableAlterations(pool), target);
|
|
78
|
-
|
|
78
|
+
consoleLog.info(`Found ${alterations.length} alteration${conditionalString(alterations.length > 1 && 's')} to deploy`);
|
|
79
79
|
// The await inside the loop is intended, alterations should run in order
|
|
80
80
|
for (const alteration of alterations) {
|
|
81
81
|
// eslint-disable-next-line no-await-in-loop
|
|
@@ -86,7 +86,7 @@ const alteration = {
|
|
|
86
86
|
else if (['rollback', 'r'].includes(action)) {
|
|
87
87
|
const pool = await createPoolFromConfig();
|
|
88
88
|
const alterations = await chooseRevertAlterationsByVersion(await getAvailableAlterations(pool, 'lte'), target ?? '');
|
|
89
|
-
|
|
89
|
+
consoleLog.info(`Found ${alterations.length} alteration${conditionalString(alterations.length > 1 && 's')} to revert`);
|
|
90
90
|
// The await inside the loop is intended, alterations should run in order
|
|
91
91
|
// eslint-disable-next-line @silverhand/fp/no-mutating-methods
|
|
92
92
|
for (const alteration of alterations.slice().reverse()) {
|
|
@@ -96,7 +96,7 @@ const alteration = {
|
|
|
96
96
|
await pool.end();
|
|
97
97
|
}
|
|
98
98
|
else {
|
|
99
|
-
|
|
99
|
+
consoleLog.fatal('Unsupported action');
|
|
100
100
|
}
|
|
101
101
|
},
|
|
102
102
|
};
|
|
@@ -2,7 +2,7 @@ import { assert, conditional } from '@silverhand/essentials';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import inquirer from 'inquirer';
|
|
4
4
|
import { SemVer, compare, eq, gt } from 'semver';
|
|
5
|
-
import { findLastIndex, isTty
|
|
5
|
+
import { consoleLog, findLastIndex, isTty } from '../../../utils.js';
|
|
6
6
|
const getVersionStringFromFilename = (filename) => filename.split('-')[0]?.replaceAll('_', '-') ?? 'unknown';
|
|
7
7
|
const getVersionFromFilename = (filename) => {
|
|
8
8
|
try {
|
|
@@ -25,7 +25,7 @@ export const chooseAlterationsByVersion = async (alterations, initialVersion) =>
|
|
|
25
25
|
if (endIndex === -1) {
|
|
26
26
|
return [];
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
consoleLog.info(`Deploy target ${chalk.green(nextTag)}`);
|
|
29
29
|
return alterations.slice(0, endIndex + 1);
|
|
30
30
|
}
|
|
31
31
|
const versions = getAlterationVersions(alterations);
|
|
@@ -56,7 +56,7 @@ export const chooseAlterationsByVersion = async (alterations, initialVersion) =>
|
|
|
56
56
|
return version;
|
|
57
57
|
};
|
|
58
58
|
const targetVersion = await getTargetVersion();
|
|
59
|
-
|
|
59
|
+
consoleLog.info(`Deploy target ${chalk.green(targetVersion.version)}`);
|
|
60
60
|
return alterations.filter(({ filename }) => {
|
|
61
61
|
const version = getVersionFromFilename(filename);
|
|
62
62
|
return version && !gt(version, targetVersion);
|
|
@@ -64,7 +64,7 @@ export const chooseAlterationsByVersion = async (alterations, initialVersion) =>
|
|
|
64
64
|
};
|
|
65
65
|
export const chooseRevertAlterationsByVersion = async (alterations, version) => {
|
|
66
66
|
const semVersion = new SemVer(version);
|
|
67
|
-
|
|
67
|
+
consoleLog.info(`Revert target ${chalk.green(semVersion.version)}`);
|
|
68
68
|
return alterations.filter(({ filename }) => {
|
|
69
69
|
if (getVersionStringFromFilename(filename) === nextTag) {
|
|
70
70
|
return true;
|
|
@@ -3,7 +3,7 @@ import { deduplicate, noop } from '@silverhand/essentials';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { createPoolFromConfig } from '../../database.js';
|
|
5
5
|
import { getRowsByKeys, updateValueByKey } from '../../queries/logto-config.js';
|
|
6
|
-
import {
|
|
6
|
+
import { consoleLog } from '../../utils.js';
|
|
7
7
|
import { generateOidcCookieKey, generateOidcPrivateKey } from './utils.js';
|
|
8
8
|
const validKeysDisplay = chalk.green(logtoConfigKeys.join(', '));
|
|
9
9
|
const validateKeys = (keys) => {
|
|
@@ -12,7 +12,7 @@ const validateKeys = (keys) => {
|
|
|
12
12
|
// eslint-disable-next-line unicorn/prefer-includes
|
|
13
13
|
(key) => !logtoConfigKeys.some((element) => element === key));
|
|
14
14
|
if (invalidKey) {
|
|
15
|
-
|
|
15
|
+
consoleLog.fatal(`Invalid config key ${chalk.red(invalidKey)} found, expected one of ${validKeysDisplay}`);
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
18
|
const validRotateKeys = Object.freeze([
|
|
@@ -23,7 +23,7 @@ const validateRotateKey = (key) => {
|
|
|
23
23
|
// Using `.includes()` will result a type error
|
|
24
24
|
// eslint-disable-next-line unicorn/prefer-includes
|
|
25
25
|
if (!validRotateKeys.some((element) => element === key)) {
|
|
26
|
-
|
|
26
|
+
consoleLog.fatal(`Invalid config key ${chalk.red(key)} found, expected one of ${validKeysDisplay}`);
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
29
|
const getConfig = {
|
|
@@ -52,7 +52,7 @@ const getConfig = {
|
|
|
52
52
|
const pool = await createPoolFromConfig();
|
|
53
53
|
const { rows } = await getRowsByKeys(pool, tenantId, queryKeys);
|
|
54
54
|
await pool.end();
|
|
55
|
-
|
|
55
|
+
consoleLog.plain(queryKeys
|
|
56
56
|
.map((currentKey) => {
|
|
57
57
|
const value = rows.find(({ key }) => currentKey === key)?.value;
|
|
58
58
|
return (chalk.magenta(currentKey) +
|
|
@@ -87,7 +87,7 @@ const setConfig = {
|
|
|
87
87
|
const pool = await createPoolFromConfig();
|
|
88
88
|
await updateValueByKey(pool, tenantId, key, guarded);
|
|
89
89
|
await pool.end();
|
|
90
|
-
|
|
90
|
+
consoleLog.info(`Update ${chalk.green(key)} succeeded`);
|
|
91
91
|
},
|
|
92
92
|
};
|
|
93
93
|
const rotateConfig = {
|
|
@@ -109,7 +109,7 @@ const rotateConfig = {
|
|
|
109
109
|
const pool = await createPoolFromConfig();
|
|
110
110
|
const { rows } = await getRowsByKeys(pool, tenantId, [key]);
|
|
111
111
|
if (!rows[0]) {
|
|
112
|
-
|
|
112
|
+
consoleLog.warn('No key found, create a new one');
|
|
113
113
|
}
|
|
114
114
|
const getValue = async () => {
|
|
115
115
|
const parsed = logtoConfigGuards[key].safeParse(rows[0]?.value);
|
|
@@ -127,7 +127,7 @@ const rotateConfig = {
|
|
|
127
127
|
const rotated = await getValue();
|
|
128
128
|
await updateValueByKey(pool, tenantId, key, rotated);
|
|
129
129
|
await pool.end();
|
|
130
|
-
|
|
130
|
+
consoleLog.info(`Rotate ${chalk.green(key)} succeeded, now it has ${rotated.length} keys`);
|
|
131
131
|
},
|
|
132
132
|
};
|
|
133
133
|
const trimConfig = {
|
|
@@ -153,25 +153,25 @@ const trimConfig = {
|
|
|
153
153
|
handler: async ({ key, length, tenantId }) => {
|
|
154
154
|
validateRotateKey(key);
|
|
155
155
|
if (length < 1) {
|
|
156
|
-
|
|
156
|
+
consoleLog.fatal('Invalid length provided');
|
|
157
157
|
}
|
|
158
158
|
const pool = await createPoolFromConfig();
|
|
159
159
|
const { rows } = await getRowsByKeys(pool, tenantId, [key]);
|
|
160
160
|
if (!rows[0]) {
|
|
161
|
-
|
|
161
|
+
consoleLog.warn('No key found, create a new one');
|
|
162
162
|
}
|
|
163
163
|
const getValue = async () => {
|
|
164
164
|
const value = logtoConfigGuards[key].parse(rows[0]?.value);
|
|
165
165
|
if (value.length - length < 1) {
|
|
166
166
|
await pool.end();
|
|
167
|
-
|
|
167
|
+
consoleLog.fatal(`You should keep at least one key in the array, current length=${value.length}`);
|
|
168
168
|
}
|
|
169
169
|
return value.slice(0, -length);
|
|
170
170
|
};
|
|
171
171
|
const trimmed = await getValue();
|
|
172
172
|
await updateValueByKey(pool, tenantId, key, trimmed);
|
|
173
173
|
await pool.end();
|
|
174
|
-
|
|
174
|
+
consoleLog.info(`Trim ${chalk.green(key)} succeeded, now it has ${trimmed.length} keys`);
|
|
175
175
|
},
|
|
176
176
|
};
|
|
177
177
|
const config = {
|
|
@@ -2,7 +2,7 @@ import { adminConsoleApplicationId, adminTenantId, defaultTenantId } from '@logt
|
|
|
2
2
|
import { GlobalValues } from '@logto/shared';
|
|
3
3
|
import { appendPath } from '@silverhand/essentials';
|
|
4
4
|
import { sql } from 'slonik';
|
|
5
|
-
import {
|
|
5
|
+
import { consoleLog } from '../../../utils.js';
|
|
6
6
|
/**
|
|
7
7
|
* Append Redirect URIs for the default tenant callback in cloud Admin Console.
|
|
8
8
|
* It reads the same env variables as core to construct the cloud `UrlSet`.
|
|
@@ -30,5 +30,5 @@ export const appendAdminConsoleRedirectUris = async (pool) => {
|
|
|
30
30
|
where id = ${adminConsoleApplicationId}
|
|
31
31
|
and tenant_id = ${adminTenantId}
|
|
32
32
|
`);
|
|
33
|
-
|
|
33
|
+
consoleLog.succeed('Appended initial Redirect URIs to Admin Console:', redirectUris.map(String));
|
|
34
34
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { createPoolAndDatabaseIfNeeded } from '../../../database.js';
|
|
3
3
|
import { doesConfigsTableExist } from '../../../queries/logto-config.js';
|
|
4
|
-
import {
|
|
4
|
+
import { consoleLog, oraPromise } from '../../../utils.js';
|
|
5
5
|
import { getLatestAlterationTimestamp } from '../alteration/index.js';
|
|
6
6
|
import { getAlterationDirectory } from '../alteration/utils.js';
|
|
7
7
|
import { createTables, seedCloud, seedTables } from './tables.js';
|
|
@@ -40,7 +40,7 @@ const seed = {
|
|
|
40
40
|
handler: async ({ swe, cloud }) => {
|
|
41
41
|
const pool = await createPoolAndDatabaseIfNeeded();
|
|
42
42
|
if (swe && (await doesConfigsTableExist(pool))) {
|
|
43
|
-
|
|
43
|
+
consoleLog.info('Seeding skipped');
|
|
44
44
|
await pool.end();
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
@@ -48,9 +48,8 @@ const seed = {
|
|
|
48
48
|
await seedByPool(pool, cloud);
|
|
49
49
|
}
|
|
50
50
|
catch (error) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
log.warn('Error ocurred during seeding your database.\n\n' +
|
|
51
|
+
consoleLog.error(error);
|
|
52
|
+
consoleLog.error('Error ocurred during seeding your database.\n\n' +
|
|
54
53
|
' Nothing has changed since the seeding process was in a transaction.\n' +
|
|
55
54
|
' Try to fix the error and seed again.');
|
|
56
55
|
}
|
|
@@ -4,7 +4,7 @@ import { getEnvAsStringArray } from '@silverhand/essentials';
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { getRowsByKeys, updateValueByKey } from '../../../queries/logto-config.js';
|
|
7
|
-
import {
|
|
7
|
+
import { consoleLog } from '../../../utils.js';
|
|
8
8
|
import { generateOidcCookieKey, generateOidcPrivateKey } from '../utils.js';
|
|
9
9
|
const isBase64FormatPrivateKey = (key) => !key.includes('-');
|
|
10
10
|
export const seedOidcConfigs = async (pool, tenantId) => {
|
|
@@ -27,7 +27,7 @@ export const seedOidcConfigs = async (pool, tenantId) => {
|
|
|
27
27
|
const validOptions = Object.values(LogtoOidcConfigKey).filter((key) => {
|
|
28
28
|
const included = existingKeys.has(key);
|
|
29
29
|
if (included) {
|
|
30
|
-
|
|
30
|
+
consoleLog.info(tenantPrefix, `Key ${chalk.green(key)} exists, skipping`);
|
|
31
31
|
}
|
|
32
32
|
return !included;
|
|
33
33
|
});
|
|
@@ -36,15 +36,15 @@ export const seedOidcConfigs = async (pool, tenantId) => {
|
|
|
36
36
|
for (const key of validOptions) {
|
|
37
37
|
const { value, fromEnv } = await oidcConfigReaders[key]();
|
|
38
38
|
if (fromEnv) {
|
|
39
|
-
|
|
39
|
+
consoleLog.info(tenantPrefix, `Read config ${chalk.green(key)} from env`);
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
42
|
-
|
|
42
|
+
consoleLog.info(tenantPrefix, `Generated config ${chalk.green(key)}`);
|
|
43
43
|
}
|
|
44
44
|
await updateValueByKey(pool, tenantId, key, value);
|
|
45
45
|
}
|
|
46
46
|
/* eslint-enable no-await-in-loop */
|
|
47
|
-
|
|
47
|
+
consoleLog.succeed(tenantPrefix, 'Seed OIDC config');
|
|
48
48
|
};
|
|
49
49
|
/**
|
|
50
50
|
* Each config reader will do the following things in order:
|
|
@@ -7,7 +7,7 @@ import { raw } from 'slonik-sql-tag-raw';
|
|
|
7
7
|
import { insertInto } from '../../../database.js';
|
|
8
8
|
import { getDatabaseName } from '../../../queries/database.js';
|
|
9
9
|
import { updateDatabaseTimestamp } from '../../../queries/system.js';
|
|
10
|
-
import {
|
|
10
|
+
import { consoleLog, getPathInModule } from '../../../utils.js';
|
|
11
11
|
import { appendAdminConsoleRedirectUris } from './cloud.js';
|
|
12
12
|
import { seedOidcConfigs } from './oidc-config.js';
|
|
13
13
|
import { assignScopesToRole, createTenant, seedAdminData } from './tenant.js';
|
|
@@ -102,7 +102,7 @@ export const seedTables = async (connection, latestTimestamp, isCloud) => {
|
|
|
102
102
|
connection.query(insertInto(createDefaultAdminConsoleApplication(), 'applications')),
|
|
103
103
|
updateDatabaseTimestamp(connection, latestTimestamp),
|
|
104
104
|
]);
|
|
105
|
-
|
|
105
|
+
consoleLog.succeed('Seed data');
|
|
106
106
|
};
|
|
107
107
|
export const seedCloud = async (connection) => {
|
|
108
108
|
await appendAdminConsoleRedirectUris(connection);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { createTenantMetadata } from '@logto/core-kit';
|
|
2
|
+
import { generateStandardId } from '@logto/shared';
|
|
3
3
|
import { assert } from '@silverhand/essentials';
|
|
4
4
|
import { sql } from 'slonik';
|
|
5
5
|
import { raw } from 'slonik-sql-tag-raw';
|
|
@@ -3,13 +3,13 @@ import { noop } from '@silverhand/essentials';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { createPoolFromConfig } from '../../database.js';
|
|
5
5
|
import { getRowByKey, updateValueByKey } from '../../queries/system.js';
|
|
6
|
-
import {
|
|
6
|
+
import { consoleLog } from '../../utils.js';
|
|
7
7
|
const validKeysDisplay = chalk.green(systemKeys.join(', '));
|
|
8
8
|
const validateKey = (key) => {
|
|
9
9
|
// Using `.includes()` will result a type error
|
|
10
10
|
// eslint-disable-next-line unicorn/prefer-includes
|
|
11
11
|
if (!systemKeys.some((element) => element === key)) {
|
|
12
|
-
|
|
12
|
+
consoleLog.fatal(`Invalid config key ${chalk.red(key)} found, expected one of ${validKeysDisplay}`);
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
15
|
const getConfig = {
|
|
@@ -26,7 +26,7 @@ const getConfig = {
|
|
|
26
26
|
const row = await getRowByKey(pool, key);
|
|
27
27
|
await pool.end();
|
|
28
28
|
const value = row?.value;
|
|
29
|
-
|
|
29
|
+
consoleLog.plain(chalk.magenta(key) +
|
|
30
30
|
'=' +
|
|
31
31
|
(value === undefined ? chalk.gray(value) : chalk.green(JSON.stringify(value))));
|
|
32
32
|
},
|
|
@@ -41,7 +41,7 @@ const setConfig = {
|
|
|
41
41
|
demandOption: true,
|
|
42
42
|
})
|
|
43
43
|
.positional('value', {
|
|
44
|
-
describe: 'The value to set, should be a valid JSON string',
|
|
44
|
+
describe: 'The value to set, should be a valid JSON string.',
|
|
45
45
|
type: 'string',
|
|
46
46
|
demandOption: true,
|
|
47
47
|
}),
|
|
@@ -51,7 +51,7 @@ const setConfig = {
|
|
|
51
51
|
const pool = await createPoolFromConfig();
|
|
52
52
|
await updateValueByKey(pool, key, guarded);
|
|
53
53
|
await pool.end();
|
|
54
|
-
|
|
54
|
+
consoleLog.info(`Update ${chalk.green(key)} succeeded`);
|
|
55
55
|
},
|
|
56
56
|
};
|
|
57
57
|
const system = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { generateKeyPair } from 'node:crypto';
|
|
2
2
|
import { promisify } from 'node:util';
|
|
3
|
-
import { generateStandardId } from '@logto/
|
|
3
|
+
import { generateStandardId } from '@logto/shared';
|
|
4
4
|
export const generateOidcPrivateKey = async (type = 'ec') => {
|
|
5
5
|
if (type === 'rsa') {
|
|
6
6
|
const { privateKey } = await promisify(generateKeyPair)('rsa', {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { getDatabaseUrlFromConfig } from '../../database.js';
|
|
3
|
-
import {
|
|
3
|
+
import { consoleLog } from '../../utils.js';
|
|
4
4
|
import { validateNodeVersion, inquireInstancePath, validateDatabase, downloadRelease, seedDatabase, createEnv, logFinale, decompress, isUrl, } from './utils.js';
|
|
5
5
|
const installLogto = async ({ path, skipSeed, downloadUrl, cloud }) => {
|
|
6
6
|
validateNodeVersion();
|
|
@@ -13,7 +13,7 @@ const installLogto = async ({ path, skipSeed, downloadUrl, cloud }) => {
|
|
|
13
13
|
await decompress(instancePath, tarPath);
|
|
14
14
|
// Seed database
|
|
15
15
|
if (skipSeed) {
|
|
16
|
-
|
|
16
|
+
consoleLog.info(`Skipped database seeding.\n\n' + ' You can use the ${chalk.green('db seed')} command to seed database when ready.\n`);
|
|
17
17
|
}
|
|
18
18
|
else {
|
|
19
19
|
await seedDatabase(instancePath, cloud);
|
|
@@ -46,7 +46,7 @@ const install = {
|
|
|
46
46
|
},
|
|
47
47
|
du: {
|
|
48
48
|
alias: 'download-url',
|
|
49
|
-
describe: 'URL for downloading Logto, can be a local path to tar',
|
|
49
|
+
describe: 'URL for downloading Logto, can be a local path to tar.',
|
|
50
50
|
type: 'string',
|
|
51
51
|
hidden: true,
|
|
52
52
|
},
|