mirror of
https://github.com/logto-io/logto.git
synced 2025-04-07 23:01:25 -05:00
refactor: align console log (#3684)
add a shared ConsoleLog class for unified console logging.
This commit is contained in:
parent
88032402dd
commit
83367569fa
44 changed files with 221 additions and 161 deletions
|
@ -91,6 +91,9 @@
|
|||
},
|
||||
"eslintConfig": {
|
||||
"extends": "@silverhand",
|
||||
"rules": {
|
||||
"no-console": "error"
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"src/package-json.ts"
|
||||
]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { CommandModule } from 'yargs';
|
||||
|
||||
import { log } from '../../utils.js';
|
||||
import { consoleLog } from '../../utils.js';
|
||||
|
||||
import { addConnectors, addOfficialConnectors, inquireInstancePath } from './utils.js';
|
||||
|
||||
|
@ -36,7 +36,7 @@ const add: CommandModule<
|
|||
const instancePath = await inquireInstancePath(path);
|
||||
|
||||
if (cloud && !official) {
|
||||
log.error('--cloud option can only be used with --official option');
|
||||
consoleLog.fatal('--cloud option can only be used with --official option');
|
||||
}
|
||||
|
||||
if (official) {
|
||||
|
@ -44,12 +44,12 @@ const add: CommandModule<
|
|||
await addOfficialConnectors(instancePath, cloud);
|
||||
} else {
|
||||
if (!packageNames?.length) {
|
||||
log.error('No connector name provided');
|
||||
consoleLog.fatal('No connector name provided');
|
||||
}
|
||||
await addConnectors(instancePath, packageNames);
|
||||
}
|
||||
|
||||
log.info('Restart your Logto instance to get the changes reflected.');
|
||||
consoleLog.info('Restart your Logto instance to get the changes reflected.');
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import path from 'node:path';
|
|||
|
||||
import type { CommandModule } from 'yargs';
|
||||
|
||||
import { log } from '../../utils.js';
|
||||
import { consoleLog } from '../../utils.js';
|
||||
|
||||
import { getConnectorDirectory, getLocalConnectorPackages, inquireInstancePath } from './utils.js';
|
||||
|
||||
|
@ -47,12 +47,12 @@ const link: CommandModule<{ path?: string }, { path?: string; cloud: boolean; mo
|
|||
await fs.rm(targetPath, { recursive: true, force: true });
|
||||
await fs.symlink(path.relative(connectorDirectory, packagePath), targetPath);
|
||||
} catch (error) {
|
||||
log.warn(error);
|
||||
consoleLog.warn(error);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
log.succeed('Linked', packagePath);
|
||||
consoleLog.succeed('Linked', packagePath);
|
||||
})
|
||||
);
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import chalk from 'chalk';
|
||||
import type { CommandModule } from 'yargs';
|
||||
|
||||
import type { ConnectorPackage } from '../../utils.js';
|
||||
import { type ConnectorPackage, consoleLog } from '../../utils.js';
|
||||
|
||||
import { getConnectorPackagesFrom, isOfficialConnector } from './utils.js';
|
||||
|
||||
|
@ -10,9 +10,9 @@ const logConnectorNames = (type: string, packages: ConnectorPackage[]) => {
|
|||
return;
|
||||
}
|
||||
|
||||
console.log();
|
||||
console.log(chalk.blue(type));
|
||||
console.log(packages.map(({ name }) => ' ' + name).join('\n'));
|
||||
consoleLog.plain();
|
||||
consoleLog.plain(chalk.blue(type));
|
||||
consoleLog.plain(packages.map(({ name }) => ' ' + name).join('\n'));
|
||||
};
|
||||
|
||||
const list: CommandModule<{ path?: string }, { path?: string }> = {
|
||||
|
|
|
@ -3,7 +3,7 @@ import fs from 'node:fs/promises';
|
|||
import chalk from 'chalk';
|
||||
import type { CommandModule } from 'yargs';
|
||||
|
||||
import { log } from '../../utils.js';
|
||||
import { consoleLog } from '../../utils.js';
|
||||
|
||||
import { getConnectorPackagesFrom } from './utils.js';
|
||||
|
||||
|
@ -19,7 +19,7 @@ const remove: CommandModule<{ path?: string }, { path?: string; packages?: strin
|
|||
}),
|
||||
handler: async ({ path: inputPath, packages: packageNames }) => {
|
||||
if (!packageNames?.length) {
|
||||
log.error('No connector name provided');
|
||||
consoleLog.fatal('No connector name provided');
|
||||
}
|
||||
|
||||
const existingPackages = await getConnectorPackagesFrom(inputPath);
|
||||
|
@ -28,7 +28,7 @@ const remove: CommandModule<{ path?: string }, { path?: string; packages?: strin
|
|||
);
|
||||
|
||||
if (notFoundPackageNames.length > 0) {
|
||||
log.error(
|
||||
consoleLog.fatal(
|
||||
`Cannot remove ${notFoundPackageNames
|
||||
.map((name) => chalk.green(name))
|
||||
.join(', ')}: not found in your Logto instance directory`
|
||||
|
@ -45,8 +45,8 @@ const remove: CommandModule<{ path?: string }, { path?: string; packages?: strin
|
|||
|
||||
return okSymbol;
|
||||
} catch (error: unknown) {
|
||||
log.warn(`Error while removing ${chalk.green(packageInfo?.name)}`);
|
||||
log.warn(error);
|
||||
consoleLog.warn(`Error while removing ${chalk.green(packageInfo?.name)}`);
|
||||
consoleLog.warn(error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ const remove: CommandModule<{ path?: string }, { path?: string; packages?: strin
|
|||
);
|
||||
const errorCount = result.filter((value) => value !== okSymbol).length;
|
||||
|
||||
log.info(`Removed ${result.length - errorCount} connectors`);
|
||||
consoleLog.info(`Removed ${result.length - errorCount} connectors`);
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import tar from 'tar';
|
|||
import { z } from 'zod';
|
||||
|
||||
import { connectorDirectory } from '../../constants.js';
|
||||
import { getConnectorPackagesFromDirectory, isTty, log, oraPromise } from '../../utils.js';
|
||||
import { consoleLog, getConnectorPackagesFromDirectory, isTty, oraPromise } from '../../utils.js';
|
||||
import { defaultPath } from '../install/utils.js';
|
||||
|
||||
const coreDirectory = 'packages/core';
|
||||
|
@ -82,7 +82,7 @@ export const inquireInstancePath = async (initialPath?: string) => {
|
|||
const validated = await validatePath(instancePath);
|
||||
|
||||
if (validated !== true) {
|
||||
log.error(validated);
|
||||
consoleLog.fatal(validated);
|
||||
}
|
||||
|
||||
return instancePath;
|
||||
|
@ -139,7 +139,7 @@ export const addConnectors = async (instancePath: string, packageNames: string[]
|
|||
await fs.mkdir(cwd, { recursive: true });
|
||||
}
|
||||
|
||||
log.info('Fetch connector metadata');
|
||||
consoleLog.info('Fetch connector metadata');
|
||||
|
||||
const limit = pLimit(10);
|
||||
const results = await Promise.all(
|
||||
|
@ -166,14 +166,14 @@ export const addConnectors = async (instancePath: string, packageNames: string[]
|
|||
await tar.extract({ cwd: packageDirectory, file: tarPath, strip: 1 });
|
||||
await fs.unlink(tarPath);
|
||||
|
||||
log.succeed(`Added ${chalk.green(name)} v${version}`);
|
||||
consoleLog.succeed(`Added ${chalk.green(name)} v${version}`);
|
||||
};
|
||||
|
||||
return limit(async () => {
|
||||
try {
|
||||
await pRetry(run, { retries: 2 });
|
||||
} catch (error: unknown) {
|
||||
console.warn(`[${packageName}]`, error);
|
||||
consoleLog.error(`[${packageName}]`, error);
|
||||
|
||||
return packageName;
|
||||
}
|
||||
|
@ -184,14 +184,14 @@ export const addConnectors = async (instancePath: string, packageNames: string[]
|
|||
const errorPackages = results.filter(Boolean);
|
||||
const errorCount = errorPackages.length;
|
||||
|
||||
log.info(
|
||||
consoleLog.info(
|
||||
errorCount
|
||||
? `Finished with ${errorCount} error${conditionalString(errorCount > 1 && 's')}.`
|
||||
: 'Finished'
|
||||
);
|
||||
|
||||
if (errorCount) {
|
||||
log.warn('Failed to add ' + errorPackages.map((name) => chalk.green(name)).join(', '));
|
||||
consoleLog.warn('Failed to add ' + errorPackages.map((name) => chalk.green(name)).join(', '));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -261,7 +261,7 @@ export const addOfficialConnectors = async (
|
|||
prefixText: chalk.blue('[info]'),
|
||||
});
|
||||
|
||||
log.info(`Found ${packages.length} official connectors`);
|
||||
consoleLog.info(`Found ${packages.length} official connectors`);
|
||||
|
||||
await addConnectors(
|
||||
instancePath,
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
getCurrentDatabaseAlterationTimestamp,
|
||||
updateDatabaseTimestamp,
|
||||
} from '../../../queries/system.js';
|
||||
import { log } from '../../../utils.js';
|
||||
import { consoleLog } from '../../../utils.js';
|
||||
|
||||
import type { AlterationFile } from './type.js';
|
||||
import { getAlterationFiles, getTimestampFromFilename } from './utils.js';
|
||||
|
@ -74,17 +74,17 @@ const deployAlteration = async (
|
|||
}
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
console.error(error);
|
||||
consoleLog.error(error);
|
||||
|
||||
await pool.end();
|
||||
log.error(
|
||||
consoleLog.fatal(
|
||||
`Error ocurred during running alteration ${chalk.blue(filename)}.\n\n` +
|
||||
" This alteration didn't change anything since it was in a transaction.\n" +
|
||||
' Try to fix the error and deploy again.'
|
||||
);
|
||||
}
|
||||
|
||||
log.info(`Run alteration ${filename} \`${action}()\` function succeeded`);
|
||||
consoleLog.info(`Run alteration ${filename} \`${action}()\` function succeeded`);
|
||||
};
|
||||
|
||||
const alteration: CommandModule<unknown, { action: string; target?: string }> = {
|
||||
|
@ -106,7 +106,7 @@ const alteration: CommandModule<unknown, { action: string; target?: string }> =
|
|||
const files = await getAlterationFiles();
|
||||
|
||||
for (const file of files) {
|
||||
console.log(file.filename);
|
||||
consoleLog.plain(file.filename);
|
||||
}
|
||||
} else if (action === 'deploy') {
|
||||
const pool = await createPoolFromConfig();
|
||||
|
@ -115,7 +115,7 @@ const alteration: CommandModule<unknown, { action: string; target?: string }> =
|
|||
target
|
||||
);
|
||||
|
||||
log.info(
|
||||
consoleLog.info(
|
||||
`Found ${alterations.length} alteration${conditionalString(
|
||||
alterations.length > 1 && 's'
|
||||
)} to deploy`
|
||||
|
@ -135,7 +135,7 @@ const alteration: CommandModule<unknown, { action: string; target?: string }> =
|
|||
target ?? ''
|
||||
);
|
||||
|
||||
log.info(
|
||||
consoleLog.info(
|
||||
`Found ${alterations.length} alteration${conditionalString(
|
||||
alterations.length > 1 && 's'
|
||||
)} to revert`
|
||||
|
@ -150,7 +150,7 @@ const alteration: CommandModule<unknown, { action: string; target?: string }> =
|
|||
|
||||
await pool.end();
|
||||
} else {
|
||||
log.error('Unsupported action');
|
||||
consoleLog.fatal('Unsupported action');
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@ import chalk from 'chalk';
|
|||
import inquirer from 'inquirer';
|
||||
import { SemVer, compare, eq, gt } from 'semver';
|
||||
|
||||
import { findLastIndex, isTty, log } from '../../../utils.js';
|
||||
import { consoleLog, findLastIndex, isTty } from '../../../utils.js';
|
||||
|
||||
import type { AlterationFile } from './type.js';
|
||||
|
||||
|
@ -43,7 +43,7 @@ export const chooseAlterationsByVersion = async (
|
|||
return [];
|
||||
}
|
||||
|
||||
log.info(`Deploy target ${chalk.green(nextTag)}`);
|
||||
consoleLog.info(`Deploy target ${chalk.green(nextTag)}`);
|
||||
|
||||
return alterations.slice(0, endIndex + 1);
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ export const chooseAlterationsByVersion = async (
|
|||
|
||||
const targetVersion = await getTargetVersion();
|
||||
|
||||
log.info(`Deploy target ${chalk.green(targetVersion.version)}`);
|
||||
consoleLog.info(`Deploy target ${chalk.green(targetVersion.version)}`);
|
||||
|
||||
return alterations.filter(({ filename }) => {
|
||||
const version = getVersionFromFilename(filename);
|
||||
|
@ -104,7 +104,7 @@ export const chooseRevertAlterationsByVersion = async (
|
|||
) => {
|
||||
const semVersion = new SemVer(version);
|
||||
|
||||
log.info(`Revert target ${chalk.green(semVersion.version)}`);
|
||||
consoleLog.info(`Revert target ${chalk.green(semVersion.version)}`);
|
||||
|
||||
return alterations.filter(({ filename }) => {
|
||||
if (getVersionStringFromFilename(filename) === nextTag) {
|
||||
|
|
|
@ -11,7 +11,7 @@ import type { CommandModule } from 'yargs';
|
|||
|
||||
import { createPoolFromConfig } from '../../database.js';
|
||||
import { getRowsByKeys, updateValueByKey } from '../../queries/logto-config.js';
|
||||
import { log } from '../../utils.js';
|
||||
import { consoleLog } from '../../utils.js';
|
||||
|
||||
import { generateOidcCookieKey, generateOidcPrivateKey } from './utils.js';
|
||||
|
||||
|
@ -30,7 +30,7 @@ const validateKeys: ValidateKeysFunction = (keys) => {
|
|||
);
|
||||
|
||||
if (invalidKey) {
|
||||
log.error(
|
||||
consoleLog.fatal(
|
||||
`Invalid config key ${chalk.red(invalidKey)} found, expected one of ${validKeysDisplay}`
|
||||
);
|
||||
}
|
||||
|
@ -47,7 +47,9 @@ const validateRotateKey: ValidateRotateKeyFunction = (key) => {
|
|||
// Using `.includes()` will result a type error
|
||||
// eslint-disable-next-line unicorn/prefer-includes
|
||||
if (!validRotateKeys.some((element) => element === key)) {
|
||||
log.error(`Invalid config key ${chalk.red(key)} found, expected one of ${validKeysDisplay}`);
|
||||
consoleLog.fatal(
|
||||
`Invalid config key ${chalk.red(key)} found, expected one of ${validKeysDisplay}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -80,7 +82,7 @@ const getConfig: CommandModule<unknown, { key: string; keys: string[]; tenantId:
|
|||
const { rows } = await getRowsByKeys(pool, tenantId, queryKeys);
|
||||
await pool.end();
|
||||
|
||||
console.log(
|
||||
consoleLog.plain(
|
||||
queryKeys
|
||||
.map((currentKey) => {
|
||||
const value = rows.find(({ key }) => currentKey === key)?.value;
|
||||
|
@ -125,7 +127,7 @@ const setConfig: CommandModule<unknown, { key: string; value: string; tenantId:
|
|||
await updateValueByKey(pool, tenantId, key, guarded);
|
||||
await pool.end();
|
||||
|
||||
log.info(`Update ${chalk.green(key)} succeeded`);
|
||||
consoleLog.info(`Update ${chalk.green(key)} succeeded`);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -152,7 +154,7 @@ const rotateConfig: CommandModule<unknown, { key: string; tenantId: string }> =
|
|||
const { rows } = await getRowsByKeys(pool, tenantId, [key]);
|
||||
|
||||
if (!rows[0]) {
|
||||
log.warn('No key found, create a new one');
|
||||
consoleLog.warn('No key found, create a new one');
|
||||
}
|
||||
|
||||
const getValue = async () => {
|
||||
|
@ -174,7 +176,7 @@ const rotateConfig: CommandModule<unknown, { key: string; tenantId: string }> =
|
|||
await updateValueByKey(pool, tenantId, key, rotated);
|
||||
await pool.end();
|
||||
|
||||
log.info(`Rotate ${chalk.green(key)} succeeded, now it has ${rotated.length} keys`);
|
||||
consoleLog.info(`Rotate ${chalk.green(key)} succeeded, now it has ${rotated.length} keys`);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -203,14 +205,14 @@ const trimConfig: CommandModule<unknown, { key: string; length: number; tenantId
|
|||
validateRotateKey(key);
|
||||
|
||||
if (length < 1) {
|
||||
log.error('Invalid length provided');
|
||||
consoleLog.fatal('Invalid length provided');
|
||||
}
|
||||
|
||||
const pool = await createPoolFromConfig();
|
||||
const { rows } = await getRowsByKeys(pool, tenantId, [key]);
|
||||
|
||||
if (!rows[0]) {
|
||||
log.warn('No key found, create a new one');
|
||||
consoleLog.warn('No key found, create a new one');
|
||||
}
|
||||
|
||||
const getValue = async () => {
|
||||
|
@ -218,7 +220,9 @@ const trimConfig: CommandModule<unknown, { key: string; length: number; tenantId
|
|||
|
||||
if (value.length - length < 1) {
|
||||
await pool.end();
|
||||
log.error(`You should keep at least one key in the array, current length=${value.length}`);
|
||||
consoleLog.fatal(
|
||||
`You should keep at least one key in the array, current length=${value.length}`
|
||||
);
|
||||
}
|
||||
|
||||
return value.slice(0, -length);
|
||||
|
@ -227,7 +231,7 @@ const trimConfig: CommandModule<unknown, { key: string; length: number; tenantId
|
|||
await updateValueByKey(pool, tenantId, key, trimmed);
|
||||
await pool.end();
|
||||
|
||||
log.info(`Trim ${chalk.green(key)} succeeded, now it has ${trimmed.length} keys`);
|
||||
consoleLog.info(`Trim ${chalk.green(key)} succeeded, now it has ${trimmed.length} keys`);
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { appendPath } from '@silverhand/essentials';
|
|||
import type { CommonQueryMethods } from 'slonik';
|
||||
import { sql } from 'slonik';
|
||||
|
||||
import { log } from '../../../utils.js';
|
||||
import { consoleLog } from '../../../utils.js';
|
||||
|
||||
/**
|
||||
* Append Redirect URIs for the default tenant callback in cloud Admin Console.
|
||||
|
@ -38,5 +38,5 @@ export const appendAdminConsoleRedirectUris = async (pool: CommonQueryMethods) =
|
|||
and tenant_id = ${adminTenantId}
|
||||
`);
|
||||
|
||||
log.succeed('Appended initial Redirect URIs to Admin Console:', redirectUris.map(String));
|
||||
consoleLog.succeed('Appended initial Redirect URIs to Admin Console:', redirectUris.map(String));
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { CommandModule } from 'yargs';
|
|||
|
||||
import { createPoolAndDatabaseIfNeeded } from '../../../database.js';
|
||||
import { doesConfigsTableExist } from '../../../queries/logto-config.js';
|
||||
import { log, oraPromise } from '../../../utils.js';
|
||||
import { consoleLog, oraPromise } from '../../../utils.js';
|
||||
import { getLatestAlterationTimestamp } from '../alteration/index.js';
|
||||
import { getAlterationDirectory } from '../alteration/utils.js';
|
||||
|
||||
|
@ -53,7 +53,7 @@ const seed: CommandModule<Record<string, unknown>, { swe?: boolean; cloud?: bool
|
|||
const pool = await createPoolAndDatabaseIfNeeded();
|
||||
|
||||
if (swe && (await doesConfigsTableExist(pool))) {
|
||||
log.info('Seeding skipped');
|
||||
consoleLog.info('Seeding skipped');
|
||||
await pool.end();
|
||||
|
||||
return;
|
||||
|
@ -62,9 +62,8 @@ const seed: CommandModule<Record<string, unknown>, { swe?: boolean; cloud?: bool
|
|||
try {
|
||||
await seedByPool(pool, cloud);
|
||||
} catch (error: unknown) {
|
||||
console.error(error);
|
||||
console.log();
|
||||
log.warn(
|
||||
consoleLog.error(error);
|
||||
consoleLog.error(
|
||||
'Error ocurred during seeding your database.\n\n' +
|
||||
' Nothing has changed since the seeding process was in a transaction.\n' +
|
||||
' Try to fix the error and seed again.'
|
||||
|
|
|
@ -8,7 +8,7 @@ import type { DatabaseTransactionConnection } from 'slonik';
|
|||
import { z } from 'zod';
|
||||
|
||||
import { getRowsByKeys, updateValueByKey } from '../../../queries/logto-config.js';
|
||||
import { log } from '../../../utils.js';
|
||||
import { consoleLog } from '../../../utils.js';
|
||||
import { generateOidcCookieKey, generateOidcPrivateKey } from '../utils.js';
|
||||
|
||||
const isBase64FormatPrivateKey = (key: string) => !key.includes('-');
|
||||
|
@ -37,7 +37,7 @@ export const seedOidcConfigs = async (pool: DatabaseTransactionConnection, tenan
|
|||
const included = existingKeys.has(key);
|
||||
|
||||
if (included) {
|
||||
log.info(tenantPrefix, `Key ${chalk.green(key)} exists, skipping`);
|
||||
consoleLog.info(tenantPrefix, `Key ${chalk.green(key)} exists, skipping`);
|
||||
}
|
||||
|
||||
return !included;
|
||||
|
@ -49,16 +49,16 @@ export const seedOidcConfigs = async (pool: DatabaseTransactionConnection, tenan
|
|||
const { value, fromEnv } = await oidcConfigReaders[key]();
|
||||
|
||||
if (fromEnv) {
|
||||
log.info(tenantPrefix, `Read config ${chalk.green(key)} from env`);
|
||||
consoleLog.info(tenantPrefix, `Read config ${chalk.green(key)} from env`);
|
||||
} else {
|
||||
log.info(tenantPrefix, `Generated config ${chalk.green(key)}`);
|
||||
consoleLog.info(tenantPrefix, `Generated config ${chalk.green(key)}`);
|
||||
}
|
||||
|
||||
await updateValueByKey(pool, tenantId, key, value);
|
||||
}
|
||||
/* eslint-enable no-await-in-loop */
|
||||
|
||||
log.succeed(tenantPrefix, 'Seed OIDC config');
|
||||
consoleLog.succeed(tenantPrefix, 'Seed OIDC config');
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,7 +25,7 @@ import { raw } from 'slonik-sql-tag-raw';
|
|||
import { insertInto } from '../../../database.js';
|
||||
import { getDatabaseName } from '../../../queries/database.js';
|
||||
import { updateDatabaseTimestamp } from '../../../queries/system.js';
|
||||
import { getPathInModule, log } from '../../../utils.js';
|
||||
import { consoleLog, getPathInModule } from '../../../utils.js';
|
||||
|
||||
import { appendAdminConsoleRedirectUris } from './cloud.js';
|
||||
import { seedOidcConfigs } from './oidc-config.js';
|
||||
|
@ -174,7 +174,7 @@ export const seedTables = async (
|
|||
updateDatabaseTimestamp(connection, latestTimestamp),
|
||||
]);
|
||||
|
||||
log.succeed('Seed data');
|
||||
consoleLog.succeed('Seed data');
|
||||
};
|
||||
|
||||
export const seedCloud = async (connection: DatabaseTransactionConnection) => {
|
||||
|
|
|
@ -6,7 +6,7 @@ import type { CommandModule } from 'yargs';
|
|||
|
||||
import { createPoolFromConfig } from '../../database.js';
|
||||
import { getRowByKey, updateValueByKey } from '../../queries/system.js';
|
||||
import { log } from '../../utils.js';
|
||||
import { consoleLog } from '../../utils.js';
|
||||
|
||||
const validKeysDisplay = chalk.green(systemKeys.join(', '));
|
||||
|
||||
|
@ -19,7 +19,9 @@ 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}`);
|
||||
consoleLog.fatal(
|
||||
`Invalid config key ${chalk.red(key)} found, expected one of ${validKeysDisplay}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -41,7 +43,7 @@ const getConfig: CommandModule<unknown, { key: string }> = {
|
|||
|
||||
const value = row?.value;
|
||||
|
||||
console.log(
|
||||
consoleLog.plain(
|
||||
chalk.magenta(key) +
|
||||
'=' +
|
||||
(value === undefined ? chalk.gray(value) : chalk.green(JSON.stringify(value)))
|
||||
|
@ -73,7 +75,7 @@ const setConfig: CommandModule<unknown, { key: string; value: string }> = {
|
|||
await updateValueByKey(pool, key, guarded);
|
||||
await pool.end();
|
||||
|
||||
log.info(`Update ${chalk.green(key)} succeeded`);
|
||||
consoleLog.info(`Update ${chalk.green(key)} succeeded`);
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import chalk from 'chalk';
|
|||
import type { CommandModule } from 'yargs';
|
||||
|
||||
import { getDatabaseUrlFromConfig } from '../../database.js';
|
||||
import { log } from '../../utils.js';
|
||||
import { consoleLog } from '../../utils.js';
|
||||
|
||||
import {
|
||||
validateNodeVersion,
|
||||
|
@ -39,7 +39,7 @@ const installLogto = async ({ path, skipSeed, downloadUrl, cloud }: InstallArgs)
|
|||
|
||||
// Seed database
|
||||
if (skipSeed) {
|
||||
log.info(
|
||||
consoleLog.info(
|
||||
`Skipped database seeding.\n\n' + ' You can use the ${chalk.green(
|
||||
'db seed'
|
||||
)} command to seed database when ready.\n`
|
||||
|
|
|
@ -15,9 +15,9 @@ import { packageJson } from '../../package-json.js';
|
|||
import {
|
||||
cliConfig,
|
||||
ConfigKey,
|
||||
consoleLog,
|
||||
downloadFile,
|
||||
isTty,
|
||||
log,
|
||||
oraPromise,
|
||||
safeExecSync,
|
||||
} from '../../utils.js';
|
||||
|
@ -32,11 +32,13 @@ export const validateNodeVersion = () => {
|
|||
const current = new semver.SemVer(execSync('node -v', { encoding: 'utf8', stdio: 'pipe' }));
|
||||
|
||||
if (required.every((version) => version.major !== current.major)) {
|
||||
log.error(`Logto requires NodeJS ${requiredVersionString}, but ${current.version} found.`);
|
||||
consoleLog.fatal(
|
||||
`Logto requires NodeJS ${requiredVersionString}, but ${current.version} found.`
|
||||
);
|
||||
}
|
||||
|
||||
if (required.some((version) => version.major === current.major && version.compare(current) > 0)) {
|
||||
log.warn(
|
||||
consoleLog.warn(
|
||||
`Logto is tested under NodeJS ${requiredVersionString}, but version ${current.version} found.`
|
||||
);
|
||||
}
|
||||
|
@ -70,7 +72,7 @@ export const inquireInstancePath = async (initialPath?: string) => {
|
|||
const validated = validatePath(instancePath);
|
||||
|
||||
if (validated !== true) {
|
||||
log.error(validated);
|
||||
consoleLog.fatal(validated);
|
||||
}
|
||||
|
||||
return instancePath;
|
||||
|
@ -96,7 +98,7 @@ export const validateDatabase = async () => {
|
|||
});
|
||||
|
||||
if (hasPostgresUrl === false) {
|
||||
log.error('Logto requires a Postgres instance to run.');
|
||||
consoleLog.fatal('Logto requires a Postgres instance to run.');
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -106,8 +108,8 @@ export const downloadRelease = async (url?: string) => {
|
|||
url ??
|
||||
`https://github.com/logto-io/logto/releases/download/v${packageJson.version}/logto.tar.gz`;
|
||||
|
||||
log.info(`Download Logto from ${from}`);
|
||||
log.info(`Target ${tarFilePath}`);
|
||||
consoleLog.info(`Download Logto from ${from}`);
|
||||
consoleLog.info(`Target ${tarFilePath}`);
|
||||
await downloadFile(from, tarFilePath);
|
||||
|
||||
return tarFilePath;
|
||||
|
@ -119,7 +121,7 @@ export const decompress = async (toPath: string, tarPath: string) => {
|
|||
await fs.mkdir(toPath);
|
||||
await tar.extract({ file: tarPath, cwd: toPath, strip: 1 });
|
||||
} catch (error: unknown) {
|
||||
log.error(error);
|
||||
consoleLog.fatal(error);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -139,14 +141,14 @@ export const seedDatabase = async (instancePath: string, cloud: boolean) => {
|
|||
await seedByPool(pool);
|
||||
await pool.end();
|
||||
} catch (error: unknown) {
|
||||
console.error(error);
|
||||
consoleLog.error(error);
|
||||
|
||||
await oraPromise(fs.rm(instancePath, { force: true, recursive: true }), {
|
||||
text: 'Clean up',
|
||||
prefixText: chalk.blue('[info]'),
|
||||
});
|
||||
|
||||
log.error(
|
||||
consoleLog.fatal(
|
||||
'Error occurred during seeding your Logto database. Nothing has changed since the seeding process was in a transaction.\n\n' +
|
||||
` To skip the database seeding, append ${chalk.green(
|
||||
'--skip-seed'
|
||||
|
@ -158,12 +160,12 @@ export const seedDatabase = async (instancePath: string, cloud: boolean) => {
|
|||
export const createEnv = async (instancePath: string, databaseUrl: string) => {
|
||||
const dotEnvPath = path.resolve(instancePath, '.env');
|
||||
await fs.writeFile(dotEnvPath, `DB_URL=${databaseUrl}`, 'utf8');
|
||||
log.info(`Saved database URL to ${chalk.blue(dotEnvPath)}`);
|
||||
consoleLog.info(`Saved database URL to ${chalk.blue(dotEnvPath)}`);
|
||||
};
|
||||
|
||||
export const logFinale = (instancePath: string) => {
|
||||
const startCommand = `cd ${instancePath} && npm start`;
|
||||
log.info(
|
||||
consoleLog.info(
|
||||
`Use the command below to start Logto. Happy hacking!\n\n ${chalk.green(startCommand)}`
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@ import { isBuiltInLanguageTag as isPhrasesBuiltInLanguageTag } from '@logto/phra
|
|||
import { isBuiltInLanguageTag as isPhrasesUiBuiltInLanguageTag } from '@logto/phrases-ui';
|
||||
import type { CommandModule } from 'yargs';
|
||||
|
||||
import { log } from '../../utils.js';
|
||||
import { consoleLog } from '../../utils.js';
|
||||
import { inquireInstancePath } from '../connector/utils.js';
|
||||
|
||||
import { createFullTranslation } from './utils.js';
|
||||
|
@ -20,18 +20,22 @@ const create: CommandModule<{ path?: string }, { path?: string; 'language-tag':
|
|||
}),
|
||||
handler: async ({ path: inputPath, languageTag }) => {
|
||||
if (!isLanguageTag(languageTag)) {
|
||||
log.error('Invalid language tag. Run `logto translate list-tags` to see available list.');
|
||||
consoleLog.fatal(
|
||||
'Invalid language tag. Run `logto translate list-tags` to see available list.'
|
||||
);
|
||||
}
|
||||
|
||||
const instancePath = await inquireInstancePath(inputPath);
|
||||
|
||||
if (isPhrasesBuiltInLanguageTag(languageTag)) {
|
||||
log.info(languageTag + ' is a built-in tag of phrases, updating untranslated phrases');
|
||||
consoleLog.info(languageTag + ' is a built-in tag of phrases, updating untranslated phrases');
|
||||
}
|
||||
await createFullTranslation({ instancePath, packageName: 'phrases', languageTag });
|
||||
|
||||
if (isPhrasesUiBuiltInLanguageTag(languageTag)) {
|
||||
log.info(languageTag + ' is a built-in tag of phrases-ui, updating untranslated phrases');
|
||||
consoleLog.info(
|
||||
languageTag + ' is a built-in tag of phrases-ui, updating untranslated phrases'
|
||||
);
|
||||
}
|
||||
await createFullTranslation({
|
||||
instancePath,
|
||||
|
|
|
@ -4,13 +4,15 @@ import { isBuiltInLanguageTag as isPhrasesUiBuiltInLanguageTag } from '@logto/ph
|
|||
import chalk from 'chalk';
|
||||
import type { CommandModule } from 'yargs';
|
||||
|
||||
import { consoleLog } from '../../utils.js';
|
||||
|
||||
const listTags: CommandModule<Record<string, unknown>> = {
|
||||
command: ['list-tags', 'list'],
|
||||
describe: 'List all available language tags',
|
||||
|
||||
handler: async () => {
|
||||
for (const tag of Object.keys(languages)) {
|
||||
console.log(
|
||||
consoleLog.plain(
|
||||
...[
|
||||
tag,
|
||||
isPhrasesBuiltInLanguageTag(tag) && chalk.blue('phrases'),
|
||||
|
|
|
@ -6,7 +6,7 @@ import { type Got, got, HTTPError } from 'got';
|
|||
import { HttpsProxyAgent } from 'hpagent';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { getProxy, log } from '../../utils.js';
|
||||
import { consoleLog, getProxy } from '../../utils.js';
|
||||
|
||||
export const createOpenaiApi = () => {
|
||||
const proxy = getProxy();
|
||||
|
@ -47,10 +47,10 @@ export const translate = async (api: Got, languageTag: LanguageTag, filePath: st
|
|||
})
|
||||
.json(),
|
||||
(error) => {
|
||||
log.warn(`Error while translating ${filePath}:`, String(error));
|
||||
consoleLog.warn(`Error while translating ${filePath}:`, String(error));
|
||||
|
||||
if (error instanceof HTTPError) {
|
||||
log.warn(error.response.body);
|
||||
consoleLog.warn(error.response.body);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -62,7 +62,7 @@ export const translate = async (api: Got, languageTag: LanguageTag, filePath: st
|
|||
const guarded = gptResponseGuard.safeParse(response);
|
||||
|
||||
if (!guarded.success) {
|
||||
log.warn(`Error while guarding response for ${filePath}:`, response);
|
||||
consoleLog.warn(`Error while guarding response for ${filePath}:`, response);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -70,13 +70,13 @@ export const translate = async (api: Got, languageTag: LanguageTag, filePath: st
|
|||
const [entity] = guarded.data.choices;
|
||||
|
||||
if (!entity) {
|
||||
log.warn(`No choice found in response when translating ${filePath}`);
|
||||
consoleLog.warn(`No choice found in response when translating ${filePath}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity.finish_reason !== 'stop') {
|
||||
log.warn(`Unexpected finish reason ${entity.finish_reason} for ${filePath}`);
|
||||
consoleLog.warn(`Unexpected finish reason ${entity.finish_reason} for ${filePath}`);
|
||||
}
|
||||
|
||||
const { content } = entity.message;
|
||||
|
@ -88,7 +88,7 @@ export const translate = async (api: Got, languageTag: LanguageTag, filePath: st
|
|||
return content;
|
||||
}
|
||||
|
||||
log.warn('No matching code snippet from response:', content);
|
||||
consoleLog.warn('No matching code snippet from response:', content);
|
||||
}
|
||||
|
||||
return matched;
|
||||
|
|
|
@ -6,7 +6,7 @@ import { type LanguageTag } from '@logto/language-kit';
|
|||
import { conditionalString } from '@silverhand/essentials';
|
||||
import PQueue from 'p-queue';
|
||||
|
||||
import { log } from '../../utils.js';
|
||||
import { consoleLog } from '../../utils.js';
|
||||
|
||||
import { createOpenaiApi, translate } from './openai.js';
|
||||
|
||||
|
@ -33,7 +33,7 @@ export const readBaseLocaleFiles = async (directory: string): Promise<string[]>
|
|||
const stat = await fs.stat(enDirectory);
|
||||
|
||||
if (!stat.isDirectory()) {
|
||||
log.error(directory, 'has no `' + baseLanguage.toLowerCase() + '` directory');
|
||||
consoleLog.fatal(directory, 'has no `' + baseLanguage.toLowerCase() + '` directory');
|
||||
}
|
||||
|
||||
return readLocaleFiles(enDirectory);
|
||||
|
@ -58,7 +58,7 @@ export const createFullTranslation = async ({
|
|||
const files = await readBaseLocaleFiles(directory);
|
||||
|
||||
if (verbose) {
|
||||
log.info(
|
||||
consoleLog.info(
|
||||
'Found ' +
|
||||
String(files.length) +
|
||||
' file' +
|
||||
|
@ -85,7 +85,9 @@ export const createFullTranslation = async ({
|
|||
}
|
||||
|
||||
if (verbose) {
|
||||
log.info(`Target path ${targetPath} exists and has no untranslated mark, skipping`);
|
||||
consoleLog.info(
|
||||
`Target path ${targetPath} exists and has no untranslated mark, skipping`
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -101,16 +103,16 @@ export const createFullTranslation = async ({
|
|||
}
|
||||
|
||||
void queue.add(async () => {
|
||||
log.info(`Translating ${translationPath}`);
|
||||
consoleLog.info(`Translating ${translationPath}`);
|
||||
const result = await translate(openai, languageTag, translationPath);
|
||||
|
||||
if (!result) {
|
||||
log.error(`Unable to translate ${translationPath}`);
|
||||
consoleLog.fatal(`Unable to translate ${translationPath}`);
|
||||
}
|
||||
|
||||
await fs.mkdir(path.parse(targetPath).dir, { recursive: true });
|
||||
await fs.writeFile(targetPath, result);
|
||||
log.succeed(`Translated ${targetPath}`);
|
||||
consoleLog.succeed(`Translated ${targetPath}`);
|
||||
});
|
||||
}
|
||||
/* eslint-enable no-await-in-loop */
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import chalk from 'chalk';
|
||||
|
||||
import { consoleLog } from '../utils.js';
|
||||
|
||||
import { notImplemented } from './consts.js';
|
||||
import { loadConnector } from './loader.js';
|
||||
import type { ConnectorFactory, ConnectorPackage } from './types.js';
|
||||
|
@ -30,10 +32,8 @@ export const loadConnectorFactories = async (
|
|||
};
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
console.log(
|
||||
`${chalk.red(
|
||||
`[load-connector] skip ${chalk.bold(name)} due to error: ${error.message}`
|
||||
)}`
|
||||
consoleLog.error(
|
||||
`[load-connector] skip ${chalk.bold(name)} due to error: ${error.message}`
|
||||
);
|
||||
|
||||
return;
|
||||
|
|
|
@ -4,6 +4,8 @@ import type { AllConnector, CreateConnector } from '@logto/connector-kit';
|
|||
import connectorKitMeta from '@logto/connector-kit/package.json' assert { type: 'json' };
|
||||
import { satisfies } from 'semver';
|
||||
|
||||
import { consoleLog } from '../utils.js';
|
||||
|
||||
import { isKeyInObject } from './utils.js';
|
||||
|
||||
const connectorKit = '@logto/connector-kit';
|
||||
|
@ -21,7 +23,7 @@ const checkConnectorKitVersion = (dependencies: unknown, ignoreVersionMismatch:
|
|||
const message = `Connector requires ${connectorKit} to be ${value}, but the version here is ${currentVersion}.`;
|
||||
|
||||
if (ignoreVersionMismatch) {
|
||||
console.warn(`[warn] ${message}\n\nThis is highly discouraged in production.`);
|
||||
consoleLog.warn(`${message}\n\nThis is highly discouraged in production.`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -52,7 +54,7 @@ export const loadConnector = async (
|
|||
// CJS pattern
|
||||
if (isKeyInObject(loaded.default, 'default')) {
|
||||
if (typeof loaded.default.default === 'function') {
|
||||
console.log(`[warn] Load connector ${connectorPath} in CJS mode`);
|
||||
consoleLog.warn(`Load connector ${connectorPath} in CJS mode`);
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
return loaded.default.default as CreateConnector<AllConnector>;
|
||||
|
|
|
@ -6,7 +6,7 @@ import { DatabaseError } from 'pg-protocol';
|
|||
import { createPool, parseDsn, sql, stringifyDsn } from 'slonik';
|
||||
import { createInterceptors } from 'slonik-interceptor-preset';
|
||||
|
||||
import { ConfigKey, getCliConfigWithPrompt, log } from './utils.js';
|
||||
import { ConfigKey, consoleLog, getCliConfigWithPrompt } from './utils.js';
|
||||
|
||||
export const defaultDatabaseUrl = 'postgresql://localhost:5432/logto';
|
||||
|
||||
|
@ -39,7 +39,7 @@ export const createPoolAndDatabaseIfNeeded = async () => {
|
|||
// Database does not exist, try to create one
|
||||
// https://www.postgresql.org/docs/14/errcodes-appendix.html
|
||||
if (!(error instanceof DatabaseError && error.code === '3D000')) {
|
||||
log.error(error);
|
||||
consoleLog.fatal(error);
|
||||
}
|
||||
|
||||
const databaseUrl = await getDatabaseUrlFromConfig();
|
||||
|
@ -59,7 +59,7 @@ export const createPoolAndDatabaseIfNeeded = async () => {
|
|||
`);
|
||||
await maintenancePool.end();
|
||||
|
||||
log.succeed(`Created database ${databaseName}`);
|
||||
consoleLog.succeed(`Created database ${databaseName}`);
|
||||
|
||||
return createPoolFromConfig();
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import database from './commands/database/index.js';
|
|||
import install from './commands/install/index.js';
|
||||
import translate from './commands/translate/index.js';
|
||||
import { packageJson } from './package-json.js';
|
||||
import { cliConfig, ConfigKey } from './utils.js';
|
||||
import { cliConfig, ConfigKey, consoleLog } from './utils.js';
|
||||
|
||||
void yargs(hideBin(process.argv))
|
||||
.version(false)
|
||||
|
@ -30,7 +30,7 @@ void yargs(hideBin(process.argv))
|
|||
})
|
||||
.middleware(({ version }) => {
|
||||
if (version) {
|
||||
console.log(packageJson.name + ' v' + packageJson.version);
|
||||
consoleLog.plain(packageJson.name + ' v' + packageJson.version);
|
||||
// eslint-disable-next-line unicorn/no-process-exit
|
||||
process.exit(0);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import { readdir, readFile } from 'node:fs/promises';
|
|||
import { createRequire } from 'node:module';
|
||||
import path from 'node:path';
|
||||
|
||||
import { ConsoleLog } from '@logto/shared';
|
||||
import type { Optional } from '@silverhand/essentials';
|
||||
import { conditionalString } from '@silverhand/essentials';
|
||||
import chalk from 'chalk';
|
||||
|
@ -21,29 +22,17 @@ export const safeExecSync = (command: string) => {
|
|||
} catch {}
|
||||
};
|
||||
|
||||
type Log = Readonly<{
|
||||
info: typeof console.log;
|
||||
succeed: typeof console.log;
|
||||
warn: typeof console.log;
|
||||
error: (...args: Parameters<typeof console.log>) => never;
|
||||
}>;
|
||||
|
||||
export const log: Log = Object.freeze({
|
||||
info: (...args) => {
|
||||
console.log(chalk.blue('[info]'), ...args);
|
||||
},
|
||||
succeed: (...args) => {
|
||||
log.info(chalk.green('✔'), ...args);
|
||||
},
|
||||
warn: (...args) => {
|
||||
console.warn(chalk.yellow('[warn]'), ...args);
|
||||
},
|
||||
error: (...args) => {
|
||||
console.error(chalk.red('[error]'), ...args);
|
||||
// eslint-disable-next-line unicorn/no-process-exit
|
||||
process.exit(1);
|
||||
},
|
||||
});
|
||||
// The explicit type annotation is required to make `.fatal()`
|
||||
// works correctly without `return`:
|
||||
//
|
||||
// ```ts
|
||||
// const foo: number | undefined;
|
||||
// consoleLog.fatal();
|
||||
// typeof foo // Still `number | undefined` without explicit type annotation
|
||||
// ```
|
||||
//
|
||||
// For now I have no idea why.
|
||||
export const consoleLog: ConsoleLog = new ConsoleLog();
|
||||
|
||||
export const getProxy = () => {
|
||||
const { HTTPS_PROXY, HTTP_PROXY, https_proxy, http_proxy } = process.env;
|
||||
|
@ -110,7 +99,7 @@ export const oraPromise = async <T>(
|
|||
spinner.fail();
|
||||
|
||||
if (exitOnError) {
|
||||
log.error(error);
|
||||
consoleLog.fatal(error);
|
||||
}
|
||||
|
||||
throw error;
|
||||
|
|
|
@ -118,7 +118,10 @@
|
|||
"node": "^18.12.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "@silverhand"
|
||||
"extends": "@silverhand",
|
||||
"rules": {
|
||||
"no-console": "error"
|
||||
}
|
||||
},
|
||||
"prettier": "@silverhand/eslint-config/.prettierrc"
|
||||
}
|
||||
|
|
|
@ -8,13 +8,14 @@ import type Koa from 'koa';
|
|||
|
||||
import { EnvSet } from '#src/env-set/index.js';
|
||||
import { TenantNotFoundError, tenantPool } from '#src/tenants/index.js';
|
||||
import { consoleLog } from '#src/utils/console.js';
|
||||
import { getTenantId } from '#src/utils/tenant.js';
|
||||
|
||||
const logListening = (type: 'core' | 'admin' = 'core') => {
|
||||
const urlSet = type === 'core' ? EnvSet.values.urlSet : EnvSet.values.adminUrlSet;
|
||||
|
||||
for (const url of urlSet.deduplicated()) {
|
||||
console.log(chalk.bold(chalk.green(`${toTitle(type)} app is running at ${url.toString()}`)));
|
||||
consoleLog.info(chalk.bold(`${toTitle(type)} app is running at ${url.toString()}`));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import { type Optional, conditional, yes } from '@silverhand/essentials';
|
|||
import { createClient, type RedisClientType } from 'redis';
|
||||
|
||||
import { EnvSet } from '#src/env-set/index.js';
|
||||
import { consoleLog } from '#src/utils/console.js';
|
||||
|
||||
import { type CacheStore } from './types.js';
|
||||
|
||||
|
@ -39,16 +40,16 @@ export class RedisCache implements CacheStore {
|
|||
async connect() {
|
||||
if (this.client) {
|
||||
await this.client.connect();
|
||||
console.log('[CACHE] Connected to Redis');
|
||||
consoleLog.info('[CACHE] Connected to Redis');
|
||||
} else {
|
||||
console.warn('[CACHE] No Redis client initialized, skipping');
|
||||
consoleLog.warn('[CACHE] No Redis client initialized, skipping');
|
||||
}
|
||||
}
|
||||
|
||||
async disconnect() {
|
||||
if (this.client) {
|
||||
await this.client.disconnect();
|
||||
console.log('[CACHE] Disconnected from Redis');
|
||||
consoleLog.info('[CACHE] Disconnected from Redis');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ import { getAvailableAlterations } from '@logto/cli/lib/commands/database/altera
|
|||
import chalk from 'chalk';
|
||||
import type { DatabasePool } from 'slonik';
|
||||
|
||||
import { consoleLog } from '#src/utils/console.js';
|
||||
|
||||
export const checkAlterationState = async (pool: DatabasePool) => {
|
||||
const alterations = await getAvailableAlterations(pool);
|
||||
|
||||
|
@ -9,10 +11,8 @@ export const checkAlterationState = async (pool: DatabasePool) => {
|
|||
return;
|
||||
}
|
||||
|
||||
console.error(
|
||||
`${chalk.red(
|
||||
'[error]'
|
||||
)} Found undeployed database alterations, you must deploy them first by ${chalk.green(
|
||||
consoleLog.error(
|
||||
`Found undeployed database alterations, you must deploy them first by ${chalk.green(
|
||||
'npm run alteration deploy'
|
||||
)} command.\n\n` +
|
||||
` See ${chalk.blue(
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import type Provider from 'oidc-provider';
|
||||
|
||||
import { consoleLog } from '#src/utils/console.js';
|
||||
|
||||
import { grantListener, grantRevocationListener } from './grant.js';
|
||||
import { interactionEndedListener, interactionStartedListener } from './interaction.js';
|
||||
|
||||
|
@ -14,6 +16,6 @@ export const addOidcEventListeners = (provider: Provider) => {
|
|||
provider.addListener('interaction.started', interactionStartedListener);
|
||||
provider.addListener('interaction.ended', interactionEndedListener);
|
||||
provider.addListener('server_error', (_, error) => {
|
||||
console.error('OIDC Provider server_error:', error);
|
||||
consoleLog.error('OIDC Provider server_error:', error);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -5,13 +5,14 @@ import Koa from 'koa';
|
|||
|
||||
import { checkAlterationState } from './env-set/check-alteration-state.js';
|
||||
import SystemContext from './tenants/SystemContext.js';
|
||||
import { consoleLog } from './utils/console.js';
|
||||
|
||||
dotenv.config({ path: await findUp('.env', {}) });
|
||||
|
||||
const { appInsights } = await import('@logto/app-insights/node');
|
||||
|
||||
if (await appInsights.setup('logto')) {
|
||||
console.debug('Initialized ApplicationInsights');
|
||||
consoleLog.info('Initialized ApplicationInsights');
|
||||
}
|
||||
|
||||
// Import after env has been configured
|
||||
|
@ -40,8 +41,8 @@ try {
|
|||
const { default: initApp } = await import('./app/init.js');
|
||||
await initApp(app);
|
||||
} catch (error: unknown) {
|
||||
console.error('Error while initializing app:');
|
||||
console.error(error);
|
||||
consoleLog.error('Error while initializing app:');
|
||||
consoleLog.error(error);
|
||||
|
||||
await Promise.all([trySafe(tenantPool.endAll()), trySafe(redisCache.disconnect())]);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import type Provider from 'oidc-provider';
|
|||
|
||||
import { LogEntry } from '#src/middleware/koa-audit-log.js';
|
||||
import type Queries from '#src/tenants/Queries.js';
|
||||
import { consoleLog } from '#src/utils/console.js';
|
||||
|
||||
const parseResponse = ({ statusCode, body }: Response) => ({
|
||||
statusCode,
|
||||
|
@ -78,7 +79,7 @@ export const createHookLibrary = (queries: Queries) => {
|
|||
|
||||
await Promise.all(
|
||||
rows.map(async ({ config: { url, headers, retries }, id }) => {
|
||||
console.log(`\tTriggering hook ${id} due to ${hookEvent} event`);
|
||||
consoleLog.info(`\tTriggering hook ${id} due to ${hookEvent} event`);
|
||||
const json: HookEventPayload = { hookId: id, ...payload };
|
||||
const logEntry = new LogEntry(`TriggerHook.${hookEvent}`);
|
||||
|
||||
|
@ -105,7 +106,7 @@ export const createHookLibrary = (queries: Queries) => {
|
|||
});
|
||||
});
|
||||
|
||||
console.log(
|
||||
consoleLog.info(
|
||||
`\tHook ${id} ${logEntry.payload.result === LogResult.Success ? 'succeeded' : 'failed'}`
|
||||
);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import chalk from 'chalk';
|
|||
import { z, ZodError } from 'zod';
|
||||
|
||||
import type Queries from '#src/tenants/Queries.js';
|
||||
import { consoleLog } from '#src/utils/console.js';
|
||||
|
||||
export const createLogtoConfigLibrary = ({ getRowsByKeys }: Queries['logtoConfigs']) => {
|
||||
const getOidcConfigs = async (): Promise<LogtoOidcConfigType> => {
|
||||
|
@ -15,17 +16,17 @@ export const createLogtoConfigLibrary = ({ getRowsByKeys }: Queries['logtoConfig
|
|||
.parse(Object.fromEntries(rows.map(({ key, value }) => [key, value])));
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof ZodError) {
|
||||
console.error(
|
||||
consoleLog.error(
|
||||
error.issues
|
||||
.map(({ message, path }) => `${message} at ${chalk.green(path.join('.'))}`)
|
||||
.join('\n')
|
||||
);
|
||||
} else {
|
||||
console.error(error);
|
||||
consoleLog.error(error);
|
||||
}
|
||||
|
||||
console.error(
|
||||
`\n${chalk.red('[error]')} Failed to get OIDC configs from your Logto database.` +
|
||||
consoleLog.error(
|
||||
`\nFailed to get OIDC configs from your Logto database.` +
|
||||
' Did you forget to seed your database?\n\n' +
|
||||
` Use ${chalk.green('npm run cli db seed')} to seed your Logto database;\n` +
|
||||
` Or use ${chalk.green('npm run cli db seed oidc')} to seed OIDC configs alone.\n`
|
||||
|
|
|
@ -11,6 +11,7 @@ import { z } from 'zod';
|
|||
import { EnvSet } from '#src/env-set/index.js';
|
||||
import RequestError from '#src/errors/RequestError/index.js';
|
||||
import assertThat from '#src/utils/assert-that.js';
|
||||
import { consoleLog } from '#src/utils/console.js';
|
||||
|
||||
import { getAdminTenantTokenValidationSet } from './utils.js';
|
||||
|
||||
|
@ -57,7 +58,7 @@ export const verifyBearerTokenFromRequest = async (
|
|||
const userId = request.headers['development-user-id']?.toString() ?? developmentUserId;
|
||||
|
||||
if ((!isProduction || isIntegrationTest) && userId) {
|
||||
console.log(`Found dev user ID ${userId}, skip token validation.`);
|
||||
consoleLog.warn(`Found dev user ID ${userId}, skip token validation.`);
|
||||
|
||||
return { sub: userId, clientId: undefined, scopes: [defaultManagementApi.scope.name] };
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { HttpError } from 'koa';
|
|||
|
||||
import { EnvSet } from '#src/env-set/index.js';
|
||||
import RequestError from '#src/errors/RequestError/index.js';
|
||||
import { consoleLog } from '#src/utils/console.js';
|
||||
|
||||
export default function koaErrorHandler<StateT, ContextT, BodyT>(): Middleware<
|
||||
StateT,
|
||||
|
@ -16,7 +17,7 @@ export default function koaErrorHandler<StateT, ContextT, BodyT>(): Middleware<
|
|||
await next();
|
||||
} catch (error: unknown) {
|
||||
if (!EnvSet.values.isProduction) {
|
||||
console.error(error);
|
||||
consoleLog.error(error);
|
||||
}
|
||||
|
||||
// Report all exceptions to ApplicationInsights
|
||||
|
@ -36,7 +37,7 @@ export default function koaErrorHandler<StateT, ContextT, BodyT>(): Middleware<
|
|||
|
||||
// Should log 500 errors in prod anyway
|
||||
if (EnvSet.values.isProduction) {
|
||||
console.error(error);
|
||||
consoleLog.error(error);
|
||||
}
|
||||
|
||||
ctx.status = 500;
|
||||
|
|
|
@ -9,6 +9,7 @@ import { EnvSet } from '#src/env-set/index.js';
|
|||
import RequestError from '#src/errors/RequestError/index.js';
|
||||
import { ResponseBodyError, StatusCodeError } from '#src/errors/ServerError/index.js';
|
||||
import assertThat from '#src/utils/assert-that.js';
|
||||
import { consoleLog } from '#src/utils/console.js';
|
||||
|
||||
/** Configure what and how to guard. */
|
||||
export type GuardConfig<QueryT, BodyT, ParametersT, ResponseT, FilesT> = {
|
||||
|
@ -167,7 +168,7 @@ export default function koaGuard<
|
|||
|
||||
if (!result.success) {
|
||||
if (!EnvSet.values.isProduction) {
|
||||
console.error('Invalid response:', result.error);
|
||||
consoleLog.error('Invalid response:', result.error);
|
||||
}
|
||||
|
||||
throw new ResponseBodyError(result.error);
|
||||
|
|
|
@ -24,6 +24,7 @@ import { isOriginAllowed, validateCustomClientMetadata } from '#src/oidc/utils.j
|
|||
import { routes } from '#src/routes/consts.js';
|
||||
import type Libraries from '#src/tenants/Libraries.js';
|
||||
import type Queries from '#src/tenants/Queries.js';
|
||||
import { consoleLog } from '#src/utils/console.js';
|
||||
|
||||
import { getUserClaimData, getUserClaims } from './scope.js';
|
||||
import { OIDCExtraParametersKey, InteractionMode } from './type.js';
|
||||
|
@ -63,7 +64,7 @@ export default function initOidc(
|
|||
const oidc = new Provider(issuer, {
|
||||
adapter: postgresAdapter.bind(null, envSet, queries),
|
||||
renderError: (_ctx, _out, error) => {
|
||||
console.error(error);
|
||||
consoleLog.error(error);
|
||||
|
||||
throw error;
|
||||
},
|
||||
|
|
|
@ -4,6 +4,8 @@ import { convertToIdentifiers } from '@logto/shared';
|
|||
import type { CommonQueryMethods } from 'slonik';
|
||||
import { sql } from 'slonik';
|
||||
|
||||
import { consoleLog } from '#src/utils/console.js';
|
||||
|
||||
const { table, fields } = convertToIdentifiers(Systems);
|
||||
|
||||
export default class SystemContext {
|
||||
|
@ -23,7 +25,7 @@ export default class SystemContext {
|
|||
const result = storageProviderDataGuard.safeParse(record.value);
|
||||
|
||||
if (!result.success) {
|
||||
console.error('Failed to parse storage provider config:', result.error);
|
||||
consoleLog.error('Failed to parse storage provider config:', result.error);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { LRUCache } from 'lru-cache';
|
|||
|
||||
import { redisCache } from '#src/caches/index.js';
|
||||
import { EnvSet } from '#src/env-set/index.js';
|
||||
import { consoleLog } from '#src/utils/console.js';
|
||||
|
||||
import Tenant from './Tenant.js';
|
||||
|
||||
|
@ -21,7 +22,7 @@ export class TenantPool {
|
|||
return tenant;
|
||||
}
|
||||
|
||||
console.log('Init tenant:', tenantId);
|
||||
consoleLog.info('Init tenant:', tenantId);
|
||||
const newTenant = Tenant.create(tenantId, redisCache);
|
||||
this.cache.set(tenantId, newTenant);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ export const createMockProvider = (
|
|||
interactionDetails?: jest.Mock,
|
||||
Grant?: typeof GrantMock
|
||||
): Provider => {
|
||||
// eslint-disable-next-line no-console
|
||||
const originalWarn = console.warn;
|
||||
const warn = jest.spyOn(console, 'warn').mockImplementation((...args) => {
|
||||
// Disable while creating. Too many warnings.
|
||||
|
|
3
packages/core/src/utils/console.ts
Normal file
3
packages/core/src/utils/console.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { ConsoleLog } from '@logto/shared';
|
||||
|
||||
export const consoleLog: ConsoleLog = new ConsoleLog();
|
|
@ -4,6 +4,8 @@ import { conditionalString } from '@silverhand/essentials';
|
|||
|
||||
import { EnvSet, getTenantEndpoint } from '#src/env-set/index.js';
|
||||
|
||||
import { consoleLog } from './console.js';
|
||||
|
||||
const normalizePathname = (pathname: string) =>
|
||||
pathname + conditionalString(!pathname.endsWith('/') && '/');
|
||||
|
||||
|
@ -57,7 +59,7 @@ export const getTenantId = (url: URL) => {
|
|||
}
|
||||
|
||||
if ((!isProduction || isIntegrationTest) && developmentTenantId) {
|
||||
console.log(`Found dev tenant ID ${developmentTenantId}.`);
|
||||
consoleLog.warn(`Found dev tenant ID ${developmentTenantId}.`);
|
||||
|
||||
return developmentTenantId;
|
||||
}
|
||||
|
|
27
packages/shared/src/node/env/ConsoleLog.ts
vendored
Normal file
27
packages/shared/src/node/env/ConsoleLog.ts
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
import chalk from 'chalk';
|
||||
|
||||
export default class ConsoleLog {
|
||||
plain = console.log;
|
||||
|
||||
info: typeof console.log = (...args) => {
|
||||
console.log(chalk.bold(chalk.blue('info')), ...args);
|
||||
};
|
||||
|
||||
succeed: typeof console.log = (...args) => {
|
||||
this.info(chalk.green('✔'), ...args);
|
||||
};
|
||||
|
||||
warn: typeof console.log = (...args) => {
|
||||
console.warn(chalk.bold(chalk.yellow('warn')), ...args);
|
||||
};
|
||||
|
||||
error: typeof console.log = (...args) => {
|
||||
console.error(chalk.bold(chalk.red('error')), ...args);
|
||||
};
|
||||
|
||||
fatal: (...args: Parameters<typeof console.log>) => never = (...args) => {
|
||||
console.error(chalk.bold(chalk.red('fatal')), ...args);
|
||||
// eslint-disable-next-line unicorn/no-process-exit
|
||||
process.exit(1);
|
||||
};
|
||||
}
|
1
packages/shared/src/node/env/index.ts
vendored
1
packages/shared/src/node/env/index.ts
vendored
|
@ -1,2 +1,3 @@
|
|||
export { default as UrlSet } from './UrlSet.js';
|
||||
export { default as GlobalValues } from './GlobalValues.js';
|
||||
export { default as ConsoleLog } from './ConsoleLog.js';
|
||||
|
|
Loading…
Add table
Reference in a new issue