mirror of
https://gitee.com/Doocs/md
synced 2025-04-29 09:32:27 +08:00
feat: add new functions for mp image upload (#637)
This commit is contained in:
parent
e4b989b8a7
commit
365816970b
3
.gitignore
vendored
3
.gitignore
vendored
@ -55,4 +55,5 @@ components.d.ts
|
||||
|
||||
.wxt
|
||||
.output
|
||||
web-ext.config.ts
|
||||
web-ext.config.ts
|
||||
.wrangler
|
@ -17,6 +17,7 @@ export default defineConfig({
|
||||
define: {
|
||||
process,
|
||||
},
|
||||
envPrefix: [`VITE_`, `CF_`], // 允许 VITE_ 和 CF_ 前缀的变量
|
||||
plugins: [
|
||||
vue(),
|
||||
UnoCSS(),
|
||||
|
16
functions/cgi-bin/material/add_material/index.ts
Normal file
16
functions/cgi-bin/material/add_material/index.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { parseFormDataRequest } from '../../../parseFormDataRequest'
|
||||
import { jsonResponse, MP_HOST } from '../../../utils'
|
||||
|
||||
export const onRequestPost: PagesFunction = async (context) => {
|
||||
const formData = (await parseFormDataRequest(context.request)) as FormData
|
||||
const url = new URL(context.request.url)
|
||||
const response = await fetch(
|
||||
`${MP_HOST}${context.functionPath}${url.search}`,
|
||||
{
|
||||
method: `POST`,
|
||||
body: formData,
|
||||
},
|
||||
)
|
||||
const json = await response.json()
|
||||
return jsonResponse(json)
|
||||
}
|
5
functions/cgi-bin/media/uploadimg/index.ts
Normal file
5
functions/cgi-bin/media/uploadimg/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { onRequestPost as post } from '../../material/add_material/index'
|
||||
|
||||
export const onRequestPost: PagesFunction = async (context) => {
|
||||
return post(context)
|
||||
}
|
13
functions/cgi-bin/stable_token.ts
Normal file
13
functions/cgi-bin/stable_token.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { jsonResponse, MP_HOST } from '../utils'
|
||||
|
||||
export const onRequestPost: PagesFunction = async (context) => {
|
||||
const response = await fetch(
|
||||
`${MP_HOST}${context.functionPath}`,
|
||||
{
|
||||
method: `POST`,
|
||||
body: context.request.body,
|
||||
},
|
||||
)
|
||||
const json = await response.json()
|
||||
return jsonResponse(json)
|
||||
}
|
40
functions/parseFormDataRequest.ts
Normal file
40
functions/parseFormDataRequest.ts
Normal file
@ -0,0 +1,40 @@
|
||||
// eslint-disable-next-line ts/ban-ts-comment
|
||||
// @ts-nocheck
|
||||
// @see https://github.com/cloudflare/images.pages.dev/blob/main/functions/utils/parseFormDataRequest.ts
|
||||
import { parseMultipart } from '@ssttevee/multipart-parser'
|
||||
|
||||
const RE_MULTIPART
|
||||
= /^multipart\/form-data;\s*boundary=(?:"((?:[^"]|\\")+)"|([^\s;]+))$/
|
||||
|
||||
function getBoundary(request: Request): string | undefined {
|
||||
const contentType = request.headers.get(`Content-Type`)
|
||||
if (!contentType)
|
||||
return
|
||||
|
||||
const matches = RE_MULTIPART.exec(contentType)
|
||||
if (!matches)
|
||||
return
|
||||
|
||||
return matches[1] || matches[2]
|
||||
}
|
||||
|
||||
export async function parseFormDataRequest(request: Request): Promise<FormData | undefined> {
|
||||
const boundary = getBoundary(request)
|
||||
if (!boundary || !request.body)
|
||||
return
|
||||
|
||||
const parts = await parseMultipart(request.body, boundary)
|
||||
|
||||
const formData = new FormData()
|
||||
|
||||
for (const { name, data, filename, contentType } of parts) {
|
||||
formData.append(
|
||||
name,
|
||||
filename
|
||||
? new File([data], filename, { type: contentType })
|
||||
: new TextDecoder().decode(data),
|
||||
)
|
||||
}
|
||||
|
||||
return formData
|
||||
}
|
8
functions/tsconfig.json
Normal file
8
functions/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"lib": ["esnext"],
|
||||
"module": "esnext",
|
||||
"types": ["@cloudflare/workers-types"]
|
||||
}
|
||||
}
|
8
functions/utils.ts
Normal file
8
functions/utils.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export const MP_HOST = `https://api.weixin.qq.com`
|
||||
|
||||
export function jsonResponse(value: any, init: ResponseInit = {}) {
|
||||
return new Response(JSON.stringify(value), {
|
||||
headers: { 'Content-Type': `application/json`, ...init.headers },
|
||||
...init,
|
||||
})
|
||||
}
|
1309
package-lock.json
generated
1309
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -13,6 +13,7 @@
|
||||
"build:cli": "npm run build && npx shx rm -rf md-cli/dist && npx shx rm -rf dist/**/*.map && npx shx cp -r dist md-cli/ && cd md-cli && npm pack",
|
||||
"build:analyze": "cross-env ANALYZE=true vite build",
|
||||
"preview": "npm run build && vite preview",
|
||||
"preview:pages": "npm run build:h5-netlify && wrangler pages dev ./dist",
|
||||
"release:cli": "node ./scripts/release.js",
|
||||
"ext:dev": "wxt",
|
||||
"ext:zip": "wxt zip",
|
||||
@ -25,6 +26,7 @@
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.777.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.777.0",
|
||||
"@ssttevee/multipart-parser": "^0.1.9",
|
||||
"@vee-validate/yup": "^4.15.0",
|
||||
"@vueuse/core": "^12.5.0",
|
||||
"axios": "^1.8.4",
|
||||
@ -62,6 +64,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "3.11.0",
|
||||
"@cloudflare/workers-types": "^4.20250419.0",
|
||||
"@types/buffer-from": "^1.1.3",
|
||||
"@types/codemirror": "^5.60.15",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
@ -90,6 +93,7 @@
|
||||
"vite-plugin-radar": "^0.10.0",
|
||||
"vite-plugin-vue-devtools": "^7.7.2",
|
||||
"vue-tsc": "^2.2.0",
|
||||
"wrangler": "^4.12.0",
|
||||
"wxt": "^0.19.29"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
|
@ -138,13 +138,35 @@ function minioOSSSubmit(formValues: any) {
|
||||
}
|
||||
|
||||
// 公众号
|
||||
const isWebsite = ref(window.location.href.startsWith(`http`))
|
||||
// 当前是否为网页(http/https 协议)
|
||||
const isWebsite = window.location.protocol.startsWith(`http`)
|
||||
|
||||
const mpSchema = toTypedSchema(yup.object({
|
||||
proxyOrigin: isWebsite.value ? yup.string().required(`代理域名不能为空`) : yup.string().optional(),
|
||||
appID: yup.string().required(`AppID 不能为空`),
|
||||
appsecret: yup.string().required(`AppSecret 不能为空`),
|
||||
}))
|
||||
// Cloudflare Pages 环境
|
||||
const isCfPage = import.meta.env.CF_PAGES === `1`
|
||||
|
||||
// 插件模式运行(如 chrome-extension://)
|
||||
const isPluginMode = !isWebsite
|
||||
|
||||
// 是否需要填写 proxyOrigin(只在 非插件 且 非CF页面 时需要)
|
||||
const isProxyRequired = computed(() => {
|
||||
return !isPluginMode && !isCfPage
|
||||
})
|
||||
|
||||
const mpPlaceholder = computed(() => {
|
||||
if (isProxyRequired.value) {
|
||||
return `如:http://proxy.example.com`
|
||||
}
|
||||
return `可不填`
|
||||
})
|
||||
const mpSchema = computed(() =>
|
||||
toTypedSchema(yup.object({
|
||||
proxyOrigin: isProxyRequired.value
|
||||
? yup.string().required(`代理域名不能为空`)
|
||||
: yup.string().optional(),
|
||||
appID: yup.string().required(`AppID 不能为空`),
|
||||
appsecret: yup.string().required(`AppSecret 不能为空`),
|
||||
})),
|
||||
)
|
||||
|
||||
const mpConfig = ref(localStorage.getItem(`mpConfig`)
|
||||
? JSON.parse(localStorage.getItem(`mpConfig`)!)
|
||||
@ -738,7 +760,7 @@ function onDrop(e: DragEvent) {
|
||||
<Input
|
||||
v-bind="field"
|
||||
v-model="field.value"
|
||||
placeholder="如:http://proxy.example.com,使用插件时可不填"
|
||||
:placeholder="mpPlaceholder"
|
||||
/>
|
||||
</FormItem>
|
||||
</Field>
|
||||
@ -778,7 +800,7 @@ function onDrop(e: DragEvent) {
|
||||
variant="link"
|
||||
class="p-0"
|
||||
as="a"
|
||||
href="https://mpmd.pages.dev/tutorial/"
|
||||
href="https://mp.honwhy.wang/tutorial/"
|
||||
target="_blank"
|
||||
>
|
||||
如何在浏览器插件中使用公众号图床?
|
||||
|
@ -10,7 +10,7 @@ export default defineBackground({
|
||||
return
|
||||
}
|
||||
if (detail.reason === `install`) {
|
||||
browser.tabs.create({ url: `https://mpmd.pages.dev/welcome` })
|
||||
browser.tabs.create({ url: `https://mp.honwhy.wang/welcome` })
|
||||
}
|
||||
else if (detail.reason === `update`) {
|
||||
browser.runtime.openOptionsPage()
|
||||
|
@ -33,7 +33,7 @@ function onOpenOption() {
|
||||
>查看文档</a></span>
|
||||
</div>
|
||||
<div>
|
||||
2.配置IP白名单<span><a href="https://mpmd.pages.dev/tutorial" target="_blank">使用教程</a></span>
|
||||
2.配置IP白名单<span><a href="https://mp.honwhy.wang/tutorial" target="_blank">使用教程</a></span>
|
||||
</div>
|
||||
<div>
|
||||
<button class="button" @click="onOpenOption">
|
||||
|
@ -17,6 +17,7 @@ export default defineConfig({
|
||||
define: {
|
||||
process,
|
||||
},
|
||||
envPrefix: [`VITE_`, `CF_`], // 允许 VITE_ 和 CF_ 前缀的变量
|
||||
plugins: [
|
||||
vue(),
|
||||
UnoCSS(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user