我最近启动了一个带有临时和生产环境的 Heroku 管道。我的应用程序是使用 React、NodeJS 和 Webpack 构建的。
当我部署到 staging 时,我的 baseURL 是生产 url,但它应该与前端 staging url 相同。
我已经能够将问题专门隔离到 Webpack 模式上。我无法控制用于暂存的 baseURL,因为 Webpack 模式和 Heroku 似乎需要开发或生产。设置 NODE_ENV=staging 似乎没有改变任何东西。
错误:
我在我的暂存控制台中看到此错误:
xhr.js:258 Refused to connect to 'http://localhost:4000/api/users/login' because it violates the following Content Security Policy directive: "connect-src 'self' https://stagingapp.herokuapp.com".
这个正在生产中:
Access to XMLHttpRequest at 'https://stagingapp.herokuapp.com/api/users/login' from origin 'https://prodapp.herokuapp.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header has a value 'https://stagingapp.herokuapp.com' that is not equal to the supplied origin.
这使得它看起来像是 CORS 和 CSP 问题,因为确实如此。但这只是一个问题,因为我的 Webpack 破坏了 baseURL。当这些对齐时,一切都会按预期进行。
这是我所做的:
在 Heroku 中,我添加了配置变量(环境变量)——NODE_ENV=staging 用于暂存,NODE_ENV=product 用于生产。
我尝试了各种调整 Webpack 模式的方法
我尝试更改我的构建脚本,但 Heroku 只接受构建或 heroku-postbuild 等。我似乎无法使用特定于暂存的脚本
我尝试使用 Axios 控制 baseURL,但似乎我的 Axios 配置根本没有做太多事情,因为 baseUrl 是在我的构建中确定的,而 Axios 在运行时工作。如果我能让我的 Axios 处理 URL,我认为这就能解决问题,但 Webpack 一直在阻碍。
我还将我的暂存环境更改为生产环境,看看是否有帮助。现在我的暂存环境可以工作,但我的产品环境也有同样的问题,它调用我的暂存 URL 作为基本 URL,而不是产品 url。
我也多次阅读了所有文档,但关于如何处理 Heroku 上的 Webpack 构建的内容并不多。
这是我的代码的相关部分:
网页包配置:
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development'
PACKAGE.JSON 构建脚本: 这已经是一百万件事了,我已经尝试过使用 NODE_ENV=staging 并且没有 --mode 生产标签。
"build": "webpack --mode production"
AXIOS CONFIG - 这似乎在生产或登台中没有做任何事情
import axios from 'axios';
// Define base URLs for different environments
const baseURLs = {
development: 'LOCAL HOST URL',
staging: 'STAGING URL',
production: 'PROD URL',
};
// Determine the environment based on NODE_ENV
const environment = process.env.NODE_ENV;
// Create an instance of Axios with a custom configuration
const instance = axios.create({
// Set the base URL for requests
baseURL: baseURLs[environment],
withCredentials: true,
});
export default instance;
服务器 CORS:
const setupCORS = () => {
// Define allowed origins based on environment
let allowedOrigin;
if (process.env.NODE_ENV === 'development') {
allowedOrigin = 'LOCAL HOST URL';
} else if (process.env.NODE_ENV === 'staging') {
allowedOrigin = 'STAGING URL';
} else if (process.env.NODE_ENV === 'production') {
allowedOrigin = 'PROD URL';
}
// Handle CORS for all routes
app.use(
cors({
origin: allowedOrigin,
credentials: true,
}),
);
};
// Invoke the CORS setup function
setupCORS();
服务器 CSP:
const setupSecurityHeaders = () => {
// CSP middleware based on environment
if (process.env.NODE_ENV === 'development') {
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", 'LOCAL HOST URL'],
connectSrc: ["'self'", 'LOCAL HOST URL'],
},
}),
);
console.log('setupSecurityHeaders in development');
} else if (process.env.NODE_ENV === 'staging') {
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", 'STAGING URL'],
connectSrc: ["'self'", 'STAGING URL'],
formAction: ["'self'", 'FORM URL'],
},
}),
);
console.log('setupSecurityHeaders in staging');
} else if (process.env.NODE_ENV === 'production') {
// Apply more restrictive CSP for production
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", 'PROD URL'],
connectSrc: ["'self'", 'PROD URL'],
formAction: ["'self'", 'FORM URL'],
},
}),
);
console.log('setupSecurityHeaders in production');
}
};
// Invoke the security headers function
setupSecurityHeaders();
在站在马桶上挂钟时滑倒并撞到头后,我找到了答案。和往常一样,这很简单。这就是我所做的:
在暂存和生产中创建一个
NODE_ENV
配置变量并将其设置为“生产”
创建一个
TARGET_ENV
配置变量,并将其设置为暂存中的“暂存”和(你猜对了)生产中的“生产”。确保在 .env 文件中将其设置为“development”。
将配置文件中的 Webpack 模式设置为以下内容:
mode: process.NODE_ENV === 'production' ? 'production' : 'development',
plugins: [
new webpack.DefinePlugin({
TARGET_ENV: JSON.stringify(process.env.TARGET_ENV),
}),
],
};
这将在构建时使用您的配置变量创建一个全局变量。
在 package.json 中保持构建脚本简单:
"build": "webpack",
在 axiosConfig.js(或存储 baseUrl 的任何位置)中,使用
TARGET_ENV
,而不附加 process.env
。 (另外,我尝试了process.env.REACT_APP_BLAH_BLAH
,但没有成功,仅供参考。)
import axios from 'axios';
// Define base URLs for different environments
const baseURLs = {
development: 'http://localhost:4000',
staging: 'https://staging.herokuapp.com',
production: 'https://prod.herokuapp.com',
};
// Determine the environment based on NODE_ENV
const environment = TARGET_ENV;
// Create an instance of Axios with a custom configuration
const instance = axios.create({
// Set the base URL for requests
baseURL: baseURLs[environment],
withCredentials: true,
});
export default instance;
在管道中,将暂存环境设置为在推送到暂存分支时自动部署,将生产分支设置为在推送到主分支时自动部署。
从控制台或 Github PR 分别直接推送到每个分支。这将为每个环境创建一个单独的构建。它对我有用,我希望它对你有用。祝你好运。
哦,我还有一个悬而未决的问题:
我不确定在运行初始生产版本后,Heroku 仪表板中的“升级到生产”按钮是否会起作用。
它只会推送最近的暂存更改,还是会覆盖我的生产版本?我最终会测试一下,但如果有人知道,请随时发表评论。