如何在 Ubuntu 上安装Vendure 22.04

介绍

Vendure 是一个开源的无头商务框架,允许构建由 Node.js、TypeScript 和 GraphQL 提供支持的生产就绪电子商务应用程序。它的工作原理是通过 API 公开所有应用程序功能。Vendure 在 GraphQL API 上公开了所有商店前端功能,并且不提供默认的商店前端界面。这提高了开发人员的灵活性,因为它允许您使用任何前端技术创建店面。

本文介绍如何在 Ubuntu 22.04 Vultr 服务器上部署 Vendure。您需要将该框架与 Vultr Object Storage(PostgreSQL 托管数据库)和 Redis® 集成,以构建生产就绪型应用程序。

先决条件

准备工作:

安装Node.js

Vendure 需要 Node.js 版本 16.x 或更高版本。按照以下步骤中的说明安装最新的 Node.js 版本。

  1. 更新服务器
     $ sudo apt update
  2. 创建目录keyrings
     $ sudo mkdir -p /etc/apt/keyrings
  3. 下载节点源 GPG 密钥并将其添加到服务器密钥
     curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
  4. 将Node.js存储库添加到源列表中。替换为您想要的版本apt20
     NODE_MAJOR=20
     echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
  5. 更新服务器程序包
     $ sudo apt update
  6. 安装Node.js
     $ sudo apt install nodejs -y
  7. 验证已安装的Node.js版本
     $ nodejs --version

    输出:

     v20.5.1

设置本地 PostgreSQL 数据库服务器

要在服务器上安装 Vendure,您需要将应用程序数据临时保存在本地 PostgreSQL 数据库服务器上。稍后,您可以先将数据迁移到 Vultr Managed Database for MySQL,然后再将应用程序部署到生产环境。如下所述设置本地数据库。

在 PostgreSQL 上启用密码身份验证

  1. 验证已安装的 PostgreSQL 版本
     $ psql --version

    输出:

     psql (PostgreSQL) 14.16 (Ubuntu 14.16-0ubuntu0.22.04.1)
  2. 根据您的 PostgreSQL 版本,使用文本编辑器编辑文件,例如pg_hba.confnano
     $ sudo nano /etc/postgresql/14/main/pg_hba.conf
  3. 查找以下配置行
     # "local" is for Unix domain socket connections only
     local   all             all                                     peer
  4. 将身份验证方法更改为peermd5
     local   all             all                                     md5

    保存并关闭文件

  5. 重新启动 PostgreSQL 数据库服务器
     $ sudo systemctl restart postgresql

创建新的 PostgreSQL 数据库

  1. 登录PostgreSQL数据库服务器
     $ sudo -u postgres psql
  2. 创建新数据库
     postgres=# CREATE DATABASE vendure_db;

    输出:

     CREATE DATABASE
  3. 使用强密码创建新的数据库用户
     postgres=# CREATE USER vendure_user WITH ENCRYPTED PASSWORD 'strong-password';

    输出:

     CREATE ROLE
  4. 授予用户对 Vendure 数据库的完全权限
     postgres=# GRANT ALL PRIVILEGES ON DATABASE vendure_db TO vendure_user;

    输出:

     GRANT
  5. 退出 PostgreSQL 控制台
     postgres-# \q

安装 Vendure

  1. 使用 Node.js 工具,使用该工具安装 Vendure。npx@vendure/create
     $ npx @vendure/create vendure-app
  2. 回复每个安装提示,如下所述按 安装软件包Y@vendure/create
     Need to install the following packages:
       @vendure/create@2.0.2
     Ok to proceed? (y) y

    选择为正在使用的数据库Postgres

     ◆  Which database are you using?
     │  ○ MySQL
     │  ○ MariaDB
     │  ● Postgres
     │  ○ SQLite
     │  ○ SQL.js

    按下可设置为 PostgreSQL 主机名ENTERlocalhost

     ◆  What's the database host address?
     │  localhost

    按下可保留为 PostgreSQL 端口号ENTER5432

     ◆  What port is the database listening on?
     │  5432

    输入您之前创建的 PostgreSQL 数据库名称

     ◆  What's the name of the database?
     │  vendure_db

    按下可将 PostgreSQL 架构设置为 publicENTER

        What's the schema name we should use?
     │  public

    输入您之前创建的 PostgreSQL 用户

     ◇  What's the database user name?
     │  vendure_user

    输入您之前创建的 PostgreSQL 数据库用户密码

     ◇  What's the database password?
     │  strong-password

    按下可保留为默认管理员用户名。将用户名更改为所需值ENTERsuperadmin

     ◇  What identifier do you want to use for the superadmin user?
     │  superadmin
     │

    输入您想要的超级管理员用户名或按下以使用默认密码ENTER

     ◇  What password do you want to use for the superadmin user?
     │  superadmin
     │

    Select and press to populate the database with sample product datayesENTER

     ◆  Populate with some sample product data?
     │  ● yes
     │  ○ no
  3. When successful, the installation process should complete with the following output:
     ◇  Server successfully initialized and populated
     │
     ◇   ──────────────────────────────────────────╮
     │                                             │
     │  Success! Created a new Vendure server at:  │
     │                                             │
     │                                             │
     │  /home/example_user/vendure-app             │
     │                                             │
     │                                             │
     │  We suggest that you start by typing:       │
     │                                             │
     │                                             │
     │  $ cd vendure-app                           │
     │  $ npm run dev                              │
     │                                             │
     ├─────────────────────────────────────────────╯
     │
     └  Happy hacking!
  4. List files in your working directory
     $ ls

    Output:

     vendure-app

    Verify that a new directory is available on the listvendure-app

