Mix-Space云函数备忘
云函数是 Mix Space 中的一个重要功能,它极大地辅助了使用者在 Mix Space 中的体验。通过云函数,Mix Space 可以额外实现实现歌单解析,追番列表等等的功能。
建议食用官方文档 https://mx-space.js.org/usage 本篇仅作参考
快速导入
具体配置需要一个一个修改,最多免去了创建过程。
Shiro主题
主题配置
这个是我当前配置 ,仅供参考,根据需要更改
Mix-Space 后台,进入「配置与云函数」页面,点击右上角的新增按钮,在编辑页面中,填入以下设置:
- 名称:shiro
- 引用:theme
数据类型:JSON
{ # 站点底部相关信息 "footer": { "otherInfo": { "date": "2022-{{now}}", "icp": { "text": "豫 ICP 备 2022029096号-2", "link": "https://beian.miit.gov.cn" } }, "linkSections": [ { "name": "😊关于", "links": [ { "name": "关于我", "href": "/about" }, { "name": "关于此项目", "href": "https://github.com/innei/Shiro", "external": true } ] }, { "name": "🧐更多", "links": [ { "name": "时间线", "href": "/timeline" }, { "name": "友链", "href": "/friends" }, { "name": "思考", "href": "/thinking", "external": true } ] }, { "name": "🤗联系", "links": [ { "name": "写留言", "href": "/message" }, { "name": "发邮件", "href": "mailto:i@vlo.cc", "external": true }, { "name": "GitHub", "href": "https://github.com/jiuyue52", "external": true } ] } ] }, "config": { "color": { "light": [ "#33A6B8", "#FF6666", "#26A69A", "#fb7287", "#69a6cc", "#F11A7B", "#78C1F3", "#FF6666", "#7ACDF6" ], "dark": [ "#F596AA", "#A0A7D4", "#ff7b7b", "#99D8CF", "#838BC6", "#FFE5AD", "#9BE8D8", "#A1CCD1", "#EAAEBA" ] }, "custom": { "css": [], "styles": [], "js": [], "scripts": [] }, # 站点图标 "site": { "favicon": "https://mio.vlo.cc/resource/image/J99Y.svg", "faviconDark": "https://mio.vlo.cc/resource/image/J99Y.svg" }, "hero": { "title": { "template": [ { "type": "h1", "text": "Hi, I'm ", "class": "font-light text-4xl" }, { "type": "h1", "text": "JiuYue", "class": "font-medium mx-2 text-4xl" }, { "type": "h1", "text": "👋。", "class": "font-light text-4xl" }, { "type": "br" }, { "type": "h1", "text": "语言是思想的直接的现实", "class": "font-light text-4xl" }, { "type": "code", "text": "<Developer />", "class": "font-medium mx-2 text-3xl rounded p-1 bg-gray-200 dark:bg-gray-800/0 hover:dark:bg-gray-800/100 bg-opacity-0 hover:bg-opacity-100 transition-background duration-200" }, { "type": "span", "class": "inline-block w-[1px] h-8 -bottom-2 relative bg-gray-800/80 dark:bg-gray-200/80 opacity-0 group-hover:opacity-100 transition-opacity duration-200 group-hover:animation-blink" } ] }, # hitokoto闭源专属,首页随机一言 "hitokoto": { "random": true }, "description": "内心湛然,则无往而不乐。" }, "module": { # 我的动态 "activity": { "enable": true, "endpoint": "/fn/ps/update" }, # 打赏地址及收款二维码 "donate": { "enable": true, "link": "https://afdian.net/@huasui", "qrcode": [ "https://mio.vlo.cc/image/2408/66b853da25ce7.webp", "https://mio.vlo.cc/image/2408/66b853da25ce7.webp" ] }, # b站直播间房间号,获取直播状态 "bilibili": { "liveId": 23359061 }, # svg签名动画,shiroi闭源版专属 "signature": { "svg": "" } } } }
我的动态
需要在主题配置中配置
我的动态
模块 新建云函数- 名称:update
- 引用:ps
- 数据类型:Function
- 请求方式:POST
这个地方还需要设置一个密钥,在 Secret 中填入 key,在 Value 中填入你自己的密钥。
export default async function handler(ctx: Context) {
const {
timestamp,
process: processName,
key,
media,
meta,
} = ctx.req.body || {}
// handle GET
{
const [processInfo, mediaInfo] = await Promise.all([
ctx.storage.cache.get('ps') as any as Promise<Process | undefined>,
ctx.storage.cache.get('media') as any as Promise<Media | undefined>,
])
if (!key) {
return {
processName: processInfo?.name,
processInfo,
mediaInfo,
}
}
}
const ts = +new Date()
// if (Math.abs(ts - timestamp) > 1000 * 10) {
// ctx.throws(400, 'this request is outdate')
// return
// }
const processInfo: Process = {
name: processName,
...meta,
}
const validKey = (await ctx.secret.key) || 'testing'
if (key != validKey)
ctx.throws(401, "You haven't permission to update process info")
const originalPsInfo: Process | null = (await ctx.storage.cache.get(
'ps',
)) as any
await ctx.storage.cache.set('ps', processInfo, 300)
if (originalPsInfo?.name !== processName)
ctx?.broadcast?.('ps-update', {
processInfo,
process: processInfo.name,
ts,
})
if (media) {
await ctx.storage.cache.set('media', media, 10)
}
const mediaInfo: Media | undefined = (await ctx.storage.cache.get(
'media',
)) as any
if (mediaInfo?.title !== media?.title)
ctx?.broadcast?.('media-update', media || null)
return {
ok: 1,
mediaInfo,
process: processInfo.name,
processInfo,
timestamp: +new Date(),
}
}
interface Media {
title: string
artist: string
}
interface Process {
name: string
iconBase64?: string
iconUrl?: string
description?: string
}
另外需要下载对应平台的状态上报软件
参考官方文档:
https://mx-space.js.org/themes/shiro/extra#配置软件
个人状态(闭源专属)
新建云函数
- 名称:status
- 引用:shiro
- 数据类型:Function
- 请求方式:ALL
这个地方还需要设置一个密钥,在 Secret 中填入 key,在 Value 中填入你自己的密钥。
interface Status {
emoji: string
icon?: string
desc?: string
ttl: number
untilAt: number
}
function assetAuth(ctx: Context) {
const body = ctx.req.body
const authKey = ctx.secret.key
if (ctx.isAuthenticated) return
if (body.key !== authKey) {
ctx.throws(401, 'Unauthorized')
}
}
export default async function handler(ctx: Context) {
const method = ctx.req.method.toLowerCase()
switch (method) {
case 'get': {
return GET(ctx)
}
case 'post': {
assetAuth(ctx)
return POST(ctx)
}
case 'delete': {
assetAuth(ctx)
return DELETE(ctx)
}
default: {
ctx.throws(405, 'Method Not Allowed')
}
}
}
const cacheKey = 'shiro:status'
function DELETE(ctx: Context) {
ctx.storage.cache.del(cacheKey)
ctx.broadcast('shiro#status', null)
}
function POST(ctx: Context) {
const body = ctx.req.body
const { emoji, icon, desc } = body as Status
const ttl = body.ttl || 86400 // 1 day
const status = {
emoji,
icon,
desc,
ttl,
untilAt: Date.now() + ttl * 1000,
} as Status
ctx.storage.cache.set(cacheKey, JSON.stringify(status), ttl)
ctx.status(204)
ctx.broadcast('shiro#status', status)
}
function GET(ctx: Context) {
const status = ctx.storage.cache.get(cacheKey)
ctx.res.type('application/json')
return status
}
设置状态,前端首页->双击左上角头像->登录,点击左上角头像的右下方设置状态。
svg签名动画(闭源专属)
在主题配置代码中找到 module,加入 signature 配置,
受限于 Json 语法规则,此处 svg 代码需替换所有的
"
为\ "
,否则会报错。