Vendure 是一個開源的無頭商務(wù)框架,允許構(gòu)建由 Node.js、TypeScript 和 GraphQL 提供支持的生產(chǎn)就緒電子商務(wù)應(yīng)用程序。它的工作原理是通過 API 公開所有應(yīng)用程序功能。Vendure 在 GraphQL API 上公開了所有商店前端功能,并且不提供默認(rèn)的商店前端界面。這提高了開發(fā)人員的靈活性,因?yàn)樗试S您使用任何前端技術(shù)創(chuàng)建店面。
本文介紹如何在 Ubuntu 22.04 Vultr 服務(wù)器上部署 Vendure。您需要將該框架與 Vultr Object Storage(PostgreSQL 托管數(shù)據(jù)庫)和 Redis? 集成,以構(gòu)建生產(chǎn)就緒型應(yīng)用程序。
準(zhǔn)備工作:
# su example_user
$ sudo apt install s3cmd
Vendure 需要 Node.js 版本 16.x 或更高版本。按照以下步驟中的說明安裝最新的 Node.js 版本。
$ sudo apt update
keyrings
$ sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
apt
20
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
$ sudo apt update
$ sudo apt install nodejs -y
$ nodejs --version
輸出:
v20.5.1
要在服務(wù)器上安裝 Vendure,您需要將應(yīng)用程序數(shù)據(jù)臨時保存在本地 PostgreSQL 數(shù)據(jù)庫服務(wù)器上。稍后,您可以先將數(shù)據(jù)遷移到 Vultr Managed Database for MySQL,然后再將應(yīng)用程序部署到生產(chǎn)環(huán)境。如下所述設(shè)置本地?cái)?shù)據(jù)庫。
$ psql --version
輸出:
psql (PostgreSQL) 14.16 (Ubuntu 14.16-0ubuntu0.22.04.1)
pg_hba.conf
nano
$ sudo nano /etc/postgresql/14/main/pg_hba.conf
# "local" is for Unix domain socket connections only
local all all peer
peer
md5
local all all md5
保存并關(guān)閉文件
$ sudo systemctl restart postgresql
$ sudo -u postgres psql
postgres=# CREATE DATABASE vendure_db;
輸出:
CREATE DATABASE
postgres=# CREATE USER vendure_user WITH ENCRYPTED PASSWORD 'strong-password';
輸出:
CREATE ROLE
postgres=# GRANT ALL PRIVILEGES ON DATABASE vendure_db TO vendure_user;
輸出:
GRANT
postgres-# \q
npx
@vendure/create
$ npx @vendure/create vendure-app
@vendure/create
Need to install the following packages:
@vendure/create@2.0.2
Ok to proceed? (y) y
選擇為正在使用的數(shù)據(jù)庫Postgres
◆ Which database are you using?
│ ○ MySQL
│ ○ MariaDB
│ ● Postgres
│ ○ SQLite
│ ○ SQL.js
按下可設(shè)置為 PostgreSQL 主機(jī)名ENTERlocalhost
◆ What's the database host address?
│ localhost
按下可保留為 PostgreSQL 端口號ENTER5432
◆ What port is the database listening on?
│ 5432
輸入您之前創(chuàng)建的 PostgreSQL 數(shù)據(jù)庫名稱
◆ What's the name of the database?
│ vendure_db
按下可將 PostgreSQL 架構(gòu)設(shè)置為 publicENTER
What's the schema name we should use?
│ public
輸入您之前創(chuàng)建的 PostgreSQL 用戶
◇ What's the database user name?
│ vendure_user
輸入您之前創(chuàng)建的 PostgreSQL 數(shù)據(jù)庫用戶密碼
◇ What's the database password?
│ strong-password
按下可保留為默認(rèn)管理員用戶名。將用戶名更改為所需值ENTERsuperadmin
◇ What identifier do you want to use for the superadmin user?
│ superadmin
│
輸入您想要的超級管理員用戶名或按下以使用默認(rèn)密碼ENTER
◇ What password do you want to use for the superadmin user?
│ superadmin
│
Select?and press?to populate the database with sample product datayes
ENTER
◆ Populate with some sample product data?
│ ● yes
│ ○ no
◇ 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!
$ ls
Output:
vendure-app
Verify that a new?directory is available on the listvendure-app
vultradmin
1234
host.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
defaultdb=> CREATE DATABASE venduredb;
Output:
CREATE DATABASE
defaultdb=> \q
venduredb.sql
$ pg_dump --no-owner -U vendure_user -d vendure_db -W > venduredb.sql
出現(xiàn)提示時,輸入您之前設(shè)置的正確的 Vendure 用戶數(shù)據(jù)庫密碼
host.vultrdb.com
vendure_db
vultradmin
$ psql -h host.vultrdb.com -d vendure_db -U vultradmin < venduredb.sql
$ psql -h host.vultrdb.com -d vendure_db -U vultradmin
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 尋呼機(jī)Q
# \q
ca-certificate.crt
scp
$ scp ca-certificate.crt example_user@SERVER-IP:/home/example_user/
$ ls
輸出:
ca-certificate.crt
/usr/local/share/ca-certificates/
$ sudo mv ca-certificate.crt /usr/local/share/ca-certificates/
vendure-config.ts
$ nano src/vendure-config.ts
readFileSync
node:fs
import { readFileSync } from 'node:fs';
dbConnectionOptions
ssl: {
rejectUnauthorized: true,
ca: readFileSync('/usr/local/share/ca-certificates/ca-certificate.crt').toString(),
},
您編輯的部分應(yīng)如下所示: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(),
},
},
保存并關(guān)閉文件。
vendure-app
$ cd vendure-app
.env
$ nano .env
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.
vendure
$ pwd
Verify that you're operating in the?directory, or switch to the directoryvendure-app
$ cd vendure-app
s3cmd
$ s3cmd sync static/assets/ s3://vendure/
Verify that the file transfer completes successfully
npm
@aws-sdk/client-s3
@aws-sdk/lib-storage
$ npm install @aws-sdk/client-s3 @aws-sdk/lib-storage --save
environment.d.ts
$ nano src/environment.d.ts
ProcessEnv
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;
}
.env
$ nano .env
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
vendure-config.ts
$ mv src/vendure-config.ts src/vendure-config.ORIG
$ nano src/vendure-config.ts
example.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.configureS3AssetStorage
asset-server-plugin
example.com
AssetServerPlugin.init({
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.
$ npm install @vendure/job-queue-plugin bullmq@1 --save
environment.d.ts
$ nano src/environment.d.ts
ProcessEnv
S3
REDIS_HOST: string;
REDIS_PORT: number;
REDIS_USERNAME: string;
REDIS_PASSWORD: string;
Save and close the file.
vendure-config.ts
$ nano src/vendure-config.ts
BullMQJobQueuePlugin
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.
.env
$ nano .env
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
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.
redis-session-cache-strategy.ts
plugins
$ nano src/plugins/redis-session-cache-strategy.ts
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
vendure-config.ts
$ nano src/vendure-config.ts
RedisSessionCachePlugin
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.
.env
$ nano .env
APP_ENV
dev
production
APP_ENV=production
Save and close the file.
$ npm install @vendure/harden-plugin --save
vendure-config.ts
$ nano src/vendure-config.ts
harden plugin
import
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,
}),
],
};
$ npm run build
npm
?writes the build files to the?directorydist
PM2
$ sudo npm install pm2 -g
$ pm2 start ./dist/index.js -i max
$ 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 │
└────┴─────────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
$ 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
$ pm2 save
wget
3000
$ 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
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
$ sudo apt install nginx
$ sudo unlink /etc/nginx/sites-enabled/default
$ sudo nano /etc/nginx/sites-available/vendure
example.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.
$ sudo ln -s /etc/nginx/sites-available/vendure /etc/nginx/sites-enabled/
$ sudo nginx -t
$ sudo nginx -s reload
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.80
443
$ sudo ufw allow 'OpenSSH'
$ sudo ufw allow 'Nginx Full'
$ sudo ufw enable
$ 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)
snap
$ sudo snap install --classic certbot
/usr/bin
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot
example.com
hello@example.com
$ sudo certbot --nginx -d example.com -m hello@example.com --agree-tos
$ sudo certbot renew --dry-run
https://example.com/admin
Log in with the?credentials you set earlier. When successful, the administrator dashboard should displaysuperadmin
https://example.com/admin-api
https://example.com/shop-api
當(dāng)您設(shè)計(jì) Vendure 商店時,前端界面會隨著您的產(chǎn)品和設(shè)計(jì)而激活。
在本指南中,您安裝了 Vendure 應(yīng)用程序并將其部署到生產(chǎn)環(huán)境。您還將 Vendure 配置為使用 Vultr 對象存儲、用于 PostgreSQL 的 Vultr 托管數(shù)據(jù)庫和用于緩存的 Vultr 托管數(shù)據(jù)庫。有關(guān) Vendure 的更多信息,請?jiān)L問官方文檔。
Google BBR 是一款免費(fèi)開源的TCP擁塞控制傳輸控制協(xié)議, 可以使 Linux 服務(wù)器顯著提高吞吐量和減少 TCP 連接的延遲。 項(xiàng)目地址:https://github.com/google/bbr
echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf
sysctl -p
sysctl net.ipv4.tcp_available_congestion_control
如果?返回
net.ipv4.tcp_available_congestion_control = bbr cubic reno
則開啟成功!
lsmod | grep bbr
如果返回
tcp_bbr 20480 14
則啟動?成功
]]>