Browse Source

feat: Add Ad Slot Styles to Proxy UI

dqzboy 1 year ago
parent
commit
9e1c5166d9
7 changed files with 23 additions and 682 deletions
  1. 0 15
      Dockerfile
  2. 0 0
      hubcmdui/config.json
  3. 23 86
      hubcmdui/docker-proxy.html
  4. 0 6
      hubcmdui/package.json
  5. 0 140
      hubcmdui/server.js
  6. 0 8
      hubcmdui/users.json
  7. 0 427
      hubcmdui/web/admin.html

+ 0 - 15
Dockerfile

@@ -1,15 +0,0 @@
-# 使用官方的 Node.js 运行时镜像作为基础镜像
-FROM node:lts-alpine
-
-# 设置工作目录
-WORKDIR /app
-# 复制项目文件到工作目录
-COPY hubcmdui/ .
-# 安装项目依赖
-RUN npm install
-
-# 暴露应用程序的端口
-EXPOSE 3000
-
-# 运行应用程序
-CMD ["node", "server.js"]

+ 0 - 0
hubcmdui/config.json


+ 23 - 86
hubcmdui/web/index.html → hubcmdui/docker-proxy.html

@@ -53,10 +53,11 @@
         }
         .container {
             max-width: 800px;
-            width: 90%;
+            min-width: 800px; /* 固定容器宽度 */
+            width: 100%;
             margin: 20px auto;
             background: white;
-            padding: 20px;
+            padding: 30px;
             border-radius: 8px;
             box-shadow: 0 2px 10px rgba(0,0,0,0.1);
             flex-grow: 1;
@@ -96,15 +97,20 @@
         button:hover {
             background-color: #2c974b;
         }
+        /* 广告容器样式 */
         .ad-container {
             display: flex;
             justify-content: center;
             align-items: center;
-            margin-top: 30px;
+            margin-top: 90px; /* 与获取命令按钮之间的间距 */
             margin-bottom: 30px;
+            max-width: 800px;
+            min-width: 800px;
         }
         .ad-container img {
             max-width: 100%;
+            width: 750; /* 广告图片宽度 */
+            height: 300px; /* 广告图片高度 */
             border-radius: 8px;
         }
         pre {
@@ -114,27 +120,20 @@
             overflow: auto;
             font-size: 14px;
             border: 1px solid #e1e4e8;
-            position: relative;
         }
         .copy-btn {
-            position: absolute;
-            top: 8px;
-            right: 8px;
+            float: right;
             padding: 6px 12px;
             font-size: 12px;
-            background-color: #f0f0f0;
-            color: #333;
+            margin-top: -5px;
+            background-color: #0366d6;
+            color: white;
             border: none;
             border-radius: 4px;
             cursor: pointer;
-            transition: background-color 0.3s, color 0.3s;
-            font-family: 'Arial', sans-serif;
-            font-weight: 500;
-            border-radius: 12px;
         }
         .copy-btn:hover {
-            background-color: #e0e0e0;
-            color: #000;
+            background-color: #0256b9;
         }
         .step {
             margin-bottom: 25px;
@@ -179,6 +178,10 @@
         #backToTopBtn:hover {
             background-color: #0256b9;
         }
+        #backToTopBtn:before {
+            display: inline-block;
+            font-size: 20px;
+        }
         #backToTopBtn::after {
             content: '返回顶部';
             display: none;
@@ -194,33 +197,6 @@
         #backToTopBtn:hover::after {
             display: block;
         }
-        @media (max-width: 768px) {
-            .nav-menu {
-                display: none;
-            }
-            .header-content {
-                flex-direction: column;
-            }
-            .logo {
-                margin-bottom: 10px;
-            }
-            .input-group {
-                flex-direction: column;
-            }
-            .container {
-                width: 100%;
-                padding: 10px;
-            }
-            .ad-container img {
-                width: 100%;
-                height: auto;
-            }
-        }
-        @media (min-width: 769px) {
-            .nav-menu {
-                display: flex;
-            }
-        }
     </style>
 </head>
 <body>
@@ -247,7 +223,7 @@
         <!-- 广告容器 -->
         <div class="ad-container" id="adContainer">
             <a href="https://example.com" target="_blank">
