喂饭,你自己的Flux-schnell网站(当然是白嫖siliconflow)

喂饭时间到,今天继续来给大家喂饭,让我们简单地糊一个自己的Flux网站,然后分享给你的朋友们。

效果如下:

需要材料如下:

  1. siliconflow API key,账号自己注册,aff我都不贴了,你自己去搞定就行,反正现在siliconflow的flux还能白嫖。

  2. 一个Cloudflare账号。

  3. 一个托管在Cloudflare的域名。

  4. 一个还能用的键盘(Ctrl+C, Ctrl+V)。

  5. 没了吧?应该…

首先获取siliconflow API key,记下来。

然后打开Cloudflare,登录,开始:

创建一个worker,

名字自己改,点击部署。

然后选择“继续处理项目”。

接下来设置环境变量:

点击“设置”—“变量”—“添加变量”。

添加以下变量:

然后点击“部署”。

接着点击“触发器”—“添加自定义域”。

输入你想要使用的域名(用来访问flux服务),这个域名必须是在Cloudflare解析的,比如你有一个在Cloudflare解析的域名是woshiqinshiwang.com, 你可以填入flux.woshiqinshiwang.comCloudflare会帮你搞定剩下的事情,包括证书。完事了记得点击“添加自定义域”完成部署。

下一步我们来设置一下Turnstile,如果你之前已经为想要使用的域名设置了Turnstile,则可以跳过这一步。

点击 “Turnstile”—“添加站点”。

进入后:

在1中填写你喜欢的名字,随便啦~~

在2中选择你想要使用的域名,记得从点击后出现的下拉菜单中选择,比如上面你添加的自定义域“flux.woshiqinshiwang.com”,那么你就应该在下拉菜单中选择“woshiqinshiwang.com”。

点3部署。

然后你会看到两个key,分别是“站点密钥”跟“密钥”,copy下来,复制到上面对应的变量中。

  • TURNSTILE_SITE_KEY: 对应“站点密钥”。

  • TURNSTILE_SECRET_KEY: 对应“密钥”。

完事了记得点击“部署”。

如果你忘记copy那两个key,可以点击 “Turnstile”—找到你刚才设置的域名点击“settings”就可以看到啦。

最后也是最简单的一步:

  1. 点击 “workers和pages”—点击“概述”–选择你刚创建的worker。

  1. 点击“编辑代码”。

  1. 进入编辑页面,删除原来所有的代码,复制粘贴下面的代码。
// Function to generate a secure random token
function generateToken() {
  return crypto.randomUUID();
}

// Function to verify Turnstile token
async function verifyTurnstileToken(token) {
  const formData = new FormData();
  formData.append('secret', TURNSTILE_SECRET_KEY);
  formData.append('response', token);

  const url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
  const result = await fetch(url, {
    body: formData,
    method: 'POST',
  });

  const outcome = await result.json();
  return outcome.success;
}

// Function to set a secure cookie
function setCookie(name, value, maxAge = 3600) {
  return `${name}=${value}; HttpOnly; Secure; SameSite=Strict; Max-Age=${maxAge}; Path=/`;
}

// Function to get cookie value
function getCookie(request, name) {
  const cookieString = request.headers.get('Cookie') || '';
  const cookies = cookieString.split(';').map(cookie => cookie.trim());
  const targetCookie = cookies.find(cookie => cookie.startsWith(`${name}=`));
  return targetCookie ? targetCookie.split('=')[1] : null;
}

// Middleware to check authentication
async function checkAuth(request) {
  const sessionToken = getCookie(request, 'session');
  if (!sessionToken) return false;

  try {
    const [username, expirationTime] = atob(sessionToken).split(':');
    if (Date.now() > parseInt(expirationTime)) return false;
    return username === AUTH_USERNAME;
  } catch {
    return false;
  }
}

