init: 狐蒂云活动监控项目初始化

This commit is contained in:
ikmkj
2026-03-18 12:16:18 +08:00
commit b0afdbd9a0
20 changed files with 16147 additions and 0 deletions

53
.dockerignore Normal file
View File

@@ -0,0 +1,53 @@
# 依赖
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
yarn.lock
# 运行时
*.log
logs
*.pid
*.seed
*.pid.lock
# 测试
coverage
.nyc_output
# 环境配置
.env
.env.local
.env.*.local
# IDE
.vscode
.idea
*.swp
*.swo
*~
# Git
.git
.gitignore
.gitattributes
# 文档
README.md
DEPLOY.md
CHANGELOG.md
*.md
# Docker
Dockerfile
docker-compose.yml
.dockerignore
# 其他
.DS_Store
Thumbs.db
# 不需要复制到容器的文件
auto-buy/node_modules

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
node_modules/
myimage.tar
.env

1767
1.html Normal file

File diff suppressed because it is too large Load Diff

1888
2.html Normal file

File diff suppressed because it is too large Load Diff

2402
3.html Normal file

File diff suppressed because it is too large Load Diff

2252
4.html Normal file

File diff suppressed because it is too large Load Diff

2555
5.1.html Normal file

File diff suppressed because it is too large Load Diff

2415
5.html Normal file

File diff suppressed because it is too large Load Diff

191
DEPLOY.md Normal file
View File

@@ -0,0 +1,191 @@
# 狐蒂云活动监控 - 部署指南
## 项目结构
```
hdy/
├── public/ # 静态资源
│ ├── index.html # 监控页面
│ └── 1671896015.mp3 # 提示音频
├── auto-buy/ # 自动抢购脚本
│ ├── index.js
│ ├── config.js
│ └── package.json
├── monitor-server.js # 主服务
├── package.json
└── DEPLOY.md # 本文件
```
## 部署方式
### 方式一:直接部署(推荐)
#### 1. 准备环境
- Node.js 16+
- npm 或 yarn
#### 2. 安装依赖
```bash
# 主项目依赖
npm install
# 自动抢购脚本依赖(可选)
cd auto-buy
npm install
cd ..
```
#### 3. 启动服务
```bash
# 开发模式
npm start
# 生产模式(使用 PM2
npm install -g pm2
pm2 start monitor-server.js --name "hdy-monitor"
pm2 save
pm2 startup
```
#### 4. 访问
- 本地http://localhost:3000
- 公网http://你的服务器IP:3000
---
### 方式二Docker 部署
#### 1. 构建镜像
```bash
docker build -t hdy-monitor .
```
#### 2. 运行容器
```bash
# 前台运行
docker run -p 3000:3000 hdy-monitor
# 后台运行
docker run -d -p 3000:3000 --name hdy-monitor hdy-monitor
# 带自动重启
docker run -d -p 3000:3000 --name hdy-monitor --restart always hdy-monitor
```
#### 3. 查看日志
```bash
docker logs -f hdy-monitor
```
#### 4. 停止/删除
```bash
docker stop hdy-monitor
docker rm hdy-monitor
```
---
### 方式三Docker Compose 部署
```bash
# 启动
docker-compose up -d
# 查看日志
docker-compose logs -f
# 停止
docker-compose down
```
---
### 方式四Vercel/Netlify 部署(仅前端)
如果只需要前端页面,可以将 `public` 文件夹部署到静态托管服务。
但需要注意API 功能需要单独部署后端服务。
---
## 生产环境配置
### 1. 修改端口
编辑 `monitor-server.js`
```javascript
const PORT = process.env.PORT || 3000;
```
### 2. 配置环境变量
创建 `.env` 文件:
```env
PORT=3000
NODE_ENV=production
```
### 3. 使用 Nginx 反向代理
```nginx
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
```
### 4. 配置 HTTPSLet's Encrypt
```bash
# 安装 Certbot
sudo apt install certbot python3-certbot-nginx
# 获取证书
sudo certbot --nginx -d your-domain.com
```
---
## 常见问题
### 1. 端口被占用
```bash
# 查找占用 3000 端口的进程
lsof -i :3000
# 或修改 monitor-server.js 中的端口
```
### 2. 防火墙设置
```bash
# Ubuntu/Debian
sudo ufw allow 3000
# CentOS
sudo firewall-cmd --permanent --add-port=3000/tcp
sudo firewall-cmd --reload
```
### 3. 云服务器安全组
- 阿里云/腾讯云/AWS 等需要在控制台开放 3000 端口
---
## 更新部署
```bash
# 拉取最新代码
git pull
# 重启服务
pm2 restart hdy-monitor
# 或 Docker
docker-compose down
docker-compose up -d
```

