如何将Angular Universal应用程序部署到Node.js生产服务器?

问题描述:

我有一个要与Universal一起使用的Angular 8应用程序,我想将其部署到共享的Web主机生产服务器上.我事先咨询了虚拟主机,他们告诉我可以在共享的虚拟主机上托管一个角度通用的Web应用程序.但是,无论我做什么,我都无法使网站正常工作.当我转到该网站时,我不断看到以下消息:无法访问该网站"

I have an Angular 8 application with Universal that I want to deploy to a shared web host production server. I checked with the web host in advance and they told me that hosting an angular universal web app is possible on their shared web hosting. However, whatever I do, I can't get the website to work. When I go to the website I keep seeing the message: "This site can't be reached"

我到目前为止所做的事情:

Things I have done so far:

  1. 使用npm构建项目运行build:ssr,该项目使用浏览器和服务器文件夹以及server.js文件创建了一个dist文件夹
  2. 将dist文件夹移动到public_html文件夹内的服务器. 然后通过SSH访问服务器并执行以下操作:
  3. 安装Node.js和npm
  4. npm安装
  5. npm install pm2 -g
  6. pm2 start dist/server.js
  1. Build project with npm run build:ssr which created a dist folder with a browser and server folder and a server.js file
  2. Move the dist folder to the server inside the public_html folder. Then accessed the server through SSH and did these:
  3. install Node.js and npm
  4. npm install
  5. npm install pm2 -g
  6. pm2 start dist/server.js

pm2启动没有问题.

pm2 starts without problems.

这些是一些项目文件.如果有任何遗漏,请询问,我将它们添加到问题中.

These are some of the project files. If any are missing please ask and I'll add them to the question.

带有脚本的package.json的一部分:

Part of the package.json with the scripts:

  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "compile:server": "webpack --config webpack.server.config.js --progress --colors",
    "serve:ssr": "node dist/server",
    "build:ssr": "npm run build:client-and-server-bundles && npm run compile:server",
    "build:client-and-server-bundles": "ng build --prod && ng run ProjectName:server:production --bundleDependencies all"
  },

server.js来自构建(仅表达部分,因为它有25000多行):

server.js from the build (only the express part, since it's 25000+ lines):

const app = express__WEBPACK_IMPORTED_MODULE_1__();
const PORT = process.env.PORT || 4000;
const DIST_FOLDER = Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(process.cwd(), 'dist/browser');
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP, ngExpressEngine, provideModuleMap } = __webpack_require__(144);
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine({
    bootstrap: AppServerModuleNgFactory,
    providers: [
        provideModuleMap(LAZY_MODULE_MAP)
    ]
}));
app.set('view engine', 'html');
app.set('views', DIST_FOLDER);
// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });
// Serve static files from /browser
app.get('*.*', express__WEBPACK_IMPORTED_MODULE_1__["static"](DIST_FOLDER, {
    maxAge: '1y'
}));
// All regular routes use the Universal engine
app.get('*', (req, res) => {
    res.render('index', { req });
});
// Start up the Node server
app.listen(PORT, () => {
    console.log(`Node Express server listening on http://localhost:${PORT}`);
});

根据SO或其他地方的几个答案,您可以简单地"将dist文件夹复制粘贴到服务器上,运行pm2,您的网站应该可以正常工作.我觉得仍然缺少很多工作.

According to several answers on SO or elsewhere you "simply" copy paste the dist folder to the server, run pm2 and your website supposedly works. I feel like there's a lot missing to get it working though.

有人知道如何正确地将Angular Universal网站部署到生产服务器吗?

Does someone know how to properly deploy an Angular Universal website to a production server?

我最终在DirectAdmin控制台中使用了一个名为"NodeJS Selector"的工具,因为PM2一直对我造成问题.它是安装应用程序,执行npm install并启动应用程序的工具.因此,基本上,您将使用SSH进行操作,但使用的是精美的UI.设置如下所示:

I ended up using a tool in my DirectAdmin console called 'NodeJS Selector', because PM2 kept causing problems for me. It's a tool to install your application, do npm install and start the app. So basically what you'd do using SSH, but in a fancy UI. The setup looks like this:

我的文件夹结构如下:

domains
    - appname.com
        - public_html
            - browser (=> this set as the document root in Apache, because index is located here)
                - index.html
                - .htaccess
                - other files...
            - server
            - server.js
            - package.json

我让我的Web主机执行2件事,因为我不是服务器上的root用户.他们将 documentroot 设置为Apache的httpd.conf文件中的浏览器文件夹.他们还在同一文件中向端口4000添加了 proxy 设置,因为这是我的应用程序在其中运行的地方.因此,在httpd.conf文件中将如下所示:

I had my web host do 2 things, because I'm not a root user on the server. They set the documentroot to the browser folder in Apache's httpd.conf file. In the same file they also added proxy settings to port 4000 because that's where my app is running on. So in the httpd.conf file will be something like this:

 DocumentRoot "/domains/appname.com/public_html/browser"

 <Proxy *>
  Order allow,deny
  Allow from all
 </Proxy>
 ProxyPreserveHost On
 ProxyRequests Off
 ProxyPass / https://localhost:4000/
 ProxyPassReverse / https://localhost:4000/

接下来,在NodeJS选择器中,将根目录设置为启动文件所在的位置(在我的情况下为server.js). package.json也必须位于public_html文件夹中,以便您可以在选择器中进行 npm安装.

Next in the NodeJS selector you set the root to where the startup file is located (in my case server.js). The package.json needs to be in de public_html folder as well so you can do npm install in the Selector.

要启动该应用程序,请单击运行JS脚本" ,然后选择serve:ssr选项,它会运行节点服务器命令.

To start the app you click 'Run JS script' and choose the serve:ssr option and it runs the node server command.

Angular文档描述了如何在服务器上重写一些规则. Apache安装在我的Web服务器上,因此我添加了.htaccess文件以及文档中的重写规则.但是,此应用程序由Node提供,并且Node可以提供实际路径.因此,就我而言,我不必添加带有重写规则的.htaccess文件.相反,当我添加.htaccess文件时,它导致我的应用的通用端无法完全呈现.有关更多信息,请参见此问题.

The Angular docs describe how you have to rewrite some rules on the server. Apache is installed on my web server so I added an .htaccess file with the rewrite rules from the docs. However this app is served by Node and Node can serve the actual paths. So in my case I didn't have to add the .htaccess file with rewrite rules. On the contrary, when I added the .htaccess file it caused the Universal side of my app not to fully render. See this question for more info.