-                <img src="https://cdn.jsdelivr.net/gh/dqzboy/Blog-Image/BlogCourse/guanggao.png?text=Ad+Banner" alt="广告图片">
+                <img src="https://github.com/user-attachments/assets/595f645a-e4da-49fd-bd67-db93566a6152?text=Ad+Banner" alt="广告图片">
             </a>
         </div>
         <!-- 结果容器 -->
@@ -268,8 +244,6 @@
         // 设置当前年份
         document.getElementById('currentYear').textContent = new Date().getFullYear();
 
-        let proxyDomain = 'your_domain'; // 默认代理加速地址
-
         // 生成加速命令
         function generateCommands() {
             const imageInput = document.getElementById('imageInput').value.trim();
@@ -277,6 +251,8 @@
                 alert('请输入 Docker 镜像');
                 return;
             }
+            // docker镜像加速地址,请把下面your_domain改为你实际的加速地址
+            const proxyDomain = 'your_domain'; //例如:hub.baidu.com
             let [imageName, tag] = imageInput.split(':');
             tag = tag || 'latest';
             
@@ -306,7 +282,8 @@
                 cmdDiv.className = 'step';
                 cmdDiv.innerHTML = `
                     <h3>${index + 1}. ${command.title}</h3>
-                    <pre><code>${command.cmd}</code><button class="copy-btn" onclick="copyToClipboard('${command.cmd}')">复制</button></pre>
+                    <pre><code>${command.cmd}</code></pre>
+                    <button class="copy-btn" onclick="copyToClipboard('${command.cmd}')">复制</button>
                 `;
                 container.appendChild(cmdDiv);
             });
@@ -333,46 +310,6 @@
                 behavior: 'smooth'
             });
         }
-
-        // 获取并加载配置
-        async function loadConfig() {
-            try {
-                const response = await fetch('/api/config');
-                const config = await response.json();
-                if (config.logo) {
-                    document.querySelector('.logo').src = config.logo;
-                }
-                if (config.menuItems && Array.isArray(config.menuItems)) {
-                    const navMenu = document.querySelector('.nav-menu');
-                    navMenu.innerHTML = ''; // 清空菜单
-                    config.menuItems.forEach(item => {
-                        const a = document.createElement('a');
-                        a.href = item.link;
-                        a.textContent = item.text;
-                        if (item.newTab) {
-                            a.target = '_blank';
-                        }
-                        navMenu.appendChild(a);
-                    });
-                }
-                if (config.adImage && config.adImage.url) {
-                    const adContainer = document.getElementById('adContainer');
-                    const adLink = adContainer.querySelector('a');
-                    const adImage = adContainer.querySelector('img');
-                    adLink.href = config.adImage.link;
-                    adImage.src = config.adImage.url;
-                    adImage.alt = config.adImage.alt || '广告图片';
-                    adContainer.style.display = 'flex';
-                }
-                if (config.proxyDomain) {
-                    proxyDomain = config.proxyDomain;
-                }
-            } catch (error) {
-                console.error('加载配置失败:', error);
-            }
-        }
-
-        loadConfig();
     </script>
 </body>
 </html>

+ 0 - 6
hubcmdui/package.json

@@ -1,6 +0,0 @@
-{
-  "dependencies": {
-    "express": "^4.19.2",
-    "express-session": "^1.18.0"
-  }
-}

+ 0 - 140
hubcmdui/server.js