24
Dockerfile Normal file
View File

@@ -0,0 +1,24 @@
# 使用 Node.js 官方镜像
FROM node:18-alpine
# 设置工作目录
WORKDIR /app
# 复制 package.json
COPY package*.json ./
# 安装生产环境依赖
RUN npm install --production && npm cache clean --force
# 复制项目文件
COPY . .
# 暴露端口
EXPOSE 3000
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
# 启动命令
CMD ["node", "monitor-server.js"]

196
checkActivity.js Normal file
View File

@@ -0,0 +1,196 @@
// checkActivity.js
const axios = require('axios');
const cheerio = require('cheerio');
const notifier = require('node-notifier'); // 新增桌面通知模块
const url = 'https://www.szhdy.com/activities/default.html?method=activity&id=9';
const checkInterval = 300000; // 5分钟检查一次
// 设置请求头,模拟浏览器(已包含完整浏览器特征)
const headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1'
};
async function checkActivity() {
try {
const response = await axios.get(url, { headers });
const html = response.data;
const $ = cheerio.load(html);
// 限定在活动主容器内检测,避免扫描其他区域
const $main = $('main.et-main.activities_bg');
if (!$main.length) {
console.error('活动主容器未找到,请检查页面结构');
return;
}
// 提取产品详情的工具函数
const extractAttribute = ($product, label) => {
const $container = $product.find('.form-container').filter((i, el) => {
const labelText = $(el).find('.form-title h5').text().trim();
// 同时匹配中文/英文冒号和空格差异
return labelText.replace(/|:/g, '').includes(label.replace(/|:/g, ''));
});
// 优先获取.form-text中的文本
let value = $container.find('.form-text').text().trim();
// 如果没有.form-text尝试获取下拉选项中的文本
if (!value) {
value = $container.find('.act-dropdown-option').text().trim();
}
return value || '未知';
};
const extractProductDetails = (product) => {
const $product = $(product);
const name = $product.find('.cat-styleCtitle h1, .cat-styleBtitle h1').text().trim() || '未知服务器';
const core = extractAttribute($product, '核心:');
const memory = extractAttribute($product, '内存:');
const bandwidth = extractAttribute($product, '带宽:');
const duration = extractAttribute($product, '时长:');
// 同时提取价格和单位
const priceElement = $product.find('.main-price-current');
const priceUnitElement = $product.find('.price-current-unit');
const price = priceElement.text().trim() +
(priceUnitElement.length ? priceUnitElement.text().trim() : '');
return { name, core, memory, bandwidth, duration, price };
};
// 格式化产品列表(精确空行格式)
const formatProducts = (zoneTitle, products) => {
if (products.length === 0) return '';
const productLines = products.map(product => {
if ($(product).hasClass('cat-styleB-promotion-card') ||
$(product).hasClass('cat-styleC-promotion-card')) {
const details = extractProductDetails(product);
return `${details.name}\n${details.core} + ${details.memory}\n${details.bandwidth}\n${details.duration} + ${details.price}`;
}
return '未知产品';
}).join('\n\n'); // 产品之间空一行
// 严格按用户要求:
// - 专区标题前空两行
// - 专区标题后空一行
// - 产品列表前空一行
return `\n\n- [${zoneTitle}] 可购买产品:\n\n${productLines}`;
};
// 动态识别专区类型解决ID变化问题
const activityZones = $main.find('.activityContent_bg');
let valueZoneProducts = [];
let qualityZoneProducts = [];
let valueZoneSoldOut = [];
let qualityZoneSoldOut = [];
const debugInfo = [];
activityZones.each((i, zone) => {
const $zone = $(zone);
const title = $zone.find('.currency-title-a').text().trim();
// 支持两种产品结构:旧版(activity-row)和新版(cat-styleB/C-promotion-card)
const oldProducts = $zone.find('.activity-row a.list-product-content');
const newProducts = $zone.find('.cat-styleB-promotion-card, .cat-styleC-promotion-card');
// 处理旧版产品结构
const oldValidProducts = oldProducts.filter((i, el) => {
const href = $(el).attr('href');
const isNotAffiliate = !href.includes('fid=9');
const isNotSoldOut = !$(el).find('.list-product-desc').text().includes('已售');
return isNotAffiliate && isNotSoldOut;
});
const oldSoldOutProducts = oldProducts.filter((i, el) => {
const href = $(el).attr('href');
const isNotAffiliate = !href.includes('fid=9');
const isSoldOut = $(el).find('.list-product-desc').text().includes('已售');
return isNotAffiliate && isSoldOut;
});
// 处理新版产品结构
const newValidProducts = newProducts.filter((i, el) => {
return !$(el).find('.form-footer-butt').hasClass('disableButton');
});
const newSoldOutProducts = newProducts.filter((i, el) => {
return $(el).find('.form-footer-butt').hasClass('disableButton');
});
// 合并新旧产品
const validProducts = [...oldValidProducts, ...newValidProducts];
const soldOutProducts = [...oldSoldOutProducts, ...newSoldOutProducts];
debugInfo.push({
title,
total: oldProducts.length + newProducts.length,
valid: validProducts.length,
soldOut: soldOutProducts.length
});
if (title.includes('性价比专区')) {
valueZoneProducts = validProducts;
valueZoneSoldOut = soldOutProducts;
}
if (title.includes('质量型专区')) {
qualityZoneProducts = validProducts;
qualityZoneSoldOut = soldOutProducts;
}
});
// 输出调试信息
console.log(`\n检测到${activityZones.length}个活动专区:`);
debugInfo.forEach(info => {
console.log(`- [${info.title}] 总产品: ${info.total}, 可购买: ${info.valid}, 已售罄: ${info.soldOut}`);
});
// 状态判断逻辑
if (valueZoneProducts.length > 0 || qualityZoneProducts.length > 0) {
let productInfo = '';
// 生成性价比专区产品列表(严格按空行要求)
if (valueZoneProducts.length > 0) {
productInfo += formatProducts('性价比专区!预算党优选云服务器!', valueZoneProducts);
}
// 生成质量型专区产品列表(两个专区之间空两行)
if (qualityZoneProducts.length > 0) {
if (productInfo) productInfo += '\n\n';
productInfo += formatProducts('质量型专区!需求稳定用户优选!', qualityZoneProducts);
}
console.log(productInfo);
notifier.notify({
title: '活动产品上架提醒',
message: `监控的活动产品已经上架!${productInfo.replace(/\\n/g, '\n')}`,
sound: true,
wait: true
});
process.exit(0);
} else if (valueZoneSoldOut.length > 0 || qualityZoneSoldOut.length > 0) {
console.log('商品已售罄');
} else {
console.log(`[${new Date().toLocaleString()}] 暂未上架,继续监控...`);
}
} catch (error) {
console.error(`请求失败: ${error.message}`);
}
}
// 立即执行一次
checkActivity();
// 定时执行
setInterval(checkActivity, checkInterval);

