0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

feat(cli): add system get and set commands (#3318)

This commit is contained in:
wangsijie 2023-03-08 10:15:05 +08:00 committed by GitHub
parent 3c0c82a4bf
commit 822ef08bab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 117 additions and 3 deletions

View file

@ -0,0 +1,5 @@
---
"@logto/cli": minor
---
Add CLI command to get/set db's system table value

View file

@ -4,11 +4,13 @@ import type { CommandModule } from 'yargs';
import alteration from './alteration/index.js';
import config from './config.js';
import seed from './seed/index.js';
import system from './system.js';
const database: CommandModule = {
command: ['database', 'db'],
describe: 'Commands for Logto database',
builder: (yargs) => yargs.command(config).command(seed).command(alteration).demandCommand(1),
builder: (yargs) =>
yargs.command(config).command(seed).command(alteration).command(system).demandCommand(1),
handler: noop,
};

View file

@ -0,0 +1,87 @@
import type { SystemKey } from '@logto/schemas';
import { systemGuards, systemKeys } from '@logto/schemas';
import { noop } from '@silverhand/essentials';
import chalk from 'chalk';
import type { CommandModule } from 'yargs';
import { createPoolFromConfig } from '../../database.js';
import { getRowByKey, updateValueByKey } from '../../queries/system.js';
import { log } from '../../utils.js';
const validKeysDisplay = chalk.green(systemKeys.join(', '));
type ValidateKeysFunction = {
(keys: string[]): asserts keys is SystemKey[];
(key: string): asserts key is SystemKey;
};
const validateKey: ValidateKeysFunction = (key) => {
// Using `.includes()` will result a type error
// eslint-disable-next-line unicorn/prefer-includes
if (!systemKeys.some((element) => element === key)) {
log.error(`Invalid config key ${chalk.red(key)} found, expected one of ${validKeysDisplay}`);
}
};
const getConfig: CommandModule<unknown, { key: string }> = {
command: 'get <key>',
describe: 'Get system value of the given key in Logto database',
builder: (yargs) =>
yargs.positional('key', {
describe: `The key to get from database system table, one of ${validKeysDisplay}`,
type: 'string',
demandOption: true,
}),
handler: async ({ key }) => {
validateKey(key);
const pool = await createPoolFromConfig();
const row = await getRowByKey(pool, key);
await pool.end();
const value = row?.value;
console.log(
chalk.magenta(key) +
'=' +
(value === undefined ? chalk.gray(value) : chalk.green(JSON.stringify(value)))
);
},
};
const setConfig: CommandModule<unknown, { key: string; value: string }> = {
command: 'set <key> <value>',
describe: 'Set config value of the given key in Logto database',
builder: (yargs) =>
yargs
.positional('key', {
describe: `The key to get from database system table, one of ${validKeysDisplay}`,
type: 'string',
demandOption: true,
})
.positional('value', {
describe: 'The value to set, should be a valid JSON string',
type: 'string',
demandOption: true,
}),
handler: async ({ key, value }) => {
validateKey(key);
const guarded = systemGuards[key].parse(JSON.parse(value));
const pool = await createPoolFromConfig();
await updateValueByKey(pool, key, guarded);
await pool.end();
log.info(`Update ${chalk.green(key)} succeeded`);
},
};
const system: CommandModule = {
command: ['system'],
describe: 'Commands for Logto system config',
builder: (yargs) => yargs.command(getConfig).command(setConfig).demandCommand(1),
handler: noop,
};
export default system;

View file

@ -1,4 +1,4 @@
import type { AlterationState, System } from '@logto/schemas';
import type { AlterationState, System, SystemKey } from '@logto/schemas';
import { systemGuards, Systems, AlterationStateKey } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import type { Nullable } from '@silverhand/essentials';
@ -6,7 +6,7 @@ import type { CommonQueryMethods, DatabaseTransactionConnection } from 'slonik';
import { sql } from 'slonik';
import { z } from 'zod';
const { fields } = convertToIdentifiers(Systems);
const { fields, table } = convertToIdentifiers(Systems);
const doesTableExist = async (pool: CommonQueryMethods, table: string) => {
const { rows } = await pool.query<{ regclass: Nullable<string> }>(
@ -67,3 +67,23 @@ export const updateDatabaseTimestamp = async (
`
);
};
export const getRowByKey = async (pool: CommonQueryMethods, key: SystemKey) =>
pool.maybeOne<System>(sql`
select ${sql.join([fields.key, fields.value], sql`,`)} from ${table}
where ${fields.key} = ${key}
`);
export const updateValueByKey = async <T extends SystemKey>(
pool: CommonQueryMethods,
key: T,
value: z.infer<(typeof systemGuards)[T]>
) =>
pool.query(
sql`
insert into ${table} (${fields.key}, ${fields.value})
values (${key}, ${sql.jsonb(value)})
on conflict (${fields.key})
do update set ${fields.value}=excluded.${fields.value}
`
);