chore(framework): Continue to move loaders to framework (#8258)

**What**
More move and cleanup

FIXES FRMW-2603
FIXES FRMW-2608
FIXES FRMW-2610
FIXES FRMW-2611

Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
Adrien de Peretti
2024-07-30 10:52:12 +02:00
committed by GitHub
parent a2a377c8ca
commit bcd9d9c2b1
30 changed files with 204 additions and 359 deletions

View File

@@ -26,6 +26,12 @@
"import": "./dist/logger/index.js",
"require": "./dist/logger/index.js",
"node": "./dist/logger/index.js"
},
"./database": {
"types": "./dist/database/index.d.ts",
"import": "./dist/database/index.js",
"require": "./dist/database/index.js",
"node": "./dist/database/index.js"
}
},
"engines": {
@@ -42,7 +48,7 @@
"author": "Medusa",
"license": "MIT",
"scripts": {
"watch": "tsc --build --watch -p ./tsconfig.build.json",
"watch": "tsc --watch -p ./tsconfig.build.json",
"watch:test": "tsc --build tsconfig.spec.json --watch",
"prepublishOnly": "cross-env NODE_ENV=production tsc -p ./tsconfig.build.json && tsc-alias -p ./tsconfig.build.json",
"build": "rimraf dist && tsc --build && tsc-alias",
@@ -50,17 +56,23 @@
"test:integration": "jest --forceExit -- integration-tests/**/__tests__/**/*.ts"
},
"devDependencies": {
"@medusajs/types": "^1.11.17-preview-20240510084332",
"@medusajs/types": "^1.11.16",
"@types/express": "^4.17.17",
"cross-env": "^7.0.3",
"ioredis": "^5.2.5",
"rimraf": "^3.0.2",
"tsc-alias": "^1.8.6",
"typescript": "^5.1.6",
"vite": "^5.2.11"
},
"dependencies": {
"@medusajs/medusa-cli": "^1.3.23",
"@medusajs/utils": "^1.12.0-preview-20240724081425",
"awilix": "^8.0.0"
"@medusajs/medusa-cli": "^1.3.22",
"@medusajs/utils": "^1.11.9",
"awilix": "^8.0.0",
"cookie-parser": "^1.4.6",
"express": "^4.18.2",
"express-session": "^1.17.3",
"ioredis": "^5.2.5",
"ioredis-mock": "8.4.0",
"morgan": "^1.9.1"
}
}

View File