// Handle login request
async function handleLogin(request) {
  if (request.method !== 'POST') {
    return new Response('Method Not Allowed', { status: 405 });
  }

  const formData = await request.formData();
  const username = formData.get('username');
  const password = formData.get('password');
  const turnstileResponse = formData.get('cf-turnstile-response');

  if (!await verifyTurnstileToken(turnstileResponse)) {
    return new Response('Turnstile verification failed', { status: 400 });
  }

  if (username === AUTH_USERNAME && password === AUTH_PASSWORD) {
    const expirationTime = Date.now() + 3600000; // 1 hour from now
    const sessionToken = btoa(`${username}:${expirationTime}`);
    const headers = new Headers();
    headers.append('Set-Cookie', setCookie('session', sessionToken));
    headers.append('Location', '/');
    return new Response(null, { status: 302, headers });
  } else {
    return new Response('Invalid credentials', { status: 401 });
  }
}

// Handle logout request
function handleLogout() {
  const headers = new Headers();
  headers.append('Set-Cookie', setCookie('session', '', 0)); // Expire the cookie
  headers.append('Location', '/login');
  return new Response(null, { status: 302, headers });
}

// Generate image
async function generateImage(request) {
  try {
    const { prompt, width, height, num_inference_steps } = await request.json();
    
    const response = await fetch(API_URL, {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${API_KEY}`
      },
      body: JSON.stringify({ prompt, width, height, num_inference_steps })
    });

    if (!response.ok) {
      throw new Error(`API request failed with status ${response.status}`);
    }

    return response;
  } catch (error) {
    return new Response(JSON.stringify({ error: error.message }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' }
    });
  }
}

// Get login page HTML
function getLoginHTML() {
  return `
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login - Flux.1-schnell Generator</title>
    <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
    <style>
        body {
            font-family: 'Roboto', sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: #f4f4f4;
        }
        form {
            background-color: white;
            padding: 2rem;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
            width: 100%;
            max-width: 400px;
        }
        h2 {
            margin-top: 0;
            color: #333;
            text-align: center;
        }
        input {
            display: block;
            width: 100%;
            padding: 0.75rem;
            margin: 1rem 0;
            border: 1px solid #ddd;
            border-radius: 4px;
            box-sizing: border-box;
            font-size: 1rem;
        }
        button {
            width: 100%;
            padding: 0.75rem;
            background-color: #3498db;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 1rem;
            transition: background-color 0.3s ease;
        }
        button:hover {
            background-color: #2980b9;
        }
    </style>
</head>
<body>
    <form method="POST" action="/login">
        <h2>Login</h2>
        <input type="text" name="username" placeholder="Username" required>
        <input type="password" name="password" placeholder="Password" required>
        <div class="cf-turnstile" data-sitekey="${TURNSTILE_SITE_KEY}"></div>
        <button type="submit">Login</button>
    </form>
</body>
</html>
  `;
}

// Get main page HTML
function getMainHTML() {
  return `
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flux.1-schnell Generator</title>
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" rel="stylesheet">
    <style>
        :root {
            --background-color: #f4f4f4;
            --card-background: #ffffff;
            --text-color: #333333;
            --primary-color: #3498db;
            --primary-hover: #2980b9;
            --error-color: #e74c3c;
        }
        body {
            font-family: 'Roboto', sans-serif;
            line-height: 1.6;
            color: var(--text-color);
            background-color: var(--background-color);
            margin: 0;
            padding: 0;
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
        }
        .container {
            width: 100%;
            max-width: 800px;
            margin: 2rem auto;
            padding: 2rem;
            background-color: var(--card-background);
            border-radius: 10px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }
        h1 {
            color: var(--text-color);
            text-align: center;
            margin-bottom: 1.5rem;
        }
        .image-container {
            margin-bottom: 2rem;
            text-align: center;
            background-color: var(--background-color);
            padding: 2rem;
            border-radius: 8px;
            min-height: 300px;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .image-container img {
            max-width: 100%;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        }
        .form-group {
            margin-bottom: 1.5rem;
        }
        label {
            display: block;
            margin-bottom: 0.5rem;
            font-weight: bold;
        }
        input[type="text"] {
            width: 100%;
            padding: 0.8rem;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 1rem;
        }
        .image-ratio-group {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
            gap: 1rem;
            margin-top: 0.5rem;
        }
        .image-ratio-option input[type="radio"] {
            display: none;
        }
        .image-ratio-option label {
            display: block;
            padding: 0.5rem;
            text-align: center;
            background-color: var(--background-color);
            border: 2px solid #ddd;
            border-radius: 4px;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        .image-ratio-option input[type="radio"]:checked + label {
            background-color: var(--primary-color);
            color: white;
            border-color: var(--primary-color);
        }
        .generate-btn {
            display: block;
            width: 100%;
            padding: 1rem;
            background-color: var(--primary-color);
            color: white;
            border: none;
            border-radius: 4px;
            font-size: 1rem;
            cursor: pointer;
            transition: background-color 0.3s ease;
        }
        .generate-btn:hover {
            background-color: var(--primary-hover);
        }
        .generate-btn:disabled {
            background-color: #bdc3c7;
            cursor: not-allowed;
        }
        .logout-btn {
            display: inline-block;
            margin-top: 2rem;
            padding: 0.8rem 1.5rem;
            background-color: var(--error-color);
            color: white;
            text-decoration: none;
            border-radius: 4px;
            transition: background-color 0.3s ease;
        }
        .logout-btn:hover {
            background-color: #c0392b;
        }
        #error-message {
            color: var(--error-color);
            text-align: center;
            margin-top: 1rem;
        }
        .loading {
            font-style: italic;
            color: #7f8c8d;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Flux.1-schnell Generator</h1>
        
        <div class="image-container" id="image-container">
            <p>Your generated image will appear here</p>
        </div>
        
        <form id="image-form">
            <div class="form-group">
                <label for="prompt">Describe your image:</label>
                <input type="text" id="prompt" name="prompt" placeholder="Enter your image description" required>
            </div>
            
            <div class="form-group">
                <label>Select image ratio:</label>
                <div class="image-ratio-group">
                    <div class="image-ratio-option">
                        <input type="radio" id="ratio-1-1" name="image_ratio" value="1:1" checked>
                        <label for="ratio-1-1">1:1</label>
                    </div>
                    <div class="image-ratio-option">
                        <input type="radio" id="ratio-1-2" name="image_ratio" value="1:2">
                        <label for="ratio-1-2">1:2</label>
                    </div>
                    <div class="image-ratio-option">
                        <input type="radio" id="ratio-3-2" name="image_ratio" value="3:2">
                        <label for="ratio-3-2">3:2</label>
                    </div>
                    <div class="image-ratio-option">
                        <input type="radio" id="ratio-3-4" name="image_ratio" value="3:4">
                        <label for="ratio-3-4">3:4</label>
                    </div>
                    <div class="image-ratio-option">
                        <input type="radio" id="ratio-16-9" name="image_ratio" value="16:9">
                        <label for="ratio-16-9">16:9</label>
                    </div>
                    <div class="image-ratio-option">
                        <input type="radio" id="ratio-9-16" name="image_ratio" value="9:16">
                        <label for="ratio-9-16">9:16</label>
                    </div>
                </div>
            </div>
            
            <button type="submit" class="generate-btn">Generate Image</button>
        </form>
        
        <div id="error-message"></div>
        <a href="/logout" class="logout-btn">Logout</a>
    </div>

    <script>
    document.getElementById('image-form').addEventListener('submit', async function(event) {
        event.preventDefault();

        const prompt = document.getElementById('prompt').value;
        const imageRatio = document.querySelector('input[name="image_ratio"]:checked').value;
        const imageContainer = document.getElementById('image-container');
        const errorMessage = document.getElementById('error-message');
        const generateButton = document.querySelector('.generate-btn');

        const numInferenceSteps = 20;

        updateUI('generating');

        const { width, height } = getImageDimensions(imageRatio);

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

            if (!response.ok) {
                throw new Error('Failed to generate image. Please try again.');
            }

            const data = await response.json();

            if (data.images && data.images.length > 0) {const imageUrl = data.images[0].url;
              imageContainer.innerHTML = '<img src="' + imageUrl + '" alt="Generated Image">';
          } else {
              throw new Error('Failed to generate image. Please try again.');
          }
      } catch (error) {
          console.error('Error:', error);
          errorMessage.textContent = error.message;
          imageContainer.innerHTML = '<p>Failed to generate image</p>';
      } finally {
          updateUI('ready');
      }
  });

  function getImageDimensions(ratio) {
      const dimensions = {
          '1:1': { width: 1024, height: 1024 },
          '1:2': { width: 512, height: 1024 },
          '3:2': { width: 768, height: 512 },
          '3:4': { width: 768, height: 1024 },
          '16:9': { width: 1024, height: 576 },
          '9:16': { width: 576, height: 1024 }
      };
      return dimensions[ratio] || dimensions['1:1'];
  }

  function updateUI(state) {
      const imageContainer = document.getElementById('image-container');
      const errorMessage = document.getElementById('error-message');
      const generateButton = document.querySelector('.generate-btn');

      if (state === 'generating') {
          imageContainer.innerHTML = '<p class="loading">Generating your image... This may take a moment.</p>';
          errorMessage.textContent = '';
          generateButton.disabled = true;
          generateButton.textContent = 'Generating...';
      } else {
          generateButton.disabled = false;
          generateButton.textContent = 'Generate Image';
      }
  }
  </script>
</body>
</html>
`;
}

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

// Login route
if (url.pathname === '/login') {
  if (request.method === 'GET') {
    return new Response(getLoginHTML(), {
      headers: { 'Content-Type': 'text/html' },
    });
  } else if (request.method === 'POST') {
    return handleLogin(request);
  }
}

// Logout route
if (url.pathname === '/logout') {
  return handleLogout();
}

// Check authentication for all other routes
const isAuthenticated = await checkAuth(request);
if (!isAuthenticated) {
  const headers = new Headers();
  headers.append('Location', '/login');
  return new Response(null, { status: 302, headers });
}

// Generate image route
if (request.method === 'POST' && url.pathname === '/generate-image') {
  return generateImage(request);
}

// Main page route
if (request.method === 'GET' && url.pathname === '/') {
  return new Response(getMainHTML(), {
    headers: { 'Content-Type': 'text/html' },
  });
}

// 404 for all other routes
return new Response('Not Found', { status: 404 });
}

// Event listener
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
  1. 完事记得点击“部署”。

好了,去爽吧。

这个教程只适合小白,大佬们见笑了,如果你觉得对你有帮助,请给我一个赞吧。

23 个赞

不错不错,感谢

Mark,马上开始直播推文

感谢分享这个教程

好详细,感谢大佬

感谢大佬的教程!

不错的教程,感谢分享

From 人工智能 to 资源荟萃

美化了一下ui

// Function to generate a secure random token
function generateToken() {
  return crypto.randomUUID();
}

// Function to verify Turnstile token
async function verifyTurnstileToken(token) {
  const formData = new FormData();
  formData.append('secret', TURNSTILE_SECRET_KEY);
  formData.append('response', token);

  const url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
  const result = await fetch(url, {
    body: formData,
    method: 'POST',
  });

  const outcome = await result.json();
  return outcome.success;
}

// Function to set a secure cookie
function setCookie(name, value, maxAge = 3600) {
  return `${name}=${value}; HttpOnly; Secure; SameSite=Strict; Max-Age=${maxAge}; Path=/`;
}

// Function to get cookie value
function getCookie(request, name) {
  const cookieString = request.headers.get('Cookie') || '';
  const cookies = cookieString.split(';').map(cookie => cookie.trim());
  const targetCookie = cookies.find(cookie => cookie.startsWith(`${name}=`));
  return targetCookie ? targetCookie.split('=')[1] : null;
}

// Middleware to check authentication
async function checkAuth(request) {
  const sessionToken = getCookie(request, 'session');
  if (!sessionToken) return false;

  try {
    const [username, expirationTime] = atob(sessionToken).split(':');
    if (Date.now() > parseInt(expirationTime)) return false;
    return username === AUTH_USERNAME;
  } catch {
    return false;
  }
}

// Handle login request
async function handleLogin(request) {
  if (request.method !== 'POST') {
    return new Response('Method Not Allowed', { status: 405 });
  }

  const formData = await request.formData();
  const username = formData.get('username');
  const password = formData.get('password');
  const turnstileResponse = formData.get('cf-turnstile-response');

  if (!await verifyTurnstileToken(turnstileResponse)) {
    return new Response('Turnstile verification failed', { status: 400 });
  }

  if (username === AUTH_USERNAME && password === AUTH_PASSWORD) {
    const expirationTime = Date.now() + 3600000; // 1 hour from now
    const sessionToken = btoa(`${username}:${expirationTime}`);
    const headers = new Headers();
    headers.append('Set-Cookie', setCookie('session', sessionToken));
    headers.append('Location', '/');
    return new Response(null, { status: 302, headers });
  } else {
    return new Response('Invalid credentials', { status: 401 });
  }
}

// Handle logout request
function handleLogout() {
  const headers = new Headers();
  headers.append('Set-Cookie', setCookie('session', '', 0)); // Expire the cookie
  headers.append('Location', '/login');
  return new Response(null, { status: 302, headers });
}

// Generate image
async function generateImage(request) {
  try {
    const { prompt, width, height, num_inference_steps } = await request.json();
    
    const response = await fetch(API_URL, {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${API_KEY}`
      },
      body: JSON.stringify({ prompt, width, height, num_inference_steps })
    });

    if (!response.ok) {
      throw new Error(`API request failed with status ${response.status}`);
    }

    return response;
  } catch (error) {
    return new Response(JSON.stringify({ error: error.message }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' }
    });
  }
}

