Skip to content

Commit

Permalink
feat: allow to set sourceRegistry by CNPMCORE_CONFIG_SOURCE_REGISTRY
Browse files Browse the repository at this point in the history
  • Loading branch information
fengmk2 committed Feb 8, 2025
1 parent aba2b36 commit 3ca06cf
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 71 deletions.
37 changes: 37 additions & 0 deletions app/common/EnvUtil.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export type ValueType = 'string' | 'boolean' | 'number';

export function env(key: string, valueType: ValueType, defaultValue: string): string;
export function env(key: string, valueType: ValueType, defaultValue: boolean): boolean;
export function env(key: string, valueType: ValueType, defaultValue: number): number;
export function env(key: string, valueType: ValueType, defaultValue: string | boolean | number): string | boolean | number {
const value = process.env[key];
if (value === undefined) {
return defaultValue;
}

if (valueType === 'string') {
return value;
}

if (valueType === 'boolean') {
let booleanValue = false;
if (value === 'true' || value === '1') {
booleanValue = true;
} else if (value === 'false' || value === '0') {
booleanValue = false;
} else {
throw new TypeError(`Invalid boolean value: ${value} on process.env.${key}`);
}
return booleanValue;
}

if (valueType === 'number') {
const numberValue = Number(value);
if (isNaN(numberValue)) {
throw new TypeError(`Invalid number value: ${value} on process.env.${key}`);
}
return numberValue;
}

throw new TypeError(`Invalid value type: ${valueType}`);
}
114 changes: 59 additions & 55 deletions config/config.default.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { strict as assert } from 'node:assert';
import { randomUUID } from 'node:crypto';
import { join } from 'node:path';
import { EggAppConfig, PowerPartial } from 'egg';
import { EggAppConfig, PowerPartial, Context } from 'egg';
import OSSClient from 'oss-cnpm';
import { patchAjv } from '../app/port/typebox';
import { ChangesStreamMode, NOT_IMPLEMENTED_PATH, SyncDeleteMode, SyncMode } from '../app/common/constants';
import { env } from '../app/common/EnvUtil';
import type { CnpmcoreConfig } from '../app/port/config';
import { database } from './database';