Set Up the Vultr Managed Database for PostgreSQL

  1. Log in to your Vultr Managed Database for PostgreSQL. Replace , , with your actual valuesvultradmin1234host.vultrdb.com
     $ psql -h host.vultrdb.com -d postgres -U vultradmin

    Or, copy and use your database connection string from your Vultr Managed Database for PostgreSQL control panel

    get-postgresql-url

  2. When logged in, create a new database
     defaultdb=> CREATE DATABASE venduredb;

    Output:

     CREATE DATABASE
  3. Quit PostgreSQL console
     defaultdb=> \q

将本地 PostgreSQL 数据库数据迁移到 Vultr PostgreSQL 托管数据库

  1. 使用之前创建的用户和数据库将 PostgreSQL 数据库备份到文件venduredb.sql
     $ pg_dump --no-owner -U vendure_user -d vendure_db -W > venduredb.sql

    出现提示时,输入您之前设置的正确的 Vendure 用户数据库密码

  2. 使用备份文件,将数据库还原到 Vultr PostgreSQL 托管数据库。将 ,, 替换为您的实际详细信息。host.vultrdb.comvendure_dbvultradmin
     $ psql -h host.vultrdb.com -d vendure_db -U vultradmin < venduredb.sql
  3. 恢复完成后,访问 Vultr Managed Database for PostgreSQL
     $ psql -h host.vultrdb.com -d vendure_db -U vultradmin 
  4. 查看 Vendure 数据库表
     vendure_db=> \dt

    输出:

                               List of relations
      Schema |                    Name                     | Type  | Owner 
     --------+---------------------------------------------+-------+-------
      public | address                                     | table | vultradmin
      public | administrator                               | table | vultradmin
      public | asset                                       | table | vultradmin
      public | asset_channels_channel                      | table | vultradmin
      public | asset_tags_tag                              | table | vultradmin
      public | authentication_method                       | table | vultradmin
      public | channel                                     | table | vultradmin
      public | collection                                  | table | vultradmin
      public | collection_asset                            | table | vultradmin
      public | collection_channels_channel                 | table | vultradmin
      public | collection_closure                          | table | vultradmin
      public | collection_product_variants_product_variant | table | vultradmin
      public | collection_translation                      | table | vultradmin
      public | customer                                    | table | vultradmin
      public | customer_channels_channel                   | table | vultradmin
      public | customer_group                              | table | vultradmin
      public | customer_groups_customer_group              | table | vultradmin
      public | facet                                       | table | vultradmin
      public | facet_channels_channel                      | table | vultradmin
     :

    Enter 退出 PostgreSQL 寻呼机Q

  5. 退出 PostgreSQL 控制台
     # \q
  6. 在 Vultr 客户门户中,下载 Vultr Managed Database for PostgreSQL 签名证书文件ca-certificate.crtdownload-certificate
  7. 下载后,在终端会话中使用文件并将其上传到服务器scp
     $ scp ca-certificate.crt example_user@SERVER-IP:/home/example_user/
  8. 上传时,请验证证书文件在用户主目录中是否可用
     $ ls

    输出:

     ca-certificate.crt
  9. 将证书文件移动到目录中。/usr/local/share/ca-certificates/
     $ sudo mv ca-certificate.crt /usr/local/share/ca-certificates/
  10. 编辑文件vendure-config.ts
     $ nano src/vendure-config.ts
  11. 从文件顶部导入方法readFileSyncnode:fs
     import { readFileSync } from 'node:fs';
  12. 将以下声明添加到该部分dbConnectionOptions
     ssl: {
         rejectUnauthorized: true,
         ca: readFileSync('/usr/local/share/ca-certificates/ca-certificate.crt').toString(),
     },

    您编辑的部分应如下所示:dbConnectionOptions

     dbConnectionOptions: {
         type: 'postgres',
         // See the README.md "Migrations" section for an explanation of
         // the `synchronize` and `migrations` options.
         synchronize: false,
         migrations: [path.join(__dirname, './migrations/*.+(js|ts)')],
         logging: false,
         database: process.env.DB_NAME,
         schema: process.env.DB_SCHEMA,
         host: process.env.DB_HOST,
         port: +process.env.DB_PORT,
         username: process.env.DB_USERNAME,
         password: process.env.DB_PASSWORD,
         ssl: {
             rejectUnauthorized: true,
             ca: readFileSync('/usr/local/share/ca-certificates/ca-certificate.crt').toString(),
         },
     },

    保存并关闭文件。