// Get login page HTML with dark theme toggle
function getLoginHTML() {
  return `
<!DOCTYPE html>
<html lang="zh" class="dark-theme">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录 - Flux.1-schnell 生成器</title>
    <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" rel="stylesheet">
    <style>
        :root {
            --background-color: #f0f4f8;
            --card-background: #ffffff;
            --text-color: #2d3748;
            --primary-color: #4299e1;
            --primary-hover: #3182ce;
            --error-color: #e53e3e;
            --success-color: #48bb78;
        }
        .dark-theme {
            --background-color: #1a202c;
            --card-background: #2d3748;
            --text-color: #e2e8f0;
            --primary-color: #63b3ed;
            --primary-hover: #4299e1;
            --error-color: #fc8181;
            --success-color: #68d391;
        }
        body {
            font-family: 'Roboto', sans-serif;
            line-height: 1.6;
            color: var(--text-color);
            background-color: var(--background-color);
            margin: 0;
            padding: 0;
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            transition: all 0.3s ease;
        }
        .login-container {
            width: 100%;
            max-width: 400px;
            margin: 2rem auto;
            padding: 2rem;
            background-color: var(--card-background);
            border-radius: 15px;
            box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
            transition: all 0.3s ease;
        }
        h2 {
            color: var(--primary-color);
            text-align: center;
            margin-bottom: 1.5rem;
            font-size: 2rem;
            transition: color 0.3s ease;
        }
        .input-group {
            margin-bottom: 1.5rem;
        }
        label {
            display: block;
            margin-bottom: 0.5rem;
            font-weight: bold;
            transition: color 0.3s ease;
        }
        input {
            width: 100%;
            padding: 0.8rem;
            border: 2px solid var(--primary-color);
            border-radius: 8px;
            font-size: 1rem;
            transition: all 0.3s ease;
            background-color: var(--card-background);
            color: var(--text-color);
        }
        input:focus {
            outline: none;
            box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
        }
        button {
            display: block;
            width: 100%;
            padding: 1rem;
            background-color: var(--primary-color);
            color: white;
            border: none;
            border-radius: 8px;
            font-size: 1.1rem;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        button:hover {
            background-color: var(--primary-hover);
            transform: translateY(-2px);
            box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
        }
        .cf-turnstile {
            margin-top: 1rem;
            margin-bottom: 1rem;
        }
        #theme-toggle {
            position: absolute;
            top: 1rem;
            right: 1rem;
            background-color: var(--primary-color);
            color: white;
            border: none;
            border-radius: 50%;
            width: 40px;
            height: 40px;
            cursor: pointer;
            transition: all 0.3s ease;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 20px;
        }
        #theme-toggle:hover {
            background-color: var(--primary-hover);
            transform: scale(1.1);
        }
    </style>
</head>
<body>
    <button id="theme-toggle">🌞</button>
    <div class="login-container">
        <form method="POST" action="/login">
            <h2>欢迎回来</h2>
            <div class="input-group">
                <label for="username">用户名</label>
                <input type="text" id="username" name="username" required>
            </div>
            <div class="input-group">
                <label for="password">密码</label>
                <input type="password" id="password" name="password" required>
            </div>
            <div class="cf-turnstile" data-sitekey="${TURNSTILE_SITE_KEY}"></div>
            <button type="submit">登录</button>
        </form>
    </div>
    
    <script>
        const themeToggle = document.getElementById('theme-toggle');
        const html = document.documentElement;

        themeToggle.addEventListener('click', () => {
            html.classList.toggle('dark-theme');
            themeToggle.textContent = html.classList.contains('dark-theme') ? '🌞' : '🌓';
        });
    </script>
</body>
</html>
  `;
}

