优化CF worker 页面展示,通过cf worker每月白嫖1000次sd绘图

借用佬的内容,对CF worker的内容进行修改,可以直接在worker页面进行绘画。

// HTML content as a string
const htmlContent = `
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI Art Generator</title>
    <!-- CSS -->
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.2/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
    <style>
        body {
            font-family: 'Roboto', sans-serif;
            line-height: 1.6;
            color: #333;
            margin: 0;
            padding: 0;
            min-height: 100vh;
            background: linear-gradient(45deg, #6a11cb 0%, #2575fc 100%);
            /* display: flex;
            justify-content: center;
            align-items: center;*/
        }

        .container {
            max-width: 800px;
            margin: 1rem;
            padding: 1rem;
            background-color: rgba(255, 255, 255, 0.9);
            border-radius: 20px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
        }

        h1 {
            font-family: 'Montserrat', sans-serif;
            color: #6a11cb;
            text-align: center;
            font-size: 2.5rem;
            margin-bottom: 2rem;
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
        }

        form {
            display: grid;
        }

        label {
            font-weight: bold;
            color: #2575fc;
        }

        input[type="text"],
        select,
        textarea {
            width: 100%;
            padding: 0.8rem;
            border: 2px solid #6a11cb;
            border-radius: 10px;
            font-size: 1rem;
            transition: all 0.3s ease;
        }

        textarea {
            width: calc(100% - 50px);
            padding-left: 35px;
            padding-right: 10px;
        }

        input[type="text"]:focus,
        select:focus,
        textarea:focus {
            outline: none;
            border-color: #2575fc;
            box-shadow: 0 0 0 2px rgba(37, 117, 252, 0.2);
        }

        button {
            background-color: #6a11cb;
            color: #fff;
            padding: 1rem 2rem;
            border: none;
            border-radius: 50px;
            cursor: pointer;
            font-size: 1.1rem;
            font-weight: bold;
            transition: all 0.3s ease;
            display: block;
            margin: 2rem auto 0;
        }

        button:hover {
            background-color: #2575fc;
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(37, 117, 252, 0.4);
        }

        #result {
            overflow: auto;
            width: 50%;
            height: 100vh;
            margin-top: 2rem;
            text-align: center;
        }

        #generatedImage {
            max-width: 100%;
            border-radius: 10px;
            box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
            transition: all 0.3s ease;
        }

        #generatedImage:hover {
            transform: scale(1.02);
        }

        @media (max-width: 600px) {
            .container {
                margin: 1rem;
                padding: 1rem;
            }

            h1 {
                font-size: 2rem;
            }
        }

        .input-icon {
            position: relative;
        }

        .input-icon i {
            position: absolute;
            left: 10px;
            top: 50%;
            transform: translateY(-50%);
            color: #6a11cb;
        }

        .input-icon input,
        .input-icon select {
            padding-left: 35px;
            box-sizing: border-box;
            /* Add this line */
        }

        .content {
            display: flex;
            justify-content: space-between;
        }

        .content .input-icon {
            flex: 1;
        }
        .popup {
    display: none;
    position: fixed;
    z-index: 1;
    padding-top: 100px;
    padding-bottom: 10px;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    overflow: auto;
    background-color: rgb(0,0,0);
    background-color: rgba(0,0,0,0.9);
  }
 
  .popup-content {
    margin: auto;
    display: block;
  }
 
  .popup-content img {
    width: 100%;
    height: auto;
  }
 
  .close {
    position: absolute;
    top: 15px;
    right: 35px;
    color: #f1f1f1;
    font-size: 40px;
    font-weight: bold;
    transition: 0.3s;
  }
 
  .close:hover,
  .close:focus {
    color: #bbb;
    text-decoration: none;
    cursor: pointer;
  }
  #imgList img {
      width: 100px;
      height: 100px;
      margin: 5px;
      cursor: pointer;
  }
  .img-btn {
      color: #ffffff;
      text-align: center;
      margin: 0px 5px 5px;
      cursor: pointer;
  }
    </style>
</head>

<body>
    <div class="row">
        <div class="col-sm">
            <form class="container" id="drawingForm">
                <div class="form-group">
                    <label>prompt</label>
                    <textarea class="form-control" id="prompt" name="prompt" rows="3"
                        placeholder="Describe your imagination..."></textarea>
                </div>
                <div class="form-group">
                    <label>negative_prompt</label>
                    <textarea class="form-control" id="negative_prompt" name="negative_prompt" rows="3"
                        placeholder="Describe your imagination..."></textarea>
                </div>
                <div class="form-group">
                    <label>model</label>
                    <select class="form-control" id="model" name="model">
                        <option value="animagineXLV3_v30.safetensors [75f2f05b]">animagineXLV3_v30.safetensors</option>
                        <option value="devlishphotorealism_sdxl15.safetensors [77cba69f]">
                            devlishphotorealism_sdxl15.safetensors</option>
                        <option value="dreamshaperXL10_alpha2.safetensors [c8afe2ef]">dreamshaperXL10_alpha2.safetensors
                        </option>
                        <option value="dynavisionXL_0411.safetensors [c39cc051]">dynavisionXL_0411.safetensors</option>
                        <option value="juggernautXL_v45.safetensors [e75f5471]">juggernautXL_v45.safetensors</option>
                        <option value="realismEngineSDXL_v10.safetensors [af771c3f]">realismEngineSDXL_v10.safetensors
                        </option>
                        <option value="realvisxlV40.safetensors [f7fdcb51]">realvisxlV40.safetensors</option>
                        <option value="sd_xl_base_1.0.safetensors [be9edd61]">sd_xl_base_1.0.safetensors</option>
                        <option value="sd_xl_base_1.0_inpainting_0.1.safetensors [5679a81a]">
                            sd_xl_base_1.0_inpainting_0.1.safetensors</option>
                        <option value="turbovisionXL_v431.safetensors [78890989]">turbovisionXL_v431.safetensors
                        </option>
                    </select>
                </div>
                <div class="form-group">
                    <label>style_preset</label>
                    <select class="form-control" id="style_preset" name="style_preset">
                        <option value="3d-model">3d-model 3D模型</option>
                        <option value="analog-film">analog-film 模拟电影</option>
                        <option value="anime">anime 日本动画片</option>
                        <option value="cinematic">cinematic 电影般的</option>
                        <option value="comic-book">comic-book 漫画书</option>
                        <option value="digital-art">digital-art 数字艺术</option>
                        <option value="enhance">enhance 提高</option>
                        <option value="fantasy-art">fantasy-art 幻想艺术</option>
                        <option value="isometric">isometric 等距</option>
                        <option value="line-art">line-art 线条艺术</option>
                        <option value="low-poly">low-poly 低聚</option>
                        <option value="neon-punk">neon-punk 霓虹朋克</option>
                        <option value="origami">origami 折纸</option>
                        <option value="photographic">photographic 摄影的</option>
                        <option value="pixel-art">pixel-art 像素艺术</option>
                        <option value="texture">texture 质地</option>
                        <option value="craft-clay">craft-clay 工艺粘土</option>
                    </select>
                </div>
                <button id="submitBtn" type="submit" class="btn btn-primary">生成</button>
            </form>
        </div>
        <div class="col-sm">
            <div class="row" id="imgList"></div>
        </div>
    </div>
    <div id="myPopup" class="popup">
        <span class="close" onclick="closePopup()">&times;</span>
        <img class="popup-content" id="popupImg" src="" alt="Popup Image"> 
      </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
    <script>
        function openPopup() {
            document.getElementById('myPopup').style.display = 'block';
        }
        document.getElementById('imgList').addEventListener('click', async(e) => {
            console.log(e.target)
            let target = e.target
            if (target.src) {
                document.getElementById('popupImg').src = e.target.src
                openPopup()
            } else if ($(target).attr('class') === 'img-btn')  {
                let jobId = target.getAttribute('data-jobId')
                try {
                    const response = await fetch('/get-image', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify({
                            jobId
                        }),
                    });

                    const data = await response.json();
                    if (data.image) {
                        $(target).siblings('img').attr('src', data.image)
                        $(target).siblings('img').attr('title', '点击图片放大')
                    } else {
                        throw new Error('图片还在制作中,请稍后再试');
                    }
                } catch (error) {
                    alert('图片还在生成中,请稍后重新获取');
                } finally {
                    button.disabled = false;
                    button.innerHTML = '生成 ';
                }
            }
        })
        function closePopup() {
            document.getElementById('myPopup').style.display = 'none';
        }
        document.getElementById('negative_prompt').value = "low resolution, blurry, distorted features, wrong fingers, extra numbers, watermarks, ugly, distorted, deformed, deformed, repetitive, missing arms and legs, multiple hands and legs, incomplete limbs, long neck, cross-eyed, glazed eyes, lax eyes, squinting, deformed eyes"
        document.getElementById('drawingForm').addEventListener('submit', async (e) => {
            e.preventDefault();
            const prompt = document.getElementById('prompt').value;
            const negative_prompt = document.getElementById('negative_prompt').value;
            const model = document.getElementById('model').value;
            const style_preset = document.getElementById('style_preset').value;
            const button = e.target.querySelector('button');
            button.disabled = true;
            button.innerHTML = '生成中...';

            try {
                const response = await fetch('/generate-image', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        prompt,
                        negative_prompt,
                        model,
                        style_preset
                    }),
                });

                const data = await response.json();
                if (data.image) {
                    let imgs = "<div class='img-list'><img title='点击图片放大' src=" + data.image + "><div class='img-btn' data-jobId=" + data.jobId +">获取图片</div></div>"
                    $('#imgList').append(imgs)
                } else if (data.jobId) {
                    let imgs = "<div class='img-list'><img src='' title='点击下方按钮再次获取图片'><div class='img-btn' data-jobId=" + data.jobId +">获取图片</div></div>"
                    $('#imgList').append(imgs)
                    alert('图片还在生成中,请稍后重新获取');
                } else {
                    throw new Error('生成失败,请重新生成');
                }
            } catch (error) {
                alert('生成失败,请重新生成');
            } finally {
                button.disabled = false;
                button.innerHTML = '生成 ';
            }
        });
    </script>
</body>

</html>
`;
const PRODIA_API_KEY = 'xxxxx'

