反向代理配置完整版

步骤 1:创建 D1 数据库
登录 Cloudflare → 左侧「Workers & Pages」→ 顶部「D1」→ 「创建数据库」;
输入数据库名称(如proxy-db)→ 「创建」;
进入数据库控制台 → 执行上面的无注释建表语句。
步骤 2:配置 Workers 绑定
进入你的 Worker → 「设置」→ 「变量」→ 「D1 数据库绑定」;
绑定名称填:PROXY_DB(必须和代码里的env.PROXY_DB一致);
选择你创建的 D1 数据库 → 「保存」。
步骤 3:部署代码
把上面的完整 Workers 代码复制到 Worker 编辑器;
点击「保存并部署」。
五、使用流程
第一次访问:打开 Worker 域名 → 进入初始化界面,设置后台路径(如/admin)、管理员账号密码 → 提交;
登录后台:访问你的域名/admin → 输入账号密码登录;
配置反代参数:
基础配置:可修改后台路径、账号密码;
反代配置:填写源站域名、Workers 域名、HTTP/HTTPS 端口、反代模式、缓存开关 / 时长;
使用反代:直接访问 Worker 域名 → 自动执行反代逻辑(按后台配置)。

D1数据库

CREATE TABLE IF NOT EXISTS proxy_config (id INTEGER PRIMARY KEY AUTOINCREMENT,admin_path TEXT NOT NULL DEFAULT '/admin',user_path TEXT NOT NULL DEFAULT '/user',username TEXT NOT NULL,password TEXT NOT NULL,target_domain TEXT DEFAULT '',worker_domain TEXT DEFAULT '',http_port INTEGER DEFAULT 0,https_port INTEGER DEFAULT 0,proxy_mode TEXT DEFAULT 'http_only',cache_enabled INTEGER DEFAULT 0,cache_ttl_seconds INTEGER DEFAULT 3600,auth_enabled INTEGER DEFAULT 0,initialized INTEGER DEFAULT 0);
CREATE TABLE IF NOT EXISTS proxy_users (id INTEGER PRIMARY KEY AUTOINCREMENT,username TEXT NOT NULL,account TEXT NOT NULL UNIQUE,password TEXT NOT NULL,qq_number TEXT NOT NULL UNIQUE,is_banned INTEGER DEFAULT 0,auth_days INTEGER DEFAULT 0,auth_start_time TEXT NOT NULL,auth_end_time TEXT NOT NULL,create_time TEXT NOT NULL);

Workers 代码