@@ -1,8 +1,14 @@
import { ConfigModule } from "./types"
import { deepCopy, isDefined } from "@medusajs/utils"
import { Logger } from "@medusajs/types"
import { logger } from "../logger"
export class ConfigManager {
/**
* Root dir from where to start
* @private
*/
#baseDir: string
/**
* A flag to specify if we are in production or not, determine whether an error would be critical and thrown or just logged as a warning in developement
* @private
@@ -18,12 +24,6 @@ export class ConfigManager {
readonly #envWorkMode?: ConfigModule["projectConfig"]["workerMode"] = process
.env.MEDUSA_WORKER_MODE as ConfigModule["projectConfig"]["workerMode"]
/**
* The logger instance to use
* @private
*/
readonly #logger: Logger
/**
* The config object after loading it
* @private
@@ -39,10 +39,16 @@ export class ConfigManager {
return this.#config
}
constructor({ logger }: { logger: Logger }) {
this.#logger = logger
get baseDir(): string {
return this.#baseDir
}
get isProduction(): boolean {
return this.#isProduction
}
constructor() {}
/**
* Rejects an error either by throwing when in production or by logging the error as a warning
* @param error
@@ -53,7 +59,7 @@ export class ConfigManager {
throw new Error(`[config] ⚠️ ${error}`)
}
this.#logger.warn(error)
logger.warn(error)
}
/**
@@ -141,17 +147,25 @@ export class ConfigManager {
/**
* Prepare the full configuration after validation and normalization
*/
loadConfig(rawConfig: Partial<ConfigModule> = {}): ConfigModule {
const projectConfig = this.normalizeProjectConfig(
rawConfig.projectConfig ?? {}
loadConfig({
projectConfig = {},
baseDir,
}: {
projectConfig: Partial<ConfigModule>
baseDir: string
}): ConfigModule {
this.#baseDir = baseDir
const normalizedProjectConfig = this.normalizeProjectConfig(
projectConfig.projectConfig ?? {}
)
this.#config = {
projectConfig,
admin: rawConfig.admin ?? {},
modules: rawConfig.modules ?? {},
featureFlags: rawConfig.featureFlags ?? {},
plugins: rawConfig.plugins ?? [],
projectConfig: normalizedProjectConfig,
admin: projectConfig.admin ?? {},
modules: projectConfig.modules ?? {},
featureFlags: projectConfig.featureFlags ?? {},
plugins: projectConfig.plugins ?? [],
}
return this.#config

View File

@@ -1,7 +1,9 @@
import { ConfigModule } from "./types"
import { getConfigFile } from "@medusajs/utils"
import { ContainerRegistrationKeys, getConfigFile } from "@medusajs/utils"
import { logger } from "../logger"
import { ConfigManager } from "./config"
import { container } from "../container"
import { asFunction } from "awilix"
const handleConfigError = (error: Error): void => {
logger.error(`Error in loading config: ${error.message}`)
@@ -12,9 +14,12 @@ const handleConfigError = (error: Error): void => {
}
// TODO: Later on we could store the config manager into the unique container
export const configManager = new ConfigManager({
logger,
})
export const configManager = new ConfigManager()
container.register(
ContainerRegistrationKeys.CONFIG_MODULE,
asFunction(() => configManager)
)
/**
* Loads the config file and returns the config module after validating, normalizing the configurations
@@ -35,5 +40,8 @@ export function configLoader(
handleConfigError(error)
}
return configManager.loadConfig(configModule)
return configManager.loadConfig({
projectConfig: configModule,
baseDir: entryDirectory,
})
}

View File

@@ -0,0 +1,3 @@
import { createMedusaContainer } from "@medusajs/utils"
export const container = createMedusaContainer()

View File

@@ -0,0 +1 @@
export * from "./pg-connection-loader"

View File

@@ -0,0 +1,44 @@
import { ContainerRegistrationKeys, ModulesSdkUtils } from "@medusajs/utils"
import { asValue } from "awilix"
import { container } from "../container"
import { configManager } from "../config"
/**
* Initialize a knex connection that can then be shared to any resources if needed
*/
export function pgConnectionLoader(): ReturnType<
typeof ModulesSdkUtils.createPgConnection
> {
if (container.hasRegistration(ContainerRegistrationKeys.PG_CONNECTION)) {
return container.resolve(ContainerRegistrationKeys.PG_CONNECTION)
}
const configModule = configManager.config
// Share a knex connection to be consumed by the shared modules
const connectionString = configModule.projectConfig.databaseUrl
const driverOptions: any =
configModule.projectConfig.databaseDriverOptions || {}
const schema = configModule.projectConfig.databaseSchema || "public"
const idleTimeoutMillis = driverOptions.pool?.idleTimeoutMillis ?? undefined // prevent null to be passed
const poolMax = driverOptions.pool?.max
delete driverOptions.pool
const pgConnection = ModulesSdkUtils.createPgConnection({
clientUrl: connectionString,
schema,
driverOptions,
pool: {
max: poolMax,
idleTimeoutMillis,
},
})
container.register(
ContainerRegistrationKeys.PG_CONNECTION,
asValue(pgConnection)
)
return pgConnection
}

View File

@@ -0,0 +1,78 @@
import createStore from "connect-redis"
import cookieParser from "cookie-parser"
import express, { Express } from "express"
import session from "express-session"
import Redis from "ioredis"
import morgan from "morgan"
import path from "path"
import { configManager } from "../config"
export async function expressLoader({ app }: { app: Express }): Promise<{
app: Express
shutdown: () => Promise<void>
}> {
const baseDir = configManager.baseDir
const configModule = configManager.config
const isProduction = configManager.isProduction
const isStaging = process.env.NODE_ENV === "staging"
const isTest = process.env.NODE_ENV === "test"
let sameSite: string | boolean = false
let secure = false
if (isProduction || isStaging) {
secure = true
sameSite = "none"
}
const { http, sessionOptions } = configModule.projectConfig
const sessionOpts = {
name: sessionOptions?.name ?? "connect.sid",
resave: sessionOptions?.resave ?? true,
rolling: sessionOptions?.rolling ?? false,
saveUninitialized: sessionOptions?.saveUninitialized ?? true,
proxy: true,
secret: sessionOptions?.secret ?? http?.cookieSecret,
cookie: {
sameSite,
secure,
maxAge: sessionOptions?.ttl ?? 10 * 60 * 60 * 1000,
},
store: null,
}
let redisClient
if (configModule?.projectConfig?.redisUrl) {
const RedisStore = createStore(session)
redisClient = new Redis(
configModule.projectConfig.redisUrl,
configModule.projectConfig.redisOptions ?? {}
)
sessionOpts.store = new RedisStore({
client: redisClient,
prefix: `${configModule?.projectConfig?.redisPrefix ?? ""}sess:`,
})
}
app.set("trust proxy", 1)
app.use(
morgan("combined", {
skip: () => isTest,
})
)
app.use(cookieParser())
app.use(session(sessionOpts))
// Currently we don't allow configuration of static files, but this can be revisited as needed.
app.use("/static", express.static(path.join(baseDir, "static")))
app.get("/health", (req, res) => {
res.status(200).send("OK")
})
const shutdown = async () => {
redisClient?.disconnect()
}
return { app, shutdown }
}

View File

@@ -0,0 +1 @@
export * from "./express-loader"

View File

@@ -1,2 +1,5 @@
export * from "./config"
export * from "./logger"
export * from "./http"
export * from "./database"
export * from "./container"