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:
committed by
GitHub
parent
a2a377c8ca
commit
bcd9d9c2b1
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
3
packages/framework/framework/src/container.ts
Normal file
3
packages/framework/framework/src/container.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { createMedusaContainer } from "@medusajs/utils"
|
||||
|
||||
export const container = createMedusaContainer()
|
||||
1
packages/framework/framework/src/database/index.ts
Normal file
1
packages/framework/framework/src/database/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./pg-connection-loader"
|
||||
@@ -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
|
||||
}
|
||||
78
packages/framework/framework/src/http/express-loader.ts
Normal file
78
packages/framework/framework/src/http/express-loader.ts
Normal 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 }
|
||||
}
|
||||
1
packages/framework/framework/src/http/index.ts
Normal file
1
packages/framework/framework/src/http/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./express-loader"
|
||||
@@ -1,2 +1,5 @@
|
||||
export * from "./config"
|
||||
export * from "./logger"
|
||||
export * from "./http"
|
||||
export * from "./database"
|
||||
export * from "./container"
|
||||
|
||||
Reference in New Issue
Block a user