后端媒体处理器
Tina支持使用外部媒体提供商,但需要用户设置/托管一个轻量级的后端媒体处理器。Tina提供了一些助手来简化这一过程,适用于cloudinary、s3和dos("Digital Ocean Spaces")。
你还需要定义自己的授权函数,以检查用户是否被允许访问处理器。
安装
yarn add @tinacms/auth
根据你网站的框架和托管提供商,有多种方式来托管媒体处理器。
在以下示例中,将<YOUR_MEDIA_STORE_NAME>替换为cloudinary、s3或dos。
选项1) Next.js API 路由(仅限NextJS)
在你的Next.js应用的pages目录下设置一个新的API路由,路径为pages/api/<YOUR_MEDIA_STORE_NAME>/[...media].ts。
然后通过调用你的媒体存储库的createMediaHandler方法添加一个新的全捕获API路由来处理媒体。
从"@tinacms/auth"导入isAuthorized。
authorized键将确保只有TinaCloud中的授权用户可以上传和编辑媒体。
// pages/api/<YOUR_MEDIA_STORE_NAME>/[...media].tsimport { createMediaHandler } from 'next-tinacms-<YOUR_MEDIA_STORE_NAME>/dist/handlers'import { isAuthorized } from '@tinacms/auth'export default createMediaHandler({// ...authorized: async (req, _res) => {try {if (process.env.NODE_ENV == 'development') {return true}const user = await isAuthorized(req)return user && user.verified} catch (e) {console.error(e)return false}},})
选项2) Vercel API 路由
Vercel支持通过在项目根目录创建一个/api目录来创建无服务器函数。要设置此项,请遵循上述NextJS特定说明,但使用/api/<YOUR_MEDIA_STORE_NAME>/[...media].ts而不是/pages/api/<YOUR_MEDIA_STORE_NAME>/[...media].ts
注意:你可能会注意到包名可能包含"next"(例如:next-tinacms-cloudinary)。你仍然可以将这些包用于其他框架。
选项3) Netlify 函数
如果你的网站托管在Netlify上,你可以使用"Netlify Functions"来托管你的媒体处理器。
首先,你必须设置重定向,以便所有对/api/*的请求都可以重定向到Netlify函数。你可以在netlify.toml中设置重定向。我们还将使用esbuild来构建我们的函数,因此我们也将在netlify.toml中设置这一点。
在项目根目录的netlify.toml文件中添加以下内容。
[[redirects]]from = '/api/*'to = '/.netlify/functions/api/:splat'status = 200[functions]node_bundler = 'esbuild'
接下来,你必须为媒体处理器设置API路由。
安装以下依赖项。
yarn add serverless-http express @tinacms/auth next-tinacms-<YOUR_MEDIA_STORE_NAME>
创建一个名为netlify/functions/api/api.js的新文件,并添加以下代码。
注意:如果你使用不同的函数目录,文件路径可能会有所不同。
import ServerlessHttp from 'serverless-http'import express, { Router } from 'express'import { isAuthorized } from '@tinacms/auth'import { createMediaHandler } from 'next-tinacms-<YOUR_MEDIA_STORE_NAME>/dist/handlers'const app = express()const router = Router()const mediaHandler = createMediaHandler({// ...// 有关createMediaHandler中内容的更多详细信息,请参见下一节authorized: async (req, _res) => {try {if (process.env.NODE_ENV == 'development') {return true}const user = await isAuthorized(req)return user && user.verified} catch (e) {console.error(e)return false}},})router.get('/cloudinary/media', mediaHandler)router.post('/cloudinary/media', mediaHandler)router.delete('/cloudinary/media/:media', (req, res) => {req.query.media = ['media', req.params.media]return mediaHandler(req, res)})app.use('/api/', router)app.use('/.netlify/functions/api/', router)export const handler = ServerlessHttp(app)
选项3) AWS Lambda
如果你的网站托管在AWS上,你可以使用AWS Lambda来托管你的媒体处理器。以下示例 使用S3媒体处理器,但你可以使用任何媒体处理器。
前提条件
npm install express @vendia/serverless-express @tinacms/auth body-parser
Lambda 函数
- 要将TinaCMS端点连接到AWS服务,你需要在Node 14.x中创建一个Lambda函数。以下是你需要的代码:
// index.tsimport express, { Router } from 'express'import serverlessExpress from '@vendia/serverless-express'import { isAuthorized } from '@tinacms/auth'import { createMediaHandler } from 'next-tinacms-s3/dist/handlers'import bodyParser from 'body-parser'// 配置TinaCMSconst mediaHandler = createMediaHandler({config: {credentials: {accessKeyId: process.env.TINA_AWS_ACCESS_KEY_ID || '',secretAccessKey: process.env.TINA_AWS_SECRET_ACCESS_KEY || '',},region: process.env.TINA_AWS_REGION,},bucket: process.env.TINA_AWS_BUCKET_NAME || '',authorized: async (req, _res): Promise<any> => {if (process.env.NODE_ENV === 'development') {return true}try {const user = await isAuthorized(req)return user && user.verified} catch (e) {console.error(e)return false}},})// 设置express应用和路由器const app = express()const router = Router()app.use(bodyParser.json())// 定义媒体处理的路由router.get('/s3/media', mediaHandler)router.post('/s3/media', mediaHandler)router.delete('/s3/media/:media', (req, res) => {req.query.media = ['media', req.params.media]return mediaHandler(req, res)})// 将路由器挂载到应用上app.use('/api/', router)// 导出处理函数exports.handler = serverlessExpress({ app })
- 确保配置必要的环境变量:
TINA_AWS_ACCESS_KEY_ID=******************TINA_AWS_BUCKET_NAME=******************TINA_AWS_REGION=********TINA_AWS_SECRET_ACCESS_KEY=******************
API 网关
- 有了Lambda函数后,你可以继续创建一个API网关:
- 点击**
Create API** - 选择REST API
- 一旦API创建完成,在根路径下创建一个名为**
/api**的资源,方法是进入Resources并选择Create Resource - 接下来,创建一个嵌套的子路径,通过创建一个使用**
{proxy+}特殊语法的资源来获取所有/api子路径。确保勾选Configure as proxy resource.**在设置ANY方法时,传递处理TinaCMS媒体管理器逻辑的Lambda函数。点击**save**并允许API网关向Lambda函数添加权限 - 通过点击Action下拉菜单并选择Deploy API来部署你的API
- 为Deployment Stage选择**
[New Stage]**并输入Stage name - 一旦API部署完成,你可以在Stages菜单中点击你创建的阶段查看Invoke URL。
- 通过进入Settings菜单并添加**
/*通配符来配置Binary Media Types**
CloudFront
- 为了完成连接,使用刚创建的API网关的Invocation URL为CloudFront创建一个新的Origin。将Origin Path设置为API部署的阶段名称。
- 为CloudFront创建一个新的Behaviour,该行为将拦截带有**
/api/s3/media*路径的请求,并使用刚创建的API网关origin。确保允许以下HTTP方法:GET、HEAD、OPTIONS、PUT、POST、PATCH和DELETE**。- 在Cache key and origin requests部分,选择Cache policy and origin request policy选项。对于Cache policy,选择CachingDisabled。对于Origin request policy,选择AllViewerExceptHostHeader。
- 重复上述过程以创建另一个行为,该行为将拦截带有**
/api/s3/media/***路径的请求。
在前端注册媒体存储
现在,你可以用外部媒体存储替换默认的基于仓库的媒体。你可以通过loadCustomStore属性注册一个媒体存储。
loadCustomStore属性可以在tina/config文件中配置。
// tina/config.{ts,js,jsx}// ...export default defineConfig({// ...media: {- tina: {- publicFolder: "",- mediaRoot: ""- },+ loadCustomStore: async () => {+ const pack = await import("next-tinacms-<YOUR_MEDIA_STORE_NAME>");+ return pack.TinaCloud<YOUR_MEDIA_STORE>;+ },}})
确保在推送到TinaCloud的生产环境时同时提交对配置和tina-lock.json文件的更改,否则你的资产仍将以https://assets.tina.io为前缀,就像你仍在使用基于仓库的媒体一样。
现在你可以在TinaCMS中管理外部媒体存储。要详细了解每个媒体存储,请参阅下一节。