feat: use mp oss in website (#471)
All checks were successful
Build and Deploy / build-and-deploy (push) Has been skipped

* add example cloudflare worker proxy
* use wsrv.nl to host image request in website
This commit is contained in:
Honwhy Wang 2024-12-14 16:14:33 +08:00 committed by GitHub
parent b83968b657
commit d37aedb95d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 86 additions and 15 deletions

View File

@ -5,7 +5,7 @@ export default antfu({
unocss: true, unocss: true,
typescript: true, typescript: true,
formatters: true, formatters: true,
ignores: [`.github`, `bin`, `md-cli`, `src/assets`], ignores: [`.github`, `bin`, `md-cli`, `src/assets`, `example`],
}, { }, {
rules: { rules: {
'semi': [`error`, `never`], 'semi': [`error`, `never`],

16
example/README.md Normal file
View File

@ -0,0 +1,16 @@
# Example
## worker.js
公众号openapi接口代理服务示例该项目将请求转发至微信公众号api。
开发调试:
```
cd example
npx wrangler dev worker.js
```
部署:
请将其部署到cloudflare workers。

36
example/worker.js Normal file
View File

@ -0,0 +1,36 @@
/**
* @typedef {object} Env
* @property
*/
export default {
/**
* @param {Request} request
* @param {Env} env
* @param {ExecutionContext} ctx
* @returns {Promise<Response>}
*/
async fetch(request, env, ctx) {
const url = new URL(request.url)
const targetUrl = `https://api.weixin.qq.com`
const proxyRequest = new Request(targetUrl + url.pathname + url.search, {
method: request.method,
headers: request.headers,
body: request.body,
})
const response = await fetch(proxyRequest)
const proxyResponse = new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: response.headers,
})
setCorsHeaders(proxyResponse.headers)
return proxyResponse
},
}
// 设置 CORS 头部
function setCorsHeaders(headers) {
headers.set(`Access-Control-Allow-Origin`, `*`)
headers.set(`Access-Control-Allow-Methods`, `GET, POST, PUT, DELETE`)
headers.set(`Access-Control-Allow-Headers`, `*`)
}

View File

@ -63,10 +63,11 @@ const minioOSS = ref({
}) })
const formMp = ref({ const formMp = ref({
proxyOrigin: ``,
appID: ``, appID: ``,
appsecret: ``, appsecret: ``,
}) })
const mpDisabled = ref(window.location.href.startsWith(`http`)) const isWebsite = ref(window.location.href.startsWith(`http`))
const formCustom = ref<{ code: string, editor: CodeMirror.EditorFromTextArea | null }>({ const formCustom = ref<{ code: string, editor: CodeMirror.EditorFromTextArea | null }>({
code: code:
localStorage.getItem(`formCustomConfig`) localStorage.getItem(`formCustomConfig`)
@ -117,7 +118,6 @@ const options = [
{ {
value: `mp`, value: `mp`,
label: `公众号素材`, label: `公众号素材`,
disabled: mpDisabled.value,
}, },
{ {
value: `formCustom`, value: `formCustom`,
@ -272,6 +272,10 @@ function saveMpConfiguration() {
ElMessage.error(`公众号图床 参数配置不全`) ElMessage.error(`公众号图床 参数配置不全`)
return return
} }
if (isWebsite.value && !formMp.value.proxyOrigin) {
ElMessage.error(`代理域名必须配置`)
return
}
localStorage.setItem(`mpConfig`, JSON.stringify(formMp.value)) localStorage.setItem(`mpConfig`, JSON.stringify(formMp.value))
ElMessage.success(`保存成功`) ElMessage.success(`保存成功`)
} }
@ -328,7 +332,6 @@ function uploadImage(params: { file: any }) {
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value" :value="item.value"
:disabled="item?.disabled"
/> />
</el-select> </el-select>
<el-upload <el-upload
@ -646,7 +649,7 @@ function uploadImage(params: { file: any }) {
<template #label> <template #label>
<el-tooltip placement="top"> <el-tooltip placement="top">
<template #content> <template #content>
由于接口请求跨域问题在浏览器插件形式中使用 由于接口请求跨域问题建议在浏览器插件形式中使用
</template> </template>
<div>公众号 图床</div> <div>公众号 图床</div>
</el-tooltip> </el-tooltip>
@ -656,8 +659,13 @@ function uploadImage(params: { file: any }) {
:model="formMp" :model="formMp"
label-position="right" label-position="right"
label-width="150px" label-width="150px"
:disabled="mpDisabled"
> >
<el-form-item label="代理域名" :required="false">
<el-input
v-model.trim="formMp.proxyOrigin"
placeholder=""
/>
</el-form-item>
<el-form-item label="appID" :required="true"> <el-form-item label="appID" :required="true">
<el-input <el-input
v-model.trim="formMp.appID" v-model.trim="formMp.appID"
@ -677,7 +685,7 @@ function uploadImage(params: { file: any }) {
如何开启公众号开发者模式并获取应用账号密钥 如何开启公众号开发者模式并获取应用账号密钥
</el-link> </el-link>
</el-form-item> </el-form-item>
<el-form-item style="margin-top: -26px;"> <el-form-item style="margin-top: -22px;">
<el-link <el-link
type="primary" type="primary"
href="https://mpmd.pages.dev/tutorial/" href="https://mpmd.pages.dev/tutorial/"

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MpMd编辑器</title> <title>公众号文章编辑器</title>
<meta name="manifest.type" content="browser_action" /> <meta name="manifest.type" content="browser_action" />
</head> </head>
<body> <body>

View File

@ -308,7 +308,7 @@ interface MpResponse {
errcode: number errcode: number
errmsg: string errmsg: string
} }
async function getMpToken(appID: string, appsecret: string) { async function getMpToken(appID: string, appsecret: string, proxyOrigin: string) {
const data = localStorage.getItem(`mpToken:${appID}`) const data = localStorage.getItem(`mpToken:${appID}`)
if (data) { if (data) {
const token = JSON.parse(data) const token = JSON.parse(data)
@ -324,7 +324,10 @@ async function getMpToken(appID: string, appsecret: string) {
secret: appsecret, secret: appsecret,
}, },
} }
const url = `https://api.weixin.qq.com/cgi-bin/stable_token` let url = `https://api.weixin.qq.com/cgi-bin/stable_token`
if (proxyOrigin) {
url = `${proxyOrigin}/cgi-bin/stable_token`
}
const res = await fetch<any, MpResponse>(url, requestOptions) const res = await fetch<any, MpResponse>(url, requestOptions)
if (res.access_token) { if (res.access_token) {
const tokenInfo = { const tokenInfo = {
@ -337,13 +340,13 @@ async function getMpToken(appID: string, appsecret: string) {
return `` return ``
} }
async function mpFileUpload(file: File) { async function mpFileUpload(file: File) {
const { appID, appsecret } = JSON.parse( const { appID, appsecret, proxyOrigin } = JSON.parse(
localStorage.getItem(`mpConfig`)!, localStorage.getItem(`mpConfig`)!,
) )
/* eslint-disable no-async-promise-executor */ /* eslint-disable no-async-promise-executor */
return new Promise<string>(async (resolve, reject) => { return new Promise<string>(async (resolve, reject) => {
try { try {
const access_token = await getMpToken(appID, appsecret).catch(e => console.error(e)) const access_token = await getMpToken(appID, appsecret, proxyOrigin).catch(e => console.error(e))
if (!access_token) { if (!access_token) {
reject(new Error(`获取 access_token 失败请检查console日志`)) reject(new Error(`获取 access_token 失败请检查console日志`))
return return
@ -354,11 +357,18 @@ async function mpFileUpload(file: File) {
method: `POST`, method: `POST`,
data: formdata, data: formdata,
} }
const url = `https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=${access_token}&type=image` let url = `https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=${access_token}&type=image`
if (proxyOrigin) {
url = `${proxyOrigin}/cgi-bin/material/add_material?access_token=${access_token}&type=image`
}
const res = await fetch<any, { const res = await fetch<any, {
url: string url: string
}>(url, requestOptions) }>(url, requestOptions)
resolve(res.url) let imageUrl = res.url
if (proxyOrigin && window.location.href.startsWith(`http`)) {
imageUrl = `https://wsrv.nl?url=${encodeURIComponent(imageUrl)}`
}
resolve(imageUrl)
} }
catch (e) { catch (e) {
reject(e) reject(e)

View File

@ -7,7 +7,8 @@ export default defineConfig({
extensionApi: `chrome`, extensionApi: `chrome`,
manifest: { manifest: {
name: `公众号文章编辑器`, name: `公众号文章编辑器`,
version: `0.0.6`, description: `一款高度简洁的微信 Markdown 编辑器:支持 Markdown 语法、色盘取色、多图上传、一键下载文档、自定义 CSS 样式、一键重置等特性`,
version: `0.0.7`,
icons: { icons: {
256: `/mpmd/icon-256.png`, 256: `/mpmd/icon-256.png`,
}, },