使用PM2搭建后端api接口

  • PM2_MySQL
  • 步骤一:进入服务器并 cd 到 /etc/www 目录下 步骤二:输入以下命令进入数据库并输入密码登陆
    sudo mysql -u root -p
    步骤三:创建数据表 userinfo
    CREATE TABLE userinfo (
     id char(20) NOT NULL,
     username varchar(40) NOT NULL,
     password varchar(40) NOT NULL,
     PRIMARY KEY (id)
    ) ENGINE=InnoDB DEFAULT CHARSET=gb2312;
    步骤四:往数据表中插入数据
    USER userinfo;
    INSER TINTO userinfo values('1','admin','123456'),('10','mysql','123456');
    步骤五:输入quit保存退出 步骤六:进入WinTerm。在/etc/www目录下创建一个 MyClassroom_server.js文件(MyClassroom_server.js内容如下)
    const express = require('express');
    const mysql = require('mysql2/promise');
    const cors = require('cors');
    const app = express();
    app.use(cors());
    app.use(express.json());
    // ? 连接你的 MariaDB 数据库
    const pool = mysql.createPool({
     host: '127.0.0.1',
     user: 'vueuser',// 数据库登陆账号
     password: '123456',// 数据库密码
     database: 'vue2Mysql'// ? 数据表
    });
    // 示例路由。如果请求成功后直接访问 http://localhost:3001/ 就可以看到请求过来的数据
    app.get('/', async (req, res) => {
     try {
      // userinfo
      const [userinfo] = await pool.query('SELECT * FROM userinfo');
      // myclassroom
      const [myclassroom] = await pool.query('SELECT * FROM myclassroom');
      // windows_server
      const [windows_server] = await pool.query('SELECT * FROM windows_server');
      // nat_server
      const [routing_switching] = await pool.query('SELECT * FROM routing_switching');
      // python_programming
      const [python_programming] = await pool.query('SELECT * FROM python_programming');
      // MyTextbook
      const [mytextbook] = await pool.query('SELECT * FROM mytextbook');
      // MyExam
      const [myexam] = await pool.query('SELECT * FROM myexam');
      res.json({
       userinfo,
       myclassroom,
       windows_server,
       routing_switching,
       python_programming,
       mytextbook,
       myexam
      });
     } catch (err) {
      console.error('服务器错误:', err);
      res.status(500).json({ error: 'Internal Server Error' });
     }
    });
    // ? 监听 0.0.0.0,开放给公网访问
    app.listen(3001, '0.0.0.0', () => {
     console.log('Server running on http://0.0.0.0:3001');
    });
    步骤七:编辑完成后保存退出 步骤八:输入以下命令使用PM2搭建后端api接口
    pm2 start MyClassroom_server.js --name MyClassroom_server_api
    最后:你的api接口已经搭建好了。您可以在浏览器中通过以下地址访问您的后端数据了:http://[您的服务器IP]:3001
  • PM2_JSON
  • 步骤一:进入WinSCP。在/etc/www/tiktok-test/api目录下创建一个 db.json 文件(db.json内容如下)
    {
     "data": [
      {
       "id": "30",
       "status": "0",
       "user": "GGGGGG",
       "password": "GGGGGGs",
       "email": "GGGGGG",
       "emailPwd": "GGGGGG",
       "country": "gG",
       "time": "2025-09-30 17:12:58",
       "reason": ""
      }
     ]
    }
    步骤二:进入WinSCP。在/etc/www/tiktok-test/api目录下创建一个 server.js文件(server.js内容如下)
    const express = require('express');
    const cors = require('cors');
    const fs = require('fs').promises;
    const path = require('path');

    const app = express();
    app.use(cors());
    app.use(express.json());

    // 定义你的 db.json 文件的绝对路径
    const DB_PATH = '/etc/www/tiktok-test/api/db.json';

    // =================================================================
    // ===== API 1: 获取所有数据列表 (从db.json读取) =====
    // =================================================================
    app.get('/api', async (req, res) => {
     console.log(`收到 GET /api/videos 请求, 准备读取 ${DB_PATH}`);
     try {
      const fileContent = await fs.readFile(DB_PATH, 'utf-8');
      const data = JSON.parse(fileContent);
      console.log(`成功读取并解析 ${DB_PATH}`);
      res.json(data);
     } catch (err) {
      console.error('读取或解析 db.json 文件时出错:', err);
      res.status(500).json({ error: '无法读取数据文件' });
     }
    });
    // =================================================================
    // ===== API 2: 更新指定ID的状态 (写入db.json) =====
    // =================================================================
    app.put('/api/:id', async (req, res) => {
     const { id } = req.params;
     const { status, reason } = req.body;
     const numericId = parseInt(id, 10);
     console.log(`收到 PUT /api/videos/${id} 请求,准备更新状态为: ${status}, 理由是: ${reason}`);
     if (status === undefined) {
      return res.status(400).json({ error: '请求体中必须包含 "status" 字段' });
     }
     try {
      const fileContent = await fs.readFile(DB_PATH, 'utf-8');
      // 修复 #1:获取对象,然后从“data”属性中获取数组
      let dbObject = JSON.parse(fileContent);
      let videos = dbObject.data;
      let itemUpdated = false;
      // 这是新的第 51 行。它不会再崩溃。
      videos = videos.map(video => {
       // 修复 #2:比较数字与数字
       if (parseInt(video.id, 10) === numericId) {
        video.status = String(status); // 确保状态是一个字符串,以匹配 db.json
        video.reason = reason || ""; // 如果没传 reason 过来,默认为空字符串
        itemUpdated = true;
       }
       // 修复 #3:始终返回对象
       return video;
      });
      if (itemUpdated) {
       // 重新组装主对象并写回
       dbObject.data = videos;
       await fs.writeFile(DB_PATH, JSON.stringify(dbObject, null, 2), 'utf-8');
       res.json({ message: '状态更新成功' });
      } else {
       res.status(404).json({ error: `未找到 ID 为 ${id} 的视频` });
      }
     } catch (error) {
      console.error('更新状态时发生服务器错误:', error);
      res.status(500).json({ error: '服务器内部错误' });
     }
    });
    // =================================================================
    // ===== API 3:添加一个新的 POST 路由来处理批量添加 (写入db.json) =====
    // =================================================================
    app.post('/api/', async (req, res) => {
     // req.body 现在应该是一个数组,比如 [{...}, {...}]
     const newVideos = req.body;
     // 检查传入的是否是一个数组
     if (!Array.isArray(newVideos)) {
      return res.status(400).json({ error: '请求体必须是一个数组 (Request body must be an array)' });
     }
     console.log(`收到批量添加请求,共 ${newVideos.length} 条数据`);
     try {
      const fileContent = await fs.readFile(DB_PATH, 'utf-8');
      const db = JSON.parse(fileContent);
      // 找到当前最大的 ID,以便我们生成新的 ID
      const maxId = db.data.reduce((max, item) => Math.max(max, parseInt(item.id)), 0);
      let currentId = maxId;
      let addedCount = 0;
      // 遍历前端发送过来的每一个视频对象
      newVideos.forEach(video => {
        currentId++; // ID 自增
        const newEntry = {
          id: String(currentId), // ID 确保是字符串
          status: "0", // 默认状态为 "0"
          ...video // 将 video 对象里的 user, password 等属性展开
        };
        db.data.unshift(newEntry); // 添加到数组的最前面
        addedCount++;
      });
      // 将更新后的数据写回 db.json 文件
      await fs.writeFile(DB_PATH, JSON.stringify(db, null, 2), 'utf-8');
      console.log(`成功添加 ${addedCount} 条数据`);
      res.status(201).json({ message: '批量添加成功 (Batch add successful)', addedCount: addedCount });
     } catch (error) {
      console.error('批量添加数据时出错:', error);
      res.status(500).json({ error: '服务器内部错误 (Internal Server Error)' });
     }
    });
    // =================================================================
    // ===== API 4: 批量添加图片 =====
    // =================================================================
    app.post('/api/imge', async (req, res) => {
     const newItems = req.body;
     if (!Array.isArray(newItems) || newItems.length === 0) {
      return res.status(400).json({ error: '请求体必须是一个非空数组' });
     }
     console.log(`[POST /api/imge] 收到批量添加请求,共 ${newItems.length} 条数据`);
     try {
      const fileContent = await fs.readFile(DB_PATH, 'utf-8');
      const db = JSON.parse(fileContent);
      // 确保 db.image 是一个数组,如果不存在则初始化
      if (!Array.isArray(db.image)) {
       db.image = [];
      }
      const maxId = db.image.reduce((max, item) => Math.max(max, parseInt(item.id, 10) || 0), 0);
      let currentId = maxId;
      // 使用 map 创建所有新条目,然后一次性添加到数组中,效率稍高
      const entriesToAdd = newItems.map(item => {
       currentId++;
       return {
        id: String(currentId),
        ...item
       };
      });
      db.image.unshift(...entriesToAdd);
      await fs.writeFile(DB_PATH, JSON.stringify(db, null, 2), 'utf-8');
      const addedCount = newItems.length;
      console.log(`[POST /api/imge] 成功添加 ${addedCount} 条数据`);
      res.status(201).json({ message: '批量添加成功', addedCount });
     } catch (error) {
      console.error('[POST /api/imge] 批量添加数据时出错:', error);
      res.status(500).json({ error: '服务器内部错误' });
     }
    });
    // =================================================================
    // ===== API 5: 删除指定ID的数据 (DELETE /api/:id) =====
    // =================================================================
    // ✅ 核心修正 #2:将 '/api/id' 修改为 '/api/:id'
    app.delete('/api/:id', async (req, res) => {
     const { id } = req.params; // 现在可以正确获取到 URL 中的 ID
     console.log(`[DELETE /api/${id}] 收到删除请求`);
     try {
      const data = await fs.readFile(DB_PATH, 'utf8');
      const db = JSON.parse(data);
      const originalLength = db.data.length;
      db.data = db.data.filter(item => item.id.toString() !== id.toString());
      if (db.data.length === originalLength) {
       console.warn(`[DELETE /api/${id}] 未找到要删除的资源`);
       return res.status(404).send({ message: '未找到要删除的资源' });
      }
      await fs.writeFile(DB_PATH, JSON.stringify(db, null, 2));
      console.log(`[DELETE /api/${id}] 成功删除了 ID 为 ${id} 的数据`);
      res.status(200).send({ message: '删除成功' });
     } catch (err) {
      console.error(`[DELETE /api/${id}] 删除操作失败:`, err);
      res.status(500).send({ message: '服务器内部错误', details: err.message });
     }
    });
    // =================================================================
    // ===== API 6: 删除指定ID的图片 (delqrcode /api/:id) =====
    // =================================================================
    app.delete('/api/images/:id', async (req, res) => {
     const { id } = req.params;
     console.log(`[DELETE /api/images/${id}] 收到删除【图片】请求`); // 日志更清晰
     try {
      const data = await fs.readFile(DB_PATH, 'utf8');
      const db = JSON.parse(data);
      const originalLength = db.image.length;
      // 注意:这里是从 db.image 中过滤
      db.image = db.image.filter(item => item.id.toString() !== id.toString());
      if (db.image.length === originalLength) {
       console.warn(`[DELETE /api/images/${id}] 未找到要删除的图片`);
       return res.status(404).send({ message: '未找到要删除的图片' });
      }
      await fs.writeFile(DB_PATH, JSON.stringify(db, null, 2));
      console.log(`[DELETE /api/images/${id}] 成功删除了 ID 为 ${id} 的图片`);
      res.status(200).send({ message: '删除成功' });
     } catch (err) {
      console.error(`[DELETE /api/images/${id}] 删除图片操作失败:`, err);
      res.status(500).send({ message: '服务器内部错误', details: err.message });
     }
    });
    // --- 启动服务器 ---
    const PORT = 3000;
    app.listen(PORT, '0.0.0.0', () => {
     console.log(`\n✅ 后端服务已成功启动`);
     console.log(` 正在监听 http://0.0.0.0:${PORT}`);
     console.log(` 数据源: ${DB_PATH}`);
     console.log("--------------------------------------------------\n");
    });
    步骤三:打开WinTerm。在/etc/www/tiktok-test/api目录下输入以下命令搭建后端API
    pm2 start server.js --name tiktok-test-api-3000
    最后:你的api接口已经搭建好了。您可以在浏览器中通过以下地址访问您的后端数据了:http://[您的服务器IP]:3000
    复制成功