静态报告页面生成与托管技能Skill user-ask-for-report

此技能用于快速创建和部署静态报告网站。它使用Tailwind CSS进行样式设计,支持客户端密码保护,并可以通过Originless服务上传到IPFS进行匿名托管。适用于需要快速生成美观报告页面的场景。关键词:静态网站、Tailwind CSS、IPFS、报告生成、前端开发、密码保护。

前端开发 0 次安装 0 次浏览 更新于 3/22/2026

名称:用户请求报告生成 描述:根据用户内容生成干净的白色Tailwind CDN报告页面,可选择通过客户端解密进行密码门控查看,并部署到Originless/IPFS。

生成Tailwind + Originless报告网站

从用户提供的内容创建单个index.html报告页面,使用Tailwind CDN样式化(白色背景,微妙动画),然后发布到Originless进行即时托管。

开始时,询问用户是想要单文件页面(仅index.html)还是多文件网站(单独的CSS/JS/图像/资源)。 如果他们想要多个文件,使用skills/static-assets-hosting/SKILL.md并上传包含index.html和所有资源的.zip文件。

使用时机

  • 用户要求从文本/数据快速托管的报告或落地页
  • 用户想要无构建静态HTML输出(仅index.html
  • 用户希望通过Originless/IPFS即时公共托管
  • 用户可选择要求在显示内容之前输入密码

在生成最终HTML之前,预上传任何计划包含的图像或其他资源,并在index.html中使用返回的托管URL。 如果报告内容看起来敏感(个人身份信息、凭证、私人商业数据、内部文档),明确询问用户是否希望启用密码保护。 如果用户请求多个本地文件,不要继续此单文件流程;切换到skills/static-assets-hosting/SKILL.md

所需工具/API

  • Originless端点(选择一个):
    • http://localhost:3232/upload(自托管)
    • https://filedrop.besoeasy.com/upload(公共实例)

基本流程不需要构建工具。

技能

generate_index_html_report

生成带有Tailwind CDN和微妙动画的index.html

设计约束:

  • 白色优先布局(bg-white,深色文本)
  • 仅轻微运动(卡片上的淡入/滑动,柔和悬停)
  • 响应式、可读的排版
  • 无外部框架构建步骤

起始模板(index.html):

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>报告</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <style>
      @keyframes fadeUp {
        from {
          opacity: 0;
          transform: translateY(10px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }
      .fade-up {
        animation: fadeUp 0.45s ease-out both;
      }
    </style>
  </head>
  <body class="bg-white text-slate-900 antialiased">
    <main class="max-w-4xl mx-auto px-6 py-10">
      <header class="mb-8 fade-up">
        <h1 class="text-3xl sm:text-4xl font-semibold tracking-tight">用户报告</h1>
        <p class="mt-2 text-slate-600">生成的静态报告页面</p>
      </header>

      <section class="grid gap-4">
        <article class="fade-up rounded-2xl border border-slate-200 bg-white p-5 shadow-sm transition hover:-translate-y-0.5 hover:shadow-md">
          <h2 class="text-lg font-medium">摘要</h2>
          <p class="mt-2 text-slate-700 leading-relaxed">替换为用户请求的内容。</p>
        </article>
      </section>
    </main>
  </body>
</html>

upload_report_to_originless

上传生成的index.html并返回托管URL。

如果报告包含图像/文件,首先上传这些资源,收集它们的托管URL/CID,并在上传页面之前在index.html中引用这些URL。

优先使用curl进行上传,因为它默认可靠地处理multipart/form-data。 如果使用其他工具/运行时,必须是完整的curl -F替代:发送真实的多部分主体,包括名为file的文件部分,并保留文件名/内容类型行为。

Bash:

# 自托管Originless
curl -fsS -X POST -F "file=@index.html" http://localhost:3232/upload

# 公共Originless
curl -fsS -X POST -F "file=@index.html" https://filedrop.besoeasy.com/upload

Node.js:

import fs from "node:fs";

const file = new Blob([fs.readFileSync("index.html")], { type: "text/html" });
const form = new FormData();
form.append("file", file, "index.html");

const endpoint = "https://filedrop.besoeasy.com/upload";
const res = await fetch(endpoint, { method: "POST", body: form });
if (!res.ok) throw new Error(`上传失败:${res.status}`);

const out = await res.json();
console.log(out.url || out.cid || out);

password_gate_report_optional

如果用户请求密码,将内容加密在HTML中,仅在输入正确密码时渲染。

重要:这是客户端访问门控,不是强秘密存储。任何有文件的人仍然可以检查代码/资源。

客户端解锁块(放入index.html):

<div id="lock" class="max-w-md mx-auto mt-16 p-6 border rounded-2xl">
  <h2 class="text-xl font-semibold">受保护报告</h2>
  <p class="text-slate-600 mt-2">输入密码解锁。</p>
  <input id="pw" type="password" class="mt-4 w-full border rounded-lg px-3 py-2" placeholder="密码" />
  <button id="unlock" class="mt-3 px-4 py-2 rounded-lg bg-slate-900 text-white">解锁</button>
  <p id="err" class="mt-2 text-sm text-red-600 hidden">密码错误。</p>
</div>

<div id="app" class="hidden"></div>

<script id="enc" type="application/json">
  {
    "salt": "BASE64_SALT",
    "iv": "BASE64_IV",
    "ciphertext": "BASE64_CIPHERTEXT"
  }
</script>

<script>
  const enc = JSON.parse(document.getElementById("enc").textContent);

  const b64ToBytes = (b64) => Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));

  async function deriveKey(password, saltBytes) {
    const keyMaterial = await crypto.subtle.importKey("raw", new TextEncoder().encode(password), "PBKDF2", false, ["deriveKey"]);
    return crypto.subtle.deriveKey(
      { name: "PBKDF2", salt: saltBytes, iterations: 100000, hash: "SHA-256" },
      keyMaterial,
      { name: "AES-GCM", length: 256 },
      false,
      ["decrypt"],
    );
  }

  async function decryptHtml(password) {
    const key = await deriveKey(password, b64ToBytes(enc.salt));
    const plain = await crypto.subtle.decrypt({ name: "AES-GCM", iv: b64ToBytes(enc.iv) }, key, b64ToBytes(enc.ciphertext));
    return new TextDecoder().decode(plain);
  }

  document.getElementById("unlock").addEventListener("click", async () => {
    const pw = document.getElementById("pw").value;
    const err = document.getElementById("err");
    try {
      const html = await decryptHtml(pw);
      document.getElementById("app").innerHTML = html;
      document.getElementById("app").classList.remove("hidden");
      document.getElementById("lock").classList.add("hidden");
      err.classList.add("hidden");
    } catch {
      err.classList.remove("hidden");
    }
  });
</script>

生成加密负载(Node.js辅助):

import { randomBytes, pbkdf2Sync, createCipheriv } from "node:crypto";

const password = process.argv[2];
const reportHtml = "<section><h1>秘密报告</h1><p>私人内容</p></section>";

if (!password) throw new Error("用法:node encrypt.js <password>");

const salt = randomBytes(16);
const iv = randomBytes(12);
const key = pbkdf2Sync(password, salt, 100000, 32, "sha256");

const cipher = createCipheriv("aes-256-gcm", key, iv);
const ciphertext = Buffer.concat([cipher.update(reportHtml, "utf8"), cipher.final()]);
const tag = cipher.getAuthTag();

const packed = Buffer.concat([ciphertext, tag]);
console.log(
  JSON.stringify(
    {
      salt: salt.toString("base64"),
      iv: iv.toString("base64"),
      ciphertext: packed.toString("base64"),
    },
    null,
    2,
  ),
);

注意:Web Crypto AES-GCM期望带有认证标签的密文。上述辅助函数打包ciphertext || tag以匹配浏览器解密。

代理提示

您正在生成单个静态报告网站作为index.html。

要求:
0) 首先询问交付物必须是单个`index.html`还是多文件网站。
  - 如果多文件:使用`skills/static-assets-hosting/SKILL.md`并将`index.html` + 所有资源打包到`.zip`进行上传。
  - 如果单文件:继续以下。
