介绍
Drive 是 AdonisJS 中的一个存储抽象库。 它提供了适用于所有存储提供商的一致 API。
Drive 有一个 S3 驱动程序来支持 S3 兼容的云存储,比如 Vultr Object Storage。 本指南解释了怎样为 Vultr 对象存储配置 AdonisJS Drive 并使用它来存储和读取文件。
先决条件
在开始之前,您应该:
创建一个非root用户 sudo 特权。
安装 Node.js
AdonisJS 至少需要 Node.js 版本 14。您可以使用节点版本管理器 (NVM) 安装最新版本的 Node.js。
安装 NVM:
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
断开并重新连接您的 ssh 会话。
安装 Node.js:
$ nvm install node
检查 Node.js 版本:
$ node -v v19.3.0
创建对象存储
登录到 Vultr 客户门户.
导航 产品 -> 对象.
添加对象存储. 选择区域并为其添加标签。
单击您的 Object Storage 并导航到 Bucket 选项卡。
创建一个 Bucket 并为其命名。
请注意
Hostname
, 这Secret Key
, 这Access Key
和Bucket Name
.
创建新的 AdonisJS 应用程序
转到主目录。
$ cd ~
为您的应用程序创建一个新目录。
$ mkdir app
前往 app
目录并使用 npm init
命令。
$ cd app
$ npm init [email protected] website
选择 Web 项目结构。
当它提示您配置 Webpack Encore 时选择“y”。
它在 website
目录。 对于其余任务,您需要在 website
目录。
$ cd website
安装 Redis
本指南中的示例应用程序使用 Redis 来存储图像文件名。
安装 Redis:
$ sudo apt install redis-server
设置 Redis 持久化模式:
打开 Redis 配置文件:
$ sudo nano /etc/redis/redis.conf
改变
appendonly no
到appendonly yes
.Save 文件并退出。
重新启动 Redis。
$ sudo systemctl restart redis-server
安装和配置 AdonisJS Redis 包:
$ npm i @adonisjs/redis
$ node ace configure @adonisjs/redis
打开 env.ts
文件:
$ nano env.ts
添加以下规则:
REDIS_CONNECTION: Env.schema.enum(['local'] as const),
REDIS_HOST: Env.schema.string({ format: 'host' }),
REDIS_PORT: Env.schema.number(),
REDIS_PASSWORD: Env.schema.string.optional(),
配置 AdonisJS 驱动器
AdonisJS Drive 有一个 S3 驱动程序,可以与 Vultr 对象存储等兼容 S3 的云存储进行交互。
安装和配置 S3 驱动程序:
$ npm i @adonisjs/drive-s3
$ node ace configure @adonisjs/drive-s3
打开 env.ts
文件:
$ nano env.ts
更新 DRIVE_DISK
规则:
DRIVE_DISK: Env.schema.enum(['local','s3'] as const),
添加以下规则:
S3_KEY: Env.schema.string(),
S3_SECRET: Env.schema.string(),
S3_BUCKET: Env.schema.string(),
S3_REGION: Env.schema.string(),
S3_ENDPOINT: Env.schema.string.optional(),
打开 config/drive.ts
文件:
$ nano config/drive.ts
在里面添加S3配置 disks
目的:
s3: {
driver: 's3',
visibility: 'public',
key: Env.get('S3_KEY'),
secret: Env.get('S3_SECRET'),
region: Env.get('S3_REGION'),
bucket: Env.get('S3_BUCKET'),
endpoint: Env.get('S3_ENDPOINT'),
}
打开 .env
文件:
$ nano .env
更新 DRIVE_DISK
值 s3
:
DRIVE_DISK=s3
添加 Vultr 对象存储凭证:
S3_KEY=
S3_SECRET=
S3_BUCKET=adonis-drive
S3_REGION=sgp1
S3_ENDPOINT=https://sgp1.vultrobjects.com
S3_KEY
是您的 Vultr 对象存储访问密钥。S3_SECRET
是您的 Vultr 对象存储密钥。S3_BUCKET` 是您的 Vultr 对象存储桶名称。
S3_ENDPOINT
是您的 Vultr 对象存储主机名。S3_REGION
是您的 Vultr 对象存储区域。
添加顺风 CSS
本指南使用 Tailwind CSS 作为 CSS 框架。 通过 NPM 安装 Tailwind CSS 及其依赖项:
$ npm install -D tailwindcss postcss autoprefixer postcss-loader
打开 webpack.config.js
文件:
$ nano webpack.config.js
启用 PostCSS 加载器:
Encore.enablePostCssLoader()
创建并打开 Tailwind CSS 配置文件:
$ npx tailwindcss init -p
$ nano tailwind.config.js
将内容更改为:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./resources/**/*.edge",
"./resources/**/*.js",
],
theme: {
extend: {},
},
plugins: [],
}
打开 resources/css/app.css
文件并将内容替换为 Tailwind CSS 指令:
@tailwind base;
@tailwind components;
@tailwind utilities;
添加 JavaScript
打开 resources/js/app.js
文件并添加以下脚本:
import '../css/app.css'
document.getElementById('fileImage').addEventListener('change',function(){
if( this.files.length > 0 ){
document.getElementById('uploadBtn').removeAttribute('disabled');
}
});
用户选择图像文件后,脚本会启用上传按钮。
创建视图
创建一个 resources/views/gallery.edge
文件:
$ nano resources/views/gallery.edge
添加以下代码:
<html>
<head>
<title>Gallery</title>
@entryPointStyles('app')
</head>
<body>
<div class="max-w-7xl m-auto">
<h1 class="text-3xl font-bold text-gray-900 text-center py-8 uppercase">Gallery</h1>
<form action="" method="post" enctype="multipart/form-data" class="flex flex-wrap text-center justify-center items-start p-4 rounded-lg">
<label class="block py-1">
<input id="fileImage" type="file" name="fileImage" class="block w-full text-sm text-slate-500 pr-6
file:cursor-pointer
file:mr-4 file:py-2 file:px-4
file:rounded-full file:border-0
file:text-sm file:font-semibold
file:bg-indigo-50 file:text-indigo-700
hover:file:bg-indigo-100
"/>
@if (flashMessages.has('errors.fileImage'))
<span class="block text-red-700 py-4 text-left">{{ flashMessages.get('errors.fileImage') }}</span>
@endif
</label>
<button id="uploadBtn" disabled class="rounded border border-transparent bg-indigo-600 px-6 py-2 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-50" type="submit">
Upload Image
</button>
</form>
<div class="grid grid-cols-2 gap-x-4 gap-y-8 sm:grid-cols-3 sm:gap-x-6 lg:grid-cols-4 xl:gap-x-8 ">
@each(image in images)
<div>
<img class="rounded" src="">
</div>
@end
</div>
</div>
@entryPointScripts('app')
</body>
</html>
创建控制器
创建并打开 GalleryController.ts
文件:
$ node ace make:controller GalleryController -e
$ nano app/Controllers/Http/GalleryController.ts
在文件顶部导入库和助手:
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import Drive from '@ioc:Adonis/Core/Drive'
import Redis from '@ioc:Adonis/Addons/Redis'
import { string } from '@ioc:Adonis/Core/Helpers'
import { schema } from '@ioc:Adonis/Core/Validator'
创建 index
行动。 它显示上传表单并列出来自 Vultr 对象存储的所有图像。 它从 Redis 获取图像文件名并调用 getUrl
Drive 库中的方法来获取每个图像的 URL。
public async index({ view }: HttpContextContract) {
const galleryString = await Redis.get('gallery')
const gallery = (galleryString) ? JSON.parse(galleryString) : []
let images:string[] = []
for (const filename of gallery) {
const url = await Drive.getUrl(`gallery/${filename}`)
images.push(url)
}
return view.render('gallery', { images })
}
创建 upload
行动。 它处理用户何时上传他们的图像。 它验证文件,将文件名保存到 Redis,然后使用 moveToDisk
方法。
public async upload({ request, response }: HttpContextContract) {
const imageSchema = schema.create({
fileImage: schema.file({
extnames: ['jpg', 'png', 'gif']
}),
})
const payload = await request.validate({ schema: imageSchema })
const filename = `${string.generateRandom(32)}.${payload.fileImage.extname}`
if (payload.fileImage) {
await payload.fileImage.moveToDisk(", {
name: `gallery/${filename}`
}, 's3')
const galleryString = await Redis.get('gallery')
let gallery:string[] = []
if (galleryString) {
gallery = JSON.parse(galleryString)
}
gallery.push(filename)
await Redis.set('gallery', JSON.stringify(gallery))
}
return response.redirect().toPath("https://www.vultr.com/")
}
您需要从 Redis 保存和获取图像文件名,因为 AdonisJS S3 驱动程序无法获取存储桶或文件夹中的文件列表。 它一次只能获取一个文件。
以下为全文 GalleryController.ts
文件:
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import Drive from '@ioc:Adonis/Core/Drive'
import Redis from '@ioc:Adonis/Addons/Redis'
import { string } from '@ioc:Adonis/Core/Helpers'
import { schema } from '@ioc:Adonis/Core/Validator'
export default class GalleryController {
public async index({ view }: HttpContextContract) {
const galleryString = await Redis.get('gallery')
const gallery = (galleryString) ? JSON.parse(galleryString) : []
let images:string[] = []
for (const filename of gallery) {
const url = await Drive.getUrl(`gallery/${filename}`)
images.push(url)
}
return view.render('gallery', { images })
}
public async upload({ request, response }: HttpContextContract) {
const imageSchema = schema.create({
fileImage: schema.file({
extnames: ['jpg', 'png', 'gif']
}),
})
const payload = await request.validate({ schema: imageSchema })
const filename = `${string.generateRandom(32)}.${payload.fileImage.extname}`
if (payload.fileImage) {
await payload.fileImage.moveToDisk(", {
name: `gallery/${filename}`
}, 's3')
const galleryString = await Redis.get('gallery')
let gallery:string[] = []
if (galleryString) {
gallery = JSON.parse(galleryString)
}
gallery.push(filename)
await Redis.set('gallery', JSON.stringify(gallery))
}
return response.redirect().toPath("https://www.vultr.com/")
}
}
添加路线
打开 start/routes.ts
文件:
$ nano start/routes.ts
添加以下代码:
Route.get("https://www.vultr.com/", 'GalleryController.index')
Route.post("https://www.vultr.com/", 'GalleryController.upload')
测试应用程序
要测试您的应用程序,您需要禁用 Ubuntu 防火墙。
$ sudo ufw disable
您可以在构建用于生产的应用程序时再次启用它。
启动开发服务器:
$ node ace serve --encore-args="--host [VULTR_VPS_IP_ADDRESS]"
打开
https://[VULTR_VPS_IP_ADDRESS]:3333
在浏览器中。您应该看到上传表单。
上传图像。
检查图像是否出现在您的 Vultr 对象存储和上传表单下方的图库中。
按 CTRL+C 停止开发服务器。
使用多个对象存储位置
Vultr 对象存储在多个位置可用。 以下是 Vultr 支持的位置:
阿姆斯特丹:
ams1.vultrobjects.com
新泽西州:
ewr1.vultrobjects.com
硅谷:
sjc1.vultrobjects.com
新加坡:
sgp1.vultrobjects.com
您可以在 AdonisJS 应用程序中使用多个对象存储位置来为您的文件添加冗余。 为此,您为每个位置添加一个磁盘配置 config/drive.ts
文件。 AdonisJS Drive 中的 Disk 表示特定的存储驱动程序和位置。
创建新的对象存储
去 Vultr 客户门户.
导航 产品 -> 对象.
添加对象存储. 选择不同的地区,因为 example阿姆斯特丹。
在新的对象存储中创建一个桶。
请注意
Hostname
, 这Secret Key
, 这Access Key
和Bucket Name
.
配置新磁盘
打开 config/drive.ts
文件:
$ nano config/drive.ts
在 S3 部分添加新的磁盘配置。 您可以将磁盘名称设置为任何名称,例如 example, s3ams
. 为环境变量名称添加后缀,以区别于第一个磁盘。
s3ams: {
driver: 's3',
visibility: 'public',
key: Env.get('S3_KEY_AMS'),
secret: Env.get('S3_SECRET_AMS'),
region: Env.get('S3_REGION_AMS'),
bucket: Env.get('S3_BUCKET_AMS'),
endpoint: Env.get('S3_ENDPOINT_AMS'),
},
打开 .env
文件:
$ nano .env
添加对象存储的凭据:
S3_KEY_AMS=
S3_SECRET_AMS=
S3_BUCKET_AMS=adonis-drive
S3_REGION_AMS=ams1
S3_ENDPOINT_AMS=https://ams1.vultrobjects.com
S3_KEY_AMS
是您的 Vultr 对象存储访问密钥。S3_SECRET_AMS
是您的 Vultr 对象存储密钥。S3_BUCKET_AMS
是您的 Vultr 对象存储桶名称。S3_ENDPOINT_AMS
是您的 Vultr 对象存储主机名。S3_REGION_AMS
是您的 Vultr 对象存储区域。
打开 env.ts
文件:
$ nano env.ts
添加 s3ams
磁盘名称 DRIVE_DISK
枚举值:
DRIVE_DISK: Env.schema.enum(['local','s3','s3ams'] as const),
添加验证规则 s3ams
磁盘环境变量:
S3_KEY_AMS: Env.schema.string(),
S3_SECRET_AMS: Env.schema.string(),
S3_BUCKET_AMS: Env.schema.string(),
S3_REGION_AMS: Env.schema.string(),
S3_ENDPOINT_AMS: Env.schema.string.optional(),
Save 文件并退出。
更新控制器
打开 GalleryController.ts
文件:
$ nano app/Controllers/Http/GalleryController.ts
在里面 upload
action,搜索负责将文件上传到对象存储的代码。
await payload.fileImage.moveToDisk(", {
name: `gallery/${filename}`
}, 's3')
复制代码并将磁盘名称更改为 s3ams
.
await payload.fileImage.moveToDisk(", {
name: `gallery/${filename}`
}, 's3')
await payload.fileImage.moveToDisk(", {
name: `gallery/${filename}`
}, 's3ams')
它将文件上传到您的两个对象存储位置。
设置默认磁盘
这 index
中的动作 GalleryController.ts
使用默认磁盘获取图像 URL。 要更改默认磁盘,请打开 .env
文件:
$ nano .env
更新 DRIVE_DISK
您想要的磁盘值:
DRIVE_DISK=s3ams
Save 文件并退出。
测试应用
启动开发服务器:
$ node ace serve --encore-args="--host [VULTR_VPS_IP_ADDRESS]"
打开
https://[VULTR_VPS_IP_ADDRESS]:3333
在浏览器中。上传图像。
检查图像是否同时出现在 Vultr 对象存储位置和上传表单下方的图库中。
按 CTRL+C 停止开发服务器。
为生产而构建
AdonisJS 应用程序使用 TypeScript。 在生产环境中运行之前,您需要将其编译为 JavaScript。
前往 website
文件夹:
$ cd ~/app/website
编译它使用 build
命令。 结果在 build
文件夹。
$ node ace build --production --ignore-ts-errors
复制 .env
文件到 build
文件夹并打开它:
$ cp .env build/.env
$ nano build/.env
设置 NODE_ENV
中的变量 .env
归档到 production
:
NODE_ENV=production
将仅生产依赖项安装到 build
文件夹:
$ cd build
$ npm ci --production
使用 PM2 在生产环境中运行应用程序
PM2 是守护进程管理器。 它可以帮助您在生产环境中运行和管理 AdonisJS 应用程序。
安装最新的 PM2 包:
$ npm install [email protected] -g
创建 PM2 生态系统文件来管理您的应用程序:
$ cd ~/app
$ nano ecosystem.config.js
将这些配置添加到 ecosystem.config.js
文件:
module.exports = {
apps : [
{
name : "website",
script : "./website/build/server.js"
}
]
}
将您的应用程序名称放在
name
范围。把生产路径
script.js
在里面script
范围。
运行你的应用程序:
$ pm2 start ecosystem.config.js
$ pm2 list
此时,您的应用程序正在运行。 但是,这个过程还不是持久的。 这意味着您必须在重新启动服务器后再次手动运行您的应用程序。
要运行持久化应用程序,您需要为 PM2 生成启动脚本。
$ pm2 startup
将显示的命令复制并粘贴到终端:
$ sudo env PATH=$PATH:/home/ubuntu/.nvm/versions/node/v19.3.0/bin /home/ubuntu/.nvm/versions/node/v19.3.0/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu
Save 您的 PM2 应用程序列表使用此命令:
$ pm2 save
Nginx 储备代理
您需要设置 Nginx 保留代理以将您的域连接到您的应用程序。 你把你的应用放在 Nginx 网络服务器后面。 它接受所有传入请求并将它们转发到您的应用程序。
添加 ondrej 存储库以获取最新版本的 Nginx。
$ sudo add-apt-repository -y ppa:ondrej/nginx-mainline
$ sudo apt update
安装 Nginx:
$ sudo apt install nginx
禁用默认的 Nginx 配置:
$ sudo unlink /etc/nginx/sites-enabled/default
新建一个 Nginx 配置文件:
$ sudo nano /etc/nginx/sites-available/website
添加以下配置。 确保更改域名 example.com
到您的域。 Save 文件并退出。
server {
listen 80;
server_name example.com;
location / {
proxy_pass https://localhost:3333;
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;
}
}
启用 Nginx 配置:
$ sudo ln -s /etc/nginx/sites-available/website /etc/nginx/sites-enabled/
从语法错误中测试您的配置:
$ sudo nginx -t
如果没有错误,那么您可以重新加载 Nginx 进程:
$ sudo systemctl reload nginx
将您的域名指向您的 Vultr VPS IP 地址。
配置防火墙
设置防火墙允许ssh端口:
$ sudo ufw allow 'OpenSSH'
允许 HTTP 和 HTTPS 端口:
$ sudo ufw allow 'Nginx Full'
启用防火墙:
$ sudo ufw enable
检查防火墙状态:
$ sudo ufw status
使用 Let’s Encrypt SSL 证书保护应用程序
Let’s Encrypt 为您的网站提供免费的 SSL 证书。 要生成证书,您需要使用 Certbot 软件工具。
安装 Certbot:
$ sudo snap install core; sudo snap refresh core
$ sudo snap install --classic certbot
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot
生成 SSL 证书:
$ sudo certbot --nginx
在浏览器中访问您的域并确认它具有 HTTPS 连接。
Let’s Encrypt 证书在 90 天后过期。 Certbot 将续订命令添加到 systemd 计时器或 Cron Job 以在证书过期之前自动续订证书。 您可以使用以下命令验证它:
$ systemctl list-timers | grep 'certbot|ACTIVATES'
结论
本指南展示了在具有单个或多个对象存储位置的 AdonisJS 中使用 Vultr 对象存储的示例,包括使应用程序准备好生产的步骤。
进一步阅读
Vultr 对象存储。
使用 PM2 和 Nginx 部署多个 Adonis.js 应用程序。
文章标题 名称(可选) 电子邮件(可选) 描述
发送建议
注:本教程在Vultr VPS上测试通过,如需部署请前往Vultr.com