切换 Vendure PostgreSQL 数据库配置

  1. 切换到目录vendure-app
     $ cd vendure-app
  2. 编辑文件.env
     $ nano .env
  3. 使用 Vultr Managed Database for PostgreSQL 详细信息更新以下现有变量
     DB_HOST=host.vultrdb.com   
     DB_PORT=1234
     DB_NAME=vendure_db
     DB_USERNAME=vultradmin
     DB_PASSWORD=managed-db-password

    Save and close the file.

Store Vendure Assets in Object Storage

Create a New Bucket

  1. Log in to the Vultr customer portal
  2. Navigate to Products -> Cloud Storage -> Object Storage.product-object-storage-menu
  3. Access your Vultr Object Storage control panelobject-storage
  4. Navigate to Bucketsbucket-tab
  5. Click the Create Bucket button, and assign the bucket a name of your choice. For example vendurecreate-bucket

Sync the Assets to Vultr Object Storage

  1. View your working directory
     $ pwd

    Verify that you're operating in the directory, or switch to the directoryvendure-app

     $ cd vendure-app
  2. Using the tool, synchronize the Vendure assets directory to your Vultr Object Storage buckets3cmd
     $ s3cmd sync static/assets/ s3://vendure/

    Verify that the file transfer completes successfully

Add the Vultr Object Storage Configuration

  1. Using , install the and packagesnpm@aws-sdk/client-s3@aws-sdk/lib-storage
     $ npm install @aws-sdk/client-s3 @aws-sdk/lib-storage --save
  2. Edit the fileenvironment.d.ts
     $ nano src/environment.d.ts
  3. Add the following configurations within the functionProcessEnv
     S3_ENDPOINT: string;
     S3_ACCESS_KEY_ID: string;
     S3_SECRET_ACCESS_KEY: string;
     S3_BUCKET_NAME: string;

    Save and close the file.

    Your edited file should look like the one below:

     namespace NodeJS {
         interface ProcessEnv {
             APP_ENV: string;
             COOKIE_SECRET: string;
             SUPERADMIN_USERNAME: string;
             SUPERADMIN_PASSWORD: string;
             DB_HOST: string;
             DB_PORT: number;
             DB_NAME: string;
             DB_USERNAME: string;
             DB_PASSWORD: string;
             DB_SCHEMA: string;
             S3_ENDPOINT: string;
             S3_ACCESS_KEY_ID: string;
             S3_SECRET_ACCESS_KEY: string;
             S3_BUCKET_NAME: string;
         }
  4. Edit the file:.env
     $ nano .env
  5. Add the following environment variables to the file. Replace the placeholder values with your actual Vultr Object Storage details
     S3_ENDPOINT=https://YOUR_VULTR_OBJECT_STORAGE_HOST
     S3_ACCESS_KEY_ID=YOUR_VULTR_OBJECT_STORAGE_ACCESS_KEY
     S3_SECRET_ACCESS_KEY=YOUR_VULTR_OBJECT_STORAGE_SECRET_KEY
     S3_BUCKET_NAME=YOUR_VULTR_OBJECT_STORAGE_BUCKET_NAME

    Save and close the file.

    You can view your Vultr Object Storage details on the instance overview section

    s3-credentials

  6. Back up the original filevendure-config.ts
     $ mv src/vendure-config.ts src/vendure-config.ORIG
  7. Create the file again
     $ nano src/vendure-config.ts 
  8. Add the following updated contents to the file. Replace with your actual domainexample.com
     import {
         dummyPaymentHandler,
         DefaultJobQueuePlugin,
         DefaultSearchPlugin,
         VendureConfig,
     } from '@vendure/core';
     import { AssetServerPlugin, configureS3AssetStorage } from '@vendure/asset-server-plugin';
     import { defaultEmailHandlers, EmailPlugin } from '@vendure/email-plugin';
     import { AdminUiPlugin } from '@vendure/admin-ui-plugin';
     import 'dotenv/config';
     import path from 'path';
    
     const IS_DEV = process.env.APP_ENV === 'dev';
    
     export const config: VendureConfig = {
         apiOptions: {
             port: 3000,
             adminApiPath: 'admin-api',
             shopApiPath: 'shop-api',
             // The following options are useful in development mode,
             // but are best turned off for production for security
             // reasons.
             ...(IS_DEV ? {
                 adminApiPlayground: {
                     settings: { 'request.credentials': 'include' } as any,
                 },
                 adminApiDebug: true,
                 shopApiPlayground: {
                     settings: { 'request.credentials': 'include' } as any,
                 },
                 shopApiDebug: true,
             } : {}),
         },
         authOptions: {
             tokenMethod: ['bearer', 'cookie'],
             superadminCredentials: {
                 identifier: process.env.SUPERADMIN_USERNAME,
                 password: process.env.SUPERADMIN_PASSWORD,
             },
             cookieOptions: {
               secret: process.env.COOKIE_SECRET,
             },
         },
         dbConnectionOptions: {
             type: 'postgres',
             // See the README.md "Migrations" section for an explanation of
             // the `synchronize` and `migrations` options.
             synchronize: false,
             migrations: [path.join(__dirname, './migrations/*.+(js|ts)')],
             logging: false,
             database: process.env.DB_NAME,
             schema: process.env.DB_SCHEMA,
             host: process.env.DB_HOST,
             port: +process.env.DB_PORT,
             username: process.env.DB_USERNAME,
             password: process.env.DB_PASSWORD,
         },
         paymentOptions: {
             paymentMethodHandlers: [dummyPaymentHandler],
         },
         // When adding or altering custom field definitions, the database will
         // need to be updated. See the "Migrations" section in README.md.
         customFields: {},
         plugins: [
             AssetServerPlugin.init({
                 route: 'assets',
                 assetUploadDir: path.join(__dirname, '../static/assets'),
                 assetUrlPrefix: IS_DEV ? undefined : 'https://example.com/assets/',
                 storageStrategyFactory: configureS3AssetStorage({
                     bucket: process.env.S3_BUCKET_NAME,
                     credentials: {
                         accessKeyId: process.env.S3_ACCESS_KEY_ID,
                         secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
                     },
                     nativeS3Configuration: {
                         endpoint: process.env.S3_ENDPOINT,
                         forcePathStyle: true,
                         signatureVersion: 'v4',
                         region: 'eu-west-1',
                     },
                 }),
             }),
             DefaultJobQueuePlugin.init({ useDatabaseForBuffer: true }),
             DefaultSearchPlugin.init({ bufferUpdates: false, indexStockStatus: true }),
             EmailPlugin.init({
                 devMode: true,
                 outputPath: path.join(__dirname, '../static/email/test-emails'),
                 route: 'mailbox',
                 handlers: defaultEmailHandlers,
                 templatePath: path.join(__dirname, '../static/email/templates'),
                 globalTemplateVars: {
                     // The following variables will change depending on your storefront implementation.
                     // Here we are assuming a storefront running at http://localhost:8080.
                     fromAddress: '"example" <noreply@example.com>',
                     verifyEmailAddressUrl: 'http://localhost:8080/verify',
                     passwordResetUrl: 'http://localhost:8080/password-reset',
                     changeEmailAddressUrl: 'http://localhost:8080/verify-email-address-change'
                 },
             }),
             AdminUiPlugin.init({
                 route: 'admin',
                 port: 3002,
             }),
         ],
     };

    Save and close the file.

    The above configuration imports the and assets to Vendure. Then, it defines the Vendure URL in the section.configureS3AssetStorageasset-server-pluginexample.comAssetServerPlugin.init({

Store the Job Queue and Session Cache in Redis®

Vendure keeps the job queue in the PostgreSQL database by default. To store the jobs in a Vultr Managed Database for Caching, use the BullMQ job queue plugin as described in the steps below.

Set Up the Job Queue

  1. Install the BullMQ job queue plugin
     $ npm install @vendure/job-queue-plugin bullmq@1 --save
  2. Edit the fileenvironment.d.ts
     $ nano src/environment.d.ts
  3. Add the following declarations to the interface after your directivesProcessEnvS3
     REDIS_HOST: string;
     REDIS_PORT: number;
     REDIS_USERNAME: string;
     REDIS_PASSWORD: string;

    Save and close the file.

  4. Edit the filevendure-config.ts
     $ nano src/vendure-config.ts
  5. Add the import directive at the top of the fileBullMQJobQueuePlugin
     import { BullMQJobQueuePlugin } from '@vendure/job-queue-plugin/package/bullmq';

    In the section, find the initialization directiveplugins:DefaultJobQueuePlugin

     DefaultJobQueuePlugin.init({ useDatabaseForBuffer: true }),

    Replace it with the following declarationsBullMQJobQueuePlugin

     BullMQJobQueuePlugin.init({
         connection: {
             port: process.env.REDIS_PORT,
             host: process.env.REDIS_HOST,
             username: process.env.REDIS_USERNAME,
             password: process.env.REDIS_PASSWORD,
             tls: {},
         }
     }),

    Save and close the file.

  6. Edit the file.env
     $ nano .env
  7. Add the following environment variables at the end of the file. Replace the placeholder values with your Vultr Managed Database for Caching details
     REDIS_HOST=host.vultrd.com
     REDIS_PORT=1234
     REDIS_USERNAME=admin
     REDIS_PASSWORD=strong-password

    You can find your Vultr Managed Database for Caching details on your instance overview section

    redis-connection-details

Configure Session Cache

Vendure stores the session object cache in your system memory. It's fast and suitable for a single-instance deployment. However, for horizontal scaling or multi-instance deployment, you must store the session cache to an external data store such as a Vultr Managed Database for Caching. To enable Redis®, create a custom session cache strategy as described below.

  1. Create a new file in the directoryredis-session-cache-strategy.tsplugins
     $ nano src/plugins/redis-session-cache-strategy.ts
  2. Add the following configurations to the file
     import { CachedSession, Logger, SessionCacheStrategy, VendurePlugin } from '@vendure/core';
     import { Redis, RedisOptions } from 'ioredis';
    
     export interface RedisSessionCachePluginOptions {
       namespace?: string;
       redisOptions?: RedisOptions;
     }
     const loggerCtx = 'RedisSessionCacheStrategy';
     const DEFAULT_NAMESPACE = 'vendure-session-cache';
    
     export class RedisSessionCacheStrategy implements SessionCacheStrategy {
       private client: Redis;
       constructor(private options: RedisSessionCachePluginOptions) {}
    
       init() {
         this.client = new Redis(this.options.redisOptions as RedisOptions);
         this.client.on('error', err => Logger.error(err.message, loggerCtx, err.stack));
       }
    
       async get(sessionToken: string): Promise<CachedSession | undefined> {
         const retrieved = await this.client.get(this.namespace(sessionToken));
         if (retrieved) {
           try {
             return JSON.parse(retrieved);
           } catch (e: any) {
             Logger.error(`Could not parse cached session data: ${e.message}`, loggerCtx);
           }
         }
       }
    
       async set(session: CachedSession) {
         await this.client.set(this.namespace(session.token), JSON.stringify(session));
       }
    
       async delete(sessionToken: string) {
         await this.client.del(this.namespace(sessionToken));
       }
    
       clear() {
         // not implemented
       }
    
       private namespace(key: string) {
         return `${this.options.namespace ?? DEFAULT_NAMESPACE}:${key}`;
       }
     }
    
     @VendurePlugin({
       configuration: config => {
         config.authOptions.sessionCacheStrategy = new RedisSessionCacheStrategy(
           RedisSessionCachePlugin.options,
         );
         return config;
       },
     })
     export class RedisSessionCachePlugin {
       static options: RedisSessionCachePluginOptions;
       static init(options: RedisSessionCachePluginOptions) {
         this.options = options;
         return this;
       }
     }

    Save and close the file

  3. Edit the filevendure-config.ts
     $ nano src/vendure-config.ts
  4. Add the directive to the import sectionRedisSessionCachePlugin
     import { RedisSessionCachePlugin } from './plugins/redis-session-cache-strategy';

    Within the section, add the following code after to initialize the Plugins: [AdminUiPlugin.init(...)RedisSessionCachePlugin

     RedisSessionCachePlugin.init({
         redisOptions: {
             port: process.env.REDIS_PORT,
             host: process.env.REDIS_HOST,
             username: process.env.REDIS_USERNAME,
             password: process.env.REDIS_PASSWORD,
             tls: {},
         }
     }),

    Save and close the file.

Build for Vendure Production

  1. Edit the file.env
     $ nano .env
  2. Change the value from to APP_ENVdevproduction
     APP_ENV=production

    Save and close the file.

  3. Install the Vendure harden plugin that locks down your schema and protects your shop API from malicious queries
     $ npm install @vendure/harden-plugin --save
  4. Edit the filevendure-config.ts
     $ nano src/vendure-config.ts
  5. Add the following import directive to the sectionharden pluginimport
     import { HardenPlugin } from '@vendure/harden-plugin';

    Within the section, add the following code to initialize the harden pluginPlugins:

     HardenPlugin.init({
         maxQueryComplexity: 500,
         apiMode: IS_DEV ? 'dev' : 'prod',
     }),

    Save and close the file.

    The edited file should look like the one below:vendure-config.ts

     import {
         dummyPaymentHandler,
         DefaultJobQueuePlugin,
         DefaultSearchPlugin,
         VendureConfig,
     } from '@vendure/core';
     import { defaultEmailHandlers, EmailPlugin } from '@vendure/email-plugin';
     import { AssetServerPlugin, configureS3AssetStorage } from '@vendure/asset-server-plugin';
     import { AdminUiPlugin } from '@vendure/admin-ui-plugin';
     import { BullMQJobQueuePlugin } from '@vendure/job-queue-plugin/package/bullmq';
     import { RedisSessionCachePlugin } from './plugins/redis-session-cache-strategy';
     import { HardenPlugin } from '@vendure/harden-plugin';
     import 'dotenv/config';
     import path from 'path';
     import { readFileSync } from 'node:fs';
    
     const IS_DEV = process.env.APP_ENV === 'dev';
    
     export const config: VendureConfig = {
         apiOptions: {
             port: 3000,
             adminApiPath: 'admin-api',
             shopApiPath: 'shop-api',
             // The following options are useful in development mode,
             // but are best turned off for production for security
             // reasons.
             ...(IS_DEV ? {
                 adminApiPlayground: {
                     settings: { 'request.credentials': 'include' } as any,
                 },
                 adminApiDebug: true,
                 shopApiPlayground: {
                     settings: { 'request.credentials': 'include' } as any,
                 },
                 shopApiDebug: true,
             } : {}),
         },
         authOptions: {
             tokenMethod: ['bearer', 'cookie'],
             superadminCredentials: {
                 identifier: process.env.SUPERADMIN_USERNAME,
                 password: process.env.SUPERADMIN_PASSWORD,
             },
             cookieOptions: {
               secret: process.env.COOKIE_SECRET,
             },
         },
         dbConnectionOptions: {
             type: 'postgres',
             // See the README.md "Migrations" section for an explanation of
             // the `synchronize` and `migrations` options.
             synchronize: false,
             migrations: [path.join(__dirname, './migrations/*.+(js|ts)')],
             logging: false,
             database: process.env.DB_NAME,
             schema: process.env.DB_SCHEMA,
             host: process.env.DB_HOST,
             port: +process.env.DB_PORT,
             username: process.env.DB_USERNAME,
             password: process.env.DB_PASSWORD,
         ssl: {
                 rejectUnauthorized: true,
                 ca: readFileSync('/usr/local/share/ca-certificates/ca-certificate.crt').toString(),
             },
         },
         paymentOptions: {
             paymentMethodHandlers: [dummyPaymentHandler],
         },
         // When adding or altering custom field definitions, the database will
         // need to be updated. See the "Migrations" section in README.md.
         customFields: {},
         plugins: [
             AssetServerPlugin.init({
                 route: 'assets',
                 assetUploadDir: path.join(__dirname, '../static/assets'),
                 assetUrlPrefix: IS_DEV ? undefined : 'https://example.hisman.org/assets/',
                 storageStrategyFactory: configureS3AssetStorage({
                     bucket: process.env.S3_BUCKET_NAME,
                     credentials: {
                         accessKeyId: process.env.S3_ACCESS_KEY_ID,
                         secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
                     },
                     nativeS3Configuration: {
                         endpoint: process.env.S3_ENDPOINT,
                         forcePathStyle: true,
                         signatureVersion: 'v4',
                         region: 'eu-west-1',
                     },
                 }),
             }),
         RedisSessionCachePlugin.init({
                 redisOptions: {
                     port: process.env.REDIS_PORT,
                     host: process.env.REDIS_HOST,
                     username: process.env.REDIS_USERNAME,
                     password: process.env.REDIS_PASSWORD,
                     tls: {},
                 }
             }),
             BullMQJobQueuePlugin.init({
                 connection: {
                     port: process.env.REDIS_PORT,
                     host: process.env.REDIS_HOST,
                     username: process.env.REDIS_USERNAME,
                     password: process.env.REDIS_PASSWORD,
                     tls: {},
                 }
             }),
             HardenPlugin.init({
                 maxQueryComplexity: 500,
                 apiMode: IS_DEV ? 'dev' : 'prod',
             }),
             DefaultSearchPlugin.init({ bufferUpdates: false, indexStockStatus: true }),
             EmailPlugin.init({
                 devMode: true,
                 outputPath: path.join(__dirname, '../static/email/test-emails'),
                 route: 'mailbox',
                 handlers: defaultEmailHandlers,
                 templatePath: path.join(__dirname, '../static/email/templates'),
                 globalTemplateVars: {
                     // The following variables will change depending on your storefront implementation.
                     // Here we are assuming a storefront running at http://localhost:8080.
                     fromAddress: '"example" <noreply@example.com>',
                     verifyEmailAddressUrl: 'http://localhost:8080/verify',
                     passwordResetUrl: 'http://localhost:8080/password-reset',
                     changeEmailAddressUrl: 'http://localhost:8080/verify-email-address-change'
                 },
             }),
             AdminUiPlugin.init({
                 route: 'admin',
                 port: 3002,
             }),
         ],
     };
  6. Build the Vendure application
     $ npm run build

    npm writes the build files to the directorydist

Run the Production Vendure App

  1. Install the packagePM2
     $ sudo npm install pm2 -g
  2. Run the Vendure app server in cluster mode
     $ pm2 start ./dist/index.js -i max
  3. Run the Vendure app worker in cluster mode
     $ pm2 start ./dist/index-worker.js -i max

    Output:

     [PM2] Starting /home/user/vendure-app/dist/index-worker.js in cluster_mode (0 instance)
     [PM2] Done.
     ┌────┬─────────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
     │ id │ name            │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
     ├────┼─────────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
     │ 0  │ index           │ default     │ 0.1.0   │ cluster │ 232015   │ 47s    │ 0    │ online    │ 0%       │ 135.0mb  │ user     │ disabled │
     │ 1  │ index-worker    │ default     │ 0.1.0   │ cluster │ 232052   │ 0s     │ 0    │ online    │ 0%       │ 36.5mb   │ user     │ disabled │
     └────┴─────────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
  4. Generate and run a startup script to start PM2 when the server reboots
     $ pm2 startup

    Output:

     [PM2] Init System found: systemd
     [PM2] To setup the Startup Script, copy/paste the following command:
     sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u example_user --hp /home/example_user
  5. Save all the running processes
     $ pm2 save
  6. Using , test that your Vendure app runs on the default port wget3000
     $ wget -S http://localhost:3000/admin/

    Output:

     --2023-08-18 10:30:28--  http://localhost:3000/admin/
     Resolving localhost (localhost)... ::1, 127.0.0.1
     Connecting to localhost (localhost)|::1|:3000... connected.
     HTTP request sent, awaiting response...
       HTTP/1.1 200 OK
       X-Powered-By: Express
       Vary: Origin
       Access-Control-Allow-Credentials: true
       Access-Control-Expose-Headers: vendure-auth-token
       Accept-Ranges: bytes
       Cache-Control: public, max-age=0
       Last-Modified: Fri, 18 Aug 2023 09:34:01 GMT
       ETag: W/"268-18a07fe9b82"
       Content-Type: text/html; charset=UTF-8
       Content-Length: 616
       Date: Fri, 18 Aug 2023 10:30:28 GMT
       Connection: keep-alive
       Keep-Alive: timeout=5
     Length: 616 [text/html]
     Saving to: ‘index.html’

    If Vendure fails to run and listen on port . Start it using the following command to view the runtime log and catch any errors3000

     $ npm run dev

    Output:

     [server] info 8/31/23, 8:25 PM - [BullMQJobQueuePlugin] Checking Redis connection... 
     [server] info 8/31/23, 8:25 PM - [BullMQJobQueuePlugin] Connected to Redis ✔ 
     [server] info 8/31/23, 8:25 PM - [NestApplication] Nest application successfully started 
     [server] info 8/31/23, 8:25 PM - [Vendure Server] ================================================ 
     [server] info 8/31/23, 8:25 PM - [Vendure Server] Vendure server (v2.0.6) now running on port 3000 
     [server] info 8/31/23, 8:25 PM - [Vendure Server] ------------------------------------------------ 
     [server] info 8/31/23, 8:25 PM - [Vendure Server] Shop API:     http://localhost:3000/shop-api 
     [server] info 8/31/23, 8:25 PM - [Vendure Server] Admin API:    http://localhost:3000/admin-api 
     [server] info 8/31/23, 8:25 PM - [Vendure Server] Asset server: http://localhost:3000/assets 
     [server] info 8/31/23, 8:25 PM - [Vendure Server] Dev mailbox:  http://localhost:3000/mailbox 
     [server] info 8/31/23, 8:25 PM - [Vendure Server] Admin UI:     http://localhost:3000/admin 
     [server] info 8/31/23, 8:25 PM - [Vendure Server] ================================================ 

    When successful, verify that Vendure runs on port 3000

Configure Nginx as a Reverse Proxy

To securely access the Vendure app through your domain name, configure Nginx as a reverse proxy to handle connections to the backend port as described below.3000

  1. Install Nginx
     $ sudo apt install nginx
  2. Disable the default Nginx configuration
     $ sudo unlink /etc/nginx/sites-enabled/default
  3. Create a new Nginx virtual host configuration file
     $ sudo nano /etc/nginx/sites-available/vendure
  4. Add the following configurations to the file. Replace with your actual domainexample.com
     server {
         listen 80;
    
         server_name example.com;
    
         location / {
             proxy_pass http://localhost:3000;
             proxy_http_version 1.1;
             proxy_set_header Upgrade $http_upgrade;
             proxy_set_header Connection 'upgrade';
             proxy_set_header Host $host;
             proxy_set_header X-Real-IP $remote_addr;
             proxy_set_header X-Forwarded-Proto $scheme;
             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
             proxy_cache_bypass $http_upgrade;
         }
     }

    Save and close the file.

  5. Enable the Nginx configuration file
     $ sudo ln -s /etc/nginx/sites-available/vendure /etc/nginx/sites-enabled/
  6. Test your configurations from syntax errors
     $ sudo nginx -t
  7. Reload Nginx configurations to save changes
     $ sudo nginx -s reload

Security

To secure Vendure for production use, allow Nginx to accept incoming connections on HTTP port and the HTTPS port . Then, securely redirect all HTTP requests to HTTPS by generating SSL certificates as described in the steps below.80443

Configure the Firewall

  1. Allow SSH port connection to the server
     $ sudo ufw allow 'OpenSSH'
  2. Allow the Nginx HTTP and HTTPS ports profile
     $ sudo ufw allow 'Nginx Full'
  3. Enable the firewall
     $ sudo ufw enable
  4. Verify the firewall status
     $ sudo ufw status

    Output:

     Status: active
    
     To                         Action      From
     --                         ------      ----
     22/tcp                     ALLOW       Anywhere
     Nginx Full                 ALLOW       Anywhere
     22/tcp (v6)                ALLOW       Anywhere (v6)
     Nginx Full (v6)            ALLOW       Anywhere (v6)

Secure Vendure with Let's Encrypt SSL Certificates

  1. Using , install the Certbot Let's Encrypt client toolsnap
     $ sudo snap install --classic certbot
  2. Create a symbolic link for the system wide Certbot command to /usr/bin
     $ sudo ln -s /snap/bin/certbot /usr/bin/certbot
  3. Generate your Let's Encrypt SSL certificate. Replace , with your domain, and email address respectivelyexample.comhello@example.com
     $ sudo certbot --nginx -d example.com -m hello@example.com --agree-tos
  4. Verify that Certbot auto renews the SSL certificate upon expiry
     $ sudo certbot renew --dry-run

Test the Application

  1. Using a web browser such as Chrome, access your Vendure shop administrator page
     https://example.com/admin

    vendure-login

    Log in with the credentials you set earlier. When successful, the administrator dashboard should displaysuperadmin

    vendure-dashboard

  2. Navigate to the Inventory menu. Verify that all sample products display on the page. Try to update or delete any of them.vendure-inventory-page
  3. Navigate to the Assets menu and upload a sample imagevendure-assets-page
  4. 在 Vultr 客户门户会话中,查看 Vultr 对象存储桶。验证映像在存储桶中是否可用vendure-bucket
  5. 除了 Vendure 管理员页面,还可以访问以下 GraphQL API 端点:
    • 管理员 GraphQL API:https://example.com/admin-api
    • 购买 GraphQL API:https://example.com/shop-api

当您设计 Vendure 商店时,前端界面会随着您的产品和设计而激活。

结论

在本指南中,您安装了 Vendure 应用程序并将其部署到生产环境。您还将 Vendure 配置为使用 Vultr 对象存储、用于 PostgreSQL 的 Vultr 托管数据库和用于缓存的 Vultr 托管数据库。有关 Vendure 的更多信息,请访问官方文档

THE END