let dbMigrated = false;
export default {
  async fetch(request, env) {
    if (!dbMigrated) {
      try {
        await env.PROXY_DB.prepare('ALTER TABLE proxy_users ADD COLUMN last_ip TEXT DEFAULT ""').run();
        await env.PROXY_DB.prepare('ALTER TABLE proxy_users ADD COLUMN last_location TEXT DEFAULT ""').run();
        await env.PROXY_DB.prepare('ALTER TABLE proxy_users ADD COLUMN session_id TEXT DEFAULT ""').run();
        await env.PROXY_DB.prepare('ALTER TABLE proxy_users ADD COLUMN login_expire_time TEXT DEFAULT ""').run();
      } catch(e) {}
      dbMigrated = true;
    }
    const url = new URL(request.url);
    const db = env.PROXY_DB;
    const getBeijingTime = () => {
      const now = new Date();
      const utc = now.getTime() + (now.getTimezoneOffset() * 60000);
      return new Date(utc + (8 * 3600000));
    };
    const calcAuthEndTime = (startTime, days) => {
      const start = new Date(startTime);
      start.setDate(start.getDate() + parseInt(days));
      return start.toISOString();
    };
    const configResult = await db.prepare('SELECT * FROM proxy_config LIMIT 1').all();
    let config = configResult.results[0] || { initialized: 0, user_path: '/user', auth_enabled: 0 };
    if (config.initialized === 0) {
      if (request.method === 'POST') {
        const formData = await request.formData();
        const adminPath = formData.get('admin_path') || '/admin';
        const userPath = formData.get('user_path') || '/user';
        const username = formData.get('username');
        const password = formData.get('password');
        if (username && password) {
          const encryptedPwd = btoa(password);
          await db.prepare('INSERT INTO proxy_config (admin_path, user_path, username, password, initialized, auth_enabled) VALUES (?, ?, ?, ?, 1, 0)').bind(adminPath, userPath, username, encryptedPwd).run();
          return Response.redirect(`${url.origin}${adminPath}`, 302);
        }
        return new Response('请填写完整信息', { status: 400 });
      }
      return new Response(getInitHtml(), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
    }
    const adminPath = config.admin_path || '/admin';
    if (url.pathname.startsWith(adminPath)) {
      const authHeader = request.headers.get('Authorization');
      const isAdminAuth = authHeader && authHeader === `Basic ${btoa(`${config.username}:${atob(config.password)}`)}`;
      if (!isAdminAuth) {
        return new Response(getLoginHtml(), {
          status: 401,
          headers: { 'WWW-Authenticate': 'Basic realm="Proxy Admin Panel"', 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' }
        });
      }
      if (request.method === 'POST') {
        const formData = await request.formData();
        const action = formData.get('action');
        
        if (action === 'update_base') {
          const newAdminPath = formData.get('new_admin_path') || config.admin_path;
          const newUserPath = formData.get('new_user_path') || config.user_path;
          const newUsername = formData.get('new_username') || config.username;
          const newPassword = formData.get('new_password');
          const authEnabled = formData.get('auth_enabled') ? 1 : 0;
          const encryptedNewPwd = newPassword ? btoa(newPassword) : config.password;
          
          await db.prepare('UPDATE proxy_config SET admin_path=?, user_path=?, username=?, password=?, auth_enabled=? WHERE id=?').bind(newAdminPath, newUserPath, newUsername, encryptedNewPwd, authEnabled, config.id).run();
          return Response.redirect(`${url.origin}${newAdminPath}`, 302);
        }
        if (action === 'update_proxy') {
          const targetDomain = formData.get('target_domain') || '';
          const workerDomain = formData.get('worker_domain') || '';
          const httpPort = parseInt(formData.get('http_port') || 0);
          const httpsPort = parseInt(formData.get('https_port') || 0);
          const proxyMode = formData.get('proxy_mode') || 'http_only';
          const cacheEnabled = formData.get('cache_enabled') ? 1 : 0;
          const cacheTtl = parseInt(formData.get('cache_ttl') || 3600);
          
          await db.prepare('UPDATE proxy_config SET target_domain=?, worker_domain=?, http_port=?, https_port=?, proxy_mode=?, cache_enabled=?, cache_ttl_seconds=? WHERE id=?').bind(targetDomain, workerDomain, httpPort, httpsPort, proxyMode, cacheEnabled, cacheTtl, config.id).run();
          return Response.redirect(`${url.origin}${adminPath}`, 302);
        }
        if (action === 'manage_user') {
          const userId = parseInt(formData.get('user_id') || 0);
          const operation = formData.get('operation');
          const authDays = parseInt(formData.get('auth_days') || 0);
          
          if (userId && operation) {
            switch (operation) {
              case 'ban':
                await db.prepare('UPDATE proxy_users SET is_banned=1 WHERE id=?').bind(userId).run();
                break;
              case 'unban':
                await db.prepare('UPDATE proxy_users SET is_banned=0 WHERE id=?').bind(userId).run();
                break;
              case 'delete':
                await db.prepare('DELETE FROM proxy_users WHERE id=?').bind(userId).run();
                break;
              case 'update_pwd': {
                const newPwd = formData.get('new_password');
                if (newPwd) {
                  await db.prepare('UPDATE proxy_users SET password=? WHERE id=?').bind(btoa(newPwd), userId).run();
                }
                break;
              }
              case 'add_auth': {
                const uRes = await db.prepare('SELECT auth_end_time FROM proxy_users WHERE id=?').bind(userId).all();
                if (uRes.results.length > 0) {
                  const nowTime = getBeijingTime();
                  let end = new Date(uRes.results[0].auth_end_time);
                  if (isNaN(end.getTime()) || end < nowTime) end = nowTime;
                  end.setDate(end.getDate() + authDays);
                  await db.prepare('UPDATE proxy_users SET auth_end_time=? WHERE id=?').bind(end.toISOString(), userId).run();
                }
                break;
              }
              case 'reduce_auth': {
                const uRes = await db.prepare('SELECT auth_end_time FROM proxy_users WHERE id=?').bind(userId).all();
                if (uRes.results.length > 0) {
                  let end = new Date(uRes.results[0].auth_end_time);
                  if (!isNaN(end.getTime())) {
                    end.setDate(end.getDate() - authDays);
                    await db.prepare('UPDATE proxy_users SET auth_end_time=? WHERE id=?').bind(end.toISOString(), userId).run();
                  }
                }
                break;
              }
            }
          }
          return Response.redirect(`${url.origin}${adminPath}#user-manage`, 302);
        }
      }
      const userListResult = await db.prepare('SELECT * FROM proxy_users').all();
      const userList = userListResult.results || [];
      return new Response(getAdminHtml(config, userList), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
    }
    const userPath = config.user_path || '/user';
    if (url.pathname.startsWith(userPath)) {
      const path = url.pathname.replace(userPath, '');
      if (path === '/register' && request.method === 'POST') {
        const formData = await request.formData();
        const username = formData.get('username');
        const account = formData.get('account');
        const password = formData.get('password');
        const qqNumber = formData.get('qq_number');
        if (!username || !account || !password || !qqNumber) {
          return new Response('请填写完整注册信息', { status: 400 });
        }
        const existCheck = await db.prepare('SELECT * FROM proxy_users WHERE account=? OR qq_number=?').bind(account, qqNumber).all();
        if (existCheck.results.length > 0) {
          return new Response('账号或QQ号已存在', { status: 400 });
        }
        const now = getBeijingTime().toISOString();
        const encryptedPwd = btoa(password);
        
        await db.prepare('INSERT INTO proxy_users (username, account, password, qq_number, is_banned, auth_days, auth_start_time, auth_end_time, create_time, login_expire_time) VALUES (?, ?, ?, ?, 0, 0, ?, ?, ?, ?)').bind(username, account, encryptedPwd, qqNumber, now, now, now, now).run();
        return Response.redirect(`${url.origin}${userPath}/login`, 302);
      }
      if (path === '/login' && request.method === 'POST') {
        const formData = await request.formData();
        const account = formData.get('account');
        const password = formData.get('password');
        const userResult = await db.prepare('SELECT * FROM proxy_users WHERE account=?').bind(account).all();
        if (userResult.results.length === 0) {
          return new Response(getUserLoginHtml('账号不存在'), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
        }
        const user = userResult.results[0];
        if (user.is_banned === 1) {
          return new Response(getUserLoginHtml('账号已被封禁,无法登录!如需解封请联系管理员QQ:3890053645'), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
        }
        if (atob(user.password) !== password) {
          return new Response(getUserLoginHtml('密码错误'), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
        }
        if (config.auth_enabled === 1) {
          const nowTime = getBeijingTime().getTime();
          const authEnd = new Date(user.auth_end_time).getTime();
          if (authEnd <= nowTime) {
            return new Response(getUserLoginHtml('账号授权已到期,无法登录!如需续期请联系管理员QQ:3890053645'), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
          }
        }
        const sessionId = crypto.randomUUID();
        const nowMs = Date.now();
        const clientIp = request.headers.get('CF-Connecting-IP') || '';
        const location = `${request.cf?.country || ''} ${request.cf?.city || ''}`.trim();
        // 登录有效期:12小时(43200秒),此处可修改有效期时长
        const expireTime = new Date(nowMs + 12 * 60 * 60 * 1000).toISOString();
        
        await db.prepare('UPDATE proxy_users SET session_id=?, last_ip=?, last_location=?, login_expire_time=? WHERE id=?').bind(sessionId, clientIp, location, expireTime, user.id).run();
        const loginToken = btoa(`${account}|${sessionId}|${clientIp}|${nowMs}`);
        
        return new Response('', {
          status: 302,
          headers: {
            'Location': `${url.origin}`,
            'Set-Cookie': `proxy_login=${loginToken}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=43200`,
            'Content-Type': 'text/html; charset=utf-8',
            'X-Frame-Options': 'SAMEORIGIN',
            'X-XSS-Protection': '1; mode=block'
          }
        });
      }
      if (path === '/find-pwd' && request.method === 'POST') {
        const formData = await request.formData();
        const qqNumber = formData.get('qq_number');
        const newPassword = formData.get('new_password');
        const userResult = await db.prepare('SELECT * FROM proxy_users WHERE qq_number=?').bind(qqNumber).all();
        if (userResult.results.length === 0) {
          return new Response(getUserFindPwdHtml('QQ号未注册'), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
        }
        const user = userResult.results[0];
        
        if (newPassword) {
          const encryptedNewPwd = btoa(newPassword);
          await db.prepare('UPDATE proxy_users SET password=? WHERE qq_number=?').bind(encryptedNewPwd, qqNumber).run();
          return new Response(getUserFindPwdHtml(`账号:${user.account}<br>密码已重置成功,请返回登录`), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
        }
        return new Response(getUserFindPwdHtml(`你的账号是:${user.account}<br>请设置新密码`), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
      }
      if (path === '/register') return new Response(getUserRegisterHtml(), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
      if (path === '/find-pwd') return new Response(getUserFindPwdHtml(''), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
      
      return new Response(getUserLoginHtml(''), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
    }
    const loginCookie = request.headers.get('Cookie') || '';
    if (!loginCookie.includes('proxy_login=')) {
      return Response.redirect(`${url.origin}${userPath}/login`, 302);
    }
    const tokenParts = loginCookie.split('proxy_login=')[1]?.split(';')[0];
    if (!tokenParts) {
      return Response.redirect(`${url.origin}${userPath}/login`, 302);
    }
    let account, sessionId, clientIp, lastValMs;
    try {
      const decoded = atob(tokenParts).split('|');
      if (decoded.length === 4) {
        account = decoded[0];
        sessionId = decoded[1];
        clientIp = decoded[2];
        lastValMs = parseInt(decoded[3]);
      } else {
        account = decoded[0].split('-')[0];
        sessionId = '';
        clientIp = '';
        lastValMs = 0;
      }
    } catch(e) {
      return Response.redirect(`${url.origin}${userPath}/login`, 302);
    }
    const userResult = await db.prepare('SELECT * FROM proxy_users WHERE account=?').bind(account).all();
    if (userResult.results.length === 0) {
      return new Response(getAccessDeniedHtml('账号不存在,请重新登录'), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
    }
    const user = userResult.results[0];
    const now = getBeijingTime();
    const expireTime = new Date(user.login_expire_time || now.toISOString());
    
    if (user.session_id && sessionId && user.session_id !== sessionId) {
      return new Response(getAccessDeniedHtml('你的账号已在其他IP登录,当前设备已下线!请重新登录'), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
    }
    if (user.is_banned === 1) {
      return new Response(getAccessDeniedHtml('你的账号已被封禁,无法访问该网站!如需解封请联系管理员QQ:3890053645'), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
    }
    if (expireTime <= now) {
      return new Response(getAccessDeniedHtml('登录已过期,请重新登录'), { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' } });
    }
    if (!config.target_domain || !config.worker_domain) {
      return new Response('请先在后台配置反代参数', { status: 500 });
    }
    let response, targetHost;
    const cfCacheConfig = {
      cacheTtl: config.cache_enabled ? config.cache_ttl_seconds : 0,
      cacheEverything: config.cache_enabled === 1,
      cacheKey: url.href,
      cacheControl: config.cache_enabled ? { browserTTL: config.cache_ttl_seconds, edgeTTL: config.cache_ttl_seconds } : {}
    };
    
    switch (config.proxy_mode) {
      case 'https_only':
        if (!config.https_port) throw new Error('HTTPS端口未配置');
        const targetHttps = new URL(`https://${config.target_domain}:${config.https_port}`);
        url.protocol = targetHttps.protocol;
        targetHost = targetHttps.host;
        url.host = targetHost;
        response = await fetch(new Request(url, {
          method: request.method,
          headers: buildHeaders(request.headers, targetHost),
          body: request.body,
          redirect: 'manual',
          duplex: 'half',
          cf: { cacheTtl: 0 }
        }), cfCacheConfig);
        break;
      case 'http_only':
        if (!config.http_port) throw new Error('HTTP端口未配置');
        const targetHttp = new URL(`http://${config.target_domain}:${config.http_port}`);
        url.protocol = targetHttp.protocol;
        targetHost = targetHttp.host;
        url.host = targetHost;
        response = await fetch(new Request(url, {
          method: request.method,
          headers: buildHeaders(request.headers, targetHost),
          body: request.body,
          redirect: 'manual',
          duplex: 'half',
          cf: { cacheTtl: 0 }
        }), cfCacheConfig);
        break;
      case 'auto':
        if (!config.https_port || !config.http_port) throw new Error('auto模式需同时配置HTTP/HTTPS端口');
        try {
          const targetHttpsAuto = new URL(`https://${config.target_domain}:${config.https_port}`);
          url.protocol = targetHttpsAuto.protocol;
          targetHost = targetHttpsAuto.host;
          url.host = targetHost;
          response = await fetch(new Request(url, {
            method: request.method,
            headers: buildHeaders(request.headers, targetHost),
            body: request.body,
            redirect: 'manual',
            duplex: 'half',
            cf: { cacheTtl: 0 }
          }), cfCacheConfig);
        } catch (e) {
          const targetHttpAuto = new URL(`http://${config.target_domain}:${config.http_port}`);
          url.protocol = targetHttpAuto.protocol;
          targetHost = targetHttpAuto.host;
          url.host = targetHost;
          response = await fetch(new Request(url, {
            method: request.method,
            headers: buildHeaders(request.headers, targetHost),
            body: request.body,
            redirect: 'manual',
            duplex: 'half',
            cf: { cacheTtl: 0 }
          }), cfCacheConfig);
        }
        break;
    }
    let resp = await handleResponse(response, config);
    return resp;
  },
};
function buildHeaders(headers, host) {
  const h = new Headers(headers);
  h.set('Host', host);
  h.set('X-Forwarded-Host', host);
  h.set('X-Forwarded-Proto', 'https');
  h.set('User-Agent', headers.get('User-Agent') || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
  h.set('Upgrade', headers.get('Upgrade') || '');
  h.set('Connection', headers.get('Connection') || '');
  h.set('Range', headers.get('Range') || '');
  h.set('If-Range', headers.get('If-Range') || '');
  h.set('Cookie', headers.get('Cookie') || '');
  h.set('Set-Cookie', headers.get('Set-Cookie') || '');
  h.delete('X-Forwarded-For');
  return h;
}
async function handleResponse(response, config) {
  let resp = new Response(response.body, response);
  const contentType = resp.headers.get('Content-Type') || '';
  if (contentType.includes('text/') || contentType.includes('html') || contentType.includes('json') || contentType.includes('javascript')) {
    let content = await response.text();
    if (config.https_port) content = content.replace(new RegExp(`https://${config.target_domain}:${config.https_port}`, 'g'), `https://${config.worker_domain}`);
    if (config.http_port) content = content.replace(new RegExp(`http://${config.target_domain}:${config.http_port}`, 'g'), `https://${config.worker_domain}`);
    content = content.replace(new RegExp(`http://${config.target_domain}`, 'g'), `https://${config.worker_domain}`);
    content = content.replace(new RegExp(`https://${config.target_domain}`, 'g'), `https://${config.worker_domain}`);
    resp = new Response(content, response);
  }
  resp.headers.set('Cache-Control', config.cache_enabled ? `max-age=${config.cache_ttl_seconds}` : 'no-store, no-cache, must-revalidate');
  resp.headers.set('Pragma', config.cache_enabled ? '' : 'no-cache');
  resp.headers.delete('X-Frame-Options');
  resp.headers.delete('Content-Security-Policy');
  resp.headers.set('Access-Control-Allow-Origin', '*');
  resp.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  resp.headers.set('Access-Control-Allow-Headers', 'Range, Cookie, Upgrade, Connection, Content-Type');
  resp.headers.set('X-Content-Type-Options', 'nosniff');
  return resp;
}
function getInitHtml() {
  return `
<!DOCTYPE html>
<html lang="zh-CN">
<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>
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
    .init-card { background: #ffffff; border-radius: 12px; box-shadow: 0 8px 32px rgba(31, 38, 135, 0.1); padding: 2.5rem; max-width: 500px; width: 100%; }
    .card-header { text-align: center; margin-bottom: 2rem; }
    .card-header h1 { font-size: 1.8rem; font-weight: 600; color: #1e293b; margin-bottom: 0.5rem; }
    .card-header p { color: #64748b; font-size: 0.95rem; }
    .form-group { margin-bottom: 1.5rem; }
    .form-label { display: block; margin-bottom: 0.75rem; font-weight: 500; color: #334155; font-size: 0.95rem; }
    .form-input { width: 100%; padding: 0.875rem 1rem; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 0.95rem; background: #f8fafc; }
    .form-input:focus { outline: none; border-color: #2563eb; background: #ffffff; }
    .submit-btn { width: 100%; padding: 1rem; background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); color: #ffffff; border: none; border-radius: 8px; font-size: 1rem; cursor: pointer; margin-top: 1rem; }
    .icon { color: #2563eb; margin-right: 0.5rem; }
  </style>
</head>
<body>
  <div class="init-card">
    <div class="card-header">
      <h1><i class="fa fa-cogs icon"></i>反代管理系统</h1>
      <p>首次使用,请完成初始化配置</p>
    </div>
    <form method="POST">
      <div class="form-group">
        <label class="form-label">后台访问路径</label>
        <input type="text" name="admin_path" class="form-input" placeholder="/admin" value="/admin" required>
      </div>
      <div class="form-group">
        <label class="form-label">用户访问路径</label>
        <input type="text" name="user_path" class="form-input" placeholder="/user" value="/user" required>
      </div>
      <div class="form-group">
        <label class="form-label">管理员账号</label>
        <input type="text" name="username" class="form-input" placeholder="请设置管理员账号" required>
      </div>
      <div class="form-group">
        <label class="form-label">管理员密码</label>
        <input type="password" name="password" class="form-input" placeholder="请设置管理员密码" required>
      </div>
      <button type="submit" class="submit-btn"><i class="fa fa-check-circle icon"></i>完成初始化</button>
    </form>
  </div>
</body>
</html>
  `;
}
function getLoginHtml() {
  return `
<!DOCTYPE html>
<html lang="zh-CN">
<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>
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
    .login-card { background: #ffffff; border-radius: 12px; box-shadow: 0 8px 32px rgba(31, 38, 135, 0.1); padding: 2.5rem; max-width: 450px; width: 100%; }
    .card-header { text-align: center; margin-bottom: 2rem; }
    .card-header h1 { font-size: 1.8rem; font-weight: 600; color: #1e293b; margin-bottom: 0.5rem; }
    .card-header p { color: #64748b; font-size: 0.95rem; }
    .alert { background: #fee2e2; color: #dc2626; padding: 1rem; border-radius: 8px; margin-bottom: 1.5rem; text-align: center; font-size: 0.95rem; border-left: 4px solid #dc2626; }
    .icon { color: #2563eb; margin-right: 0.5rem; }
  </style>
</head>
<body>
  <div class="login-card">
    <div class="card-header">
      <h1><i class="fa fa-lock icon"></i>管理员登录</h1>
      <p>请输入正确的账号和密码</p>
    </div>
    <div class="alert"><i class="fa fa-exclamation-circle"></i> 身份验证失败,请重新登录</div>
    <p class="text-center text-gray-600 text-sm">若未初始化,请先完成系统初始化配置</p>
  </div>
</body>
</html>
  `;
}
function getAdminHtml(config, userList) {
  const formatBeijingTime = (isoStr) => {
    const date = new Date(isoStr);
    return date.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
  };
  let userTableHtml = '';
  if (userList.length > 0) {
    userTableHtml = userList.map(user => {
      const pwdHtml = `
        <div class="mb-1">${atob(user.password)}</div>
        <form method="POST" style="display: flex; gap: 0.2rem;">
          <input type="hidden" name="action" value="manage_user">
          <input type="hidden" name="user_id" value="${user.id}">
          <input type="hidden" name="operation" value="update_pwd">
          <input type="text" name="new_password" placeholder="新密码" class="px-1 py-0.5 text-xs border rounded w-16" required>
          <button type="submit" class="px-1 py-0.5 text-xs bg-gray-600 text-white rounded">改密</button>
        </form>
      `;
      const statusActionHtml = `
        <div class="mb-1">${user.is_banned === 1 ? '<span class="text-red-600">已封禁</span>' : '<span class="text-green-600">正常</span>'}</div>
        <div style="display: flex; gap: 0.2rem;">
          <form method="POST">
            <input type="hidden" name="action" value="manage_user">
            <input type="hidden" name="user_id" value="${user.id}">
            <input type="hidden" name="operation" value="${user.is_banned === 1 ? 'unban' : 'ban'}">
            <button type="submit" class="px-2 py-1 text-xs rounded ${user.is_banned === 1 ? 'bg-green-600' : 'bg-red-600'} text-white">
              ${user.is_banned === 1 ? '解禁' : '封禁'}
            </button>
          </form>
          <form method="POST" onsubmit="return confirm('确定要删除此用户吗?');">
            <input type="hidden" name="action" value="manage_user">
            <input type="hidden" name="user_id" value="${user.id}">
            <input type="hidden" name="operation" value="delete">
            <button type="submit" class="px-2 py-1 text-xs bg-red-800 text-white rounded">删除</button>
          </form>
        </div>
      `;
      const ipHtml = `
        <div class="text-xs text-gray-600 whitespace-nowrap">${user.last_ip || '-'}</div>
        <div class="text-xs text-gray-400 whitespace-nowrap">${user.last_location || '-'}</div>
      `;
      const authOpHtml = `
        <form method="POST" style="display: flex; gap: 0.2rem; align-items: center;">
          <input type="hidden" name="action" value="manage_user">
          <input type="hidden" name="user_id" value="${user.id}">
          <input type="number" name="auth_days" placeholder="天数" class="px-1 py-0.5 text-xs border rounded w-12" required>
          <button type="submit" name="operation" value="add_auth" class="px-1 py-0.5 text-xs bg-blue-600 text-white rounded">增加</button>
          <button type="submit" name="operation" value="reduce_auth" class="px-1 py-0.5 text-xs bg-orange-500 text-white rounded">减少</button>
        </form>
      `;
      return `
        <tr class="border-b border-gray-200 hover:bg-gray-50">
          <td class="px-4 py-3">${user.username}</td>
          <td class="px-4 py-3">${user.account}</td>
          <td class="px-4 py-3">${pwdHtml}</td>
          <td class="px-4 py-3">${user.qq_number}</td>
          <td class="px-4 py-3">${statusActionHtml}</td>
          <td class="px-4 py-3">${ipHtml}</td>
          <td class="px-4 py-3">${formatBeijingTime(user.auth_end_time)}</td>
          <td class="px-4 py-3">${authOpHtml}</td>
        </tr>
      `;
    }).join('');
  } else {
    userTableHtml = '<tr><td colspan="8" class="px-4 py-3 text-center text-gray-500">暂无用户</td></tr>';
  }
  return `
<!DOCTYPE html>
<html lang="zh-CN">
<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>
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { background: #f8fafc; color: #1e293b; min-height: 100vh; display: flex; }
    .sidebar { width: 250px; background: #1e293b; color: #f8fafc; min-height: 100vh; padding: 1.5rem 0; position: fixed; }
    .sidebar-header { padding: 0 1.5rem 1.5rem; border-bottom: 1px solid #334155; margin-bottom: 1rem; }
    .sidebar-header h2 { font-size: 1.2rem; font-weight: 600; color: #ffffff; display: flex; align-items: center; }
    .sidebar-header h2 i { margin-right: 0.75rem; color: #38bdf8; }
    .sidebar-menu { padding: 0.5rem 0; }
    .menu-item { padding: 0.875rem 1.5rem; display: flex; align-items: center; color: #94a3b8; text-decoration: none; transition: all 0.2s ease; cursor: pointer; border-left: 3px solid transparent; }
    .menu-item.active { background: #334155; color: #ffffff; border-left-color: #38bdf8; }
    .menu-item:hover { background: #273449; color: #e2e8f0; }
    .menu-item i { margin-right: 0.75rem; font-size: 1rem; }
    .main-content { margin-left: 250px; flex: 1; padding: 2rem; }
    .content-header { margin-bottom: 2rem; padding-bottom: 1rem; border-bottom: 1px solid #e2e8f0; }
    .content-header h1 { font-size: 1.75rem; font-weight: 600; color: #0f172a; }
    .content-card { background: #ffffff; border-radius: 12px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1); padding: 2rem; margin-bottom: 2rem; overflow-x: auto; }
    .card-title { font-size: 1.25rem; font-weight: 600; color: #1e293b; margin-bottom: 1.5rem; display: flex; align-items: center; }
    .card-title i { margin-right: 0.75rem; color: #2563eb; }
    .form-group { margin-bottom: 1.5rem; }
    .form-label { display: block; margin-bottom: 0.75rem; font-weight: 500; color: #334155; font-size: 0.95rem; }
    .form-input, .form-select { width: 100%; padding: 0.875rem 1rem; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 0.95rem; background: #f8fafc; }
    .form-input:focus, .form-select:focus { outline: none; border-color: #2563eb; background: #ffffff; }
    .switch-group { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1.5rem; }
    .switch-group input { height: 1.25rem; width: 1.25rem; accent-color: #2563eb; }
    .submit-btn { padding: 0.875rem 1.5rem; background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); color: #ffffff; border: none; border-radius: 8px; cursor: pointer; margin-top: 0.5rem; }
    .tab-content { display: none; }
    .tab-content.active { display: block; }
    .user-table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
    .user-table th { padding: 0.75rem 1rem; text-align: left; background: #f1f5f9; font-weight: 500; color: #334155; }
    .user-table td { padding: 0.75rem 1rem; color: #1e293b; }
    @media (max-width: 768px) {
      .sidebar { width: 80px; padding: 1rem 0; }
      .sidebar-header h2 span, .menu-item span { display: none; }
      .menu-item { justify-content: center; padding: 1rem; }
      .menu-item i { margin-right: 0; font-size: 1.2rem; }
      .main-content { margin-left: 80px; padding: 1.5rem 1rem; }
    }
  </style>
</head>
<body>
  <div class="sidebar">
    <div class="sidebar-header">
      <h2><i class="fa fa-cogs"></i> <span>反代管理系统</span></h2>
    </div>
    <div class="sidebar-menu">
      <div class="menu-item active" onclick="switchTab('base-tab', event)">
        <i class="fa fa-user-circle"></i><span>基础配置</span>
      </div>
      <div class="menu-item" onclick="switchTab('proxy-tab', event)">
        <i class="fa fa-cloud"></i><span>反代配置</span>
      </div>
      <div class="menu-item" onclick="switchTab('user-manage-tab', event)">
        <i class="fa fa-users"></i><span>用户管理</span>
      </div>
    </div>
  </div>
  <div class="main-content">
    <div class="content-header"><h1>后台管理中心</h1></div>
    <div id="base-tab" class="tab-content active">
      <div class="content-card">
        <h3 class="card-title"><i class="fa fa-user-circle"></i>基础配置</h3>
        <form method="POST">
          <input type="hidden" name="action" value="update_base">
          <div class="form-group">
            <label class="form-label">后台访问路径</label>
            <input type="text" name="new_admin_path" class="form-input" value="${config.admin_path || '/admin'}" required>
          </div>
          <div class="form-group">
            <label class="form-label">用户访问路径(注册/登录)</label>
            <input type="text" name="new_user_path" class="form-input" value="${config.user_path || '/user'}" required>
          </div>
          <div class="form-group">
            <label class="form-label">管理员账号</label>
            <input type="text" name="new_username" class="form-input" value="${config.username || ''}" required>
          </div>
          <div class="form-group">
            <label class="form-label">管理员密码(留空则不修改)</label>
            <input type="password" name="new_password" class="form-input" placeholder="留空不修改密码">
          </div>
          <div class="switch-group">
            <input type="checkbox" name="auth_enabled" id="auth_enabled" ${config.auth_enabled === 1 ? 'checked' : ''}>
            <label for="auth_enabled" class="form-label mb-0">开启授权验证(开启后仅授权用户可登录)</label>
          </div>
          <button type="submit" class="submit-btn"><i class="fa fa-save"></i> 保存基础配置</button>
        </form>
      </div>
    </div>
    <div id="proxy-tab" class="tab-content">
      <div class="content-card">
        <h3 class="card-title"><i class="fa fa-cloud"></i>反代配置</h3>
        <form method="POST">
          <input type="hidden" name="action" value="update_proxy">
          <div class="form-group">
            <label class="form-label">源站域名</label>
            <input type="text" name="target_domain" class="form-input" value="${config.target_domain || ''}" placeholder="例如:your-domain.com">
          </div>
          <div class="form-group">
            <label class="form-label">Workers中转域名</label>
            <input type="text" name="worker_domain" class="form-input" value="${config.worker_domain || ''}" placeholder="例如:your-worker.workers.dev">
          </div>
          <div class="form-group" style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem;">
            <div><label class="form-label">HTTP端口</label><input type="number" name="http_port" class="form-input" value="${config.http_port || 0}"></div>
            <div><label class="form-label">HTTPS端口</label><input type="number" name="https_port" class="form-input" value="${config.https_port || 0}"></div>
          </div>
          <div class="form-group">
            <label class="form-label">反代模式</label>
            <select name="proxy_mode" class="form-select">
              <option value="http_only" ${config.proxy_mode === 'http_only' ? 'selected' : ''}>仅HTTP</option>
              <option value="https_only" ${config.proxy_mode === 'https_only' ? 'selected' : ''}>仅HTTPS</option>
              <option value="auto" ${config.proxy_mode === 'auto' ? 'selected' : ''}>HTTP+HTTPS自适应</option>
            </select>
          </div>
          <div class="switch-group">
            <input type="checkbox" name="cache_enabled" id="cache_enabled" ${config.cache_enabled === 1 ? 'checked' : ''}>
            <label for="cache_enabled" class="form-label mb-0">开启边缘缓存</label>
          </div>
          <div class="form-group">
            <label class="form-label">缓存时长(秒)</label>
            <input type="number" name="cache_ttl" class="form-input" value="${config.cache_ttl_seconds || 3600}">
          </div>
          <button type="submit" class="submit-btn"><i class="fa fa-save"></i> 保存反代配置</button>
        </form>
      </div>
    </div>
    <div id="user-manage-tab" class="tab-content">
      <div class="content-card">
        <h3 class="card-title"><i class="fa fa-users"></i>用户管理</h3>
        <table class="user-table text-sm">
          <thead>
            <tr class="border-b border-gray-300">
              <th>用户名</th>
              <th>账号</th>
              <th>密码管理</th>
              <th>QQ号</th>
              <th>状态管理</th>
              <th>IP/位置</th>
              <th>到期时间</th>
              <th>授权操作</th>
            </tr>
          </thead>
          <tbody>${userTableHtml}</tbody>
        </table>
      </div>
    </div>
  </div>
  <script>
    if (window.location.hash) {
      const tabId = window.location.hash.substring(1);
      if(document.getElementById(tabId + '-tab')) {
        document.querySelectorAll('.tab-content').forEach(tab => tab.classList.remove('active'));
        document.querySelectorAll('.menu-item').forEach(item => item.classList.remove('active'));
        document.getElementById(tabId + '-tab').classList.add('active');
        const items = document.querySelectorAll('.menu-item');
        for(let item of items) {
          if(item.getAttribute('onclick').includes(tabId)) {
            item.classList.add('active');
            break;
          }
        }
      }
    }
    function switchTab(tabId, event) {
      document.querySelectorAll('.tab-content').forEach(tab => tab.classList.remove('active'));
      document.querySelectorAll('.menu-item').forEach(item => item.classList.remove('active'));
      document.getElementById(tabId).classList.add('active');
      if(event && event.currentTarget) event.currentTarget.classList.add('active');
      window.history.replaceState(null, null, '#' + tabId.replace('-tab', ''));
    }
  </script>
</body>
</html>
  `;
}
function getUserRegisterHtml() {
  return `
<!DOCTYPE html>
<html lang="zh-CN">
<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>
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
    .card { background: #ffffff; border-radius: 12px; box-shadow: 0 8px 32px rgba(31,38,135,0.1); padding: 2.5rem; max-width: 500px; width: 100%; }
    .card-header { text-align: center; margin-bottom: 2rem; }
    .card-header h1 { font-size: 1.8rem; font-weight: 600; color: #1e293b; margin-bottom: 0.5rem; }
    .card-header p { color: #64748b; font-size: 0.95rem; }
    .form-group { margin-bottom: 1.5rem; }
    .form-label { display: block; margin-bottom: 0.75rem; font-weight: 500; color: #334155; font-size: 0.95rem; }
    .form-input { width: 100%; padding: 0.875rem 1rem; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 0.95rem; background: #f8fafc; }
    .form-input:focus { outline: none; border-color: #2563eb; background: #ffffff; }
    .submit-btn { width: 100%; padding: 1rem; background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); color: #ffffff; border: none; border-radius: 8px; font-size: 1rem; cursor: pointer; margin-top: 1rem; }
    .link-group { text-align: center; margin-top: 1.5rem; font-size: 0.95rem; }
    .link-group a { color: #2563eb; text-decoration: none; }
    .icon { color: #2563eb; margin-right: 0.5rem; }
  </style>
</head>
<body>
  <div class="card">
    <div class="card-header">
      <h1><i class="fa fa-user-plus icon"></i>用户注册</h1>
      <p>填写信息完成注册,即可访问反代服务</p>
    </div>
    <form method="POST">
      <div class="form-group">
        <label class="form-label">用户名</label>
        <input type="text" name="username" class="form-input" placeholder="请输入用户名" required>
      </div>
      <div class="form-group">
        <label class="form-label">登录账号</label>
        <input type="text" name="account" class="form-input" placeholder="请设置登录账号" required>
      </div>
      <div class="form-group">
        <label class="form-label">登录密码</label>
        <input type="password" name="password" class="form-input" placeholder="请设置登录密码" required>
      </div>
      <div class="form-group">
        <label class="form-label">QQ号(用于找回密码)</label>
        <input type="text" name="qq_number" class="form-input" placeholder="请输入QQ号" required>
      </div>
      <button type="submit" class="submit-btn"><i class="fa fa-check-circle icon"></i>完成注册</button>
    </form>
    <div class="link-group">
      已有账号?<a href="./login">立即登录</a> | <a href="./find-pwd">找回密码</a>
    </div>
  </div>
</body>
</html>
  `;
}
function getUserLoginHtml(errorMsg = '') {
  return `
<!DOCTYPE html>
<html lang="zh-CN">
<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>
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
    .card { background: #ffffff; border-radius: 12px; box-shadow: 0 8px 32px rgba(31,38,135,0.1); padding: 2.5rem; max-width: 500px; width: 100%; }
    .card-header { text-align: center; margin-bottom: 2rem; }
    .card-header h1 { font-size: 1.8rem; font-weight: 600; color: #1e293b; margin-bottom: 0.5rem; }
    .alert { background: #fee2e2; color: #dc2626; padding: 1rem; border-radius: 8px; margin-bottom: 1.5rem; text-align: center; font-size: 0.95rem; border-left: 4px solid #dc2626; }
    .form-group { margin-bottom: 1.5rem; }
    .form-label { display: block; margin-bottom: 0.75rem; font-weight: 500; color: #334155; font-size: 0.95rem; }
    .form-input { width: 100%; padding: 0.875rem 1rem; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 0.95rem; background: #f8fafc; }
    .form-input:focus { outline: none; border-color: #2563eb; background: #ffffff; }
    .submit-btn { width: 100%; padding: 1rem; background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); color: #ffffff; border: none; border-radius: 8px; font-size: 1rem; cursor: pointer; margin-top: 1rem; }
    .link-group { text-align: center; margin-top: 1.5rem; font-size: 0.95rem; }
    .link-group a { color: #2563eb; text-decoration: none; }
    .icon { color: #2563eb; margin-right: 0.5rem; }
  </style>
</head>
<body>
  <div class="card">
    <div class="card-header">
      <h1><i class="fa fa-lock icon"></i>用户登录</h1>
      <p>登录后即可访问反代服务</p>
    </div>
    ${errorMsg ? `<div class="alert"><i class="fa fa-exclamation-circle"></i> ${errorMsg}</div>` : ''}
    <form method="POST">
      <div class="form-group">
        <label class="form-label">登录账号</label>
        <input type="text" name="account" class="form-input" placeholder="请输入登录账号" required>
      </div>
      <div class="form-group">
        <label class="form-label">登录密码</label>
        <input type="password" name="password" class="form-input" placeholder="请输入登录密码" required>
      </div>
      <button type="submit" class="submit-btn"><i class="fa fa-sign-in icon"></i>立即登录</button>
    </form>
    <div class="link-group">
      没有账号?<a href="./register">立即注册</a> | <a href="./find-pwd">找回密码</a>
    </div>
  </div>
</body>
</html>
  `;
}
function getUserFindPwdHtml(msg = '') {
  return `
<!DOCTYPE html>
<html lang="zh-CN">
<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>
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
    .card { background: #ffffff; border-radius: 12px; box-shadow: 0 8px 32px rgba(31,38,135,0.1); padding: 2.5rem; max-width: 500px; width: 100%; }
    .card-header { text-align: center; margin-bottom: 2rem; }
    .card-header h1 { font-size: 1.8rem; font-weight: 600; color: #1e293b; margin-bottom: 0.5rem; }
    .msg-box { background: #e0f2fe; color: #0369a1; padding: 1rem; border-radius: 8px; margin-bottom: 1.5rem; text-align: center; font-size: 0.95rem; border-left: 4px solid #0369a1; }
    .form-group { margin-bottom: 1.5rem; }
    .form-label { display: block; margin-bottom: 0.75rem; font-weight: 500; color: #334155; font-size: 0.95rem; }
    .form-input { width: 100%; padding: 0.875rem 1rem; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 0.95rem; background: #f8fafc; }
    .form-input:focus { outline: none; border-color: #2563eb; background: #ffffff; }
    .submit-btn { width: 100%; padding: 1rem; background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); color: #ffffff; border: none; border-radius: 8px; font-size: 1rem; cursor: pointer; margin-top: 1rem; }
    .link-group { text-align: center; margin-top: 1.5rem; font-size: 0.95rem; }
    .link-group a { color: #2563eb; text-decoration: none; }
    .icon { color: #2563eb; margin-right: 0.5rem; }
  </style>
</head>
<body>
  <div class="card">
    <div class="card-header">
      <h1><i class="fa fa-key icon"></i>找回密码</h1>
      <p>输入QQ号找回账号并重置密码</p>
    </div>
    ${msg ? `<div class="msg-box"><i class="fa fa-info-circle"></i> ${msg}</div>` : ''}
    <form method="POST">
      <div class="form-group">
        <label class="form-label">注册时填写的QQ号</label>
        <input type="text" name="qq_number" class="form-input" placeholder="请输入QQ号" required>
      </div>
      <div class="form-group">
        <label class="form-label">新密码(留空仅查询账号)</label>
        <input type="password" name="new_password" class="form-input" placeholder="请设置新密码">
      </div>
      <button type="submit" class="submit-btn"><i class="fa fa-search icon"></i>找回/重置</button>
    </form>
    <div class="link-group">
      返回 <a href="./login">登录</a> | <a href="./register">注册</a>
    </div>
  </div>
</body>
</html>
  `;
}
function getAccessDeniedHtml(msg) {
  return `
<!DOCTYPE html>
<html lang="zh-CN">
<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>
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
    .denied-card { background: #ffffff; border-radius: 12px; box-shadow: 0 8px 32px rgba(31,38,135,0.1); padding: 3rem 2.5rem; max-width: 500px; width: 100%; text-align: center; }
    .denied-icon { font-size: 4rem; color: #dc2626; margin-bottom: 1.5rem; }
    .denied-title { font-size: 1.8rem; font-weight: 600; color: #1e293b; margin-bottom: 1rem; }
    .denied-msg { font-size: 1.1rem; color: #dc2626; line-height: 1.6; margin-bottom: 2rem; }
    .back-btn { display: inline-block; padding: 0.875rem 2rem; background: linear-gradient(135deg, #2563eb 0%, #3b82f6 100%); color: #ffffff; border-radius: 8px; text-decoration: none; }
  </style>
</head>
<body>
  <div class="denied-card">
    <div class="denied-icon"><i class="fa fa-ban"></i></div>
    <h2 class="denied-title">访问被拒绝</h2>
    <div class="denied-msg">${msg}</div>
    <a href="./user/login" class="back-btn">返回重新登录</a>
  </div>
</body>
</html>
  `;
}

 

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容