<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>User Auth</title>
    <style>
        body { font-family: Arial, sans-serif; }
        .form-container { max-width: 300px; margin: auto; }
        .hidden { display: none; }
        .error-message { color: red; }
    </style>
</head>
<body>
    <div class="form-container">
        <h2>用户注册</h2>
        <form id="registerForm">
            <input type="text" id="registerUsername" placeholder="Username" required>
            <input type="password" id="registerPassword" placeholder="Password" required>
            <button type="submit">注册</button>
        </form>
        <p class="error-message hidden" id="registerError"></p>
        
        <h2>用户登录</h2>
        <form id="loginForm">
            <input type="text" id="loginUsername" placeholder="Username" required>
            <input type="password" id="loginPassword" placeholder="Password" required>
            <button type="submit">登录</button>
        </form>
        <p class="error-message hidden" id="loginError"></p>
    </div>

    <script>
        document.getElementById('registerForm').addEventListener('submit', async (event) => {
            event.preventDefault();
            const username = document.getElementById('registerUsername').value;
            const password = document.getElementById('registerPassword').value;

            const response = await fetch('http://api/users/register', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ username, password })
            });

            const data = await response.json();
            if (response.ok) {
                alert(data.message);
                document.getElementById('registerForm').reset();
                document.getElementById('registerError').classList.add('hidden');
            } else {
                document.getElementById('registerError').textContent = data.message;
                document.getElementById('registerError').classList.remove('hidden');
            }
        });

        document.getElementById('loginForm').addEventListener('submit', async (event) => {
    event.preventDefault();
    const username = document.getElementById('loginUsername').value;
    const password = document.getElementById('loginPassword').value;

    const response = await fetch('/api/users/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password })
    });

    const data = await response.json();
    if (response.ok) {
        alert(data.message);
        document.getElementById('loginForm').reset();
        document.getElementById('loginError').classList.add('hidden');

        // 重定向到 tags.html 页面
        window.location.href = '/tags.html';
    } else {
        document.getElementById('loginError').textContent = data.message;
        document.getElementById('loginError').classList.remove('hidden');
    }
});
    </script>
</body>
</html><!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>User Tags</title>
    <style>
        body { font-family: Arial, sans-serif; }
        #tagList { list-style-type: none; padding: 0; }
        #tagList li { padding: 5px; }
    </style>
</head>
<body>
    <h1>Your Tags</h1>
    <ul id="tagList"></ul>
    <script>
        // 页面加载时获取标签
        async function fetchTags() {
            const response = await fetch('/api/tags');
            const tags = await response.json();
            const tagList = document.getElementById('tagList');
            tagList.innerHTML = ''; // 清空当前标签

            tags.forEach(tag => {
                const li = document.createElement('li');
                li.textContent = tag.name; // 假设标签表中有一个 `name` 字段
                tagList.appendChild(li);
            });
        }

        fetchTags(); // 页面加载时自动调用获取标签
    </script>
</body>
</html>const express = require('express');
const router = express.Router();
const db = require('../db');

// 获取所有标签
router.get('/', (req, res) => {
    const sql = 'SELECT * FROM tags'; // 假设你有一个 tags 表

    db.query(sql, (err, results) => {
        if (err) {
            return res.status(500).json({ message: 'Error retrieving tags' });
        }
        res.status(200).json(results);
    });
});

module.exports = router;// routes/user.js
const express = require('express');
const router = express.Router();
const db = require('../db');
const bcrypt = require('bcrypt');

// 用户注册
router.post('/register', async (req, res) => {
    const { username, password } = req.body;
    const hashedPassword = await bcrypt.hash(password, 10); // 加密密码
    const sql = 'INSERT INTO users (username, password) VALUES (?, ?)';

    db.query(sql, [username, hashedPassword], (err, result) => {
        if (err) {
            return res.status(500).json({ message: 'Error registering user' });
        }
        res.status(201).json({ message: 'User registered successfully' });
    });
});

// 用户登录
router.post('/login', (req, res) => {
    const { username, password } = req.body;
    const sql = 'SELECT * FROM users WHERE username = ?';

    db.query(sql, [username], async (err, results) => {
        if (err) {
            return res.status(500).json({ message: 'Error logging in' });
        }
        if (results.length > 0) {
            const user = results[0];
            const match = await bcrypt.compare(password, user.password);
            if (match) {
                res.status(200).json({ message: 'Login successful' });
            } else {
                res.status(401).json({ message: 'Invalid credentials' });
            }
        } else {
            res.status(401).json({ message: 'Invalid credentials' });
        }
    });
});

module.exports = router;// server.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const path = require('path');
const db = require('./db'); // 引入数据库连接
const userRoutes = require('./routes/user'); // 用户路由
const tagRoutes = require('./routes/tag'); // 标签路由
const app = express();

const PORT = process.env.PORT || 3000;

// 使用标签路由
app.use('/api/tags', tagRoutes);
// Middleware
app.use(cors());
app.use(bodyParser.json());
app.use(express.static('public')); // 提供静态文件服务

// 使用用户路由
app.use('/api/users', userRoutes);

// 根路由，返回 HTML 页面
app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

// 启动服务器
app.listen(PORT, () => {
    console.log(`服务器正在运行 http://localhost:${PORT}`);
});               报错Access to fetch at 'file:///E:/api/users/login' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: chrome-extension, chrome-untrusted, data, edge, http, https, isolated-app.
/E:/api/users/login:1 
        
        
        Failed to load resource: net::ERR_FAILED
index.html:61 
        
        
       
        
        Uncaught (in promise) TypeError: Failed to fetch
    at HTMLFormElement.<anonymous> (index.html:61:36)       修改代码，让他能运行