@@ -1,140 +0,0 @@
-// server.js
-const express = require('express');
-const fs = require('fs').promises;
-const path = require('path');
-const bodyParser = require('body-parser');
-const session = require('express-session');
-
-const app = express();
-app.use(express.json());
-app.use(express.static('web'));
-app.use(bodyParser.urlencoded({ extended: true }));
-app.use(session({
-  secret: 'OhTq3faqSKoxbV%NJV',
-  resave: false,
-  saveUninitialized: true,
-  cookie: { secure: false } // 设置为true如果使用HTTPS
-}));
-
-app.get('/admin', (req, res) => {
-  res.sendFile(path.join(__dirname, 'web', 'admin.html'));
-});
-
-const CONFIG_FILE = path.join(__dirname, 'config.json');
-const USERS_FILE = path.join(__dirname, 'users.json');
-
-// 读取配置
-async function readConfig() {
-  try {
-    const data = await fs.readFile(CONFIG_FILE, 'utf8');
-    return JSON.parse(data);
-  } catch (error) {
-    if (error.code === 'ENOENT') {
-      return {
-        logo: '',
-        menuItems: [],
-        adImage: { url: '', link: '' }
-      };
-    }
-    throw error;
-  }
-}
-
-// 写入配置
-async function writeConfig(config) {
-  await fs.writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf8');
-}
-
-// 读取用户
-async function readUsers() {
-  try {
-    const data = await fs.readFile(USERS_FILE, 'utf8');
-    return JSON.parse(data);
-  } catch (error) {
-    if (error.code === 'ENOENT') {
-      return {
-        users: [{ username: 'root', password: 'admin' }]
-      };
-    }
-    throw error;
-  }
-}
-
-// 写入用户
-async function writeUsers(users) {
-  await fs.writeFile(USERS_FILE, JSON.stringify(users, null, 2), 'utf8');
-}
-
-// 登录验证
-app.post('/api/login', async (req, res) => {
-  const { username, password } = req.body;
-  const users = await readUsers();
-  const user = users.users.find(u => u.username === username && u.password === password);
-  if (user) {
-    req.session.user = user;
-    res.json({ success: true });
-  } else {
-    res.status(401).json({ error: 'Invalid credentials' });
-  }
-});
-
-// 修改密码
-app.post('/api/change-password', async (req, res) => {
-  if (!req.session.user) {
-    return res.status(401).json({ error: 'Not logged in' });
-  }
-  const { currentPassword, newPassword } = req.body;
-  const users = await readUsers();
-  const user = users.users.find(u => u.username === req.session.user.username);
-  if (user && user.password === currentPassword) {
-    user.password = newPassword;
-    await writeUsers(users);
-    res.json({ success: true });
-  } else {
-    res.status(401).json({ error: 'Invalid current password' });
-  }
-});
-
-// 需要登录验证的中间件
-function requireLogin(req, res, next) {
-  if (req.session.user) {
-    next();
-  } else {
-    res.status(401).json({ error: 'Not logged in' });
-  }
-}
-
-// API 端点:获取配置
-app.get('/api/config', requireLogin, async (req, res) => {
-  try {
-    const config = await readConfig();
-    res.json(config);
-  } catch (error) {
-    res.status(500).json({ error: 'Failed to read config' });
-  }
-});
-
-// API 端点:保存配置
-app.post('/api/config', requireLogin, async (req, res) => {
-  try {
-    await writeConfig(req.body);
-    res.json({ success: true });
-  } catch (error) {
-    res.status(500).json({ error: 'Failed to save config' });
-  }
-});
-
-// API 端点:检查会话状态
-app.get('/api/check-session', (req, res) => {
-  if (req.session.user) {
-    res.json({ success: true });
-  } else {
-    res.status(401).json({ error: 'Not logged in' });
-  }
-});
-
-// 启动服务器
-const PORT = process.env.PORT || 3000;
-app.listen(PORT, () => {
-  console.log(`Server is running on http://localhost:${PORT}`);
-});

+ 0 - 8
hubcmdui/users.json

@@ -1,8 +0,0 @@
-{
-  "users": [
-    {
-      "username": "root",
-      "password": "admin"
-    }
-  ]
-}

+ 0 - 427
hubcmdui/web/admin.html