21
docker-compose.yml Normal file
View File

@@ -0,0 +1,21 @@
version: '3.8'
services:
hdy-monitor:
build:
context: .
dockerfile: Dockerfile
image: hdy-monitor:latest
container_name: hdy-monitor
ports:
- "${PORT:-3000}:3000"
restart: unless-stopped
environment:
- NODE_ENV=production
- PORT=3000
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s

95
monitor-server.js Normal file
View File

@@ -0,0 +1,95 @@
const express = require('express');
const cors = require('cors');
const path = require('path');
const app = express();
const PORT = 3000;
app.use(cors());
app.use(express.static(path.join(__dirname, 'public')));
const ACTIVITY_URL = 'https://www.szhdy.com/activities/default.html?method=activity&id=9';
const REQUEST_HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
};
// 存储 Cookie
let cachedCookies = '';
let cookieExpiry = 0;
// 从 set-cookie 响应头提取 Cookie
function extractCookies(response) {
const setCookieHeaders = response.headers.getSetCookie?.() || [];
const cookies = setCookieHeaders.map(c => c.split(';')[0]).join('; ');
return cookies;
}
// 获取有效 Cookie过期或为空时重新获取
async function getValidCookies() {
if (cachedCookies && Date.now() < cookieExpiry) {
return cachedCookies;
}
console.log('[Cookie] 正在获取新的 Cookie...');
const response = await fetch(ACTIVITY_URL, { headers: REQUEST_HEADERS, redirect: 'manual' });
cachedCookies = extractCookies(response);
// Cookie 有效期设为 1 小时(服务器 max-age=86400保守取 1 小时)
cookieExpiry = Date.now() + 3600 * 1000;
console.log('[Cookie] 获取成功:', cachedCookies);
return cachedCookies;
}
app.get('/api/activity', async (req, res) => {
try {
const cookies = await getValidCookies();
const response = await fetch(ACTIVITY_URL, {
headers: {
...REQUEST_HEADERS,
'Cookie': cookies,
}
});
const html = await response.text();
// 如果返回内容过短,可能 Cookie 失效,清除缓存并重试一次
if (html.length < 1000) {
console.log('[Cookie] 响应内容过短,尝试刷新 Cookie 重试...');
cachedCookies = '';
cookieExpiry = 0;
const newCookies = await getValidCookies();
const retryResponse = await fetch(ACTIVITY_URL, {
headers: {
...REQUEST_HEADERS,
'Cookie': newCookies,
}
});
const retryHtml = await retryResponse.text();
// 更新重试后的 Cookie
const retryCookies = extractCookies(retryResponse);
if (retryCookies) {
cachedCookies = retryCookies;
cookieExpiry = Date.now() + 3600 * 1000;
}
return res.json({ success: true, html: retryHtml });
}
// 更新响应中的新 Cookie
const newCookies = extractCookies(response);
if (newCookies) {
cachedCookies = newCookies;
cookieExpiry = Date.now() + 3600 * 1000;
}
res.json({ success: true, html });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
app.listen(PORT, () => {
console.log(`活动监控服务已启动: http://localhost:${PORT}`);
});

866
package-lock.json generated Normal file
View File

@@ -0,0 +1,866 @@
{
"name": "hdy-activity-monitor",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "hdy-activity-monitor",
"version": "1.0.0",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.2"
}
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"license": "MIT",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT"
},
"node_modules/body-parser": {
"version": "1.20.4",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
"integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
"license": "MIT",
"dependencies": {
"bytes": "~3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "~1.2.0",
"http-errors": "~2.0.1",
"iconv-lite": "~0.4.24",
"on-finished": "~2.4.1",
"qs": "~6.14.0",
"raw-body": "~2.5.3",
"type-is": "~1.6.18",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/body-parser/node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/call-bound": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"get-intrinsic": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
"integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
"license": "MIT"
},
"node_modules/cors": {
"version": "2.8.6",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
"integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
"license": "MIT",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"license": "MIT",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
"license": "MIT"
},
"node_modules/encodeurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"license": "MIT"
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "~1.20.3",
"content-disposition": "~0.5.4",
"content-type": "~1.0.4",
"cookie": "~0.7.1",
"cookie-signature": "~1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.3.1",
"fresh": "~0.5.2",
"http-errors": "~2.0.0",
"merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "~2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "~0.1.12",
"proxy-addr": "~2.0.7",
"qs": "~6.14.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "~0.19.0",
"serve-static": "~1.16.2",
"setprototypeof": "1.2.0",
"statuses": "~2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/finalhandler": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
"integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"on-finished": "~2.4.1",
"parseurl": "~1.3.3",
"statuses": "~2.0.2",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/http-errors": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"license": "MIT",
"dependencies": {
"depd": "~2.0.0",
"inherits": "~2.0.4",
"setprototypeof": "~1.2.0",
"statuses": "~2.0.2",
"toidentifier": "~1.0.1"
},
"engines": {
"node": ">= 0.8"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"license": "MIT",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-inspect": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"license": "MIT",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-to-regexp": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
"license": "MIT"
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"license": "MIT",
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
"integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
"license": "MIT",
"dependencies": {
"bytes": "~3.1.2",
"http-errors": "~2.0.1",
"iconv-lite": "~0.4.24",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/raw-body/node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
"node_modules/send": {
"version": "0.19.2",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
"integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "~0.5.2",
"http-errors": "~2.0.1",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "~2.4.1",
"range-parser": "~1.2.1",
"statuses": "~2.0.2"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/serve-static": {
"version": "1.16.3",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
"integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
"license": "MIT",
"dependencies": {
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "~0.19.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"license": "ISC"
},
"node_modules/side-channel": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3",
"side-channel-list": "^1.0.0",
"side-channel-map": "^1.0.1",
"side-channel-weakmap": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-list": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-map": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-weakmap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3",
"side-channel-map": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/statuses": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"license": "MIT",
"engines": {
"node": ">=0.6"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"license": "MIT",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
}
}
}

13
package.json Normal file
View File

@@ -0,0 +1,13 @@
{
"name": "hdy-activity-monitor",
"version": "1.0.0",
"description": "狐蒂云活动监控页面",
"main": "monitor-server.js",
"scripts": {
"start": "node monitor-server.js"
},
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5"
}
}

BIN
public/1671896015.mp3 Normal file

Binary file not shown.

1055
public/index.html Normal file

File diff suppressed because it is too large Load Diff

33
run_checkActivity.bat Normal file
View File

@@ -0,0 +1,33 @@
@echo off
chcp 65001 >nul
title Activity Monitoring Script
echo Starting activity monitoring script...
echo.
REM Switch to the script directory
cd /d "%~dp0"
REM Check if Node.js is installed
node --version >nul 2>&1
if %errorlevel% neq 0 (
echo Error: Node.js not found. Please ensure Node.js is installed and added to PATH.
pause
exit /b 1
)
REM Check if dependencies are installed
if not exist "node_modules" (
echo Dependencies not found, installing...
npm install
if %errorlevel% neq 0 (
echo Failed to install dependencies
pause
exit /b 1
)
)
REM Run the Node.js script
echo Starting activity monitoring...
node checkActivity.js
pause

314
市场的页面/index.html Normal file
View File

@@ -0,0 +1,314 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>日本云服务器列表</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: #f5f5f5;
padding: 10px;
}
.refresh-btn {
display: block;
margin: 0 auto 15px;
padding: 8px 20px;
background: #1a73e8;
color: #fff;
border: none;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
}
.refresh-btn:hover {
background: #1557b0;
}
.container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 10px;
max-width: 1200px;
margin: 0 auto;
}
.server-card {
background-color: #fff;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 14px;
width: 320px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.server-title {
font-size: 15px;
font-weight: bold;
color: #1a73e8;
}
.arrow-icon {
color: #1a73e8;
font-size: 14px;
}
.buy-link {
color: #1a73e8;
font-size: 13px;
text-decoration: none;
display: flex;
align-items: center;
gap: 2px;
}
.buy-link:hover {
text-decoration: underline;
}
.badges {
display: flex;
gap: 6px;
margin-bottom: 8px;
align-items: flex-start;
flex-wrap: wrap;
min-height: 66px;
}
.description-text {
color: #666;
font-size: 12px;
line-height: 1.4;
width: 100%;
margin-top: 4px;
}
.badge {
padding: 2px 8px;
border-radius: 3px;
font-size: 11px;
border: 1px solid;
}
.badge-orange {
color: #ff9800;
border-color: #ff9800;
background-color: #fff8e1;
}
.badge-green {
color: #4caf50;
border-color: #4caf50;
background-color: #e8f5e9;
}
.badge-blue {
color: #2196f3;
border-color: #2196f3;
background-color: #e3f2fd;
}
.info-grid {
display: flex;
flex-direction: column;
gap: 4px;
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
}
.info-item {
display: flex;
align-items: center;
font-size: 12px;
}
.info-label {
color: #666;
margin-right: 6px;
}
.info-value {
color: #333;
font-weight: 500;
}
.date-row {
display: flex;
align-items: center;
padding: 8px 0;
color: #666;
font-size: 11px;
border-bottom: 1px solid #f0f0f0;
}
.date-row .icon {
margin-left: 10px;
display: flex;
align-items: center;
gap: 8px;
}
.progress-section {
padding: 8px 0;
}
.progress-bar {
height: 14px;
background: linear-gradient(to right, #1a73e8 0%, #4285f4 100%);
border-radius: 7px;
position: relative;
overflow: hidden;
}
.progress-text {
position: absolute;
width: 100%;
text-align: center;
line-height: 14px;
color: #fff;
font-size: 10px;
}
.price-section {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid #f0f0f0;
}
.current-price {
color: #f44336;
font-size: 18px;
font-weight: bold;
}
.current-price .unit {
font-size: 12px;
font-weight: normal;
}
.original-price {
color: #999;
font-size: 12px;
text-decoration: line-through;
}
.server-card.highlight {
border: 2px solid #ff5722;
position: relative;
box-shadow: 0 4px 12px rgba(255, 87, 34, 0.3);
}
.highlight-badge {
position: absolute;
top: -1px;
right: -1px;
background: linear-gradient(135deg, #ff5722 0%, #f44336 100%);
color: #fff;
font-size: 11px;
font-weight: bold;
padding: 4px 10px;
border-bottom-left-radius: 10px;
}
</style>
</head>
<body>
<button class="refresh-btn" onclick="fetchAndRenderServers()">🔄 刷新</button>
<div class="container" id="server-list"></div>
<script>
const cycleNameMap = {
'hour': '小时',
'day': '天',
'monthly': '月',
'quarterly': '季度',
'semiannually': '半年',
'annually': '年',
'biennially': '两年',
'triennially': '三年',
'fourly': '四年',
'fively': '五年',
'sixly': '六年',
'sevenly': '七年',
'eightly': '八年',
'ninely': '九年',
'tenly': '十年',
'onetime': '一次性'
};
const apiUrl = 'https://py.szhdy.com/api/public/market-products?page=1&page_size=12&order_by=latest&_t=' + new Date().getTime();
function fetchAndRenderServers() {
fetch(apiUrl)
.then(response => response.json())
.then(data => {
const servers = data.data.list.filter(server =>
server.product_name.includes('日本') ||
(server.billing_cycle.includes('annually') && server.price < 100) ||
(server.billing_cycle === 'biennially' && server.price < 121)
);
renderServers(servers);
})
.catch(error => console.error('Error fetching data:', error));
}
function renderServers(servers) {
const container = document.getElementById('server-list');
container.innerHTML = '';
servers.forEach(server => {
const card = document.createElement('div');
const isHighlight = server.product_name.includes('日本') && String(server.total_cost).includes('120');
card.className = 'server-card' + (isHighlight ? ' highlight' : '');
const cycleLabel = cycleNameMap[server.billing_cycle] || server.billing_cycle;
card.innerHTML = `
${isHighlight ? '<div class="highlight-badge">推荐</div>' : ''}
<div class="card-header">
<span class="server-title">${server.product_name}</span>
<a href="https://py.szhdy.com/buy/${server.id}.html" class="buy-link" target="_blank">购买 </a>
</div>
<div class="badges">
<span class="badge badge-orange">${cycleLabel}</span>
<span class="badge badge-green">剩余${server.remaining_days}天</span>
${server.description ? `<span class="description-text">${server.description}</span>` : '<span class="description-text">&nbsp;</span>'}
</div>
<div class="info-grid">
<div class="info-item">
<span class="info-label">实例配置</span>
<span class="info-value">${server.cpu_cores} ${server.memory_size}</span>
</div>
<div class="info-item">
<span class="info-label">存储</span>
<span class="info-value">${server.disk_size}</span>
</div>
<div class="info-item">
<span class="info-label">网络</span>
<span class="info-value">${server.bandwidth} ${server.ip_count}IP</span>
</div>
<div class="info-item">
<span class="info-label">月流量</span>
<span class="info-value">${server.bwlimit_label}</span>
</div>
<div class="info-item">
<span class="info-label">Windows</span>
<span class="info-value">${server.windows_support ? '支持' : '不支持'}</span>
</div>
</div>
<div class="date-row">
<span>${server.purchase_time} ~ ${server.expire_time}</span>
<span class="icon">
<span>👁 ${server.views}</span>
<span>🤍 ${server.want}</span>
</span>
</div>
<div class="progress-section">
<div class="progress-bar">
<span class="progress-text">${server.total_days}天/${server.remaining_days}天 剩余${server.remaining_percent}%</span>
</div>
</div>
<div class="price-section">
<span class="current-price">¥${server.price} <span class="unit">元</span></span>
<span class="original-price">¥${server.original_price}</span>
</div>
`;
container.appendChild(card);
});
}
// Initial fetch and render
fetchAndRenderServers();
// Fetch and render every 3 minutes
setInterval(fetchAndRenderServers, 3 * 60 * 1000);
</script>
</body>
</html>

4
活动对应的url.md Normal file
View File

@@ -0,0 +1,4 @@
开工大吉!福利优惠!用云无忧!
https://www.szhdy.com/activities/default.html?method=activity&id=9
获取到的页面有1234——>html