// The rest of the Worker script remains the same
addEventListener('fetch', event => {
    event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
    const url = new URL(request.url);

    if (request.method === 'GET' && url.pathname === '/') {
        return new Response(htmlContent, {
            headers: {
                'Content-Type': 'text/html'
            },
        });
    } else if (request.method === 'POST' && url.pathname === '/generate-image') {
        return generateImageByText(request);
    } else if (request.method === 'POST' && url.pathname === '/get-image') {
        return getImage(request);
    } else {
        return new Response('Not Found', {
            status: 404
        });
    }
}

async function generateImageByText(request) {
    const {
        prompt,
        negative_prompt,
        model,
        style_preset
    } = await request.json();
    // First request to generate the image
    const generateResponse = await fetch('https://api.prodia.com/v1/sdxl/generate', {
        method: 'POST',
        headers: {
            accept: 'application/json',
            'content-type': 'application/json',
            'X-Prodia-Key': PRODIA_API_KEY
        },
        body: JSON.stringify({
            model: model,
            prompt: prompt,
            negative_prompt: negative_prompt,
            style_preset: style_preset,
            steps: 20,
            cfg_scale: 7,
            seed: -1,
            sampler: 'DPM++ 2M Karras',
            width: 1024,
            height: 1024
        })
    });
    const generateData = await generateResponse.json();
    const jobId = generateData.job;
    console.log(jobId)
    // Polling for the job status
    return new Response(JSON.stringify({jobId: jobId}), {
        headers: {
            'Content-Type': 'application/json'
        }
    });
}
async function getImage(request) {
    const {
        jobId
    } = await request.json();
    // Polling for the job status
    const statusResponse = await fetch(`https://api.prodia.com/v1/job/${jobId}`, {
        method: 'GET',
        headers: {
            accept: 'application/json',
            'X-Prodia-Key': PRODIA_API_KEY
        }
    });
    const statusData = await statusResponse.json();
    return new Response(JSON.stringify({image: statusData.imageUrl}), {
        headers: {
            'Content-Type': 'application/json'
        }
    });
}




大家记得把PRODIA_API_KEY换成自己key
再次感谢大佬提供的内容。。。
以下是大佬的贴着内容

通过cf worker每月白嫖1000次sd绘图(支持api,支持多key轮询,支持多种绘图样式模型)

本项目由 johnson
基于以下项目二次开发:

【第一弹】【更新】用不完,根本用不完,部署cf worker无限免费绘画,可Api,支持多模型切换 - 资源荟萃 - LINUX DO

【第二弹】用不完,根本用不完,部署cf worker无限免费绘画,可Api,支持多模型切换 - 资源荟萃 - LINUX DO

由于 johnson 是个低调的大佬,我就代劳帮 johnson 发布文章,白嫖stars啦! :tieba_013:


1.我们需要在 Prodiap 注册一个账户,并获取 PRODIA_API_KEY

2.在Cloud Flare中的Workers 和 Pages中创建一个Worker项目并点击编辑代码:

3.复制下方的js项目粘贴至worker中修改相应参数后部署
(如何获取cf_accountID以及cf_token请自行查阅学习)

API_KEY可自定义比如:sk-123456789

//prodia.com API_KEY
const PRODIA_API_KEY = "XXX";

//本项目授权api_key,防止被恶意调用
const API_KEY = "sk-XXX";
//cloudflare账号列表,每次请求都会随机从列表里取一个账号
const CF_ACCOUNT_LIST = [
  { account_id: "cf_accountID", token: "cf_token" }
];
//在你输入的prompt中添加 ---ntl可强制禁止提示词翻译、优化功能
//在你输入的prompt中添加 ---tl可强制开启提示词翻译、优化功能
//是否开启提示词翻译、优化功能
const CF_IS_TRANSLATE = true;
//示词翻译、优化模型
const CF_TRANSLATE_MODEL = "@cf/qwen/qwen1.5-14b-chat-awq";
//模型映射,设置客户端可用的模型。one-api,new-api在添加渠道时可使用"获取模型列表"功能,一键添加模型
const CUSTOMER_MODEL_MAP = {
    "animagineXLV3_v30.safetensors": "animagineXLV3_v30.safetensors [75f2f05b]",
    "devlishphotorealism_sdxl15.safetensors": "devlishphotorealism_sdxl15.safetensors [77cba69f]",
    "dreamshaperXL10_alpha2.safetensors": "dreamshaperXL10_alpha2.safetensors [c8afe2ef]",
    "dynavisionXL_0411.safetensors": "dynavisionXL_0411.safetensors [c39cc051]",
    "juggernautXL_v45.safetensors": "juggernautXL_v45.safetensors [e75f5471]",
    "realismEngineSDXL_v10.safetensors": "realismEngineSDXL_v10.safetensors [af771c3f]",
    "realvisxlV40.safetensors": "realvisxlV40.safetensors [f7fdcb51]",
    "sd_xl_base_1.0.safetensors": "sd_xl_base_1.0.safetensors [be9edd61]",
    "sd_xl_base_1.0_inpainting_0.1.safetensors": "sd_xl_base_1.0_inpainting_0.1.safetensors [5679a81a]",
    "turbovisionXL_v431.safetensors": "turbovisionXL_v431.safetensors [78890989]"
};

/**
 * Handles incoming requests to the Cloudflare Worker.
 * @param {Request} request - The incoming request object.
 * @returns {Response} - The response object.
 * @throws {Error} - If the request is invalid or the response fails.
 */
async function handleRequest(request) {
  try {
    if (request.method === "OPTIONS") {
      return new Response("", {
        status: 204,
        headers: {
          'Access-Control-Allow-Origin': '*',
          "Access-Control-Allow-Headers": '*'
        }
      });
    }

    const authHeader = request.headers.get("Authorization");
    if (!authHeader || !authHeader.startsWith("Bearer ") || authHeader.split(" ")[1] !== API_KEY) {
      return new Response("Unauthorized", { status: 401 });
    }

    if (request.url.endsWith("/v1/models")) {
      const arrs = [];
      Object.keys(CUSTOMER_MODEL_MAP).map(element => arrs.push({ id: element, object: "model" }))
      const response = {
        data: arrs,
        success: true
      };
      return new Response(JSON.stringify(response), {
        headers: {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Headers': '*'
        }
      });
    }

    if (request.method !== "POST") {
      return new Response("Only POST requests are allowed", {
        status: 405,
        headers: {
          'Access-Control-Allow-Origin': '*',
          "Access-Control-Allow-Headers": '*'
        }
      });
    }

    if (!request.url.endsWith("/v1/chat/completions")) {
      return new Response("Not Found", {
        status: 404,
        headers: {
          'Access-Control-Allow-Origin': '*',
          "Access-Control-Allow-Headers": '*'
        }
      });
    }

    const data = await request.json();
    const messages = data.messages || [];
    const model = CUSTOMER_MODEL_MAP[data.model] || CUSTOMER_MODEL_MAP["v1-5-inpainting.safetensors"];
    const stream = data.stream || false;
    const userMessage = messages.reverse().find((msg) => msg.role === "user")?.content;
    if (!userMessage) {
      return new Response(JSON.stringify({ error: "未找到用户消息" }), {
        status: 400,
        headers: {
          'Content-Type': 'application/json',
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Headers': '*'
        }
      });
    }

    const is_translate = extractTranslate(userMessage);
    const originalPrompt = cleanPromptString(userMessage);
    const translatedPrompt = is_translate ? await getPrompt(originalPrompt) : originalPrompt;

    const imageUrl = await generateImageByText(model, translatedPrompt);

    if (stream) {
      return handleStreamResponse(originalPrompt, translatedPrompt,"1024x1024", model, imageUrl);
    } else {
      return handleNonStreamResponse(originalPrompt, translatedPrompt, "1024x1024", model, imageUrl);
    }
  } catch (error) {
    return new Response("Internal Server Error: " + error.message, {
      status: 500,
      headers: {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Headers': '*'
      }
    });
  }
}

/**
 * @description
 *  Translate a prompt into a stable diffusion prompt style.
 * @param {string} prompt - The prompt to translate.
 * @returns {Promise<string>} The translated prompt.
 * @throws {Error} If the translation fails.
 */
async function getPrompt(prompt) {
  const requestBodyJson = {
    messages: [
      {
        role: "system",
        content: `作为 Stable Diffusion Prompt 提示词专家,您将从关键词中创建提示,通常来自 Danbooru 等数据库。

        提示通常描述图像,使用常见词汇,按重要性排列,并用逗号分隔。避免使用"-"或".",但可以接受空格和自然语言。避免词汇重复。

        为了强调关键词,请将其放在括号中以增加其权重。例如,"(flowers)"将'flowers'的权重增加1.1倍,而"(((flowers)))"将其增加1.331倍。使用"(flowers:1.5)"将'flowers'的权重增加1.5倍。只为重要的标签增加权重。

        提示包括三个部分:**前缀**(质量标签+风格词+效果器)+ **主题**(图像的主要焦点)+ **场景**(背景、环境)。

        *   前缀影响图像质量。像"masterpiece"、"best quality"、"4k"这样的标签可以提高图像的细节。像"illustration"、"lensflare"这样的风格词定义图像的风格。像"bestlighting"、"lensflare"、"depthoffield"这样的效果器会影响光照和深度。

        *   主题是图像的主要焦点,如角色或场景。对主题进行详细描述可以确保图像丰富而详细。增加主题的权重以增强其清晰度。对于角色,描述面部、头发、身体、服装、姿势等特征。

        *   场景描述环境。没有场景,图像的背景是平淡的,主题显得过大。某些主题本身包含场景(例如建筑物、风景)。像"花草草地"、"阳光"、"河流"这样的环境词可以丰富场景。你的任务是设计图像生成的提示。请按照以下步骤进行操作:

        1.  我会发送给您一个图像场景。需要你生成详细的图像描述
        2.  图像描述必须是英文,输出为Positive Prompt。

        示例:

        我发送:二战时期的护士。
        您回复只回复:
        A WWII-era nurse in a German uniform, holding a wine bottle and stethoscope, sitting at a table in white attire, with a table in the background, masterpiece, best quality, 4k, illustration style, best lighting, depth of field, detailed character, detailed environment.`
      },
      {
        role: "user",
        content: prompt
      }
    ]
  };

  const response = await postRequest(CF_TRANSLATE_MODEL, requestBodyJson);

  if (!response.ok) {
    return prompt;
  }

  const jsonResponse = await response.json();
  const res = jsonResponse.result.response;
  return res;
}

/**
 * Generate an image from a given text prompt using the Prodia AI API
 * @param {string} model - The name of the AI model to use for image generation
 * @param {string} prompt - The text prompt to generate an image from
 * @returns {string} - The URL of the generated image
 * @throws {Error} - If the image generation fails
 * @see https://docs.prodia.ai/docs/api-reference
 */
async function generateImageByText(model, prompt) {
  // First request to generate the image
  const generateOptions = {
    method: 'POST',
    headers: {
      accept: 'application/json',
      'content-type': 'application/json',
      'X-Prodia-Key': PRODIA_API_KEY
    },
    body: JSON.stringify({
      model: model,
      prompt: prompt,
      negative_prompt: 'low resolution, blurry, distorted features, wrong fingers, extra numbers, watermarks, ugly, distorted, deformed, deformed, repetitive, missing arms and legs, multiple hands and legs, incomplete limbs, long neck, cross-eyed, glazed eyes, lax eyes, squinting, deformed eyes',
      steps: 20,
      cfg_scale: 7,
      seed: -1,
      sampler: 'DPM++ 2M Karras',
      width: 1024,
      height: 1024
    })
  };

  try {
    const generateResponse = await fetch('https://api.prodia.com/v1/sdxl/generate', generateOptions);
    const generateData = await generateResponse.json();

    if (generateData.status !== 'queued') {
      throw new Error('Failed to queue the job');
    }

    const jobId = generateData.job;

    // Polling for the job status
    const statusOptions = {
      method: 'GET',
      headers: {
        accept: 'application/json',
        'X-Prodia-Key': PRODIA_API_KEY
      }
    };

    let statusData;
    while (true) {
      const statusResponse = await fetch(`https://api.prodia.com/v1/job/${jobId}`, statusOptions);
      statusData = await statusResponse.json();

      if (statusData.status === 'succeeded') {
        return statusData.imageUrl;
      } else if (statusData.status === 'failed') {
        throw new Error('Image generation failed');
      }

      // Wait for a short period before checking again
      await new Promise(resolve => setTimeout(resolve, 5000));
    }
  } catch (error) {
    return "图像生成或转换失败,请检查!" + error.message;
  }
}

/**
 * Return a streaming response with the generated image.
 *
 * The response will contain the generated image as a base64 encoded string
 * and the original and translated prompts as text. The response will be sent
 * as a Server-Sent Event (SSE) stream, with the `data` event containing the
 * response payload.
 *
 * @param {string} originalPrompt - The original prompt given to the model.
 * @param {string} translatedPrompt - The translated prompt given to the model.
 * @param {string} size - The size of the generated image.
 * @param {string} model - The model used to generate the image.
 * @param {string} imageUrl - The URL of the generated image.
 * @returns {Response} - The response object.
 */
function handleStreamResponse(originalPrompt, translatedPrompt, size, model, imageUrl) {
  const uniqueId = `chatcmpl-${Date.now()}`;
  const createdTimestamp = Math.floor(Date.now() / 1000);
  const systemFingerprint = "fp_" + Math.random().toString(36).substr(2, 9);
  const content = `🎨 原始提示词:${originalPrompt}\n` +
    `🌐 翻译后的提示词:${translatedPrompt}\n` +
    `📐 图像规格:${size}\n` +
    `🌟 图像生成成功!\n` +
    `以下是结果:\n\n` +
    `![生成的图像](${imageUrl})`;

  const responsePayload = {
    id: uniqueId,
    object: "chat.completion.chunk",
    created: createdTimestamp,
    model: model,
    system_fingerprint: systemFingerprint,
    choices: [
      {
        index: 0,
        delta: {
          content: content,
        },
        finish_reason: "stop",
      },
    ],
  };

  const dataString = JSON.stringify(responsePayload);

  return new Response(`data: ${dataString}\n\n`, {
    status: 200,
    headers: {
      "Content-Type": "text/event-stream",
      'Access-Control-Allow-Origin': '*',
      "Access-Control-Allow-Headers": '*',
    },
  });
}

/**
 * Return a non-streaming response with the generated image.
 *
 * The response will contain the generated image as a base64 encoded string
 * and the original and translated prompts as text.
 *
 * @param {string} originalPrompt - The original prompt given to the model.
 * @param {string} translatedPrompt - The translated prompt given to the model.
 * @param {string} size - The size of the generated image (e.g. 1024x1024).
 * * @param {string} model - The model used to generate the image (e.g. @cf/stabilityai/stable-diffusion-xl-base-1.0).
 * @param {string} imageUrl - The URL of the generated image.
 * @return {Response} - The response object with the generated image and prompts.
 */
function handleNonStreamResponse(originalPrompt, translatedPrompt, size, model, imageUrl) {
  const uniqueId = `chatcmpl-${Date.now()}`;
  const createdTimestamp = Math.floor(Date.now() / 1000);
  const systemFingerprint = "fp_" + Math.random().toString(36).substr(2, 9);
  const content = `🎨 原始提示词:${originalPrompt}\n` +
    `🌐 翻译后的提示词:${translatedPrompt}\n` +
    `📐 图像规格:${size}\n` +
    `🌟 图像生成成功!\n` +
    `以下是结果:\n\n` +
    `![生成的图像](${imageUrl})`;

  const response = {
    id: uniqueId,
    object: "chat.completion",
    created: createdTimestamp,
    model: model,
    system_fingerprint: systemFingerprint,
    choices: [{
      index: 0,
      message: {
        role: "assistant",
        content: content
      },
      finish_reason: "stop"
    }],
    usage: {
      prompt_tokens: translatedPrompt.length,
      completion_tokens: content.length,
      total_tokens: translatedPrompt.length + content.length
    }
  };

  const dataString = JSON.stringify(response);

  return new Response(dataString, {
    status: 200,
    headers: {
      'Content-Type': 'application/json',
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Headers': '*'
    }
  });
}

/**
 * @description
 * POST request to Cloudflare AI API
 * @param {string} model - AI model name
 * @param {object} jsonBody - JSON object to be sent in the body of the request
 * @returns {Promise<Response>} - Response object
 * @throws {Error} - If response status is not OK
 */
async function postRequest(model, jsonBody) {
  const cf_account = CF_ACCOUNT_LIST[Math.floor(Math.random() * CF_ACCOUNT_LIST.length)];
  const apiUrl = `https://api.cloudflare.com/client/v4/accounts/${cf_account.account_id}/ai/run/${model}`;
  const response = await fetch(apiUrl, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${cf_account.token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(jsonBody)
  });

  if (!response.ok) {
    throw new Error('Unexpected response ' + response.status);
  }
  return response;
}

/**
 * Extract translate flag from prompt string.
 *
 * This function will parse the flag from the given prompt string and return the
 * translate flag. If the flag is not found, it will return the default translate
 * flag set in CF_IS_TRANSLATE.
 *
 * @param {string} prompt The prompt string to parse the flag from.
 * @return {boolean} The translate flag parsed from the prompt string.
 */
function extractTranslate(prompt) {
  const match = prompt.match(/---n?tl/);
  if (match && match[0]) {
    if (match[0] == "---ntl") {
      return false;
    }
    else if (match[0] == "---tl") {
      return true;
    }
  }
  return CF_IS_TRANSLATE;
}

/**
 * Remove translate flag from prompt string.
 *
 * This function will remove the translate flag ("---ntl" or "---tl") from the
 * given prompt string and return the cleaned prompt string.
 *
 * @param {string} prompt The prompt string to clean.
 * @return {string} The cleaned prompt string.
 */
function cleanPromptString(prompt) {
  return prompt.replace(/---n?tl/, "").trim();
}

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

4.在刚刚创建的worker中点击设置,在域和路由中添加自定义域,并填写自己的子域名,如image.xxxx.com
(此处需自行查阅学习如何给cf添加自己的域名)

5.复制自己的域名在任意对话平台或API中转站中添加自定义接口以及自定义模型:
(此处以ChatGPT-Next-Web为例)

以下是所有模型的介绍,可以根据自己的需求来选择模型:

  1. animagineXLV3_v30 :适合想要动画风格的创作者,可能在动画效果上表现优越。
  2. devlishphotorealism_sdxl15 :如果你需要超真实的图像效果,这个模型可能是个好选择,特别适合用于摄影风格的创作。
  3. dreamshaperXL10_alpha2 :注重创意和想象力,适合艺术风格较强的作品。
  4. juggernautXL_v45 :可能在生成大型复杂场景方面表现出色,可以考虑。
  5. realismEngineSDXL_v10 :专注于真实效果的生成,适合需要高度逼真图像的项目。
  6. sd_xl_base_1.0sd_xl_base_1.0_inpainting_0.1 :这些是基础模型,适合多种用途,也可以在需要时进行细微调整。
  7. turbovisionXL_v431 :可能在速度和效率上有优势,适合需要快速生成的场景。
16 个赞

这是cf的 ai绘图吗?部署在cf,worker上吗!

qiaong强强学习了

收藏涅捏捏。

这网站有啥限制吗,我咋进不去

可能需要梯子

挂了也不行,我换个梯子试试

换个梯子就行了。。。

哇!感谢大佬!

这个是全部放到worker的编辑代码里替换原js代码吗?佬

对,替换原来的worker.js

谢谢,试试去

牛了,有空搞一下

1、能加个进页面的密码吗?免得被人刷完额度
2、能增加个生成图分辨率选择吗
3、能增加一个输入临时api key的设置吗,或者可以配置新key,方便验证key或换key

烦请佬权衡

感谢分享好帖

感谢大佬分享

感谢大佬,不错