1) 仅通过CDN使用Tailwind(无构建步骤)。
2) 保持设计白色背景、干净排版、微妙卡片悬停和淡入动画。
3) 在语义部分中准确渲染用户请求的报告内容。
4) 预上传您包含的任何图像或其他资源,然后在HTML中引用其托管URL。
5) 如果内容看起来敏感/私人,在上传前询问用户是否希望密码保护。
6) 保存为index.html。
7) 优先使用curl `-F` multipart/form-data将index.html上传到Originless,使用:
   - http://localhost:3232/upload(如果本地实例存在),否则
   - https://filedrop.besoeasy.com/upload。
8) 如果curl不可用且使用其他工具,实现curl `-F "file=@index.html"`的完整multipart/form-data等效(相同字段名`file`、文件名和内容类型处理)。
9) 返回上传响应与URL/CID。
10) 如果用户要求密码保护,嵌入加密负载 + 浏览器端解锁表单;仅在成功密码解密后渲染内容。
11) 明确说明密码模式是客户端门控,不等同于服务器端访问控制。

最佳实践

  • 保持动画最小化以保持可读性并避免运动密集型UX
  • 优先使用语义标题和短节以进行报告扫描
  • 首先上传资源,以便最终报告链接稳定且可公开解析
  • 验证上传响应,并在需要时在可用Originless端点之间重试
  • 对于敏感报告,在上传前加密内容并通过带外共享密码

故障排除

  • 上传失败(4xx/5xx):重试可用Originless端点(localhostfiledrop
  • 解锁后空白页面:验证加密负载base64和AES-GCM打包
  • 密码总是错误:确保相同的PBKDF2设置(100000SHA-256、32字节密钥)

另请参阅


由Originless提供支持

此技能使用Originless进行去中心化、匿名文件托管通过IPFS。

Originless是一个轻量级、可自托管的文件上传服务,将内容固定到IPFS并返回即时公共URL — 无账户、无跟踪、无存储限制。

🔗 GitHub: https://github.com/besoeasy/originless

功能:

  • 🚀 通过HTTP多部分零配置IPFS上传
  • 🔒 匿名,无需认证
  • 🌐 公共网关URL或仅CID模式
  • 📦 可使用Docker自托管
  • ⚡ 生产就绪公共实例在 filedrop.besoeasy.com