From 313cb0658bd58316ff57c3419d936c071f703f9b Mon Sep 17 00:00:00 2001 From: Adrien de Peretti Date: Thu, 21 Apr 2022 12:49:56 +0200 Subject: [PATCH] feat(medusa): Improve config loading (#1290) --- .../api/factories/simple-product-factory.ts | 10 +-- integration-tests/api/medusa-config.js | 2 + integration-tests/jest.config.js | 1 + integration-tests/plugins/medusa-config.js | 2 + packages/medusa/package.json | 1 - .../api/routes/admin/auth/create-session.ts | 8 +-- .../api/routes/store/auth/create-session.ts | 17 +++--- .../routes/store/customers/create-customer.ts | 6 +- packages/medusa/src/config/index.js | 44 ------------- packages/medusa/src/helpers/test-request.js | 23 ++++--- packages/medusa/src/loaders/api.ts | 13 ++-- packages/medusa/src/loaders/config.ts | 61 +++++++++++++++++++ packages/medusa/src/loaders/database.ts | 11 +--- packages/medusa/src/loaders/express.ts | 12 ++-- packages/medusa/src/loaders/index.ts | 26 ++++---- packages/medusa/src/loaders/passport.ts | 8 +-- packages/medusa/src/loaders/plugins.ts | 40 ++++++------ packages/medusa/src/loaders/redis.ts | 8 +-- packages/medusa/src/loaders/services.ts | 5 +- packages/medusa/src/loaders/strategies.ts | 2 +- .../src/services/__tests__/event-bus.js | 4 +- .../medusa/src/services/__tests__/invite.js | 8 +++ packages/medusa/src/services/invite.ts | 54 +++++++++------- packages/medusa/src/types/global.ts | 28 +++++++++ packages/medusa/yarn.lock | 6 -- 25 files changed, 225 insertions(+), 175 deletions(-) delete mode 100644 packages/medusa/src/config/index.js create mode 100644 packages/medusa/src/loaders/config.ts diff --git a/integration-tests/api/factories/simple-product-factory.ts b/integration-tests/api/factories/simple-product-factory.ts index fa89ab9242..d01ba8195f 100644 --- a/integration-tests/api/factories/simple-product-factory.ts +++ b/integration-tests/api/factories/simple-product-factory.ts @@ -28,7 +28,7 @@ export const simpleProductFactory = async ( connection: Connection, data: ProductFactoryData = {}, seed?: number -): Promise => { +): Promise => { if (typeof seed !== "undefined") { faker.seed(seed) } @@ -50,9 +50,9 @@ export const simpleProductFactory = async ( status: data.status, is_giftcard: data.is_giftcard || false, discountable: !data.is_giftcard, - tags: [], - profile_id: data.is_giftcard ? gcProfile.id : defaultProfile.id, - } + tags: [] as ProductTag[], + profile_id: data.is_giftcard ? gcProfile?.id : defaultProfile?.id, + } as Product if (typeof data.tags !== "undefined") { for (let i = 0; i < data.tags.length; i++) { @@ -112,5 +112,5 @@ export const simpleProductFactory = async ( await simpleProductVariantFactory(connection, factoryData) } - return await manager.findOne(Product, { id: prodId }, { relations: ["tags"] }) + return manager.findOne(Product, { id: prodId }, { relations: ["tags"] }) } diff --git a/integration-tests/api/medusa-config.js b/integration-tests/api/medusa-config.js index f4b027d81b..05a0bc3bce 100644 --- a/integration-tests/api/medusa-config.js +++ b/integration-tests/api/medusa-config.js @@ -8,5 +8,7 @@ module.exports = { // redis_url: REDIS_URL, database_url: `postgres://${DB_USERNAME}:${DB_PASSWORD}@localhost/medusa-integration-${workerId}`, database_type: "postgres", + jwt_secret: 'test', + cookie_secret: 'test' }, } diff --git a/integration-tests/jest.config.js b/integration-tests/jest.config.js index aac35ad400..9c43ae0e76 100644 --- a/integration-tests/jest.config.js +++ b/integration-tests/jest.config.js @@ -6,6 +6,7 @@ const pkgs = glob module.exports = { testEnvironment: `node`, + testTimeout: 10000, globalSetup: "/integration-tests/globalSetup.js", globalTeardown: "/integration-tests/globalTeardown.js", rootDir: `../`, diff --git a/integration-tests/plugins/medusa-config.js b/integration-tests/plugins/medusa-config.js index c4bab70d40..91896bce98 100644 --- a/integration-tests/plugins/medusa-config.js +++ b/integration-tests/plugins/medusa-config.js @@ -24,5 +24,7 @@ module.exports = { // redis_url: REDIS_URL, database_url: `postgres://${DB_USERNAME}:${DB_PASSWORD}@localhost/medusa-integration-${workerId}`, database_type: "postgres", + jwt_secret: 'test', + cookie_secret: 'test' }, } diff --git a/packages/medusa/package.json b/packages/medusa/package.json index 2e3fc0e675..5635a93bef 100644 --- a/packages/medusa/package.json +++ b/packages/medusa/package.json @@ -62,7 +62,6 @@ "core-js": "^3.6.5", "cors": "^2.8.5", "cross-spawn": "^7.0.3", - "dotenv": "^8.2.0", "express": "^4.17.1", "express-session": "^1.17.1", "fs-exists-cached": "^1.0.0", diff --git a/packages/medusa/src/api/routes/admin/auth/create-session.ts b/packages/medusa/src/api/routes/admin/auth/create-session.ts index 4824ba292a..f6d5a9bd91 100644 --- a/packages/medusa/src/api/routes/admin/auth/create-session.ts +++ b/packages/medusa/src/api/routes/admin/auth/create-session.ts @@ -1,6 +1,5 @@ import _ from "lodash" import jwt from "jsonwebtoken" -import config from "../../../../config" import { validator } from "../../../../utils/validator" import { IsEmail, IsNotEmpty, IsString } from "class-validator" import AuthService from "../../../../services/auth" @@ -28,10 +27,11 @@ import { MedusaError } from "medusa-core-utils" * $ref: "#/components/schemas/user" */ export default async (req, res) => { - if (!config.jwtSecret) { + const { projectConfig: { jwt_secret } } = req.scope.resolve('configModule') + if (!jwt_secret) { throw new MedusaError( MedusaError.Types.NOT_FOUND, - "Please configure jwtSecret in your environment" + "Please configure jwt_secret in your environment" ) } const validated = await validator(AdminPostAuthReq, req.body) @@ -44,7 +44,7 @@ export default async (req, res) => { if (result.success && result.user) { // Add JWT to cookie - req.session.jwt = jwt.sign({ userId: result.user.id }, config.jwtSecret, { + req.session.jwt = jwt.sign({ userId: result.user.id }, jwt_secret, { expiresIn: "24h", }) diff --git a/packages/medusa/src/api/routes/store/auth/create-session.ts b/packages/medusa/src/api/routes/store/auth/create-session.ts index b6563ef68e..4421759806 100644 --- a/packages/medusa/src/api/routes/store/auth/create-session.ts +++ b/packages/medusa/src/api/routes/store/auth/create-session.ts @@ -1,6 +1,5 @@ import { IsEmail, IsNotEmpty } from "class-validator" import jwt from "jsonwebtoken" -import config from "../../../../config" import AuthService from "../../../../services/auth" import CustomerService from "../../../../services/customer" import { validator } from "../../../../utils/validator" @@ -29,8 +28,6 @@ export default async (req, res) => { const validated = await validator(StorePostAuthReq, req.body) const authService: AuthService = req.scope.resolve("authService") - const customerService: CustomerService = req.scope.resolve("customerService") - const result = await authService.authenticateCustomer( validated.email, validated.password @@ -41,14 +38,14 @@ export default async (req, res) => { } // Add JWT to cookie - req.session.jwt = jwt.sign( - { customer_id: result.customer?.id }, - config.jwtSecret as string, - { - expiresIn: "30d", - } - ) + const { + projectConfig: { jwt_secret }, + } = req.scope.resolve("configModule") + req.session.jwt = jwt.sign({ customer_id: result.customer?.id }, jwt_secret!, { + expiresIn: "30d", + }) + const customerService: CustomerService = req.scope.resolve("customerService") const customer = await customerService.retrieve(result.customer?.id || "", { relations: ["orders", "orders.items"], }) diff --git a/packages/medusa/src/api/routes/store/customers/create-customer.ts b/packages/medusa/src/api/routes/store/customers/create-customer.ts index 3856180de8..4ef56fa9bf 100644 --- a/packages/medusa/src/api/routes/store/customers/create-customer.ts +++ b/packages/medusa/src/api/routes/store/customers/create-customer.ts @@ -2,7 +2,6 @@ import { IsEmail, IsOptional, IsString } from "class-validator" import jwt from "jsonwebtoken" import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "." import { Customer } from "../../../.." -import config from "../../../../config" import CustomerService from "../../../../services/customer" import { validator } from "../../../../utils/validator" @@ -36,7 +35,10 @@ export default async (req, res) => { let customer: Customer = await customerService.create(validated) // Add JWT to cookie - req.session.jwt = jwt.sign({ customer_id: customer.id }, config.jwtSecret!, { + const { + projectConfig: { jwt_secret }, + } = req.scope.resolve("configModule") + req.session.jwt = jwt.sign({ customer_id: customer.id }, jwt_secret!, { expiresIn: "30d", }) diff --git a/packages/medusa/src/config/index.js b/packages/medusa/src/config/index.js deleted file mode 100644 index bceeefda5d..0000000000 --- a/packages/medusa/src/config/index.js +++ /dev/null @@ -1,44 +0,0 @@ -import dotenv from "dotenv" - -// Set the NODE_ENV to 'development' by default -process.env.NODE_ENV = process.env.NODE_ENV || "development" - -const envFound = dotenv.config() -if (!envFound) { - // This error should crash whole process - throw new Error("⚠️ Couldn't find .env file ⚠️") -} - -const config = { - /** - * Your favorite port - */ - port: parseInt(process.env.PORT, 10), - - databaseURL: process.env.MONGODB_URI, - redisURI: process.env.REDIS_URI, - - /** - * Your secret sauce - */ - jwtSecret: process.env.NODE_ENV === "test" ? "test" : process.env.JWT_SECRET, - - cookieSecret: - process.env.NODE_ENV === "test" ? "test" : process.env.COOKIE_SECRET, - - /** - * Used by winston logger - */ - logs: { - level: process.env.LOG_LEVEL || "silly", - }, - - /** - * API configs - */ - api: { - prefix: "/api", - }, -} - -export default config diff --git a/packages/medusa/src/helpers/test-request.js b/packages/medusa/src/helpers/test-request.js index 59c78154c2..98d14d1b81 100644 --- a/packages/medusa/src/helpers/test-request.js +++ b/packages/medusa/src/helpers/test-request.js @@ -4,7 +4,6 @@ import jwt from "jsonwebtoken" import { MockManager } from "medusa-test-utils" import "reflect-metadata" import supertest from "supertest" -import config from "../config" import apiLoader from "../loaders/api" import passportLoader from "../loaders/passport" import servicesLoader from "../loaders/services" @@ -22,9 +21,19 @@ const clientSessionOpts = { secret: "test", } +const config = { + projectConfig: { + jwt_secret: 'supersecret', + cookie_secret: 'superSecret', + admin_cors: '', + store_cors: '' + } +} + const testApp = express() const container = createContainer() +container.register('configModule', asValue(config)) container.register({ logger: asValue({ error: () => {}, @@ -45,16 +54,16 @@ testApp.use((req, res, next) => { next() }) -servicesLoader({ container }) -strategiesLoader({ container }) -passportLoader({ app: testApp, container }) +servicesLoader({ container, configModule: config }) +strategiesLoader({ container, configModule: config }) +passportLoader({ app: testApp, container, configModule: config }) testApp.use((req, res, next) => { req.scope = container.createScope() next() }) -apiLoader({ container, rootDirectory: ".", app: testApp }) +apiLoader({ container, app: testApp, configModule: config }) const supertestRequest = supertest(testApp) @@ -68,7 +77,7 @@ export async function request(method, url, opts = {}) { if (opts.adminSession.jwt) { opts.adminSession.jwt = jwt.sign( opts.adminSession.jwt, - config.jwtSecret, + config.projectConfig.jwt_secret, { expiresIn: "30m", } @@ -80,7 +89,7 @@ export async function request(method, url, opts = {}) { if (opts.clientSession.jwt) { opts.clientSession.jwt = jwt.sign( opts.clientSession.jwt, - config.jwtSecret, + config.projectConfig.jwt_secret, { expiresIn: "30d", } diff --git a/packages/medusa/src/loaders/api.ts b/packages/medusa/src/loaders/api.ts index a54667c46d..9757505c35 100644 --- a/packages/medusa/src/loaders/api.ts +++ b/packages/medusa/src/loaders/api.ts @@ -1,21 +1,18 @@ import { Express } from 'express' import bodyParser from "body-parser" -import { getConfigFile } from "medusa-core-utils" import routes from "../api" import { AwilixContainer } from "awilix" +import { ConfigModule } from "../types/global" type Options = { - app: Express; - rootDirectory: string; + app: Express container: AwilixContainer + configModule: ConfigModule } -export default async ({ app, rootDirectory, container }: Options) => { - const { configModule } = getConfigFile(rootDirectory, `medusa-config`) as { configModule: Record } - const config = (configModule && configModule.projectConfig) || {} - +export default async ({ app, container, configModule }: Options) => { app.use(bodyParser.json()) - app.use("/", routes(container, config)) + app.use("/", routes(container, configModule.projectConfig)) return app } diff --git a/packages/medusa/src/loaders/config.ts b/packages/medusa/src/loaders/config.ts new file mode 100644 index 0000000000..6302f604fd --- /dev/null +++ b/packages/medusa/src/loaders/config.ts @@ -0,0 +1,61 @@ +import { ConfigModule } from "../types/global" +import { getConfigFile } from "medusa-core-utils/dist" + +const isProduction = ["production", "prod"].includes(process.env.NODE_ENV || "") + +const errorHandler = isProduction + ? (msg: string): never => { + throw new Error(msg) + } + : console.log + +export default (rootDirectory: string): ConfigModule => { + const { configModule } = getConfigFile(rootDirectory, `medusa-config`) as { + configModule: ConfigModule + } + + if (!configModule?.projectConfig?.redis_url) { + console.log( + `[medusa-config] ⚠️ redis_url not found. A fake redis instance will be used.` + ) + } + + const jwt_secret = + configModule?.projectConfig?.jwt_secret ?? process.env.JWT_SECRET + if (!jwt_secret) { + errorHandler( + `[medusa-config] ⚠️ jwt_secret not found.${ + isProduction + ? "" + : " fallback to either cookie_secret or default 'supersecret'." + }` + ) + } + + const cookie_secret = + configModule?.projectConfig?.cookie_secret ?? process.env.COOKIE_SECRET + if (!cookie_secret) { + errorHandler( + `[medusa-config] ⚠️ cookie_secret not found.${ + isProduction + ? "" + : " fallback to either cookie_secret or default 'supersecret'." + }` + ) + } + + if (!configModule?.projectConfig?.database_type) { + console.log( + `[medusa-config] ⚠️ database_type not found. fallback to default sqlite.` + ) + } + + return { + projectConfig: { + jwt_secret: jwt_secret ?? "supersecret", + cookie_secret: cookie_secret ?? "supersecret", + ...configModule?.projectConfig, + }, + plugins: configModule?.plugins ?? [], + } +} diff --git a/packages/medusa/src/loaders/database.ts b/packages/medusa/src/loaders/database.ts index 290c8c68fe..672ee45b6e 100644 --- a/packages/medusa/src/loaders/database.ts +++ b/packages/medusa/src/loaders/database.ts @@ -2,17 +2,10 @@ import { Connection, createConnection, LoggerOptions } from "typeorm" import { ShortenedNamingStrategy } from "../utils/naming-strategy" import { AwilixContainer } from "awilix" import { ConnectionOptions } from "typeorm/connection/ConnectionOptions" - -export type DatabaseConfig = { - database_type: string; - database_url?: string; - database_database?: string; - database_extra?: Record; - database_logging: LoggerOptions -} +import { ConfigModule } from "../types/global" type Options = { - configModule: { projectConfig: DatabaseConfig }; + configModule: ConfigModule container: AwilixContainer } diff --git a/packages/medusa/src/loaders/express.ts b/packages/medusa/src/loaders/express.ts index 7860bdcb00..d99b83b91c 100644 --- a/packages/medusa/src/loaders/express.ts +++ b/packages/medusa/src/loaders/express.ts @@ -4,12 +4,11 @@ import cookieParser from "cookie-parser" import morgan from "morgan" import redis, { RedisConfig } from "redis" import createStore from "connect-redis" - -import config from "../config" +import { ConfigModule } from "../types/global" type Options = { app: Express; - configModule: { projectConfig: RedisConfig } + configModule: ConfigModule } export default async ({ app, configModule }: Options): Promise => { @@ -23,12 +22,13 @@ export default async ({ app, configModule }: Options): Promise => { sameSite = "none" } - const sessionOpts = { + const { cookie_secret } = configModule.projectConfig + let sessionOpts = { resave: true, saveUninitialized: true, cookieName: "session", proxy: true, - secret: config.cookieSecret, + secret: cookie_secret, cookie: { sameSite, secure, @@ -37,7 +37,7 @@ export default async ({ app, configModule }: Options): Promise => { store: null } - if (configModule.projectConfig.redis_url) { + if (configModule?.projectConfig?.redis_url) { const RedisStore = createStore(session) const redisClient = redis.createClient(configModule.projectConfig.redis_url) sessionOpts.store = new RedisStore({ client: redisClient }) diff --git a/packages/medusa/src/loaders/index.ts b/packages/medusa/src/loaders/index.ts index d68be1d1a1..cdfeaf4b95 100644 --- a/packages/medusa/src/loaders/index.ts +++ b/packages/medusa/src/loaders/index.ts @@ -1,13 +1,14 @@ +import loadConfig from './config' import "reflect-metadata" import Logger from "./logger" import apiLoader from "./api" -import databaseLoader, { DatabaseConfig } from "./database" +import databaseLoader from "./database" import defaultsLoader from "./defaults" import expressLoader from "./express" import modelsLoader from "./models" import passportLoader from "./passport" import pluginsLoader, { registerPluginModels } from "./plugins" -import redisLoader, { RedisConfig } from "./redis" +import redisLoader from "./redis" import repositoriesLoader from "./repositories" import requestIp from "request-ip" import searchIndexLoader from "./search-index" @@ -18,7 +19,6 @@ import { ClassOrFunctionReturning } from "awilix/lib/container" import { Connection, getManager } from "typeorm" import { Express, NextFunction, Request, Response } from "express" import { asFunction, asValue, AwilixContainer, createContainer, Resolver } from "awilix" -import { getConfigFile } from "medusa-core-utils" import { track } from "medusa-telemetry" import { MedusaContainer } from "../types/global" @@ -28,14 +28,6 @@ type Options = { isTest: boolean } -export type ConfigModule = { - projectConfig: DatabaseConfig & RedisConfig; - plugins: { - resolve: string; - options: Record - }[]; -} - export default async ( { directory: rootDirectory, @@ -43,9 +35,11 @@ export default async ( isTest }: Options ): Promise<{ container: MedusaContainer; dbConnection: Connection; app: Express }> => { - const { configModule } = getConfigFile(rootDirectory, `medusa-config`) as { configModule: ConfigModule } + const configModule = loadConfig(rootDirectory) const container = createContainer() as MedusaContainer + container.register('configModule', asValue(configModule)) + container.registerAdd = function (this: MedusaContainer, name: string, registration: typeof asFunction | typeof asValue) { const storeKey = name + "_STORE" @@ -73,7 +67,7 @@ export default async ( }) container.register({ - logger: asValue(Logger), + logger: asValue(Logger) }) await redisLoader({ container, configModule, logger: Logger }) @@ -89,6 +83,7 @@ export default async ( await registerPluginModels({ rootDirectory, container, + configModule }) const pmAct = Logger.success(pmActivity, "Plugin models initialized") || {} track("PLUGIN_MODELS_INIT_COMPLETED", { duration: pmAct.duration }) @@ -122,7 +117,7 @@ export default async ( const expActivity = Logger.activity("Initializing express") track("EXPRESS_INIT_STARTED") await expressLoader({ app: expressApp, configModule }) - await passportLoader({ app: expressApp, container }) + await passportLoader({ app: expressApp, container, configModule }) const exAct = Logger.success(expActivity, "Express intialized") || {} track("EXPRESS_INIT_COMPLETED", { duration: exAct.duration }) @@ -138,6 +133,7 @@ export default async ( await pluginsLoader({ container, rootDirectory, + configModule, app: expressApp, activityId: pluginsActivity, }) @@ -152,7 +148,7 @@ export default async ( const apiActivity = Logger.activity("Initializing API") track("API_INIT_STARTED") - await apiLoader({ container, rootDirectory, app: expressApp }) + await apiLoader({ container, app: expressApp, configModule }) const apiAct = Logger.success(apiActivity, "API initialized") || {} track("API_INIT_COMPLETED", { duration: apiAct.duration }) diff --git a/packages/medusa/src/loaders/passport.ts b/packages/medusa/src/loaders/passport.ts index 037f7f2801..9016c4bb29 100644 --- a/packages/medusa/src/loaders/passport.ts +++ b/packages/medusa/src/loaders/passport.ts @@ -1,13 +1,12 @@ -import config from "../config" import passport from "passport" import { AuthService } from "../services" import { Express } from 'express' -import { MedusaContainer } from "../types/global" +import { ConfigModule, MedusaContainer } from "../types/global" import { Strategy as BearerStrategy } from "passport-http-bearer" import { Strategy as JWTStrategy } from "passport-jwt" import { Strategy as LocalStrategy } from "passport-local" -export default async ({ app, container }: { app: Express; container: MedusaContainer }): Promise => { +export default async ({ app, container, configModule }: { app: Express; container: MedusaContainer; configModule: ConfigModule; }): Promise => { const authService = container.resolve("authService") // For good old email password authentication @@ -34,11 +33,12 @@ export default async ({ app, container }: { app: Express; container: MedusaConta // After a user has authenticated a JWT will be placed on a cookie, all // calls will be authenticated based on the JWT + const { jwt_secret } = configModule.projectConfig passport.use( new JWTStrategy( { jwtFromRequest: (req) => req.session.jwt, - secretOrKey: config.jwtSecret, + secretOrKey: jwt_secret, }, async (jwtPayload, done) => { return done(null, jwtPayload) diff --git a/packages/medusa/src/loaders/plugins.ts b/packages/medusa/src/loaders/plugins.ts index 21a426c9c3..ae831ddbeb 100644 --- a/packages/medusa/src/loaders/plugins.ts +++ b/packages/medusa/src/loaders/plugins.ts @@ -18,30 +18,30 @@ import { asValue, asClass, asFunction, aliasTo } from "awilix" import { sync as existsSync } from "fs-exists-cached" import { AbstractTaxService, isTaxCalculationStrategy } from "../interfaces" import formatRegistrationName from "../utils/format-registration-name" -import { ClassConstructor, Logger, MedusaContainer } from "../types/global" -import { ConfigModule } from "./index" +import { ClassConstructor, ConfigModule, Logger, MedusaContainer } from "../types/global" import { MiddlewareService } from "../services" type Options = { - rootDirectory: string; - container: MedusaContainer; - app: Express; + rootDirectory: string + container: MedusaContainer + configModule: ConfigModule + app: Express activityId: string } type PluginDetails = { - resolve: string; - name: string; - id: string; - options: Record; - version: string; + resolve: string + name: string + id: string + options: Record + version: string } /** * Registers all services in the services directory */ -export default async ({ rootDirectory, container, app, activityId }: Options): Promise => { - const resolved = getResolvedPlugins(rootDirectory) || [] +export default async ({ rootDirectory, container, app, configModule, activityId }: Options): Promise => { + const resolved = getResolvedPlugins(rootDirectory, configModule) || [] await Promise.all( resolved.map(async (pluginDetails) => { @@ -59,13 +59,7 @@ export default async ({ rootDirectory, container, app, activityId }: Options): P ) } -function getResolvedPlugins(rootDirectory: string): undefined | PluginDetails[] { - const { configModule } = getConfigFile(rootDirectory, `medusa-config`) as { configModule: ConfigModule } - - if (!configModule) { - return - } - +function getResolvedPlugins(rootDirectory: string, configModule: ConfigModule): undefined | PluginDetails[] { const { plugins } = configModule const resolved = plugins.map((plugin) => { @@ -90,8 +84,12 @@ function getResolvedPlugins(rootDirectory: string): undefined | PluginDetails[] return resolved } -export async function registerPluginModels({ rootDirectory, container }: { rootDirectory: string; container: MedusaContainer }): Promise { - const resolved = getResolvedPlugins(rootDirectory) || [] +export async function registerPluginModels({ + rootDirectory, + container, + configModule +}: { rootDirectory: string; container: MedusaContainer; configModule: ConfigModule; }): Promise { + const resolved = getResolvedPlugins(rootDirectory, configModule) || [] await Promise.all( resolved.map(async (pluginDetails) => { registerModels(pluginDetails, container) diff --git a/packages/medusa/src/loaders/redis.ts b/packages/medusa/src/loaders/redis.ts index cdd5b09d1a..f10a63d9df 100644 --- a/packages/medusa/src/loaders/redis.ts +++ b/packages/medusa/src/loaders/redis.ts @@ -1,16 +1,12 @@ import { asValue } from "awilix" import RealRedis from "ioredis" import FakeRedis from "ioredis-mock" -import { MedusaContainer } from "../types/global" +import { ConfigModule, MedusaContainer } from "../types/global" import { Logger } from "../types/global" -export type RedisConfig = { - redis_url?: string; -} - type Options = { container: MedusaContainer; - configModule: { projectConfig: RedisConfig }; + configModule: ConfigModule; logger: Logger; } diff --git a/packages/medusa/src/loaders/services.ts b/packages/medusa/src/loaders/services.ts index 4a989c7ff6..a97ea6604c 100644 --- a/packages/medusa/src/loaders/services.ts +++ b/packages/medusa/src/loaders/services.ts @@ -2,13 +2,12 @@ import glob from "glob" import path from "path" import { asFunction } from "awilix" import formatRegistrationName from "../utils/format-registration-name" -import { MedusaContainer } from "../types/global" -import { ConfigModule } from "./index" +import { ConfigModule, MedusaContainer } from "../types/global" type Options = { container: MedusaContainer; configModule: ConfigModule - isTest: boolean; + isTest?: boolean; } /** diff --git a/packages/medusa/src/loaders/strategies.ts b/packages/medusa/src/loaders/strategies.ts index be97e95c1c..3aeef61e59 100644 --- a/packages/medusa/src/loaders/strategies.ts +++ b/packages/medusa/src/loaders/strategies.ts @@ -7,7 +7,7 @@ import formatRegistrationName from "../utils/format-registration-name" type LoaderOptions = { container: AwilixContainer configModule: object - isTest: boolean + isTest?: boolean } /** diff --git a/packages/medusa/src/services/__tests__/event-bus.js b/packages/medusa/src/services/__tests__/event-bus.js index 792d93c43a..9dfc2b5f81 100644 --- a/packages/medusa/src/services/__tests__/event-bus.js +++ b/packages/medusa/src/services/__tests__/event-bus.js @@ -1,11 +1,11 @@ import Bull from "bull" import { MockRepository, MockManager } from "medusa-test-utils" import EventBusService from "../event-bus" -import config from "../../config" +import config from "../../loaders/config" jest.genMockFromModule("bull") jest.mock("bull") -jest.mock("../../config") +jest.mock("../../loaders/config") config.redisURI = "testhost" diff --git a/packages/medusa/src/services/__tests__/invite.js b/packages/medusa/src/services/__tests__/invite.js index a375a8f356..5184f82ac0 100644 --- a/packages/medusa/src/services/__tests__/invite.js +++ b/packages/medusa/src/services/__tests__/invite.js @@ -19,6 +19,8 @@ describe("InviteService", () => { userRepository: {}, inviteRepository: inviteRepo, eventBusService: EventBusServiceMock, + }, { + projectConfig: { jwt_secret: 'superSecret' } }) it("calls invite repository find", async () => { @@ -40,6 +42,8 @@ describe("InviteService", () => { userRepository: {}, inviteRepository: {}, eventBusService: EventBusServiceMock, + }, { + projectConfig: { jwt_secret: 'superSecret' } }) it("validating a signed token succeeds", () => { @@ -108,6 +112,8 @@ describe("InviteService", () => { userRepository: userRepo, inviteRepository: inviteRepo, eventBusService: EventBusServiceMock, + }, { + projectConfig: { jwt_secret: 'superSecret' } }) beforeEach(() => jest.clearAllMocks()) @@ -200,6 +206,8 @@ describe("InviteService", () => { userRepository: {}, inviteRepository: inviteRepo, eventBusService: EventBusServiceMock, + }, { + projectConfig: { jwt_secret: 'superSecret' } }) inviteService.generateToken = jest.fn() diff --git a/packages/medusa/src/services/invite.ts b/packages/medusa/src/services/invite.ts index 6fbbdcc3ea..3ec9023a54 100644 --- a/packages/medusa/src/services/invite.ts +++ b/packages/medusa/src/services/invite.ts @@ -4,11 +4,11 @@ import { BaseService } from "medusa-interfaces" import { EntityManager } from "typeorm" import { EventBusService, UserService } from "." import { User } from ".." -import config from "../config" import { UserRoles } from "../models/user" import { InviteRepository } from "../repositories/invite" import { UserRepository } from "../repositories/user" import { ListInvite } from "../types/invites" +import { ConfigModule } from "../types/global" // 7 days const DEFAULT_VALID_DURATION = 1000 * 60 * 60 * 24 * 7 @@ -32,15 +32,22 @@ class InviteService extends BaseService { private inviteRepository_: InviteRepository private eventBus_: EventBusService - constructor({ - manager, - userService, - userRepository, - inviteRepository, - eventBusService, - }: InviteServiceProps) { + protected readonly configModule_: ConfigModule + + constructor( + { + manager, + userService, + userRepository, + inviteRepository, + eventBusService, + }: InviteServiceProps, + configModule: ConfigModule + ) { super() + this.configModule_ = configModule + /** @private @constant {EntityManager} */ this.manager_ = manager @@ -62,13 +69,16 @@ class InviteService extends BaseService { return this } - const cloned = new InviteService({ - manager, - inviteRepository: this.inviteRepository_, - userService: this.userService_, - userRepository: this.userRepo_, - eventBusService: this.eventBus_, - }) + const cloned = new InviteService( + { + manager, + inviteRepository: this.inviteRepository_, + userService: this.userService_, + userRepository: this.userRepo_, + eventBusService: this.eventBus_, + }, + this.configModule_ + ) cloned.transactionManager_ = manager @@ -76,12 +86,13 @@ class InviteService extends BaseService { } generateToken(data): string { - if (config.jwtSecret) { - return jwt.sign(data, config.jwtSecret) + const { jwt_secret } = this.configModule_.projectConfig + if (jwt_secret) { + return jwt.sign(data, jwt_secret) } throw new MedusaError( MedusaError.Types.INVALID_DATA, - "Please configure JwtSecret" + "Please configure jwt_secret" ) } @@ -248,12 +259,13 @@ class InviteService extends BaseService { } verifyToken(token): JwtPayload | string { - if (config.jwtSecret) { - return jwt.verify(token, config.jwtSecret) + const { jwt_secret } = this.configModule_.projectConfig + if (jwt_secret) { + return jwt.verify(token, jwt_secret) } throw new MedusaError( MedusaError.Types.INVALID_DATA, - "Please configure JwtSecret" + "Please configure jwt_secret" ) } diff --git a/packages/medusa/src/types/global.ts b/packages/medusa/src/types/global.ts index 002975d8b7..c6539e0477 100644 --- a/packages/medusa/src/types/global.ts +++ b/packages/medusa/src/types/global.ts @@ -1,5 +1,6 @@ import { AwilixContainer } from "awilix" import { Logger as _Logger } from "winston" +import { LoggerOptions } from "typeorm" export type ClassConstructor = { new (...args: unknown[]): T @@ -12,3 +13,30 @@ export type MedusaContainer = AwilixContainer & { export type Logger = _Logger & { progress: (activityId: string, msg: string) => void } + +export type ConfigModule = { + projectConfig: { + redis_url?: string + + jwt_secret?: string + cookie_secret?: string + + database_url?: string + database_type: string + database_database?: string + database_logging: LoggerOptions + + database_extra?: Record & { + ssl: { rejectUnauthorized: false } + } + store_cors?: string + admin_cors?: string + } + plugins: ( + | { + resolve: string + options: Record + } + | string + )[] +} diff --git a/packages/medusa/yarn.lock b/packages/medusa/yarn.lock index 3a7564c1c7..7fe78dce92 100644 --- a/packages/medusa/yarn.lock +++ b/packages/medusa/yarn.lock @@ -1519,7 +1519,6 @@ chalk "^4.0.0" configstore "5.0.1" core-js "^3.6.5" - dotenv "^8.2.0" execa "^5.1.1" fs-exists-cached "^1.0.0" fs-extra "^10.0.0" @@ -3242,11 +3241,6 @@ dot-prop@^5.2.0: dependencies: is-obj "^2.0.0" -dotenv@^8.2.0: - version "8.6.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" - integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== - duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"