mirror of
https://github.com/logto-io/logto.git
synced 2025-02-17 22:04:19 -05:00
refactor(cli): use env to replace config json
This commit is contained in:
parent
4a5dfb1b8a
commit
a8c364e9b3
10 changed files with 28 additions and 104 deletions
|
@ -39,7 +39,7 @@
|
|||
"@silverhand/essentials": "^1.2.1",
|
||||
"chalk": "^4.1.2",
|
||||
"decamelize": "^5.0.0",
|
||||
"find-up": "^5.0.0",
|
||||
"dotenv": "^16.0.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"got": "^11.8.2",
|
||||
"hpagent": "^1.0.0",
|
||||
|
|
|
@ -7,7 +7,7 @@ import { copy, existsSync, remove, readdir } from 'fs-extra';
|
|||
import { DatabasePool } from 'slonik';
|
||||
import { CommandModule } from 'yargs';
|
||||
|
||||
import { createPoolFromConfig } from '../../database';
|
||||
import { createPoolFromEnv } from '../../database';
|
||||
import {
|
||||
getCurrentDatabaseAlterationTimestamp,
|
||||
updateDatabaseTimestamp,
|
||||
|
@ -116,7 +116,7 @@ const alteration: CommandModule<unknown, { action: string }> = {
|
|||
log.error('Unsupported action');
|
||||
}
|
||||
|
||||
const pool = await createPoolFromConfig();
|
||||
const pool = await createPoolFromEnv();
|
||||
const alterations = await getUndeployedAlterations(pool);
|
||||
|
||||
log.info(
|
||||
|
|
|
@ -2,7 +2,7 @@ import { logtoConfigGuards, LogtoConfigKey, logtoConfigKeys } from '@logto/schem
|
|||
import chalk from 'chalk';
|
||||
import { CommandModule } from 'yargs';
|
||||
|
||||
import { createPoolFromConfig } from '../../database';
|
||||
import { createPoolFromEnv } from '../../database';
|
||||
import { getRowsByKeys, updateValueByKey } from '../../queries/logto-config';
|
||||
import { deduplicate, log } from '../../utilities';
|
||||
|
||||
|
@ -47,7 +47,7 @@ export const getConfig: CommandModule<unknown, { key: string; keys: string[] }>
|
|||
const queryKeys = deduplicate([key, ...keys]);
|
||||
validateKeys(queryKeys);
|
||||
|
||||
const pool = await createPoolFromConfig();
|
||||
const pool = await createPoolFromEnv();
|
||||
const { rows } = await getRowsByKeys(pool, queryKeys);
|
||||
await pool.end();
|
||||
|
||||
|
@ -87,7 +87,7 @@ export const setConfig: CommandModule<unknown, { key: string; value: string }> =
|
|||
|
||||
const guarded = logtoConfigGuards[key].parse(JSON.parse(value));
|
||||
|
||||
const pool = await createPoolFromConfig();
|
||||
const pool = await createPoolFromEnv();
|
||||
await updateValueByKey(pool, key, guarded);
|
||||
await pool.end();
|
||||
|
||||
|
|
|
@ -4,20 +4,12 @@ import { noop } from '../../utilities';
|
|||
import alteration from './alteration';
|
||||
import { getConfig, setConfig } from './config';
|
||||
import seed from './seed';
|
||||
import { getUrl, setUrl } from './url';
|
||||
|
||||
const database: CommandModule = {
|
||||
command: ['database', 'db'],
|
||||
describe: 'Commands for Logto database',
|
||||
builder: (yargs) =>
|
||||
yargs
|
||||
.command(getUrl)
|
||||
.command(setUrl)
|
||||
.command(getConfig)
|
||||
.command(setConfig)
|
||||
.command(seed)
|
||||
.command(alteration)
|
||||
.demandCommand(1),
|
||||
yargs.command(getConfig).command(setConfig).command(seed).command(alteration).demandCommand(1),
|
||||
handler: noop,
|
||||
};
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
import { CommandModule } from 'yargs';
|
||||
|
||||
import { getConfig, patchConfig } from '../../config';
|
||||
|
||||
export const getUrl: CommandModule = {
|
||||
command: 'get-url',
|
||||
describe: 'Get database URL in Logto config file',
|
||||
handler: async () => {
|
||||
const { databaseUrl } = await getConfig();
|
||||
console.log(databaseUrl);
|
||||
},
|
||||
};
|
||||
|
||||
export const setUrl: CommandModule<unknown, { url: string }> = {
|
||||
command: 'set-url <url>',
|
||||
describe: 'Set database URL and save to config file',
|
||||
builder: (yargs) =>
|
||||
yargs.positional('url', {
|
||||
describe: 'The database URL (DSN) to use, including database name',
|
||||
type: 'string',
|
||||
demandOption: true,
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
await patchConfig({ databaseUrl: String(argv.url) });
|
||||
},
|
||||
};
|
|
@ -12,7 +12,7 @@ import * as semver from 'semver';
|
|||
import tar from 'tar';
|
||||
import { CommandModule } from 'yargs';
|
||||
|
||||
import { createPoolAndDatabaseIfNeeded, getDatabaseUrlFromConfig } from '../database';
|
||||
import { createPoolAndDatabaseIfNeeded, getDatabaseUrlFromEnv } from '../database';
|
||||
import { downloadFile, log, oraPromise, safeExecSync } from '../utilities';
|
||||
import { seedByPool } from './database/seed';
|
||||
|
||||
|
@ -149,7 +149,7 @@ const installLogto = async ({ path: pathArgument = defaultPath, silent = false }
|
|||
}
|
||||
|
||||
// Save to dot env
|
||||
const databaseUrl = await getDatabaseUrlFromConfig();
|
||||
const databaseUrl = await getDatabaseUrlFromEnv();
|
||||
const dotEnvPath = path.resolve(instancePath, '.env');
|
||||
await writeFile(dotEnvPath, `DB_URL=${databaseUrl}`, {
|
||||
encoding: 'utf8',
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
import { readFile, writeFile } from 'fs/promises';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import findUp from 'find-up';
|
||||
// eslint-disable-next-line id-length
|
||||
import z from 'zod';
|
||||
|
||||
import { log } from './utilities';
|
||||
|
||||
// Logto config
|
||||
const logtoConfigFilename = '.logto.json';
|
||||
const getConfigPath = async () =>
|
||||
(await findUp(logtoConfigFilename)) ?? path.join(os.homedir(), logtoConfigFilename);
|
||||
|
||||
const getConfigJson = async () => {
|
||||
const configPath = await getConfigPath();
|
||||
|
||||
try {
|
||||
const raw = await readFile(configPath, 'utf8');
|
||||
|
||||
// Prefer `unknown` over the original return type `any`, will guard later
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
return JSON.parse(raw) as unknown;
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const configGuard = z
|
||||
.object({
|
||||
databaseUrl: z.string().optional(),
|
||||
})
|
||||
.default({});
|
||||
|
||||
type LogtoConfig = z.infer<typeof configGuard>;
|
||||
|
||||
export const getConfig = async () => {
|
||||
return configGuard.parse(await getConfigJson());
|
||||
};
|
||||
|
||||
export const patchConfig = async (config: LogtoConfig) => {
|
||||
const configPath = await getConfigPath();
|
||||
await writeFile(configPath, JSON.stringify({ ...(await getConfig()), ...config }, undefined, 2));
|
||||
log.info(`Updated config in ${chalk.blue(configPath)}`);
|
||||
};
|
|
@ -6,13 +6,12 @@ import { createPool, IdentifierSqlToken, parseDsn, sql, SqlToken, stringifyDsn }
|
|||
import { createInterceptors } from 'slonik-interceptor-preset';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { getConfig, patchConfig } from './config';
|
||||
import { log } from './utilities';
|
||||
|
||||
export const defaultDatabaseUrl = 'postgresql://localhost:5432/logto';
|
||||
|
||||
export const getDatabaseUrlFromConfig = async () => {
|
||||
const { databaseUrl } = await getConfig();
|
||||
export const getDatabaseUrlFromEnv = async () => {
|
||||
const { DB_URL: databaseUrl } = process.env;
|
||||
|
||||
if (!databaseUrl) {
|
||||
const { value } = await inquirer
|
||||
|
@ -24,18 +23,13 @@ export const getDatabaseUrlFromConfig = async () => {
|
|||
})
|
||||
.catch(async (error) => {
|
||||
if (error.isTtyError) {
|
||||
log.error(
|
||||
`No database URL configured. Set it via ${chalk.green(
|
||||
'database set-url'
|
||||
)} command first.`
|
||||
);
|
||||
log.error('No database URL configured in env');
|
||||
}
|
||||
|
||||
// The type definition does not give us type except `any`, throw it directly will honor the original behavior.
|
||||
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||
throw error;
|
||||
});
|
||||
await patchConfig({ databaseUrl: value });
|
||||
|
||||
return value;
|
||||
}
|
||||
|
@ -43,8 +37,8 @@ export const getDatabaseUrlFromConfig = async () => {
|
|||
return databaseUrl;
|
||||
};
|
||||
|
||||
export const createPoolFromConfig = async () => {
|
||||
const databaseUrl = await getDatabaseUrlFromConfig();
|
||||
export const createPoolFromEnv = async () => {
|
||||
const databaseUrl = await getDatabaseUrlFromEnv();
|
||||
|
||||
return createPool(databaseUrl, {
|
||||
interceptors: createInterceptors(),
|
||||
|
@ -59,7 +53,7 @@ export const createPoolFromConfig = async () => {
|
|||
*/
|
||||
export const createPoolAndDatabaseIfNeeded = async () => {
|
||||
try {
|
||||
return await createPoolFromConfig();
|
||||
return await createPoolFromEnv();
|
||||
} catch (error: unknown) {
|
||||
const result = z.object({ code: z.string() }).safeParse(error);
|
||||
|
||||
|
@ -69,7 +63,7 @@ export const createPoolAndDatabaseIfNeeded = async () => {
|
|||
log.error(error);
|
||||
}
|
||||
|
||||
const databaseUrl = await getDatabaseUrlFromConfig();
|
||||
const databaseUrl = await getDatabaseUrlFromEnv();
|
||||
const dsn = parseDsn(databaseUrl);
|
||||
// It's ok to fall back to '?' since:
|
||||
// - Database name is required to connect in the previous pool
|
||||
|
@ -86,7 +80,7 @@ export const createPoolAndDatabaseIfNeeded = async () => {
|
|||
|
||||
log.info(`${chalk.green('✔')} Created database ${databaseName}`);
|
||||
|
||||
return createPoolFromConfig();
|
||||
return createPoolFromEnv();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import dotenv from 'dotenv';
|
||||
import yargs from 'yargs';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
|
||||
import database from './commands/database';
|
||||
import install from './commands/install';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
void yargs(hideBin(process.argv))
|
||||
.command(install)
|
||||
.command(database)
|
||||
|
|
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
|
@ -35,8 +35,8 @@ importers:
|
|||
'@types/yargs': ^17.0.13
|
||||
chalk: ^4.1.2
|
||||
decamelize: ^5.0.0
|
||||
dotenv: ^16.0.0
|
||||
eslint: ^8.21.0
|
||||
find-up: ^5.0.0
|
||||
fs-extra: ^10.1.0
|
||||
got: ^11.8.2
|
||||
hpagent: ^1.0.0
|
||||
|
@ -62,7 +62,7 @@ importers:
|
|||
'@silverhand/essentials': 1.2.1
|
||||
chalk: 4.1.2
|
||||
decamelize: 5.0.1
|
||||
find-up: 5.0.0
|
||||
dotenv: 16.0.0
|
||||
fs-extra: 10.1.0
|
||||
got: 11.8.3
|
||||
hpagent: 1.0.0
|
||||
|
@ -7528,6 +7528,7 @@ packages:
|
|||
dependencies:
|
||||
locate-path: 6.0.0
|
||||
path-exists: 4.0.0
|
||||
dev: true
|
||||
|
||||
/flat-cache/3.0.4:
|
||||
resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
|
||||
|
@ -10327,6 +10328,7 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
p-locate: 5.0.0
|
||||
dev: true
|
||||
|
||||
/lodash._reinterpolate/3.0.0:
|
||||
resolution: {integrity: sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=}
|
||||
|
@ -11963,6 +11965,7 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
yocto-queue: 0.1.0
|
||||
dev: true
|
||||
|
||||
/p-locate/2.0.0:
|
||||
resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==}
|
||||
|
@ -11983,6 +11986,7 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
p-limit: 3.1.0
|
||||
dev: true
|
||||
|
||||
/p-map-series/2.1.0:
|
||||
resolution: {integrity: sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q==}
|
||||
|
@ -12247,6 +12251,7 @@ packages:
|
|||
/path-exists/4.0.0:
|
||||
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/path-is-absolute/1.0.1:
|
||||
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
|
||||
|
@ -15810,6 +15815,7 @@ packages:
|
|||
/yocto-queue/0.1.0:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/zod/3.18.0:
|
||||
resolution: {integrity: sha512-gwTm8RfUCe8l9rDwN5r2A17DkAa8Ez4Yl4yXqc5VqeGaXaJahzYYXbTwvhroZi0SNBqTwh/bKm2N0mpCzuw4bA==}
|
||||
|
|
Loading…
Add table
Reference in a new issue