@logto/cli 1.0.0-beta.16 → 1.0.0-beta.19
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/logto.js +2 -0
- package/lib/commands/connector/add.js +8 -10
- package/lib/commands/connector/index.js +9 -14
- package/lib/commands/connector/list.js +10 -16
- package/lib/commands/connector/remove.js +13 -18
- package/lib/commands/connector/utils.d.ts +1 -1
- package/lib/commands/connector/utils.js +100 -86
- package/lib/commands/database/alteration/index.d.ts +1 -2
- package/lib/commands/database/alteration/index.js +36 -98
- package/lib/commands/database/alteration/index.test.js +36 -57
- package/lib/commands/database/alteration/meta-url.d.ts +1 -0
- package/lib/commands/database/alteration/meta-url.js +4 -0
- package/lib/commands/database/alteration/type.d.ts +1 -1
- package/lib/commands/database/alteration/type.js +1 -2
- package/lib/commands/database/alteration/utils.d.ts +4 -0
- package/lib/commands/database/alteration/utils.js +39 -0
- package/lib/commands/database/alteration/version.d.ts +1 -1
- package/lib/commands/database/alteration/version.js +18 -25
- package/lib/commands/database/config.js +44 -49
- package/lib/commands/database/index.js +7 -12
- package/lib/commands/database/seed/index.d.ts +1 -1
- package/lib/commands/database/seed/index.js +59 -55
- package/lib/commands/database/seed/oidc-config.js +15 -18
- package/lib/commands/database/utilities.js +6 -11
- package/lib/commands/install/index.d.ts +1 -1
- package/lib/commands/install/index.js +14 -28
- package/lib/commands/install/utils.d.ts +0 -1
- package/lib/commands/install/utils.js +59 -106
- package/lib/constants.js +1 -4
- package/lib/database.js +33 -43
- package/lib/index.js +18 -23
- package/lib/meta-url.d.ts +1 -0
- package/lib/meta-url.js +4 -0
- package/lib/package-json.d.ts +7 -3
- package/lib/package-json.js +28 -27
- package/lib/queries/logto-config.js +18 -26
- package/lib/queries/logto-config.test.js +29 -30
- package/lib/test-utilities.d.ts +2 -2
- package/lib/test-utilities.js +1 -5
- package/lib/utilities.d.ts +5 -5
- package/lib/utilities.js +49 -64
- package/package.json +27 -23
- package/bin/logto +0 -2
|
@@ -1,57 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
const slonik_1 = require("slonik");
|
|
27
|
-
const functions = __importStar(require("."));
|
|
28
|
-
const queries = __importStar(require("../../../queries/logto-config"));
|
|
29
|
-
const version_1 = require("./version");
|
|
30
|
-
const mockQuery = jest.fn();
|
|
31
|
-
const pool = (0, slonik_1.createMockPool)({
|
|
32
|
-
query: async (sql, values) => {
|
|
33
|
-
return mockQuery(sql, values);
|
|
34
|
-
},
|
|
1
|
+
import { createMockUtils } from '@logto/shared/esm';
|
|
2
|
+
import Sinon from 'sinon';
|
|
3
|
+
import { createMockPool } from 'slonik';
|
|
4
|
+
import { chooseAlterationsByVersion } from './version.js';
|
|
5
|
+
const { jest } = import.meta;
|
|
6
|
+
const { mockEsmWithActual } = createMockUtils(jest);
|
|
7
|
+
const pool = createMockPool({
|
|
8
|
+
query: jest.fn(),
|
|
35
9
|
});
|
|
10
|
+
const files = Object.freeze([
|
|
11
|
+
{ filename: '1.0.0-1663923770-a.js', path: '/alterations-js/1.0.0-1663923770-a.js' },
|
|
12
|
+
{ filename: '1.0.0-1663923771-b.js', path: '/alterations-js/1.0.0-1663923771-b.js' },
|
|
13
|
+
{ filename: '1.0.0-1663923772-c.js', path: '/alterations-js/1.0.0-1663923772-c.js' },
|
|
14
|
+
]);
|
|
15
|
+
await mockEsmWithActual('./utils.js', () => ({
|
|
16
|
+
getAlterationFiles: async () => files,
|
|
17
|
+
}));
|
|
18
|
+
const { getCurrentDatabaseAlterationTimestamp } = await mockEsmWithActual('../../../queries/logto-config.js', () => ({
|
|
19
|
+
getCurrentDatabaseAlterationTimestamp: jest.fn(),
|
|
20
|
+
}));
|
|
21
|
+
const { getUndeployedAlterations } = await import('./index.js');
|
|
36
22
|
describe('getUndeployedAlterations()', () => {
|
|
37
|
-
const files = Object.freeze([
|
|
38
|
-
{ filename: '1.0.0-1663923770-a.js', path: '/alterations-js/1.0.0-1663923770-a.js' },
|
|
39
|
-
{ filename: '1.0.0-1663923771-b.js', path: '/alterations-js/1.0.0-1663923771-b.js' },
|
|
40
|
-
{ filename: '1.0.0-1663923772-c.js', path: '/alterations-js/1.0.0-1663923772-c.js' },
|
|
41
|
-
]);
|
|
42
|
-
beforeEach(() => {
|
|
43
|
-
// `getAlterationFiles()` will ensure the order
|
|
44
|
-
jest.spyOn(functions, 'getAlterationFiles').mockResolvedValueOnce([...files]);
|
|
45
|
-
});
|
|
46
23
|
it('returns all files if database timestamp is 0', async () => {
|
|
47
|
-
|
|
48
|
-
await expect(
|
|
24
|
+
getCurrentDatabaseAlterationTimestamp.mockResolvedValue(0);
|
|
25
|
+
await expect(getUndeployedAlterations(pool)).resolves.toEqual(files);
|
|
49
26
|
});
|
|
50
27
|
it('returns files whose timestamp is greater then database timestamp', async () => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
.mockResolvedValueOnce(1663923770);
|
|
54
|
-
await expect(functions.getUndeployedAlterations(pool)).resolves.toEqual([files[1], files[2]]);
|
|
28
|
+
getCurrentDatabaseAlterationTimestamp.mockResolvedValue(1663923770);
|
|
29
|
+
await expect(getUndeployedAlterations(pool)).resolves.toEqual([files[1], files[2]]);
|
|
55
30
|
});
|
|
56
31
|
});
|
|
57
32
|
describe('chooseAlterationsByVersion()', () => {
|
|
@@ -70,18 +45,22 @@ describe('chooseAlterationsByVersion()', () => {
|
|
|
70
45
|
'next-1663923780-c.js',
|
|
71
46
|
'next1-1663923781-c.js',
|
|
72
47
|
].map((filename) => ({ filename, path: '/alterations/' + filename })));
|
|
48
|
+
const stub = Sinon.stub(global, 'process').value({ stdin: { isTTY: false } });
|
|
49
|
+
afterAll(() => {
|
|
50
|
+
stub.restore();
|
|
51
|
+
});
|
|
73
52
|
it('chooses nothing when input version is invalid', async () => {
|
|
74
|
-
await expect(
|
|
75
|
-
await expect(
|
|
53
|
+
await expect(chooseAlterationsByVersion(files, 'next1')).rejects.toThrow(new TypeError('Invalid Version: next1'));
|
|
54
|
+
await expect(chooseAlterationsByVersion([], 'ok')).rejects.toThrow(new TypeError('Invalid Version: ok'));
|
|
76
55
|
});
|
|
77
56
|
it('chooses correct alteration files', async () => {
|
|
78
57
|
await Promise.all([
|
|
79
|
-
expect(
|
|
80
|
-
expect(
|
|
81
|
-
expect(
|
|
82
|
-
expect(
|
|
83
|
-
expect(
|
|
84
|
-
expect(
|
|
58
|
+
expect(chooseAlterationsByVersion([], 'v1.0.0')).resolves.toEqual([]),
|
|
59
|
+
expect(chooseAlterationsByVersion(files, 'v1.0.0')).resolves.toEqual(files.slice(0, 7)),
|
|
60
|
+
expect(chooseAlterationsByVersion(files, 'v1.0.0-beta.10')).resolves.toEqual(files.slice(0, 3)),
|
|
61
|
+
expect(chooseAlterationsByVersion(files, 'v1.1.0')).resolves.toEqual(files.slice(0, 8)),
|
|
62
|
+
expect(chooseAlterationsByVersion(files, 'v1.2.0')).resolves.toEqual(files.slice(0, 9)),
|
|
63
|
+
expect(chooseAlterationsByVersion(files, 'next')).resolves.toEqual(files.slice(0, 12)),
|
|
85
64
|
]);
|
|
86
65
|
});
|
|
87
66
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const metaUrl: string;
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { existsSync } from 'fs';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { findPackage } from '@logto/shared';
|
|
6
|
+
import { getPathInModule } from '../../../utilities.js';
|
|
7
|
+
import { metaUrl } from './meta-url.js';
|
|
8
|
+
const currentDirname = path.dirname(fileURLToPath(metaUrl));
|
|
9
|
+
const alterationFilenameRegex = /-(\d+)-?.*\.js$/;
|
|
10
|
+
export const getTimestampFromFilename = (filename) => {
|
|
11
|
+
const match = alterationFilenameRegex.exec(filename);
|
|
12
|
+
if (!match?.[1]) {
|
|
13
|
+
throw new Error(`Can not get timestamp: ${filename}`);
|
|
14
|
+
}
|
|
15
|
+
return Number(match[1]);
|
|
16
|
+
};
|
|
17
|
+
export const getAlterationDirectory = () => getPathInModule('@logto/schemas', 'alterations-js');
|
|
18
|
+
export const getAlterationFiles = async () => {
|
|
19
|
+
const alterationDirectory = getAlterationDirectory();
|
|
20
|
+
/**
|
|
21
|
+
* We copy all alteration scripts to the CLI package root directory,
|
|
22
|
+
* since they need a proper context that includes required dependencies (such as slonik) in `node_modules/`.
|
|
23
|
+
* While the original `@logto/schemas` may remove them in production.
|
|
24
|
+
*/
|
|
25
|
+
const packageDirectory = await findPackage(currentDirname);
|
|
26
|
+
const localAlterationDirectory = path.resolve(packageDirectory ?? currentDirname, 'alteration-scripts');
|
|
27
|
+
if (!existsSync(alterationDirectory)) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
// We need to copy alteration files to execute in the CLI context to make `slonik` available
|
|
31
|
+
await fs.rm(localAlterationDirectory, { force: true, recursive: true });
|
|
32
|
+
await fs.cp(alterationDirectory, localAlterationDirectory, { recursive: true });
|
|
33
|
+
const directory = await fs.readdir(localAlterationDirectory);
|
|
34
|
+
const files = directory.filter((file) => alterationFilenameRegex.test(file));
|
|
35
|
+
return files
|
|
36
|
+
.slice()
|
|
37
|
+
.sort((file1, file2) => getTimestampFromFilename(file1) - getTimestampFromFilename(file2))
|
|
38
|
+
.map((filename) => ({ path: path.join(localAlterationDirectory, filename), filename }));
|
|
39
|
+
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { AlterationFile } from './type';
|
|
1
|
+
import type { AlterationFile } from './type.js';
|
|
2
2
|
export declare const chooseAlterationsByVersion: (alterations: readonly AlterationFile[], initialVersion?: string) => Promise<AlterationFile[]>;
|
|
@@ -1,39 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
exports.chooseAlterationsByVersion = void 0;
|
|
7
|
-
const essentials_1 = require("@silverhand/essentials");
|
|
8
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
-
const inquirer_1 = __importDefault(require("inquirer"));
|
|
10
|
-
const semver_1 = require("semver");
|
|
11
|
-
const utilities_1 = require("../../../utilities");
|
|
1
|
+
import { assert, conditional } from '@silverhand/essentials';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { SemVer, compare, eq, gt } from 'semver';
|
|
5
|
+
import { findLastIndex, isTty, log } from '../../../utilities.js';
|
|
12
6
|
const getVersionFromFilename = (filename) => {
|
|
13
7
|
try {
|
|
14
|
-
return new
|
|
8
|
+
return new SemVer(filename.split('-')[0]?.replaceAll('_', '-') ?? 'unknown');
|
|
15
9
|
}
|
|
16
10
|
catch { }
|
|
17
11
|
};
|
|
18
12
|
const latestTag = 'latest';
|
|
19
13
|
const nextTag = 'next';
|
|
20
|
-
const chooseAlterationsByVersion = async (alterations, initialVersion) => {
|
|
14
|
+
export const chooseAlterationsByVersion = async (alterations, initialVersion) => {
|
|
21
15
|
if (initialVersion === nextTag) {
|
|
22
|
-
const endIndex =
|
|
16
|
+
const endIndex = findLastIndex(alterations, ({ filename }) => filename.startsWith(nextTag + '-') || Boolean(getVersionFromFilename(filename)));
|
|
23
17
|
if (endIndex === -1) {
|
|
24
18
|
return [];
|
|
25
19
|
}
|
|
26
|
-
|
|
20
|
+
log.info(`Deploy target ${chalk.green(nextTag)}`);
|
|
27
21
|
return alterations.slice(0, endIndex + 1);
|
|
28
22
|
}
|
|
29
23
|
const versions = alterations
|
|
30
24
|
.map(({ filename }) => getVersionFromFilename(filename))
|
|
31
|
-
.filter((version) => version instanceof
|
|
25
|
+
.filter((version) => version instanceof SemVer)
|
|
32
26
|
// Cannot use `Set` to deduplicate since it's a class
|
|
33
|
-
.filter((version, index, self) => index === self.findIndex((another) =>
|
|
27
|
+
.filter((version, index, self) => index === self.findIndex((another) => eq(version, another)))
|
|
34
28
|
.slice()
|
|
35
|
-
.sort((i, j) =>
|
|
36
|
-
const initialSemVersion =
|
|
29
|
+
.sort((i, j) => compare(j, i));
|
|
30
|
+
const initialSemVersion = conditional(initialVersion && initialVersion !== latestTag && new SemVer(initialVersion));
|
|
37
31
|
const firstVersion = versions[0];
|
|
38
32
|
if (!firstVersion) {
|
|
39
33
|
return [];
|
|
@@ -42,11 +36,11 @@ const chooseAlterationsByVersion = async (alterations, initialVersion) => {
|
|
|
42
36
|
if (initialVersion === latestTag) {
|
|
43
37
|
return firstVersion;
|
|
44
38
|
}
|
|
45
|
-
if (!
|
|
46
|
-
|
|
39
|
+
if (!isTty()) {
|
|
40
|
+
assert(initialSemVersion, new Error('Missing target version'));
|
|
47
41
|
return initialSemVersion;
|
|
48
42
|
}
|
|
49
|
-
const { version } = await
|
|
43
|
+
const { version } = await inquirer.prompt({
|
|
50
44
|
type: 'list',
|
|
51
45
|
message: 'Choose the alteration target version',
|
|
52
46
|
name: 'version',
|
|
@@ -60,10 +54,9 @@ const chooseAlterationsByVersion = async (alterations, initialVersion) => {
|
|
|
60
54
|
return version;
|
|
61
55
|
};
|
|
62
56
|
const targetVersion = await getTargetVersion();
|
|
63
|
-
|
|
57
|
+
log.info(`Deploy target ${chalk.green(targetVersion.version)}`);
|
|
64
58
|
return alterations.filter(({ filename }) => {
|
|
65
59
|
const version = getVersionFromFilename(filename);
|
|
66
|
-
return version && !
|
|
60
|
+
return version && !gt(version, targetVersion);
|
|
67
61
|
});
|
|
68
62
|
};
|
|
69
|
-
exports.chooseAlterationsByVersion = chooseAlterationsByVersion;
|
|
@@ -1,34 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const database_1 = require("../../database");
|
|
10
|
-
const logto_config_1 = require("../../queries/logto-config");
|
|
11
|
-
const utilities_1 = require("../../utilities");
|
|
12
|
-
const utilities_2 = require("./utilities");
|
|
13
|
-
const validKeysDisplay = chalk_1.default.green(schemas_1.logtoConfigKeys.join(', '));
|
|
1
|
+
import { LogtoOidcConfigKey, logtoConfigGuards, logtoConfigKeys } from '@logto/schemas';
|
|
2
|
+
import { deduplicate, noop } from '@silverhand/essentials';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { createPoolFromConfig } from '../../database.js';
|
|
5
|
+
import { getRowsByKeys, updateValueByKey } from '../../queries/logto-config.js';
|
|
6
|
+
import { log } from '../../utilities.js';
|
|
7
|
+
import { generateOidcCookieKey, generateOidcPrivateKey } from './utilities.js';
|
|
8
|
+
const validKeysDisplay = chalk.green(logtoConfigKeys.join(', '));
|
|
14
9
|
const validateKeys = (keys) => {
|
|
15
10
|
const invalidKey = (Array.isArray(keys) ? keys : [keys]).find(
|
|
16
11
|
// Using `.includes()` will result a type error
|
|
17
12
|
// eslint-disable-next-line unicorn/prefer-includes
|
|
18
|
-
(key) => !
|
|
13
|
+
(key) => !logtoConfigKeys.some((element) => element === key));
|
|
19
14
|
if (invalidKey) {
|
|
20
|
-
|
|
15
|
+
log.error(`Invalid config key ${chalk.red(invalidKey)} found, expected one of ${validKeysDisplay}`);
|
|
21
16
|
}
|
|
22
17
|
};
|
|
23
18
|
const validRotateKeys = Object.freeze([
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
LogtoOidcConfigKey.PrivateKeys,
|
|
20
|
+
LogtoOidcConfigKey.CookieKeys,
|
|
26
21
|
]);
|
|
27
22
|
const validateRotateKey = (key) => {
|
|
28
23
|
// Using `.includes()` will result a type error
|
|
29
24
|
// eslint-disable-next-line unicorn/prefer-includes
|
|
30
25
|
if (!validRotateKeys.some((element) => element === key)) {
|
|
31
|
-
|
|
26
|
+
log.error(`Invalid config key ${chalk.red(key)} found, expected one of ${validKeysDisplay}`);
|
|
32
27
|
}
|
|
33
28
|
};
|
|
34
29
|
const getConfig = {
|
|
@@ -47,17 +42,17 @@ const getConfig = {
|
|
|
47
42
|
default: [],
|
|
48
43
|
}),
|
|
49
44
|
handler: async ({ key, keys }) => {
|
|
50
|
-
const queryKeys =
|
|
45
|
+
const queryKeys = deduplicate([key, ...keys]);
|
|
51
46
|
validateKeys(queryKeys);
|
|
52
|
-
const pool = await
|
|
53
|
-
const { rows } = await
|
|
47
|
+
const pool = await createPoolFromConfig();
|
|
48
|
+
const { rows } = await getRowsByKeys(pool, queryKeys);
|
|
54
49
|
await pool.end();
|
|
55
50
|
console.log(queryKeys
|
|
56
51
|
.map((currentKey) => {
|
|
57
52
|
const value = rows.find(({ key }) => currentKey === key)?.value;
|
|
58
|
-
return (
|
|
53
|
+
return (chalk.magenta(currentKey) +
|
|
59
54
|
'=' +
|
|
60
|
-
(value === undefined ?
|
|
55
|
+
(value === undefined ? chalk.gray(value) : chalk.green(JSON.stringify(value))));
|
|
61
56
|
})
|
|
62
57
|
.join('\n'));
|
|
63
58
|
},
|
|
@@ -78,44 +73,44 @@ const setConfig = {
|
|
|
78
73
|
}),
|
|
79
74
|
handler: async ({ key, value }) => {
|
|
80
75
|
validateKeys(key);
|
|
81
|
-
const guarded =
|
|
82
|
-
const pool = await
|
|
83
|
-
await
|
|
76
|
+
const guarded = logtoConfigGuards[key].parse(JSON.parse(value));
|
|
77
|
+
const pool = await createPoolFromConfig();
|
|
78
|
+
await updateValueByKey(pool, key, guarded);
|
|
84
79
|
await pool.end();
|
|
85
|
-
|
|
80
|
+
log.info(`Update ${chalk.green(key)} succeeded`);
|
|
86
81
|
},
|
|
87
82
|
};
|
|
88
83
|
const rotateConfig = {
|
|
89
84
|
command: 'rotate <key>',
|
|
90
85
|
describe: 'Generate a new private or secret key for the given config key and prepend to the key array',
|
|
91
86
|
builder: (yargs) => yargs.positional('key', {
|
|
92
|
-
describe: `The key to rotate, one of ${
|
|
87
|
+
describe: `The key to rotate, one of ${chalk.green(validRotateKeys.join(', '))}`,
|
|
93
88
|
type: 'string',
|
|
94
89
|
demandOption: true,
|
|
95
90
|
}),
|
|
96
91
|
handler: async ({ key }) => {
|
|
97
92
|
validateRotateKey(key);
|
|
98
|
-
const pool = await
|
|
99
|
-
const { rows } = await
|
|
93
|
+
const pool = await createPoolFromConfig();
|
|
94
|
+
const { rows } = await getRowsByKeys(pool, [key]);
|
|
100
95
|
if (!rows[0]) {
|
|
101
|
-
|
|
96
|
+
log.warn('No key found, create a new one');
|
|
102
97
|
}
|
|
103
98
|
const getValue = async () => {
|
|
104
|
-
const parsed =
|
|
99
|
+
const parsed = logtoConfigGuards[key].safeParse(rows[0]?.value);
|
|
105
100
|
const original = parsed.success ? parsed.data : [];
|
|
106
101
|
// No need for default. It's already exhaustive
|
|
107
102
|
// eslint-disable-next-line default-case
|
|
108
103
|
switch (key) {
|
|
109
|
-
case
|
|
110
|
-
return [await
|
|
111
|
-
case
|
|
112
|
-
return [
|
|
104
|
+
case LogtoOidcConfigKey.PrivateKeys:
|
|
105
|
+
return [await generateOidcPrivateKey(), ...original];
|
|
106
|
+
case LogtoOidcConfigKey.CookieKeys:
|
|
107
|
+
return [generateOidcCookieKey(), ...original];
|
|
113
108
|
}
|
|
114
109
|
};
|
|
115
110
|
const rotated = await getValue();
|
|
116
|
-
await
|
|
111
|
+
await updateValueByKey(pool, key, rotated);
|
|
117
112
|
await pool.end();
|
|
118
|
-
|
|
113
|
+
log.info(`Rotate ${chalk.green(key)} succeeded, now it has ${rotated.length} keys`);
|
|
119
114
|
},
|
|
120
115
|
};
|
|
121
116
|
const trimConfig = {
|
|
@@ -123,7 +118,7 @@ const trimConfig = {
|
|
|
123
118
|
describe: 'Remove the last [length] number of private or secret keys for the given config key',
|
|
124
119
|
builder: (yargs) => yargs
|
|
125
120
|
.positional('key', {
|
|
126
|
-
describe: `The config key to trim, one of ${
|
|
121
|
+
describe: `The config key to trim, one of ${chalk.green(validRotateKeys.join(', '))}`,
|
|
127
122
|
type: 'string',
|
|
128
123
|
demandOption: true,
|
|
129
124
|
})
|
|
@@ -136,25 +131,25 @@ const trimConfig = {
|
|
|
136
131
|
handler: async ({ key, length }) => {
|
|
137
132
|
validateRotateKey(key);
|
|
138
133
|
if (length < 1) {
|
|
139
|
-
|
|
134
|
+
log.error('Invalid length provided');
|
|
140
135
|
}
|
|
141
|
-
const pool = await
|
|
142
|
-
const { rows } = await
|
|
136
|
+
const pool = await createPoolFromConfig();
|
|
137
|
+
const { rows } = await getRowsByKeys(pool, [key]);
|
|
143
138
|
if (!rows[0]) {
|
|
144
|
-
|
|
139
|
+
log.warn('No key found, create a new one');
|
|
145
140
|
}
|
|
146
141
|
const getValue = async () => {
|
|
147
|
-
const value =
|
|
142
|
+
const value = logtoConfigGuards[key].parse(rows[0]?.value);
|
|
148
143
|
if (value.length - length < 1) {
|
|
149
144
|
await pool.end();
|
|
150
|
-
|
|
145
|
+
log.error(`You should keep at least one key in the array, current length=${value.length}`);
|
|
151
146
|
}
|
|
152
147
|
return value.slice(0, -length);
|
|
153
148
|
};
|
|
154
149
|
const trimmed = await getValue();
|
|
155
|
-
await
|
|
150
|
+
await updateValueByKey(pool, key, trimmed);
|
|
156
151
|
await pool.end();
|
|
157
|
-
|
|
152
|
+
log.info(`Trim ${chalk.green(key)} succeeded, now it has ${trimmed.length} keys`);
|
|
158
153
|
},
|
|
159
154
|
};
|
|
160
155
|
const config = {
|
|
@@ -166,6 +161,6 @@ const config = {
|
|
|
166
161
|
.command(rotateConfig)
|
|
167
162
|
.command(trimConfig)
|
|
168
163
|
.demandCommand(1),
|
|
169
|
-
handler:
|
|
164
|
+
handler: noop,
|
|
170
165
|
};
|
|
171
|
-
|
|
166
|
+
export default config;
|
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const essentials_1 = require("@silverhand/essentials");
|
|
7
|
-
const alteration_1 = __importDefault(require("./alteration"));
|
|
8
|
-
const config_1 = __importDefault(require("./config"));
|
|
9
|
-
const seed_1 = __importDefault(require("./seed"));
|
|
1
|
+
import { noop } from '@silverhand/essentials';
|
|
2
|
+
import alteration from './alteration/index.js';
|
|
3
|
+
import config from './config.js';
|
|
4
|
+
import seed from './seed/index.js';
|
|
10
5
|
const database = {
|
|
11
6
|
command: ['database', 'db'],
|
|
12
7
|
describe: 'Commands for Logto database',
|
|
13
|
-
builder: (yargs) => yargs.command(
|
|
14
|
-
handler:
|
|
8
|
+
builder: (yargs) => yargs.command(config).command(seed).command(alteration).demandCommand(1),
|
|
9
|
+
handler: noop,
|
|
15
10
|
};
|
|
16
|
-
|
|
11
|
+
export default database;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { DatabasePool } from 'slonik';
|
|
2
2
|
import type { CommandModule } from 'yargs';
|
|
3
3
|
declare const seedChoices: readonly ["all", "oidc"];
|
|
4
|
-
|
|
4
|
+
type SeedChoice = typeof seedChoices[number];
|
|
5
5
|
export declare const seedByPool: (pool: DatabasePool, type: SeedChoice) => Promise<void>;
|
|
6
6
|
declare const seed: CommandModule<Record<string, unknown>, {
|
|
7
7
|
type: string;
|