@@ -1,427 +0,0 @@
-<!DOCTYPE html>
-<html lang="zh-CN">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Docker 镜像代理加速 - 管理面板</title>
-    <link rel="icon" href="https://cdn.jsdelivr.net/gh/dqzboy/Blog-Image/BlogCourse/docker-proxy.png" type="image/png">
-    <style>
-        body {
-            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Lato', 'Helvetica', 'Arial', sans-serif;
-            line-height: 1.6;
-            color: #24292e;
-            margin: 0;
-            padding: 20px;
-            background-color: #f6f8fa;
-        }
-        .container {
-            max-width: 1000px;
-            margin: 0 auto;
-            background: white;
-            padding: 20px;
-            border-radius: 8px;
-            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
-        }
-        h1, h2 {
-            border-bottom: 2px solid #eaecef;
-            padding-bottom: 0.5em;
-            margin-bottom: 1em;
-            color: #0366d6;
-        }
-        label {
-            display: block;
-            margin-top: 20px;
-            border-bottom: none;
-            padding-bottom: 0;
-            color: #24292e;
-            font-size: 20px;
-        }
-        input[type="text"], input[type="url"], input[type="password"], select {
-            width: 100%;
-            padding: 8px;
-            margin-top: 5px;
-            border: 1px solid #d1d5da;
-            border-radius: 4px;
-            box-sizing: border-box;
-            font-family: inherit;
-            font-size: inherit;
-        }
-        button {
-            background-color: #2ea44f;
-            color: white;
-            border: none;
-            padding: 10px 20px;
-            margin-top: 20px;
-            border-radius: 4px;
-            cursor: pointer;
-            font-family: inherit;
-            font-size: inherit;
-        }
-        button:hover {
-            background-color: #2c974b;
-        }
-        table {
-            width: 100%;
-            border-collapse: collapse;
-            margin-top: 20px;
-        }
-        th, td {
-            border: 1px solid #d1d5da;
-            padding: 8px;
-            text-align: left;
-        }
-        th {
-            background-color: #f6f8fa;
-            font-weight: normal;
-        }
-        .action-btn {
-            background-color: #0366d6;
-            color: white;
-            border: none;
-            padding: 5px 10px;
-            border-radius: 4px;
-            cursor: pointer;
-            margin-right: 5px;
-            font-family: inherit;
-            font-size: inherit;
-        }
-        .action-btn:hover {
-            background-color: #0256b9;
-        }
-        .add-btn {
-            background-color: #2ea44f;
-            color: white;
-            border: none;
-            padding: 10px 20px;
-            margin-top: 10px;
-            border-radius: 4px;
-            cursor: pointer;
-            font-family: inherit;
-            font-size: inherit;
-        }
-        .add-btn:hover {
-            background-color: #2c974b;
-        }
-        #menuPreview {
-            margin-top: 20px;
-            padding: 10px;
-            background-color: #f6f8fa;
-            border: 1px solid #d1d5da;
-            border-radius: 4px;
-        }
-        #menuPreview a {
-            margin-right: 15px;
-            color: #0366d6;
-            text-decoration: none;
-        }
-        #menuPreview a:hover {
-            text-decoration: underline;
-        }
-        .hidden {
-            display: none;
-        }
-        .login-modal {
-            display: none;
-            position: fixed;
-            z-index: 1;
-            left: 0;
-            top: 0;
-            width: 100%;
-            height: 100%;
-            overflow: auto;
-            background-color: rgba(0,0,0,0.4);
-        }
-        .login-content {
-            background-color: #fefefe;
-            margin: 15% auto;
-            padding: 20px;
-            border: 1px solid #888;
-            width: 30%;
-            border-radius: 8px;
-        }
-        .user-management {
-            position: absolute;
-            top: 20px;
-            right: 20px;
-            cursor: pointer;
-        }
-        .user-management i {
-            font-size: 24px;
-            color: #0366d6;
-        }
-        .menu-label {
-            font-size: 20px;
-            color: #24292e;
-        }
-        .admin-title {
-            font-size: 24px;
-            color: #0366d6;
-        }
-    </style>
-</head>
-<body>
-    <div class="container hidden" id="adminContainer">
-        <h1 class="admin-title">Docker 镜像代理加速 - 管理面板</h1>
-        <form id="adminForm">
-            <label for="logoUrl">Logo URL: (可选)</label>
-            <input type="url" id="logoUrl" name="logoUrl">
-            
-            <label for="proxyDomain">Docker镜像代理地址: (必填)</label>
-            <input type="text" id="proxyDomain" name="proxyDomain" required>
-            
-            <h2 class="menu-label">菜单项管理</h2>
-            <table id="menuTable">
-                <thead>
-                    <tr>
-                        <th>文本</th>
-                        <th>链接 (可选)</th>
-                        <th>新标签页打开</th>
-                        <th>操作</th>
-                    </tr>
-                </thead>
-                <tbody id="menuTableBody">
-                    <!-- 菜单项将在这里动态添加 -->
-                </tbody>
-                <tfoot>
-                    <tr id="newMenuItemRow" class="hidden">
-                        <td><input type="text" id="newMenuItemText" placeholder="菜单项文本"></td>
-                        <td><input type="url" id="newMenuItemLink" placeholder="菜单项链接 (可选)"></td>
-                        <td>
-                            <select id="newMenuItemNewTab">
-                                <option value="false">否</option>
-                                <option value="true">是</option>
-                            </select>
-                        </td>
-                        <td>
-                            <button type="button" class="action-btn" onclick="saveNewMenuItem()">保存</button>
-                            <button type="button" class="action-btn" onclick="cancelNewMenuItem()">取消</button>
-                        </td>
-                    </tr>
-                </tfoot>
-            </table>
-            <button type="button" class="add-btn" onclick="showNewMenuItemRow()">添加菜单项</button>
-            
-            <h2 class="menu-label">菜单预览</h2>
-            <div id="menuPreview">
-                <!-- 菜单预览将在这里显示 -->
-            </div>
-            
-            <label for="adImageUrl">广告图片 URL: (可选)</label>
-            <input type="url" id="adImageUrl" name="adImageUrl">
-            
-            <!-- 添加修改密码的选项 -->
-            <h2 class="menu-label">修改密码</h2>
-            <label for="currentPassword">当前密码</label>
-            <input type="password" id="currentPassword" name="currentPassword" required>
-            <label for="newPassword">新密码</label>
-            <input type="password" id="newPassword" name="newPassword" required>
-            <button type="button" onclick="changePassword()">修改密码</button>
-            
-            <button type="submit">保存更改</button>
-        </form>
-    </div>
-
-    <div class="login-modal" id="loginModal">
-        <div class="login-content">
-            <h2>登录</h2>
-            <label for="username">用户名</label>
-            <input type="text" id="username" name="username" required>
-            <label for="password">密码</label>
-            <input type="password" id="password" name="password" required>
-            <button type="button" onclick="login()">登录</button>
-        </div>
-    </div>
-
-    <script>
-        let menuItems = [];
-        let isLoggedIn = false;
-
-        function getMenuItems() {
-            return menuItems;
-        }
-
-        function setMenuItems(items) {
-            menuItems = items;
-            renderMenuItems();
-            updateMenuPreview();
-        }
-
-        function renderMenuItems() {
-            const tbody = document.getElementById('menuTableBody');
-            tbody.innerHTML = '';
-            menuItems.forEach((item, index) => {
-                const row = `
-                    <tr>
-                        <td>${item.text}</td>
-                        <td>${item.link || ''}</td>
-                        <td>${item.newTab ? '是' : '否'}</td>
-                        <td>
-                            <button type="button" class="action-btn" onclick="editMenuItem(${index})">编辑</button>
-                            <button type="button" class="action-btn" onclick="deleteMenuItem(${index})">删除</button>
-                        </td>
-                    </tr>
-                `;
-                tbody.innerHTML += row;
-            });
-        }
-
-        function updateMenuPreview() {
-            const preview = document.getElementById('menuPreview');
-            preview.innerHTML = menuItems.map(item => 
-                `<a href="${item.link || '#'}" ${item.newTab ? 'target="_blank"' : ''}>${item.text}</a>`
-            ).join(' ');
-        }
-
-        function showNewMenuItemRow() {
-            document.getElementById('newMenuItemRow').classList.remove('hidden');
-        }
-
-        function saveNewMenuItem() {
-            const text = document.getElementById('newMenuItemText').value;
-            const link = document.getElementById('newMenuItemLink').value;
-            const newTab = document.getElementById('newMenuItemNewTab').value === 'true';
-            
-            if (text) {
-                menuItems.push({ text, link, newTab });
-                renderMenuItems();
-                updateMenuPreview();
-                cancelNewMenuItem();
-            } else {
-                alert('请填写菜单项文本');
-            }
-        }
-
-        function cancelNewMenuItem() {
-            document.getElementById('newMenuItemRow').classList.add('hidden');
-            document.getElementById('newMenuItemText').value = '';
-            document.getElementById('newMenuItemLink').value = '';
-            document.getElementById('newMenuItemNewTab').value = 'false';
-        }
-
-        function editMenuItem(index) {
-            const item = menuItems[index];
-            document.getElementById('newMenuItemText').value = item.text;
-            document.getElementById('newMenuItemLink').value = item.link;
-            document.getElementById('newMenuItemNewTab').value = item.newTab.toString();
-            showNewMenuItemRow();
-            menuItems.splice(index, 1);
-            renderMenuItems();
-            updateMenuPreview();
-        }
-
-        function deleteMenuItem(index) {
-            menuItems.splice(index, 1);
-            renderMenuItems();
-            updateMenuPreview();
-        }
-
-        async function saveConfig() {
-            const config = {
-                logo: document.getElementById('logoUrl').value,
-                proxyDomain: document.getElementById('proxyDomain').value,
-                menuItems: getMenuItems(),
-                adImage: document.getElementById('adImageUrl').value
-            };
-
-            try {
-                const response = await fetch('/api/config', {
-                    method: 'POST',
-                    headers: { 'Content-Type': 'application/json' },
-                    body: JSON.stringify(config)
-                });
-                if (response.ok) {
-                    alert('配置已保存');
-                } else {
-                    throw new Error('保存失败');
-                }
-            } catch (error) {
-                alert('保存失败: ' + error.message);
-            }
-        }
-
-        async function loadConfig() {
-            try {
-                const response = await fetch('/api/config');
-                const config = await response.json();
-                document.getElementById('logoUrl').value = config.logo || '';
-                document.getElementById('proxyDomain').value = config.proxyDomain || '';
-                setMenuItems(config.menuItems || []);
-                document.getElementById('adImageUrl').value = config.adImage || '';
-            } catch (error) {
-                console.error('加载配置失败:', error);
-            }
-        }
-
-        async function login() {
-            const username = document.getElementById('username').value;
-            const password = document.getElementById('password').value;
-            try {
-                const response = await fetch('/api/login', {
-                    method: 'POST',
-                    headers: { 'Content-Type': 'application/json' },
-                    body: JSON.stringify({ username, password })
-                });
-                if (response.ok) {
-                    isLoggedIn = true;
-                    localStorage.setItem('isLoggedIn', 'true');  // 存储登录状态
-                    document.getElementById('loginModal').style.display = 'none';
-                    document.getElementById('adminContainer').classList.remove('hidden');
-                    loadConfig();
-                } else {
-                    alert('登录失败');
-                }
-            } catch (error) {
-                alert('登录失败: ' + error.message);
-            }
-        }
-
-        async function changePassword() {
-            const currentPassword = document.getElementById('currentPassword').value;
-            const newPassword = document.getElementById('newPassword').value;
-            try {
-                const response = await fetch('/api/change-password', {
-                    method: 'POST',
-                    headers: { 'Content-Type': 'application/json' },
-                    body: JSON.stringify({ currentPassword, newPassword })
-                });
-                if (response.ok) {
-                    alert('密码已修改');
-                } else {
-                    alert('修改密码失败');
-                }
-            } catch (error) {
-                alert('修改密码失败: ' + error.message);
-            }
-        }
-
-        // 页面加载时检查登录状态
-        window.onload = async function() {
-            try {
-                const response = await fetch('/api/check-session');
-                if (response.ok) {
-                    isLoggedIn = localStorage.getItem('isLoggedIn') === 'true';  // 读取登录状态
-                    if (isLoggedIn) {
-                        document.getElementById('loginModal').style.display = 'none';
-                        document.getElementById('adminContainer').classList.remove('hidden');
-                        loadConfig();
-                    } else {
-                        document.getElementById('loginModal').style.display = 'block';
-                    }
-                } else {
-                    localStorage.removeItem('isLoggedIn');  // 清除登录状态
-                    document.getElementById('loginModal').style.display = 'block';
-                }
-            } catch (error) {
-                localStorage.removeItem('isLoggedIn');  // 清除登录状态
-                document.getElementById('loginModal').style.display = 'block';
-            }
-        };
-
-        // 表单提交事件监听器
-        document.getElementById('adminForm').addEventListener('submit', async function(e) {
-            e.preventDefault();
-            await saveConfig();
-        });
-    </script>
-</body>
-</html>