// Get main page HTML with dark theme toggle
function getMainHTML() {
  return `
  <!DOCTYPE html>
  <html lang="zh" class="dark-theme">
  <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Flux.1-schnell 图像生成器</title>
      <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" rel="stylesheet">
      <style>
          :root {
              --background-color: #f0f4f8;
              --card-background: #ffffff;
              --text-color: #2d3748;
              --primary-color: #4299e1;
              --primary-hover: #3182ce;
              --error-color: #e53e3e;
              --success-color: #48bb78;
          }
          .dark-theme {
              --background-color: #1a202c;
              --card-background: #2d3748;
              --text-color: #e2e8f0;
              --primary-color: #63b3ed;
              --primary-hover: #4299e1;
              --error-color: #fc8181;
              --success-color: #68d391;
          }
          body {
              font-family: 'Roboto', sans-serif;
              line-height: 1.6;
              color: var(--text-color);
              background-color: var(--background-color);
              margin: 0;
              padding: 0;
              min-height: 100vh;
              display: flex;
              flex-direction: column;
              justify-content: center;
              align-items: center;
              transition: all 0.3s ease;
          }
          .container {
              width: 100%;
              max-width: 800px;
              margin: 2rem auto;
              padding: 2rem;
              background-color: var(--card-background);
              border-radius: 15px;
              box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
              transition: all 0.3s ease;
          }
          h1 {
              color: var(--primary-color);
              text-align: center;
              margin-bottom: 1.5rem;
              font-size: 2.5rem;
              transition: color 0.3s ease;
          }
          .image-container {
              margin-bottom: 2rem;
              text-align: center;
              background-color: var(--background-color);
              padding: 2rem;
              border-radius: 10px;
              min-height: 300px;
              display: flex;
              align-items: center;
              justify-content: center;
              transition: all 0.3s ease;
          }
          .image-container img {
              max-width: 100%;
              border-radius: 10px;
              box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
          }
          .form-group {
              margin-bottom: 1.5rem;
          }
          label {
              display: block;
              margin-bottom: 0.5rem;
              font-weight: bold;
              transition: color 0.3s ease;
          }
          input[type="text"] {
              width: 100%;
              padding: 0.8rem;
              border: 2px solid var(--primary-color);
              border-radius: 8px;
              font-size: 1rem;
              transition: all 0.3s ease;
              background-color: var(--card-background);
              color: var(--text-color);
          }
          input[type="text"]:focus {
              outline: none;
              box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
          }
          .image-ratio-group {
              display: grid;
              grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
              gap: 1rem;
              margin-top: 0.5rem;
          }
          .image-ratio-option input[type="radio"] {
              display: none;
          }
          .image-ratio-option label {
              display: block;
              padding: 0.5rem;
              text-align: center;
              background-color: var(--background-color);
              border: 2px solid var(--primary-color);
              border-radius: 8px;
              cursor: pointer;
              transition: all 0.3s ease;
          }
          .image-ratio-option input[type="radio"]:checked + label {
              background-color: var(--primary-color);
              color: white;
          }
          .generate-btn {
              display: block;
              width: 100%;
              padding: 1rem;
              background-color: var(--primary-color);
              color: white;
              border: none;
              border-radius: 8px;
              font-size: 1.1rem;
              font-weight: bold;
              cursor: pointer;
              transition: all 0.3s ease;
          }
          .generate-btn:hover {
              background-color: var(--primary-hover);
              transform: translateY(-2px);
              box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
          }
          .generate-btn:disabled {
              background-color: #a0aec0;
              cursor: not-allowed;
              transform: none;
              box-shadow: none;
          }
          .logout-btn {
              display: inline-block;
              margin-top: 2rem;
              padding: 0.8rem 1.5rem;
              background-color: var(--error-color);
              color: white;
              text-decoration: none;
              border-radius: 8px;
              transition: all 0.3s ease;
          }
          .logout-btn:hover {
              background-color: #c53030;
              transform: translateY(-2px);
              box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
          }
          #error-message {
              color: var(--error-color);
              text-align: center;
              margin-top: 1rem;
              font-weight: bold;
              transition: color 0.3s ease;
          }
          .loading {
              font-style: italic;
              color: var(--primary-color);
              transition: color 0.3s ease;
          }
          #theme-toggle {
              position: absolute;
              top: 1rem;
              right: 1rem;
              background-color: var(--primary-color);
              color: white;
              border: none;
              border-radius: 50%;
              width: 40px;
              height: 40px;
              cursor: pointer;
              transition: all 0.3s ease;
              display: flex;
              justify-content: center;
              align-items: center;
              font-size: 20px;
          }
          #theme-toggle:hover {
              background-color: var(--primary-hover);
              transform: scale(1.1);
          }
      </style>
  </head>
  <body>
      <button id="theme-toggle">🌞</button>
      <div class="container">
          <h1>Flux.1-schnell 图像生成器</h1>
          
          <div class="image-container" id="image-container">
              <p>您生成的图像将显示在这里</p>
          </div>
          
          <form id="image-form">
              <div class="form-group">
                  <label for="prompt">描述您的图像:</label>
                  <input type="text" id="prompt" name="prompt" placeholder="输入您的图像描述" required>
              </div>
              
              <div class="form-group">
                  <label>选择图像比例:</label>
                  <div class="image-ratio-group">
                      <div class="image-ratio-option">
                          <input type="radio" id="ratio-1-1" name="image_ratio" value="1:1" checked>
                          <label for="ratio-1-1">1:1</label>
                      </div>
                      <div class="image-ratio-option">
                          <input type="radio" id="ratio-1-2" name="image_ratio" value="1:2">
                          <label for="ratio-1-2">1:2</label>
                      </div>
                      <div class="image-ratio-option">
                          <input type="radio" id="ratio-3-2" name="image_ratio" value="3:2">
                          <label for="ratio-3-2">3:2</label>
                      </div>
                      <div class="image-ratio-option">
                          <input type="radio" id="ratio-3-4" name="image_ratio" value="3:4">
                          <label for="ratio-3-4">3:4</label>
                      </div>
                      <div class="image-ratio-option">
                          <input type="radio" id="ratio-16-9" name="image_ratio" value="16:9">
                          <label for="ratio-16-9">16:9</label>
                      </div>
                      <div class="image-ratio-option">
                          <input type="radio" id="ratio-9-16" name="image_ratio" value="9:16">
                          <label for="ratio-9-16">9:16</label>
                      </div>
                  </div>
              </div>
              
              <button type="submit" class="generate-btn">生成图像</button>
          </form>
          
          <div id="error-message"></div>
          <a href="/logout" class="logout-btn">登出</a>
      </div>
  
      <script>
      document.getElementById('image-form').addEventListener('submit', async function(event) {
          event.preventDefault();
  
          const prompt = document.getElementById('prompt').value;
          const imageRatio = document.querySelector('input[name="image_ratio"]:checked').value;
          const imageContainer = document.getElementById('image-container');
          const errorMessage = document.getElementById('error-message');
          const generateButton = document.querySelector('.generate-btn');
  
          const numInferenceSteps = 20;
  
          updateUI('generating');
  
          const { width, height } = getImageDimensions(imageRatio);
  
          try {
              const response = await fetch('/generate-image', {
                  method: 'POST',
                  headers: {
                      'Content-Type': 'application/json',
                  },
                  body: JSON.stringify({
                      prompt,
                      width,
                      height,
                      num_inference_steps: numInferenceSteps
                  })
              });
  
              if (!response.ok) {
                  throw new Error('生成图像失败。请重试。');
              }
  
              const data = await response.json();
  
              if (data.images && data.images.length > 0) {
                  const imageUrl = data.images[0].url;
                  imageContainer.innerHTML = '<img src="' + imageUrl + '" alt="生成的图像">';
              } else {
                  throw new Error('生成图像失败。请重试。');
              }
          } catch (error) {
              console.error('错误:', error);
              errorMessage.textContent = error.message;
              imageContainer.innerHTML = '<p>生成图像失败</p>';
          } finally {
              updateUI('ready');
          }
      });
  
      function getImageDimensions(ratio) {
          const dimensions = {
              '1:1': { width: 1024, height: 1024 },
              '1:2': { width: 512, height: 1024 },
              '3:2': { width: 768, height: 512 },
              '3:4': { width: 768, height: 1024 },
              '16:9': { width: 1024, height: 576 },
              '9:16': { width: 576, height: 1024 }
          };
          return dimensions[ratio] || dimensions['1:1'];
      }
  
      function updateUI(state) {
          const imageContainer = document.getElementById('image-container');
          const errorMessage = document.getElementById('error-message');
          const generateButton = document.querySelector('.generate-btn');
  
          if (state === 'generating') {
              imageContainer.innerHTML = '<p class="loading">正在生成您的图像... 这可能需要一点时间。</p>';
              errorMessage.textContent = '';
              generateButton.disabled = true;
              generateButton.textContent = '生成中...';
          } else {
              generateButton.disabled = false;
              generateButton.textContent = '生成图像';
          }
      }
  
      const themeToggle = document.getElementById('theme-toggle');
      const html = document.documentElement;
  
      function updateTheme() {
          const isDarkTheme = html.classList.contains('dark-theme');
          themeToggle.textContent = isDarkTheme ? '🌞' : '🌓';
      }
  
      updateTheme();
  
      themeToggle.addEventListener('click', () => {
          html.classList.toggle('dark-theme');
          updateTheme();
      });
      </script>
  </body>
  </html>
`;
}
 
// Main request handler
async function handleRequest(request) {
const url = new URL(request.url);

// Login route
if (url.pathname === '/login') {
  if (request.method === 'GET') {
    return new Response(getLoginHTML(), {
      headers: { 'Content-Type': 'text/html' },
    });
  } else if (request.method === 'POST') {
    return handleLogin(request);
  }
}

// Logout route
if (url.pathname === '/logout') {
  return handleLogout();
}

// Check authentication for all other routes
const isAuthenticated = await checkAuth(request);
if (!isAuthenticated) {
  const headers = new Headers();
  headers.append('Location', '/login');
  return new Response(null, { status: 302, headers });
}

// Generate image route
if (request.method === 'POST' && url.pathname === '/generate-image') {
  return generateImage(request);
}

// Main page route
if (request.method === 'GET' && url.pathname === '/') {
  return new Response(getMainHTML(), {
    headers: { 'Content-Type': 'text/html' },
  });
}

// 404 for all other routes
return new Response('Not Found', { status: 404 });
}

// Event listener
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
2 个赞

这就去试试·····

感谢佬,mark