export const cnpmcoreConfig: CnpmcoreConfig = {
name: 'cnpm',
hookEnable: false,
hooksLimit: 20,
sourceRegistry: 'https://registry.npmjs.org',
sourceRegistryIsCNpm: false,
sourceRegistry: env('CNPMCORE_CONFIG_SOURCE_REGISTRY', 'string', 'https://registry.npmjs.org'),
sourceRegistryIsCNpm: env('CNPMCORE_CONFIG_SOURCE_REGISTRY_IS_CNPM', 'boolean', false),
syncUpstreamFirst: false,
sourceRegistrySyncTimeout: 180000,
taskQueueHighWaterSize: 100,
Expand All @@ -33,7 +34,7 @@ export const cnpmcoreConfig: CnpmcoreConfig = {
checkChangesStreamInterval: 500,
changesStreamRegistry: 'https://replicate.npmjs.com',
changesStreamRegistryMode: ChangesStreamMode.streaming,
registry: process.env.CNPMCORE_CONFIG_REGISTRY || 'http://localhost:7001',
registry: env('CNPMCORE_CONFIG_REGISTRY', 'string', 'http://localhost:7001'),
alwaysAuth: false,
allowScopes: [
'@cnpm',
Expand All @@ -45,7 +46,7 @@ export const cnpmcoreConfig: CnpmcoreConfig = {
admins: {
cnpmcore_admin: '[email protected]',
},
enableWebAuthn: !!process.env.CNPMCORE_CONFIG_ENABLE_WEB_AUTHN,
enableWebAuthn: env('CNPMCORE_CONFIG_ENABLE_WEB_AUTHN', 'boolean', false),
enableCDN: false,
cdnCacheControlHeader: 'public, max-age=300',
cdnVaryHeader: 'Accept, Accept-Encoding',
Expand All @@ -57,7 +58,7 @@ export const cnpmcoreConfig: CnpmcoreConfig = {
enableSyncUnpkgFiles: true,
enableSyncUnpkgFilesWhiteList: false,
strictSyncSpecivicVersion: false,
enableElasticsearch: !!process.env.CNPMCORE_CONFIG_ENABLE_ES,
enableElasticsearch: env('CNPMCORE_CONFIG_ENABLE_ES', 'boolean', false),
elasticsearchIndex: 'cnpmcore_packages',
strictValidateTarballPkg: false,
strictValidatePackageDeps: false,
Expand All @@ -69,11 +70,11 @@ export const cnpmcoreConfig: CnpmcoreConfig = {
export default (appInfo: EggAppConfig) => {
const config = {} as PowerPartial<EggAppConfig>;

config.keys = process.env.CNPMCORE_EGG_KEYS || randomUUID();
config.keys = env('CNPMCORE_EGG_KEYS', 'string', randomUUID());
config.cnpmcore = cnpmcoreConfig;

// override config from framework / plugin
config.dataDir = process.env.CNPMCORE_DATA_DIR || join(appInfo.root, '.cnpmcore');
config.dataDir = env('CNPMCORE_DATA_DIR', 'string', join(appInfo.root, '.cnpmcore'));
config.orm = {
...database,
database: database.name ?? 'cnpmcore',
Expand All @@ -90,10 +91,10 @@ export default (appInfo: EggAppConfig) => {

config.redis = {
client: {
port: Number(process.env.CNPMCORE_REDIS_PORT || 6379),
host: process.env.CNPMCORE_REDIS_HOST || '127.0.0.1',
password: process.env.CNPMCORE_REDIS_PASSWORD || '',
db: Number(process.env.CNPMCORE_REDIS_DB || 0),
port: env('CNPMCORE_REDIS_PORT', 'number', 6379),
host: env('CNPMCORE_REDIS_HOST', 'string', '127.0.0.1'),
password: env('CNPMCORE_REDIS_PASSWORD', 'string', ''),
db: env('CNPMCORE_REDIS_DB', 'number', 0),
},
};

Expand All @@ -105,7 +106,7 @@ export default (appInfo: EggAppConfig) => {

config.cors = {
// allow all domains
origin: (ctx): string => {
origin: (ctx: Context): string => {
return ctx.get('Origin');
},
credentials: true,
Expand All @@ -115,59 +116,61 @@ export default (appInfo: EggAppConfig) => {

config.nfs = {
client: null,
dir: process.env.CNPMCORE_NFS_DIR || join(config.dataDir, 'nfs'),
dir: env('CNPMCORE_NFS_DIR', 'string', join(config.dataDir, 'nfs')),
};
/* c8 ignore next 17 */
// enable oss nfs store by env values
if (process.env.CNPMCORE_NFS_TYPE === 'oss') {
assert(process.env.CNPMCORE_NFS_OSS_BUCKET, 'require env CNPMCORE_NFS_OSS_BUCKET');
assert(process.env.CNPMCORE_NFS_OSS_ENDPOINT, 'require env CNPMCORE_NFS_OSS_ENDPOINT');
assert(process.env.CNPMCORE_NFS_OSS_ID, 'require env CNPMCORE_NFS_OSS_ID');
assert(process.env.CNPMCORE_NFS_OSS_SECRET, 'require env CNPMCORE_NFS_OSS_SECRET');
config.nfs.client = new OSSClient({
cdnBaseUrl: process.env.CNPMCORE_NFS_OSS_CDN,
endpoint: process.env.CNPMCORE_NFS_OSS_ENDPOINT,
bucket: process.env.CNPMCORE_NFS_OSS_BUCKET,
accessKeyId: process.env.CNPMCORE_NFS_OSS_ID,
accessKeySecret: process.env.CNPMCORE_NFS_OSS_SECRET,
const nfsType = env('CNPMCORE_NFS_TYPE', 'string', '');
if (nfsType === 'oss') {
const ossConfig = {
cdnBaseUrl: env('CNPMCORE_NFS_OSS_CDN', 'string', ''),
endpoint: env('CNPMCORE_NFS_OSS_ENDPOINT', 'string', ''),
bucket: env('CNPMCORE_NFS_OSS_BUCKET', 'string', ''),
accessKeyId: env('CNPMCORE_NFS_OSS_ID', 'string', ''),
accessKeySecret: env('CNPMCORE_NFS_OSS_SECRET', 'string', ''),
defaultHeaders: {
'Cache-Control': 'max-age=0, s-maxage=60',
},
});
} else if (process.env.CNPMCORE_NFS_TYPE === 's3') {
assert(process.env.CNPMCORE_NFS_S3_CLIENT_ENDPOINT, 'require env CNPMCORE_NFS_S3_CLIENT_ENDPOINT');
assert(process.env.CNPMCORE_NFS_S3_CLIENT_ID, 'require env CNPMCORE_NFS_S3_CLIENT_ID');
assert(process.env.CNPMCORE_NFS_S3_CLIENT_SECRET, 'require env CNPMCORE_NFS_S3_CLIENT_SECRET');
assert(process.env.CNPMCORE_NFS_S3_CLIENT_BUCKET, 'require env CNPMCORE_NFS_S3_CLIENT_BUCKET');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const S3Client = require('s3-cnpmcore');
config.nfs.client = new S3Client({
region: process.env.CNPMCORE_NFS_S3_CLIENT_REGION || 'default',
endpoint: process.env.CNPMCORE_NFS_S3_CLIENT_ENDPOINT,
};
assert(ossConfig.cdnBaseUrl, 'require env CNPMCORE_NFS_OSS_BUCKET');
assert(ossConfig.endpoint, 'require env CNPMCORE_NFS_OSS_ENDPOINT');
assert(ossConfig.accessKeyId, 'require env CNPMCORE_NFS_OSS_ID');
assert(ossConfig.accessKeySecret, 'require env CNPMCORE_NFS_OSS_SECRET');
config.nfs.client = new OSSClient(ossConfig);
} else if (nfsType === 's3') {
const s3Config = {
region: env('CNPMCORE_NFS_S3_CLIENT_REGION', 'string', 'default'),
endpoint: env('CNPMCORE_NFS_S3_CLIENT_ENDPOINT', 'string', ''),
credentials: {
accessKeyId: process.env.CNPMCORE_NFS_S3_CLIENT_ID,
secretAccessKey: process.env.CNPMCORE_NFS_S3_CLIENT_SECRET,
accessKeyId: env('CNPMCORE_NFS_S3_CLIENT_ID', 'string', ''),
secretAccessKey: env('CNPMCORE_NFS_S3_CLIENT_SECRET', 'string', ''),
},
bucket: process.env.CNPMCORE_NFS_S3_CLIENT_BUCKET,
forcePathStyle: !!process.env.CNPMCORE_NFS_S3_CLIENT_FORCE_PATH_STYLE,
disableURL: !!process.env.CNPMCORE_NFS_S3_CLIENT_DISABLE_URL,
});
bucket: env('CNPMCORE_NFS_S3_CLIENT_BUCKET', 'string', ''),
forcePathStyle: env('CNPMCORE_NFS_S3_CLIENT_FORCE_PATH_STYLE', 'boolean', false),
disableURL: env('CNPMCORE_NFS_S3_CLIENT_DISABLE_URL', 'boolean', false),
};
assert(s3Config.endpoint, 'require env CNPMCORE_NFS_S3_CLIENT_ENDPOINT');
assert(s3Config.credentials.accessKeyId, 'require env CNPMCORE_NFS_S3_CLIENT_ID');
assert(s3Config.credentials.secretAccessKey, 'require env CNPMCORE_NFS_S3_CLIENT_SECRET');
assert(s3Config.bucket, 'require env CNPMCORE_NFS_S3_CLIENT_BUCKET');
// TODO(@fengmk2): should change to use import to support esm
// eslint-disable-next-line @typescript-eslint/no-var-requires
const S3Client = require('s3-cnpmcore');
config.nfs.client = new S3Client(s3Config);
}

config.logger = {
enablePerformanceTimer: true,
enableFastContextLogger: true,
appLogName: process.env.CNPMCORE_APP_LOG_NAME || `${appInfo.name}-web.log`,
coreLogName: process.env.CNPMCORE_CORE_LOG_NAME || 'egg-web.log',
agentLogName: process.env.CNPMCORE_AGENT_LOG_NAME || 'egg-agent.log',
errorLogName: process.env.CNPMCORE_ERROR_LOG_NAME || 'common-error.log',
outputJSON: Boolean(process.env.CNPMCORE_LOG_JSON_OUTPUT || false),
appLogName: env('CNPMCORE_APP_LOG_NAME', 'string', `${appInfo.name}-web.log`),
coreLogName: env('CNPMCORE_CORE_LOG_NAME', 'string', 'egg-web.log'),
agentLogName: env('CNPMCORE_AGENT_LOG_NAME', 'string', 'egg-agent.log'),
errorLogName: env('CNPMCORE_ERROR_LOG_NAME', 'string', 'common-error.log'),
outputJSON: env('CNPMCORE_LOG_JSON_OUTPUT', 'boolean', false),
};
if (process.env.CNPMCORE_LOG_DIR) {
config.logger.dir = process.env.CNPMCORE_LOG_DIR;
}
if (process.env.CNPMCORE_LOG_JSON_OUTPUT) {
config.logger.outputJSON = Boolean(process.env.CNPMCORE_LOG_JSON_OUTPUT);
const logDir = env('CNPMCORE_LOG_DIR', 'string', '');
if (logDir) {
config.logger.dir = logDir;
}

config.logrotator = {
Expand Down Expand Up @@ -207,14 +210,15 @@ export default (appInfo: EggAppConfig) => {
if (config.cnpmcore.enableElasticsearch) {
config.elasticsearch = {
client: {
node: process.env.CNPMCORE_CONFIG_ES_CLIENT_NODE,
node: env('CNPMCORE_CONFIG_ES_CLIENT_NODE', 'string', ''),
auth: {
username: process.env.CNPMCORE_CONFIG_ES_CLIENT_AUTH_USERNAME as string,
password: process.env.CNPMCORE_CONFIG_ES_CLIENT_AUTH_PASSWORD as string,
username: env('CNPMCORE_CONFIG_ES_CLIENT_AUTH_USERNAME', 'string', ''),
password: env('CNPMCORE_CONFIG_ES_CLIENT_AUTH_PASSWORD', 'string', ''),
},
},
};
}

return config;
};

34 changes: 18 additions & 16 deletions config/database.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,35 @@
import { env } from "../app/common/EnvUtil";

Check failure on line 1 in config/database.ts

View workflow job for this annotation

GitHub Actions / test-postgresql-fs-nfs (18.20.0, ubuntu-latest)

Strings must use singlequote

Check failure on line 1 in config/database.ts

View workflow job for this annotation

GitHub Actions / test-mysql57-fs-nfs (18.20.0, ubuntu-latest)

Strings must use singlequote

Check failure on line 1 in config/database.ts

View workflow job for this annotation

GitHub Actions / test-postgresql-fs-nfs (18, ubuntu-latest)

Strings must use singlequote

Check failure on line 1 in config/database.ts

View workflow job for this annotation

GitHub Actions / test-mysql57-fs-nfs (18, ubuntu-latest)

Strings must use singlequote

Check failure on line 1 in config/database.ts

View workflow job for this annotation

GitHub Actions / test-postgresql-fs-nfs (20, ubuntu-latest)

Strings must use singlequote

Check failure on line 1 in config/database.ts

View workflow job for this annotation

GitHub Actions / test-mysql57-fs-nfs (20, ubuntu-latest)

Strings must use singlequote

Check failure on line 1 in config/database.ts

View workflow job for this annotation

GitHub Actions / test-mysql57-fs-nfs (22, ubuntu-latest)

Strings must use singlequote

Check failure on line 1 in config/database.ts

View workflow job for this annotation

GitHub Actions / test-postgresql-fs-nfs (22, ubuntu-latest)

Strings must use singlequote

export enum DATABASE_TYPE {
MySQL = 'MySQL',
PostgreSQL = 'PostgreSQL',
SQLite = 'SQLite',
}

const dbType = process.env.CNPMCORE_DATABASE_TYPE ?? DATABASE_TYPE.MySQL;
let dbName = process.env.CNPMCORE_DATABASE_NAME;
let dbHost = process.env.CNPMCORE_DATABASE_HOST;
let dbPort = process.env.CNPMCORE_DATABASE_PORT;
let dbUser = process.env.CNPMCORE_DATABASE_USER;
let dbPassword = process.env.CNPMCORE_DATABASE_PASSWORD;
const dbType = env('CNPMCORE_DATABASE_TYPE', 'string', DATABASE_TYPE.MySQL);
let dbName = env('CNPMCORE_DATABASE_NAME', 'string', '');
let dbHost = env('CNPMCORE_DATABASE_HOST', 'string', '');
let dbPort = env('CNPMCORE_DATABASE_PORT', 'number', 0);
let dbUser = env('CNPMCORE_DATABASE_USER', 'string', '');
let dbPassword = env('CNPMCORE_DATABASE_PASSWORD', 'string', '');
let dialect = 'mysql';
let dbClient = 'mysql2';
if (dbType === DATABASE_TYPE.MySQL) {
// Compatible mysql configurations
dbName = dbName ?? process.env.CNPMCORE_MYSQL_DATABASE ?? process.env.MYSQL_DATABASE;
dbHost = dbHost ?? process.env.CNPMCORE_MYSQL_HOST ?? process.env.MYSQL_HOST ?? '127.0.0.1';
dbPort = dbPort ?? process.env.CNPMCORE_MYSQL_PORT ?? process.env.MYSQL_PORT ?? '3306';
dbUser = dbUser ?? process.env.CNPMCORE_MYSQL_USER ?? process.env.MYSQL_USER ?? 'root';
dbPassword = dbPassword ?? process.env.CNPMCORE_MYSQL_PASSWORD ?? process.env.MYSQL_PASSWORD;
dbName = dbName || env('CNPMCORE_MYSQL_DATABASE', 'string', '') || env('MYSQL_DATABASE', 'string', '') || '';
dbHost = dbHost || env('CNPMCORE_MYSQL_HOST', 'string', '') || env('MYSQL_HOST', 'string', '') || '127.0.0.1';
dbPort = dbPort || env('CNPMCORE_MYSQL_PORT', 'number', 0) || env('MYSQL_PORT', 'number', 0) || 3306;
dbUser = dbUser || env('CNPMCORE_MYSQL_USER', 'string', '') || env('MYSQL_USER', 'string', '') || 'root';

Check failure

Code scanning / CodeQL

Hard-coded credentials Critical

The hard-coded value "root" is used as
user name
.
dbPassword = dbPassword || env('CNPMCORE_MYSQL_PASSWORD', 'string', '') || env('MYSQL_PASSWORD', 'string', '');
} else if (dbType === DATABASE_TYPE.PostgreSQL) {
dbClient = 'pg';
dialect = 'postgres';
dbHost = dbHost ?? process.env.CNPMCORE_POSTGRES_HOST ?? process.env.POSTGRES_HOST;
dbPort = dbPort ?? process.env.CNPMCORE_POSTGRES_PORT ?? process.env.POSTGRES_PORT ?? '5432';
dbUser = dbUser ?? process.env.CNPMCORE_POSTGRES_USER ?? process.env.POSTGRES_USER;
dbPassword = dbPassword ?? process.env.CNPMCORE_POSTGRES_PASSWORD ?? process.env.POSTGRES_PASSWORD;
dbHost = dbHost || env('CNPMCORE_POSTGRES_HOST', 'string', '') || env('POSTGRES_HOST', 'string', '') || '';
dbPort = dbPort || env('CNPMCORE_POSTGRES_PORT', 'number', 0) || env('POSTGRES_PORT', 'number', 0) || 5432;
dbUser = dbUser || env('CNPMCORE_POSTGRES_USER', 'string', '') || env('POSTGRES_USER', 'string', '') || '';
dbPassword = dbPassword || env('CNPMCORE_POSTGRES_PASSWORD', 'string', '') || env('POSTGRES_PASSWORD', 'string', '');
} else if (dbType === DATABASE_TYPE.SQLite) {
// TODO
// TODO: Implement SQLite
dbClient = 'sqlite';
dialect = 'sqlite';
}
Expand Down

0 comments on commit 3ca06cf

Please sign in to comment.