万籁俱寂,万字将成。
Huasui
Stay hungry. Stay foolish.
© 2022-2026
Powered by Mix Space&
余白 / Yohaku
.
正在被0人看爆
😊关于
关于我隐私权 ↗
🧐更多
时间线朋友们思考 ↗
🤗联系
写留言发邮件 ↗GitHub ↗
Huasui
Stay hungry. Stay foolish.
链接
关于我·隐私权·时间线·朋友们·思考·写留言·发邮件·GitHub
© 2022-2026 Powered by Mix Space&
余白 / Yohaku
.
正在被0人看爆
豫 ICP 备 2022029096号-2
RSS 订阅·站点地图·
··|
RSS 订阅·站点地图·|··|豫 ICP 备 2022029096号-2
稍候片刻,月出文自明。

Mix-Space云函数备忘

·(已编辑)· / , ·262 阅读·3 喜欢
这篇文章上次修改于,可能部分内容已经不适用,如有疑问可询问作者。
关键洞察
AI · GEN

Mix-Space云函数备忘

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • 云函数是 Mix Space 中的一个重要功能,它极大地辅助了使用者在 Mix Space 中的体验。通过云函数,Mix Space 可以额外实现实现歌单解析,追番列表等等的功能。

    建议食用官方文档 https://mx-space.js.org/usage 本篇仅作参考

    快速导入

    具体配置需要一个一个修改,最多免去了创建过程。

    Shiro主题

    主题配置

    这个是我当前配置 ,仅供参考,根据需要更改

    Mix-Space 后台,进入「配置与云函数」页面,点击右上角的新增按钮,在编辑页面中,填入以下设置:

    • 名称:shiro
    • 引用:theme
    • 数据类型:JSON

      CodeBlock Loading...

      我的动态

      需要在主题配置中配置我的动态模块

      新建云函数

    • 名称:update
    • 引用:ps
    • 数据类型:Function
    • 请求方式:POST

    这个地方还需要设置一个密钥,在 Secret 中填入 key,在 Value 中填入你自己的密钥。

    TS
    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 中填入你自己的密钥。

    CodeBlock Loading...

    设置状态,前端首页->双击左上角头像->登录,点击左上角头像的右下方设置状态。

    svg签名动画(闭源专属)

    在主题配置代码中找到 module,加入 signature 配置,

    受限于 Json 语法规则,此处 svg 代码需替换所有的 " 为 \ ",否则会报错。
    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": ""  
        }
      }
    }
    }
    
    TS
    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
    }