修改小程序
This commit is contained in:
132
docker-compose.yml
Normal file
132
docker-compose.yml
Normal file
@@ -0,0 +1,132 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# MySQL数据库服务
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
container_name: nxxmdata-mysql
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: aiotAiot123!
|
||||
MYSQL_DATABASE: ningxia_zhengfu
|
||||
MYSQL_USER: nxxmdata_user
|
||||
MYSQL_PASSWORD: NxxmData2024!@#
|
||||
volumes:
|
||||
- mysql_data:/var/lib/mysql
|
||||
- ./government-backend/database/init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||
ports:
|
||||
- "3306:3306"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- nxxmdata-network
|
||||
|
||||
# Redis缓存服务
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: nxxmdata-redis
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- nxxmdata-network
|
||||
|
||||
# Government Backend服务
|
||||
government-backend:
|
||||
build: ./government-backend
|
||||
container_name: nxxmdata-government-backend
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 5352
|
||||
DB_DIALECT: mysql
|
||||
DB_HOST: mysql
|
||||
DB_PORT: 3306
|
||||
DB_DATABASE: ningxia_zhengfu
|
||||
DB_USER: root
|
||||
DB_PASSWORD: aiotAiot123!
|
||||
JWT_SECRET: government_super_secret_jwt_key_2024_very_long_and_secure
|
||||
LOG_LEVEL: info
|
||||
CORS_ORIGIN: "*"
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: 6379
|
||||
ports:
|
||||
- "5352:5352"
|
||||
depends_on:
|
||||
- mysql
|
||||
- redis
|
||||
volumes:
|
||||
- uploads_data:/app/uploads
|
||||
- logs_data:/app/logs
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- nxxmdata-network
|
||||
|
||||
# 养殖端后端服务
|
||||
backend:
|
||||
build: ./backend
|
||||
container_name: nxxmdata-backend
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 5350
|
||||
DB_HOST: mysql
|
||||
REDIS_HOST: redis
|
||||
ports:
|
||||
- "5350:5350"
|
||||
depends_on:
|
||||
- mysql
|
||||
- redis
|
||||
volumes:
|
||||
- uploads_data:/app/uploads
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- nxxmdata-network
|
||||
|
||||
# 管理后台前端
|
||||
admin:
|
||||
build: ./admin-system
|
||||
container_name: nxxmdata-admin
|
||||
ports:
|
||||
- "5300:80"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- nxxmdata-network
|
||||
|
||||
# 官网前端
|
||||
website:
|
||||
build: ./website
|
||||
container_name: nxxmdata-website
|
||||
ports:
|
||||
- "8081:80"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- nxxmdata-network
|
||||
|
||||
# Nginx反向代理
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: nxxmdata-nginx
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
|
||||
- ./nginx/ssl:/etc/ssl
|
||||
- uploads_data:/var/www/uploads
|
||||
depends_on:
|
||||
- government-backend
|
||||
- backend
|
||||
- admin
|
||||
- website
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- nxxmdata-network
|
||||
|
||||
volumes:
|
||||
mysql_data:
|
||||
redis_data:
|
||||
uploads_data:
|
||||
logs_data:
|
||||
|
||||
networks:
|
||||
nxxmdata-network:
|
||||
driver: bridge
|
||||
4
government-admin/.env.development
Normal file
4
government-admin/.env.development
Normal file
@@ -0,0 +1,4 @@
|
||||
# 开发环境配置
|
||||
VITE_API_BASE_URL=http://localhost:5352/api
|
||||
VITE_APP_TITLE=政府管理系统
|
||||
VITE_APP_ENV=development
|
||||
4
government-admin/.env.production
Normal file
4
government-admin/.env.production
Normal file
@@ -0,0 +1,4 @@
|
||||
# 生产环境配置
|
||||
VITE_API_BASE_URL=https://ad.ningmuyun.com/api/government
|
||||
VITE_APP_TITLE=政府管理系统
|
||||
VITE_APP_ENV=production
|
||||
25
government-admin/package-lock.json
generated
25
government-admin/package-lock.json
generated
@@ -10,7 +10,6 @@
|
||||
"dependencies": {
|
||||
"@ant-design/icons-vue": "^6.1.0",
|
||||
"ant-design-vue": "^4.0.0",
|
||||
"axios": "^1.12.2",
|
||||
"dayjs": "^1.11.18",
|
||||
"echarts": "^5.4.2",
|
||||
"pinia": "^2.1.6",
|
||||
@@ -20,6 +19,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"axios": "^1.12.2",
|
||||
"eslint": "^8.45.0",
|
||||
"eslint-plugin-vue": "^9.15.1",
|
||||
"sass": "^1.93.0",
|
||||
@@ -1238,12 +1238,14 @@
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.12.2.tgz",
|
||||
"integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
@@ -1294,6 +1296,7 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
@@ -1370,6 +1373,7 @@
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
@@ -1471,6 +1475,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
@@ -1519,6 +1524,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
@@ -1555,6 +1561,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -1564,6 +1571,7 @@
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -1573,6 +1581,7 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
@@ -1585,6 +1594,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
@@ -1928,6 +1938,7 @@
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@@ -1948,6 +1959,7 @@
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.4.tgz",
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
@@ -1986,6 +1998,7 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@@ -1995,6 +2008,7 @@
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
@@ -2019,6 +2033,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
@@ -2083,6 +2098,7 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -2112,6 +2128,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -2124,6 +2141,7 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
@@ -2139,6 +2157,7 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
@@ -2394,6 +2413,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -2418,6 +2438,7 @@
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
@@ -2427,6 +2448,7 @@
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
@@ -2708,6 +2730,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { message } from 'ant-design-vue'
|
||||
|
||||
// 创建axios实例
|
||||
const instance = axios.create({
|
||||
baseURL: 'http://localhost:5352/api',
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:5352/api',
|
||||
timeout: 10000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import axios from 'axios'
|
||||
|
||||
const API_BASE_URL = 'http://localhost:5352/api/approval-process'
|
||||
const API_BASE_URL = `${import.meta.env.VITE_API_BASE_URL || 'http://localhost:5352/api'}/approval-process`
|
||||
|
||||
// 创建axios实例
|
||||
const api = axios.create({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import axios from 'axios'
|
||||
|
||||
const API_BASE_URL = 'http://localhost:5352/api/cattle-academy'
|
||||
const API_BASE_URL = `${import.meta.env.VITE_API_BASE_URL || 'http://localhost:5352/api'}/cattle-academy`
|
||||
|
||||
// 创建axios实例
|
||||
const api = axios.create({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import axios from 'axios';
|
||||
import axios from 'axios'
|
||||
|
||||
const API_BASE_URL = 'http://localhost:5352/api/device-warning';
|
||||
const API_BASE_URL = `${import.meta.env.VITE_API_BASE_URL || 'http://localhost:5352/api'}/device-warning`;
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import axios from 'axios'
|
||||
|
||||
const API_BASE_URL = 'http://localhost:5352/api/epidemic-activity'
|
||||
const API_BASE_URL = `${import.meta.env.VITE_API_BASE_URL || 'http://localhost:5352/api'}/epidemic-activity`
|
||||
|
||||
// 创建axios实例
|
||||
const api = axios.create({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import axios from 'axios'
|
||||
|
||||
const API_BASE_URL = 'http://localhost:5352/api/production-material-certification'
|
||||
const API_BASE_URL = `${import.meta.env.VITE_API_BASE_URL || 'http://localhost:5352/api'}/production-material-certification`
|
||||
|
||||
// 创建axios实例
|
||||
const api = axios.create({
|
||||
|
||||
135
government-admin/部署修复说明.md
Normal file
135
government-admin/部署修复说明.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# 政府管理后台部署修复说明
|
||||
|
||||
## 问题描述
|
||||
|
||||
部署后前端仍然使用 `http://localhost:5352` 访问API,导致无法正常访问后端服务。
|
||||
|
||||
## 问题原因
|
||||
|
||||
前端代码中硬编码了本地开发环境的API地址,没有根据部署环境动态配置。
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 1. 环境变量配置
|
||||
|
||||
已创建环境配置文件:
|
||||
|
||||
**开发环境** (`.env.development`):
|
||||
```env
|
||||
VITE_API_BASE_URL=http://localhost:5352/api
|
||||
VITE_APP_TITLE=政府管理系统
|
||||
VITE_APP_ENV=development
|
||||
```
|
||||
|
||||
**生产环境** (`.env.production`):
|
||||
```env
|
||||
VITE_API_BASE_URL=https://ad.ningmuyun.com/api/government
|
||||
VITE_APP_TITLE=政府管理系统
|
||||
VITE_APP_ENV=production
|
||||
```
|
||||
|
||||
### 2. API配置文件修改
|
||||
|
||||
已修改以下文件使用环境变量:
|
||||
|
||||
- `src/utils/api.js` - 主要API配置
|
||||
- `src/utils/productionMaterialCertificationApi.js` - 生产物料认证API
|
||||
- `src/utils/deviceWarningApi.js` - 设备预警API
|
||||
- `src/utils/cattleAcademyApi.js` - 牛只学院API
|
||||
- `src/utils/epidemicActivityApi.js` - 疫情活动API
|
||||
- `src/utils/approvalProcessApi.js` - 审批流程API
|
||||
|
||||
### 3. 部署步骤
|
||||
|
||||
#### 3.1 重新构建前端
|
||||
|
||||
```bash
|
||||
cd government-admin
|
||||
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 生产环境构建
|
||||
npm run build
|
||||
```
|
||||
|
||||
#### 3.2 部署到服务器
|
||||
|
||||
将构建后的 `dist` 目录上传到服务器的 `/var/www/html/government/` 目录。
|
||||
|
||||
#### 3.3 验证Nginx配置
|
||||
|
||||
确保服务器上的Nginx配置正确:
|
||||
|
||||
```nginx
|
||||
# 政府端前端静态文件
|
||||
location /government/ {
|
||||
alias /var/www/html/government/;
|
||||
try_files $uri $uri/ /government/index.html;
|
||||
index index.html;
|
||||
}
|
||||
|
||||
# 政府端API代理
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# CORS配置
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
|
||||
add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.4 重启服务
|
||||
|
||||
```bash
|
||||
# 重新加载Nginx配置
|
||||
sudo nginx -t
|
||||
sudo nginx -s reload
|
||||
|
||||
# 确保government-backend服务运行
|
||||
pm2 restart government-backend
|
||||
```
|
||||
|
||||
### 4. 验证修复
|
||||
|
||||
访问以下地址验证修复结果:
|
||||
|
||||
- 前端页面: `https://ad.ningmuyun.com/government/`
|
||||
- API接口: `https://ad.ningmuyun.com/api/government/departments`
|
||||
|
||||
### 5. 注意事项
|
||||
|
||||
1. **环境变量**: 确保在生产环境构建时使用正确的环境变量
|
||||
2. **缓存清理**: 部署后清理浏览器缓存
|
||||
3. **HTTPS证书**: 确保SSL证书配置正确
|
||||
4. **防火墙**: 确保端口443和80开放
|
||||
5. **服务状态**: 确保government-backend服务正常运行
|
||||
|
||||
### 6. 故障排查
|
||||
|
||||
如果仍有问题,请检查:
|
||||
|
||||
1. **浏览器开发者工具**: 查看网络请求是否使用正确的URL
|
||||
2. **Nginx日志**: 检查 `/var/log/nginx/error.log`
|
||||
3. **后端日志**: 检查government-backend服务日志
|
||||
4. **端口监听**: 确认5352端口正常监听
|
||||
|
||||
```bash
|
||||
# 检查端口监听
|
||||
netstat -tlnp | grep 5352
|
||||
|
||||
# 检查服务状态
|
||||
pm2 status government-backend
|
||||
|
||||
# 测试API连通性
|
||||
curl -k https://ad.ningmuyun.com/api/government/departments
|
||||
```
|
||||
50
government-backend/Dockerfile
Normal file
50
government-backend/Dockerfile
Normal file
@@ -0,0 +1,50 @@
|
||||
# Government Backend Dockerfile
|
||||
# 基于Node.js 18 Alpine镜像构建
|
||||
FROM node:18-alpine
|
||||
|
||||
# 设置工作目录
|
||||
WORKDIR /app
|
||||
|
||||
# 安装系统依赖
|
||||
RUN apk add --no-cache \
|
||||
# 用于数据库备份的MySQL客户端
|
||||
mysql-client \
|
||||
# 开发工具
|
||||
git \
|
||||
# 用于健康检查的curl
|
||||
curl \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# 复制package.json和package-lock.json
|
||||
COPY package*.json ./
|
||||
|
||||
# 安装Node.js依赖
|
||||
RUN npm ci --only=production && npm cache clean --force
|
||||
|
||||
# 复制应用代码
|
||||
COPY . .
|
||||
|
||||
# 创建必要的目录
|
||||
RUN mkdir -p logs uploads temp
|
||||
|
||||
# 设置文件权限
|
||||
RUN chown -R node:node /app
|
||||
|
||||
# 切换到非root用户
|
||||
USER node
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 5352
|
||||
|
||||
# 健康检查
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:5352/api/health || exit 1
|
||||
|
||||
# 启动应用
|
||||
CMD ["npm", "start"]
|
||||
|
||||
# 元数据标签
|
||||
LABEL maintainer="宁夏智慧牧场政府端 <support@nxxm.com>" \
|
||||
description="宁夏智慧牧场政府端后端服务" \
|
||||
application="nxxm-government-platform" \
|
||||
tier="backend"
|
||||
133
government-backend/NGINX_404_FIX_FINAL.md
Normal file
133
government-backend/NGINX_404_FIX_FINAL.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# Nginx 404 错误最终修复方案
|
||||
|
||||
## 🔍 问题现状
|
||||
|
||||
根据测试结果:
|
||||
- ✅ 后端服务正常:`http://localhost:5352/api/government/departments` 返回 200
|
||||
- ✅ 登录接口正常:`https://ad.ningmuyun.com/api/government/auth/login` 返回 401
|
||||
- ❌ 正确路径 404:`https://ad.ningmuyun.com/api/government/departments` 返回 404
|
||||
- ❌ 正确路径 404:`https://ad.ningmuyun.com/api/government/data-center` 返回 404
|
||||
- ❌ 正确路径 404:`https://ad.ningmuyun.com/api/government/market-price` 返回 404
|
||||
- ✅ 重复路径正常:`https://ad.ningmuyun.com/api/government/government/departments` 返回 200
|
||||
|
||||
## 📊 问题分析
|
||||
|
||||
### 关键发现
|
||||
1. 登录接口 `/api/government/auth/login` 正常工作
|
||||
2. 其他政府接口 `/api/government/departments` 等返回 404
|
||||
3. 重复路径 `/api/government/government/departments` 却能正常工作
|
||||
|
||||
### 推断
|
||||
这说明 nginx 配置中:
|
||||
- `location ^~ /api/government/auth/` 规则工作正常
|
||||
- `location ^~ /api/government/` 规则没有生效
|
||||
- 重复路径能工作,说明可能有其他规则在处理请求
|
||||
|
||||
## 🔧 当前 Nginx 配置
|
||||
|
||||
```nginx
|
||||
# 政府认证API代理 - 处理登录等认证接口
|
||||
location ^~ /api/government/auth/ {
|
||||
proxy_pass http://localhost:5352/api/auth/;
|
||||
# ... CORS 配置
|
||||
}
|
||||
|
||||
# 政府API代理 - 处理其他政府相关接口
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
# ... CORS 配置
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 可能的原因和解决方案
|
||||
|
||||
### 方案 1:Nginx 配置未重新加载
|
||||
最可能的原因是 nginx 配置修改后没有重新加载。
|
||||
|
||||
**解决步骤:**
|
||||
```bash
|
||||
# 1. 检查 nginx 配置语法
|
||||
sudo nginx -t
|
||||
|
||||
# 2. 重新加载 nginx 配置
|
||||
sudo systemctl reload nginx
|
||||
|
||||
# 3. 如果 reload 不行,尝试重启
|
||||
sudo systemctl restart nginx
|
||||
|
||||
# 4. 检查 nginx 状态
|
||||
sudo systemctl status nginx
|
||||
|
||||
# 5. 查看 nginx 错误日志
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
### 方案 2:浏览器缓存问题
|
||||
**解决步骤:**
|
||||
1. 清除浏览器缓存
|
||||
2. 使用无痕模式测试
|
||||
3. 或使用 curl 命令测试(绕过浏览器)
|
||||
|
||||
### 方案 3:Location 规则匹配优先级问题
|
||||
如果还不行,可能需要调整 location 规则的顺序或写法。
|
||||
|
||||
**尝试修改为:**
|
||||
```nginx
|
||||
# 方案 A:使用正则表达式匹配
|
||||
location ~ ^/api/government/(?!auth/) {
|
||||
proxy_pass http://localhost:5352;
|
||||
# ... 其他配置
|
||||
}
|
||||
|
||||
# 方案 B:去掉尾部斜杠
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352;
|
||||
rewrite ^/api/government/(.*)$ /api/government/$1 break;
|
||||
# ... 其他配置
|
||||
}
|
||||
```
|
||||
|
||||
## 📝 验证步骤
|
||||
|
||||
修复后运行以下命令验证:
|
||||
```bash
|
||||
node test-final-fix.js
|
||||
```
|
||||
|
||||
或使用 curl 测试:
|
||||
```bash
|
||||
# 测试部门接口
|
||||
curl -X GET https://ad.ningmuyun.com/api/government/departments
|
||||
|
||||
# 测试数据中心接口
|
||||
curl -X GET https://ad.ningmuyun.com/api/government/data-center
|
||||
|
||||
# 测试市场价格接口
|
||||
curl -X GET "https://ad.ningmuyun.com/api/government/market-price?type=beef"
|
||||
```
|
||||
|
||||
## 🚨 重要提醒
|
||||
|
||||
1. **必须重启 nginx**:修改配置文件后,必须重启或重新加载 nginx 服务
|
||||
2. **清除缓存**:测试时要清除浏览器缓存或使用无痕模式
|
||||
3. **检查日志**:如果问题持续,查看 nginx 错误日志以获取更多信息
|
||||
|
||||
## 📞 下一步行动
|
||||
|
||||
请在服务器上执行以下命令:
|
||||
```bash
|
||||
# 1. 检查配置语法
|
||||
sudo nginx -t
|
||||
|
||||
# 2. 重新加载配置
|
||||
sudo systemctl reload nginx
|
||||
|
||||
# 3. 验证修复效果
|
||||
curl -X GET https://ad.ningmuyun.com/api/government/departments
|
||||
```
|
||||
|
||||
如果执行后问题仍然存在,请提供:
|
||||
1. nginx -t 的输出结果
|
||||
2. nginx error.log 的最新内容
|
||||
3. curl 命令的完整输出
|
||||
|
||||
792
government-backend/_etc_nginx_conf.d_ningmuyun_one.conf
Normal file
792
government-backend/_etc_nginx_conf.d_ningmuyun_one.conf
Normal file
@@ -0,0 +1,792 @@
|
||||
server {
|
||||
if ($host = datav.ningmuyun.com) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
||||
|
||||
if ($host = wapi.ningmuyun.com) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
||||
|
||||
if ($host = ad.ningmuyun.com) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
||||
|
||||
if ($host = www.ningmuyun.com) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
||||
|
||||
listen 80;
|
||||
server_name www.ningmuyun.com ad.ningmuyun.com datav.ningmuyun.com wapi.ningmuyun.com;
|
||||
return 301 https://$host$request_uri;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
# HTTPS服务器配置
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name www.ningmuyun.com;
|
||||
root /data/website/ningmuyun/;
|
||||
# SSL 证书和私钥文件的路径
|
||||
|
||||
ssl_session_timeout 5m;
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
|
||||
index index.html;
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html; # 关键修复
|
||||
add_header Cache-Control "no-cache, no-store";
|
||||
expires 0;
|
||||
}
|
||||
# 日志配置
|
||||
access_log /var/log/nginx/www.ningmuyun.com.access.log;
|
||||
error_log /var/log/nginx/www.ningmuyun.com.error.log;
|
||||
|
||||
# 百度地图API代理
|
||||
location /map-api/ {
|
||||
proxy_pass https://api.map.baidu.com/;
|
||||
proxy_set_header Host api.map.baidu.com;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# 缓存地图API响应
|
||||
proxy_cache_valid 200 1h;
|
||||
proxy_cache_valid 404 1m;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/www.ningmuyun.com/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/www.ningmuyun.com/privkey.pem; # managed by Certbot
|
||||
}
|
||||
|
||||
# HTTPS服务器配置
|
||||
server {
|
||||
listen 443 ;
|
||||
server_name wapi.ningmuyun.com;
|
||||
ssl_certificate /etc/letsencrypt/live/wapi.ningmuyun.com/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/wapi.ningmuyun.com/privkey.pem; # managed by Certbot
|
||||
ssl_session_timeout 5m;
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
# 启用HSTS
|
||||
add_header Strict-Transport-Security "max-age=31536000" always;
|
||||
|
||||
# 客户端上传大小限制
|
||||
client_max_body_size 10M;
|
||||
|
||||
# 日志配置
|
||||
access_log /var/log/nginx/wapi.ningmuyun.com.access.log;
|
||||
error_log /var/log/nginx/wapi.ningmuyun.com.error.log;
|
||||
|
||||
# API反向代理
|
||||
location /api/ {
|
||||
proxy_pass http://localhost:5350/api/;
|
||||
limit_except GET POST OPTIONS { # 确保包含 POST
|
||||
deny all;
|
||||
}
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# 代理超时设置
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 120s;
|
||||
|
||||
# 缓冲区设置
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 8k;
|
||||
proxy_buffers 8 16k;
|
||||
proxy_busy_buffers_size 16k;
|
||||
# 建议添加 CORS 配置,如果前端与API不同域[1](@ref)
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
|
||||
# WebSocket代理
|
||||
location /socket.io/ {
|
||||
proxy_pass http://localhost:5350;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 86400;
|
||||
}
|
||||
|
||||
# 百度地图API代理
|
||||
location /map-api/ {
|
||||
proxy_pass https://api.map.baidu.com/;
|
||||
proxy_set_header Host api.map.baidu.com;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# 缓存地图API响应
|
||||
proxy_cache_valid 200 1h;
|
||||
proxy_cache_valid 404 1m;
|
||||
}
|
||||
# ===== 静态资源服务 - 修复重复问题 =====
|
||||
# 合并所有静态资源处理
|
||||
location ~ ^/(assets/|admin/assets/) {
|
||||
# 尝试多个可能的资源位置
|
||||
try_files $uri $uri/ @check_assets;
|
||||
|
||||
# 缓存策略
|
||||
expires 30d;
|
||||
access_log off;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
# 管理后台页面 - 关键修复:明确指定路径
|
||||
location /admin {
|
||||
alias /data/vue/ningmuyun/farm/dist;
|
||||
index index.html;
|
||||
# 处理SPA路由
|
||||
try_files $uri $uri/ /admin/index.html;
|
||||
|
||||
# 禁用HTML缓存
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
add_header Expires "0";
|
||||
}
|
||||
|
||||
# Dashboard页面
|
||||
location /dashboard {
|
||||
alias /data/vue/ningmuyun/farm/dist;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /dashboard/index.html;
|
||||
|
||||
# 禁用HTML缓存
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
# 登录页面
|
||||
location /login {
|
||||
alias /data/vue/ningmuyun/farm/dist;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /login/index.html;
|
||||
|
||||
# 禁用HTML缓存
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
# 健康检查端点
|
||||
location /health {
|
||||
access_log off;
|
||||
proxy_pass http://localhost:5350/health;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
# 错误页面配置
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
# HTTPS服务器配置
|
||||
server {
|
||||
listen 443 ;
|
||||
server_name ad.ningmuyun.com;
|
||||
# root /data/vue/ningmuyun/admin-system/dist/;
|
||||
# index index.html;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/ad.ningmuyun.com/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/ad.ningmuyun.com/privkey.pem; # managed by Certbot
|
||||
ssl_session_timeout 5m;
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
# 启用HSTS
|
||||
add_header Strict-Transport-Security "max-age=31536000" always;
|
||||
|
||||
# 客户端上传大小限制
|
||||
client_max_body_size 10M;
|
||||
# root /data/vue/ningmuyun/;
|
||||
# 日志配置
|
||||
access_log /var/log/nginx/ad.ningmuyun.com.access.log;
|
||||
error_log /var/log/nginx/ad.ningmuyun.com.error.log;
|
||||
|
||||
|
||||
# ==================== 银行项目 (bank) ====================
|
||||
location ^~ /bank/ {
|
||||
# 银行项目前端静态文件目录
|
||||
#
|
||||
alias /data/vue/ningmuyun/bank/dist/; # ✅ 确保末尾有斜杠
|
||||
index index.html;
|
||||
try_files $uri $uri/ /bank/index.html;
|
||||
# 加上 Content-Type 调试
|
||||
add_header Content-Type "text/html" always;
|
||||
# 静态资源长期缓存
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# HTML文件不缓存
|
||||
location ~* \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
expires 0;
|
||||
}
|
||||
}
|
||||
|
||||
# 银行项目后端API代理
|
||||
location ^~ /bank/api/ {
|
||||
proxy_pass http://localhost:5351/api/; # 银行后端服务端口
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# CORS配置
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
|
||||
# 预检请求处理
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
|
||||
# ==================== 保险项目 (baoxian) ====================
|
||||
location ^~ /insurance/ {
|
||||
# 银行项目前端静态文件目录
|
||||
#
|
||||
alias /data/vue/ningmuyun/insurance/dist/; # ✅ 确保末尾有斜杠
|
||||
index index.html;
|
||||
try_files $uri $uri/ /insurance/index.html;
|
||||
# 加上 Content-Type 调试
|
||||
add_header Content-Type "text/html" always;
|
||||
# 静态资源长期缓存
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# HTML文件不缓存
|
||||
location ~* \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
expires 0;
|
||||
}
|
||||
}
|
||||
|
||||
# 银行项目后端API代理
|
||||
location ^~ /insurance/api/ {
|
||||
proxy_pass http://localhost:3000/api/; # 银行后端服务端口
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# CORS配置
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
|
||||
# 预检请求处理
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
|
||||
# 政府认证API代理 - 处理登录等认证接口(优先级最高)
|
||||
location ^~ /api/government/auth/ {
|
||||
proxy_pass http://localhost:5352/api/auth/; # 政府后端认证服务端口
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# CORS配置
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
|
||||
# 政府API代理 - 处理其他政府相关接口(优先级第二)
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# CORS配置(同上)
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
|
||||
# ==================== 政府项目 (zhengfu) ====================
|
||||
location ^~ /government/ {
|
||||
alias /data/vue/ningmuyun/government/dist/;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /zhengfu/index.html;
|
||||
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location ~* \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
expires 0;
|
||||
}
|
||||
}
|
||||
|
||||
# location ^~ /government/api/ {
|
||||
# proxy_pass http://localhost:5352/; # 政府后端服务端口
|
||||
# }
|
||||
|
||||
# ==================== 养殖项目 (yangzhi) ====================
|
||||
location ^~ /farm/ {
|
||||
alias /data/vue/ningmuyun/farm/dist/;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /farm/index.html;
|
||||
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location ~* \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
expires 0;
|
||||
}
|
||||
}
|
||||
|
||||
location ^~ /farm/api/ {
|
||||
proxy_pass http://localhost:5350/api/; # 养殖后端服务端口
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# CORS配置(同上)
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# location /api/ {
|
||||
# proxy_pass http://localhost:5350/api/;
|
||||
# limit_except GET POST OPTIONS { # 确保包含 POST
|
||||
# deny all;
|
||||
# }
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# # 代理超时设置
|
||||
# proxy_connect_timeout 60s;
|
||||
# proxy_send_timeout 60s;
|
||||
# proxy_read_timeout 120s;
|
||||
|
||||
# # 缓冲区设置
|
||||
# proxy_buffering on;
|
||||
# proxy_buffer_size 8k;
|
||||
# proxy_buffers 8 16k;
|
||||
# proxy_busy_buffers_size 16k;
|
||||
# # 建议添加 CORS 配置,如果前端与API不同域[1](@ref)
|
||||
# add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com' always;
|
||||
# add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
# add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
# add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
# if ($request_method = 'OPTIONS') {
|
||||
# add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com';
|
||||
# add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
# add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
# add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
# add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
# add_header 'Content-Length' 0;
|
||||
# return 204;
|
||||
# }
|
||||
# }
|
||||
|
||||
# # WebSocket代理
|
||||
# location /socket.io/ {
|
||||
# proxy_pass http://localhost:5350;
|
||||
# proxy_http_version 1.1;
|
||||
# proxy_set_header Upgrade $http_upgrade;
|
||||
# proxy_set_header Connection "upgrade";
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# proxy_read_timeout 86400;
|
||||
# }
|
||||
|
||||
# 百度地图API代理
|
||||
location /map-api/ {
|
||||
proxy_pass https://api.map.baidu.com/;
|
||||
proxy_set_header Host api.map.baidu.com;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# 缓存地图API响应
|
||||
proxy_cache_valid 200 1h;
|
||||
proxy_cache_valid 404 1m;
|
||||
}
|
||||
# ===== 静态资源服务 - 修复重复问题 =====
|
||||
# 合并所有静态资源处理
|
||||
location ~ ^/(assets/|admin/assets/) {
|
||||
# 尝试多个可能的资源位置
|
||||
try_files $uri $uri/ @check_assets;
|
||||
|
||||
# 缓存策略
|
||||
expires 30d;
|
||||
access_log off;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
# 管理后台页面 - 关键修复:明确指定路径
|
||||
location /admin {
|
||||
alias /data/vue/ningmuyun/farm/dist;
|
||||
index index.html;
|
||||
# 处理SPA路由
|
||||
try_files $uri $uri/ /admin/index.html;
|
||||
|
||||
# 禁用HTML缓存
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
add_header Expires "0";
|
||||
}
|
||||
|
||||
# Dashboard页面
|
||||
location /dashboard {
|
||||
alias /data/vue/ningmuyun/farm/dist;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /dashboard/index.html;
|
||||
|
||||
# 禁用HTML缓存
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
# 登录页面
|
||||
location /login {
|
||||
alias /data/vue/ningmuyun/farm/dist;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /login/index.html;
|
||||
|
||||
# 禁用HTML缓存
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
}
|
||||
# 健康检查端点
|
||||
location /health {
|
||||
access_log off;
|
||||
proxy_pass http://localhost:5350/health;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
# 错误页面配置
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# server {
|
||||
# listen 443 ;
|
||||
# server_name datav.ningmuyun.com;
|
||||
# root /data/datav/ningmuyun/dist/;
|
||||
# index index.html;
|
||||
|
||||
# ssl_session_timeout 5m;
|
||||
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
# ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
|
||||
# ssl_prefer_server_ciphers on;
|
||||
# # 启用HSTS
|
||||
# add_header Strict-Transport-Security "max-age=31536000" always;
|
||||
|
||||
# # 客户端上传大小限制
|
||||
# client_max_body_size 10M;
|
||||
|
||||
# # 日志配置
|
||||
# access_log /var/log/nginx/datav.ningmuyun.com.access.log;
|
||||
# error_log /var/log/nginx/datav.ningmuyun.com.error.log;
|
||||
|
||||
# location / {
|
||||
# try_files $uri $uri/ /index.html; # 关键修复
|
||||
# add_header Cache-Control "no-cache, no-store";
|
||||
# expires 0;
|
||||
# }
|
||||
|
||||
# ssl_certificate /etc/letsencrypt/live/datav.ningmuyun.com/fullchain.pem; # managed by Certbot
|
||||
# ssl_certificate_key /etc/letsencrypt/live/datav.ningmuyun.com/privkey.pem; # managed by Certbot
|
||||
# }
|
||||
# ============================================================
|
||||
# datav.ningmuyun.com → 四项目(与 ad.ningmuyun.com 同构)
|
||||
# ============================================================
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name datav.ningmuyun.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/datav.ningmuyun.com/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/datav.ningmuyun.com/privkey.pem; # managed by Certbot
|
||||
ssl_session_timeout 5m;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
|
||||
ssl_prefer_server_ciphers on;
|
||||
add_header Strict-Transport-Security "max-age=31536000" always;
|
||||
|
||||
client_max_body_size 10M;
|
||||
|
||||
access_log /var/log/nginx/datav.ningmuyun.com.access.log;
|
||||
error_log /var/log/nginx/datav.ningmuyun.com.error.log;
|
||||
|
||||
# --------------- 1. 银行项目 ---------------
|
||||
location ^~ /bank/ {
|
||||
alias /data/datav/ningmuyun/bank/dist/;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /bank/index.html;
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||
expires 1y; add_header Cache-Control "public, immutable"; access_log off;
|
||||
}
|
||||
location ~* \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Pragma "no-cache"; expires 0;
|
||||
}
|
||||
}
|
||||
# location ^~ /bank/api/ {
|
||||
# proxy_pass http://localhost:5351/api/;
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# # CORS
|
||||
# add_header 'Access-Control-Allow-Origin' 'https://datav.ningmuyun.com' always;
|
||||
# add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
# add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
# add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
# if ($request_method = 'OPTIONS') {
|
||||
# add_header 'Access-Control-Allow-Origin' 'https://datav.ningmuyun.com';
|
||||
# add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
# add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
# add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
# add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
# add_header 'Content-Length' 0;
|
||||
# return 204;
|
||||
# }
|
||||
# }
|
||||
|
||||
# --------------- 2. 保险项目 ---------------
|
||||
location ^~ /insurance/ {
|
||||
alias /data/datav/ningmuyun/insurance/dist/;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /insurance/index.html;
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||
expires 1y; add_header Cache-Control "public, immutable"; access_log off;
|
||||
}
|
||||
location ~* \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Pragma "no-cache"; expires 0;
|
||||
}
|
||||
}
|
||||
# location ^~ /insurance/api/ {
|
||||
# proxy_pass http://localhost:3000/api/;
|
||||
# # 同上 CORS
|
||||
# add_header 'Access-Control-Allow-Origin' 'https://datav.ningmuyun.com' always;
|
||||
# add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
# add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
# add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
# if ($request_method = 'OPTIONS') {
|
||||
# add_header 'Access-Control-Allow-Origin' 'https://datav.ningmuyun.com';
|
||||
# add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
# add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
# add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
# add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
# add_header 'Content-Length' 0;
|
||||
# return 204;
|
||||
# }
|
||||
# }
|
||||
|
||||
# --------------- 3. 政府项目 ---------------
|
||||
location ^~ /government/ {
|
||||
alias /data/datav/ningmuyun/government/dist/;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /zhengfu/index.html;
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||
expires 1y; add_header Cache-Control "public, immutable"; access_log off;
|
||||
}
|
||||
location ~* \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Pragma "no-cache"; expires 0;
|
||||
}
|
||||
}
|
||||
# location ^~ /zhengfu/api/ {
|
||||
# proxy_pass http://localhost:5353/;
|
||||
# # 同上 CORS
|
||||
# add_header 'Access-Control-Allow-Origin' 'https://datav.ningmuyun.com' always;
|
||||
# add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
# add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
# add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
# if ($request_method = 'OPTIONS') {
|
||||
# add_header 'Access-Control-Allow-Origin' 'https://datav.ningmuyun.com';
|
||||
# add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
# add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
# add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
# add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
# add_header 'Content-Length' 0;
|
||||
# return 204;
|
||||
# }
|
||||
# }
|
||||
|
||||
# --------------- 4. 养殖项目 ---------------
|
||||
location ^~ /farm/ {
|
||||
alias /data/datav/ningmuyun/farm/dist/;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /farm/index.html;
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||||
expires 1y; add_header Cache-Control "public, immutable"; access_log off;
|
||||
}
|
||||
location ~* \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Pragma "no-cache"; expires 0;
|
||||
}
|
||||
}
|
||||
# location ^~ /farm/api/ {
|
||||
# proxy_pass http://localhost:5350/api/;
|
||||
# # 同上 CORS
|
||||
# add_header 'Access-Control-Allow-Origin' 'https://datav.ningmuyun.com' always;
|
||||
# add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
# add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
# add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
# if ($request_method = 'OPTIONS') {
|
||||
# add_header 'Access-Control-Allow-Origin' 'https://datav.ningmuyun.com';
|
||||
# add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
# add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
# add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
# add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
# add_header 'Content-Length' 0;
|
||||
# return 204;
|
||||
# }
|
||||
# }
|
||||
|
||||
# --------------- 公共附加:WebSocket + 地图 ---------------
|
||||
# location /socket.io/ {
|
||||
# proxy_pass http://localhost:5350;
|
||||
# proxy_http_version 1.1;
|
||||
# proxy_set_header Upgrade $http_upgrade;
|
||||
# proxy_set_header Connection "upgrade";
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# proxy_read_timeout 86400;
|
||||
# }
|
||||
|
||||
# location /map-api/ {
|
||||
# proxy_pass https://api.map.baidu.com/;
|
||||
# proxy_set_header Host api.map.baidu.com;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# proxy_cache_valid 200 1h;
|
||||
# proxy_cache_valid 404 1m;
|
||||
# }
|
||||
|
||||
# --------------- 默认兜底(可选) ---------------
|
||||
location / {
|
||||
alias /data/datav/ningmuyun/farm/dist/;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ app.use((err, req, res, next) => {
|
||||
|
||||
// 启动服务器
|
||||
const PORT = process.env.PORT || 5352;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`政府管理系统后端服务已启动,端口: ${PORT}`);
|
||||
const HOST = process.env.HOST || '0.0.0.0';
|
||||
app.listen(PORT, HOST, () => {
|
||||
console.log(`政府管理系统后端服务已启动,地址: ${HOST}:${PORT}`);
|
||||
});
|
||||
106
government-backend/debug-nginx-issue.js
Normal file
106
government-backend/debug-nginx-issue.js
Normal file
@@ -0,0 +1,106 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 调试nginx问题
|
||||
async function debugNginxIssue() {
|
||||
console.log('🔍 调试nginx问题...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '直接测试后端服务',
|
||||
url: 'http://localhost:5352/api/government/departments',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试部门接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
expected: '应该返回401认证错误'
|
||||
},
|
||||
{
|
||||
name: '测试重复路径接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/departments',
|
||||
expected: '应该返回200(如果nginx配置有问题)'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 接口不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: nginx配置问题,路径映射不正确`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 调试完成!');
|
||||
console.log('');
|
||||
console.log('📝 可能的问题:');
|
||||
console.log(' 1. nginx配置语法错误');
|
||||
console.log(' 2. nginx服务未重启');
|
||||
console.log(' 3. location规则冲突');
|
||||
console.log(' 4. proxy_pass配置错误');
|
||||
console.log('');
|
||||
console.log('🔄 建议的解决步骤:');
|
||||
console.log(' 1. 检查nginx配置语法: sudo nginx -t');
|
||||
console.log(' 2. 重启nginx服务: sudo systemctl reload nginx');
|
||||
console.log(' 3. 检查nginx错误日志: sudo tail -f /var/log/nginx/error.log');
|
||||
console.log(' 4. 检查nginx访问日志: sudo tail -f /var/log/nginx/ad.ningmuyun.com.access.log');
|
||||
}
|
||||
|
||||
// 运行调试
|
||||
debugNginxIssue().catch(console.error);
|
||||
98
government-backend/deploy.sh
Normal file
98
government-backend/deploy.sh
Normal file
@@ -0,0 +1,98 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Government Backend 部署脚本
|
||||
# 用于自动化部署 government-backend 服务
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 开始部署 Government Backend..."
|
||||
|
||||
# 1. 检查环境
|
||||
echo "📋 检查环境..."
|
||||
if ! command -v node &> /dev/null; then
|
||||
echo "❌ Node.js 未安装"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v npm &> /dev/null; then
|
||||
echo "❌ npm 未安装"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v pm2 &> /dev/null; then
|
||||
echo "📦 安装 PM2..."
|
||||
npm install -g pm2
|
||||
fi
|
||||
|
||||
# 2. 进入项目目录
|
||||
PROJECT_DIR="/var/www/government-backend"
|
||||
if [ ! -d "$PROJECT_DIR" ]; then
|
||||
echo "❌ 项目目录不存在: $PROJECT_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd $PROJECT_DIR
|
||||
|
||||
# 3. 更新代码
|
||||
echo "📥 更新代码..."
|
||||
git pull origin main
|
||||
|
||||
# 4. 安装依赖
|
||||
echo "📦 安装依赖..."
|
||||
npm install --production
|
||||
|
||||
# 5. 创建日志目录
|
||||
echo "📁 创建日志目录..."
|
||||
mkdir -p logs
|
||||
|
||||
# 6. 检查配置文件
|
||||
if [ ! -f ".env" ]; then
|
||||
echo "⚠️ .env 文件不存在,创建默认配置..."
|
||||
cat > .env << EOF
|
||||
NODE_ENV=production
|
||||
PORT=5352
|
||||
DB_DIALECT=mysql
|
||||
DB_HOST=129.211.213.226
|
||||
DB_PORT=9527
|
||||
DB_DATABASE=ningxia_zhengfu
|
||||
DB_USER=root
|
||||
DB_PASSWORD=aiotAiot123!
|
||||
JWT_SECRET=government_super_secret_jwt_key_2024_very_long_and_secure
|
||||
LOG_LEVEL=info
|
||||
CORS_ORIGIN=*
|
||||
UPLOAD_PATH=./uploads
|
||||
MAX_FILE_SIZE=10485760
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=
|
||||
EOF
|
||||
fi
|
||||
|
||||
# 7. 停止现有进程
|
||||
echo "🛑 停止现有进程..."
|
||||
pm2 stop government-backend 2>/dev/null || true
|
||||
pm2 delete government-backend 2>/dev/null || true
|
||||
|
||||
# 8. 启动服务
|
||||
echo "🚀 启动服务..."
|
||||
pm2 start ecosystem.config.js --env production
|
||||
|
||||
# 9. 保存PM2配置
|
||||
echo "💾 保存PM2配置..."
|
||||
pm2 save
|
||||
|
||||
# 10. 设置开机自启
|
||||
echo "🔄 设置开机自启..."
|
||||
pm2 startup
|
||||
|
||||
# 11. 检查服务状态
|
||||
echo "✅ 检查服务状态..."
|
||||
sleep 3
|
||||
pm2 status
|
||||
|
||||
echo ""
|
||||
echo "🎉 部署完成!"
|
||||
echo "📊 服务状态: pm2 status"
|
||||
echo "📝 查看日志: pm2 logs government-backend"
|
||||
echo "🔍 监控面板: pm2 monit"
|
||||
echo "🌐 服务地址: http://localhost:5352"
|
||||
75
government-backend/diagnose-url-issue.js
Normal file
75
government-backend/diagnose-url-issue.js
Normal file
@@ -0,0 +1,75 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 诊断URL重复问题
|
||||
async function diagnoseURLIssue() {
|
||||
console.log('🔍 诊断政府端API URL重复问题...\n');
|
||||
|
||||
const testURLs = [
|
||||
{
|
||||
name: '正确的部门接口URL',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
description: '应该是正确的API路径'
|
||||
},
|
||||
{
|
||||
name: '错误的重复URL',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/departments',
|
||||
description: '您提到的重复路径问题'
|
||||
},
|
||||
{
|
||||
name: '静态文件路径测试',
|
||||
url: 'https://ad.ningmuyun.com/government/',
|
||||
description: '测试静态文件路径是否正常'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testURLs) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 说明: ${test.description}`);
|
||||
|
||||
try {
|
||||
const response = await axios.get(test.url, {
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
});
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
console.log(` ✅ 响应类型: ${response.headers['content-type']}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
if (response.headers['content-type']?.includes('application/json')) {
|
||||
console.log(` ✅ 返回JSON数据: ${JSON.stringify(response.data).substring(0, 100)}...`);
|
||||
} else {
|
||||
console.log(` ✅ 返回HTML内容 (可能是静态文件)`);
|
||||
}
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 404错误: 路径不存在`);
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 401错误: 需要认证 (API接口正常)`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
} else {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 诊断完成!');
|
||||
console.log('');
|
||||
console.log('📝 分析结果:');
|
||||
console.log(' - 如果正确的URL返回404,说明nginx配置有问题');
|
||||
console.log(' - 如果错误的URL也能访问,说明有路径重写问题');
|
||||
console.log(' - 如果静态文件路径正常,说明nginx基本配置正确');
|
||||
}
|
||||
|
||||
// 运行诊断
|
||||
diagnoseURLIssue().catch(console.error);
|
||||
38
government-backend/ecosystem.config.js
Normal file
38
government-backend/ecosystem.config.js
Normal file
@@ -0,0 +1,38 @@
|
||||
module.exports = {
|
||||
apps: [{
|
||||
name: 'government-backend',
|
||||
script: 'app.js',
|
||||
cwd: '/var/www/government-backend',
|
||||
instances: 1,
|
||||
autorestart: true,
|
||||
watch: false,
|
||||
max_memory_restart: '1G',
|
||||
env: {
|
||||
NODE_ENV: 'development',
|
||||
PORT: 5352
|
||||
},
|
||||
env_production: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 5352,
|
||||
DB_DIALECT: 'mysql',
|
||||
DB_HOST: '129.211.213.226',
|
||||
DB_PORT: 9527,
|
||||
DB_DATABASE: 'ningxia_zhengfu',
|
||||
DB_USER: 'root',
|
||||
DB_PASSWORD: 'aiotAiot123!',
|
||||
JWT_SECRET: 'government_super_secret_jwt_key_2024_very_long_and_secure',
|
||||
LOG_LEVEL: 'info'
|
||||
},
|
||||
error_file: './logs/err.log',
|
||||
out_file: './logs/out.log',
|
||||
log_file: './logs/combined.log',
|
||||
time: true,
|
||||
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
|
||||
merge_logs: true,
|
||||
max_restarts: 10,
|
||||
min_uptime: '10s',
|
||||
kill_timeout: 5000,
|
||||
listen_timeout: 8000,
|
||||
shutdown_with_message: true
|
||||
}]
|
||||
};
|
||||
173
government-backend/final-nginx-test.js
Normal file
173
government-backend/final-nginx-test.js
Normal file
@@ -0,0 +1,173 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 最终nginx配置测试
|
||||
async function finalNginxTest() {
|
||||
console.log('🔍 最终nginx配置测试...\n');
|
||||
console.log('📝 修复说明:');
|
||||
console.log(' 将 proxy_pass http://localhost:5352/api/government/');
|
||||
console.log(' 改为 proxy_pass http://localhost:5352');
|
||||
console.log(' 这样可以保持完整路径,避免路径重复\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '部门列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '数据中心接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '市场价格接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '养殖户列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farmers',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
shouldWork: true,
|
||||
expectedStatus: 401
|
||||
},
|
||||
{
|
||||
name: '重复路径接口(应该404)',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/departments',
|
||||
shouldWork: false
|
||||
}
|
||||
];
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500;
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` 状态码: ${response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
if (test.expectedStatus) {
|
||||
if (response.status === test.expectedStatus) {
|
||||
console.log(` ✅ 成功: 返回预期状态码 ${test.expectedStatus}`);
|
||||
successCount++;
|
||||
} else {
|
||||
console.log(` ❌ 失败: 预期状态码 ${test.expectedStatus},实际 ${response.status}`);
|
||||
failCount++;
|
||||
}
|
||||
} else if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
successCount++;
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 失败: 接口返回 404`);
|
||||
failCount++;
|
||||
}
|
||||
} else {
|
||||
if (response.status === 404) {
|
||||
console.log(` ✅ 成功: 正确返回 404(重复路径应该不存在)`);
|
||||
successCount++;
|
||||
} else if (response.status === 200) {
|
||||
console.log(` ❌ 失败: 重复路径不应该工作`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` 状态码: ${error.response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
console.log(` ❌ 失败: ${error.response.statusText}`);
|
||||
failCount++;
|
||||
} else {
|
||||
if (error.response.status === 404) {
|
||||
console.log(` ✅ 成功: 正确返回 404`);
|
||||
successCount++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log(` ✅ 成功: ${successCount} 个`);
|
||||
console.log(` ❌ 失败: ${failCount} 个`);
|
||||
console.log('');
|
||||
|
||||
if (failCount === 0) {
|
||||
console.log('🎉 所有测试通过!Nginx配置修复成功!');
|
||||
} else {
|
||||
console.log('⚠️ 还有测试失败,请检查:');
|
||||
console.log(' 1. Nginx配置是否正确修改');
|
||||
console.log(' 2. Nginx服务是否已重启');
|
||||
console.log(' 3. 浏览器缓存是否已清除');
|
||||
console.log('');
|
||||
console.log('🔄 重启Nginx:');
|
||||
console.log(' sudo nginx -t');
|
||||
console.log(' sudo systemctl reload nginx');
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log('📝 最终nginx配置:');
|
||||
console.log(' location ^~ /api/government/auth/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/auth/;');
|
||||
console.log(' }');
|
||||
console.log(' location ^~ /api/government/ {');
|
||||
console.log(' proxy_pass http://localhost:5352;');
|
||||
console.log(' }');
|
||||
console.log('');
|
||||
console.log('🔄 路径映射:');
|
||||
console.log(' /api/government/departments → http://localhost:5352/api/government/departments');
|
||||
console.log(' /api/government/data-center → http://localhost:5352/api/government/data-center');
|
||||
console.log(' /api/government/market-price → http://localhost:5352/api/government/market-price');
|
||||
console.log('');
|
||||
console.log('🚨 重要提醒:');
|
||||
console.log(' 修改nginx配置后,必须重启nginx服务才能生效!');
|
||||
console.log(' sudo nginx -t # 检查配置语法');
|
||||
console.log(' sudo systemctl reload nginx # 重新加载配置');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
finalNginxTest().catch(console.error);
|
||||
@@ -1,119 +0,0 @@
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name ad.ningmuyun.com;
|
||||
|
||||
# SSL证书配置(需要替换为实际的证书路径)
|
||||
ssl_certificate /etc/ssl/certs/ad.ningmuyun.com.crt;
|
||||
ssl_certificate_key /etc/ssl/private/ad.ningmuyun.com.key;
|
||||
|
||||
# SSL配置
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
# 政府端前端静态文件
|
||||
location /government/ {
|
||||
alias /var/www/government-admin-system/dist/;
|
||||
try_files $uri $uri/ /government/index.html;
|
||||
|
||||
# 静态资源缓存
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
|
||||
# 政府端后端API代理
|
||||
location /api/government/ {
|
||||
proxy_pass http://127.0.0.1:5352/api/government/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
|
||||
# CORS headers
|
||||
add_header Access-Control-Allow-Origin https://ad.ningmuyun.com;
|
||||
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
|
||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With";
|
||||
add_header Access-Control-Allow-Credentials true;
|
||||
|
||||
# 处理预检请求
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header Access-Control-Allow-Origin https://ad.ningmuyun.com;
|
||||
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
|
||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With";
|
||||
add_header Access-Control-Allow-Credentials true;
|
||||
add_header Content-Length 0;
|
||||
add_header Content-Type text/plain;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
|
||||
# 保险端前端静态文件
|
||||
location /insurance/ {
|
||||
alias /var/www/insurance-admin-system/dist/;
|
||||
try_files $uri $uri/ /insurance/index.html;
|
||||
|
||||
# 静态资源缓存
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
|
||||
# 保险端后端API代理
|
||||
location /insurance/api/ {
|
||||
proxy_pass http://127.0.0.1:3000/api/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
|
||||
# CORS headers
|
||||
add_header Access-Control-Allow-Origin https://ad.ningmuyun.com;
|
||||
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
|
||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With";
|
||||
add_header Access-Control-Allow-Credentials true;
|
||||
|
||||
# 处理预检请求
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header Access-Control-Allow-Origin https://ad.ningmuyun.com;
|
||||
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
|
||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With";
|
||||
add_header Access-Control-Allow-Credentials true;
|
||||
add_header Content-Length 0;
|
||||
add_header Content-Type text/plain;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
location /health {
|
||||
proxy_pass http://127.0.0.1:5352/health;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# 根路径重定向到政府端
|
||||
location = / {
|
||||
return 301 /government/;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTP重定向到HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name ad.ningmuyun.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
63
government-backend/nginx-config-check.md
Normal file
63
government-backend/nginx-config-check.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Nginx配置检查说明
|
||||
|
||||
## 问题分析
|
||||
|
||||
### 当前状态
|
||||
- ✅ 后端服务正常:`http://localhost:5352/api/government/departments` 返回200
|
||||
- ✅ 登录接口正常:`https://ad.ningmuyun.com/api/government/auth/login` 返回401
|
||||
- ❌ 其他接口404:`https://ad.ningmuyun.com/api/government/departments` 返回404
|
||||
|
||||
### 问题根因
|
||||
nginx配置中的 `location ^~ /api/government/` 规则没有正确工作。
|
||||
|
||||
### 当前nginx配置
|
||||
```nginx
|
||||
# 政府认证API代理 - 处理登录等认证接口(优先级高于通用government代理)
|
||||
location ^~ /api/government/auth/ {
|
||||
proxy_pass http://localhost:5352/api/auth/;
|
||||
# ... CORS配置
|
||||
}
|
||||
|
||||
# 政府API代理 - 处理其他政府相关接口
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
# ... CORS配置
|
||||
}
|
||||
```
|
||||
|
||||
### 可能的问题
|
||||
1. **nginx配置语法错误** - 需要检查配置语法
|
||||
2. **nginx服务未重启** - 配置修改后需要重启
|
||||
3. **location规则冲突** - 可能有其他规则干扰
|
||||
4. **proxy_pass配置错误** - 路径映射不正确
|
||||
|
||||
## 解决步骤
|
||||
|
||||
### 1. 检查nginx配置语法
|
||||
```bash
|
||||
sudo nginx -t
|
||||
```
|
||||
|
||||
### 2. 重启nginx服务
|
||||
```bash
|
||||
sudo systemctl reload nginx
|
||||
# 或者
|
||||
sudo systemctl restart nginx
|
||||
```
|
||||
|
||||
### 3. 检查nginx错误日志
|
||||
```bash
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
### 4. 检查nginx访问日志
|
||||
```bash
|
||||
sudo tail -f /var/log/nginx/ad.ningmuyun.com.access.log
|
||||
```
|
||||
|
||||
## 预期结果
|
||||
修复后,以下接口应该正常工作:
|
||||
- ✅ `https://ad.ningmuyun.com/api/government/auth/login` (登录)
|
||||
- ✅ `https://ad.ningmuyun.com/api/government/departments` (部门列表)
|
||||
- ✅ `https://ad.ningmuyun.com/api/government/data-center` (数据中心)
|
||||
- ✅ `https://ad.ningmuyun.com/api/government/market-price` (市场价格)
|
||||
77
government-backend/nginx-config-test.md
Normal file
77
government-backend/nginx-config-test.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Nginx配置测试说明
|
||||
|
||||
## 问题描述
|
||||
- 登录接口:`/api/government/auth/login` ✅ 正常
|
||||
- 其他接口:`/api/government/departments` ❌ 报错
|
||||
- 错误URL:`/api/government/government/departments` (路径重复)
|
||||
|
||||
## 当前nginx配置分析
|
||||
|
||||
### Server块1: ad.ningmuyun.com (HTTPS 443)
|
||||
```nginx
|
||||
# 静态文件服务
|
||||
location ^~ /government/ {
|
||||
alias /data/vue/ningmuyun/government/dist/;
|
||||
# 处理前端静态文件
|
||||
}
|
||||
|
||||
# 认证API代理 (优先级最高)
|
||||
location ^~ /api/government/auth/ {
|
||||
proxy_pass http://localhost:5352/api/auth/;
|
||||
# 处理登录等认证接口
|
||||
}
|
||||
|
||||
# 其他API代理 (优先级较低)
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
# 处理其他政府API接口
|
||||
}
|
||||
```
|
||||
|
||||
## 路径映射测试
|
||||
|
||||
| 请求路径 | 匹配规则 | 代理目标 | 预期结果 |
|
||||
|---------|---------|---------|---------|
|
||||
| `/api/government/auth/login` | `^~ /api/government/auth/` | `/api/auth/login` | ✅ 正常 |
|
||||
| `/api/government/departments` | `^~ /api/government/` | `/api/government/departments` | ✅ 应该正常 |
|
||||
| `/api/government/government/departments` | 无匹配 | 404 | ❌ 不应该出现 |
|
||||
|
||||
## 可能的问题原因
|
||||
|
||||
1. **前端请求路径错误**:前端可能错误地构造了重复路径
|
||||
2. **nginx重写规则**:可能有隐藏的rewrite规则导致路径重复
|
||||
3. **代理配置问题**:proxy_pass的路径处理可能有问题
|
||||
|
||||
## 测试步骤
|
||||
|
||||
1. 运行诊断脚本:
|
||||
```bash
|
||||
node diagnose-url-issue.js
|
||||
```
|
||||
|
||||
2. 检查nginx错误日志:
|
||||
```bash
|
||||
sudo tail -f /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
3. 检查nginx访问日志:
|
||||
```bash
|
||||
sudo tail -f /var/log/nginx/ad.ningmuyun.com.access.log
|
||||
```
|
||||
|
||||
## 修复建议
|
||||
|
||||
如果确认是nginx配置问题,可能需要:
|
||||
|
||||
1. 调整location规则的顺序
|
||||
2. 添加更具体的路径匹配规则
|
||||
3. 检查是否有隐藏的rewrite规则
|
||||
4. 验证proxy_pass的路径处理
|
||||
|
||||
## 验证方法
|
||||
|
||||
修复后,以下URL应该正常工作:
|
||||
- ✅ `https://ad.ningmuyun.com/api/government/auth/login`
|
||||
- ✅ `https://ad.ningmuyun.com/api/government/departments`
|
||||
- ✅ `https://ad.ningmuyun.com/api/government/market-price`
|
||||
- ❌ `https://ad.ningmuyun.com/api/government/government/departments` (不应该存在)
|
||||
113
government-backend/nginx-问题分析报告.md
Normal file
113
government-backend/nginx-问题分析报告.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# 政府端API 404错误问题分析报告
|
||||
|
||||
## 问题描述
|
||||
- **错误URL**: `https://ad.ningmuyun.com/api/government/market-price?type=beef`
|
||||
- **错误状态**: 404 Not Found
|
||||
- **影响范围**: 政府端管理系统市场价格功能
|
||||
|
||||
## 问题根因分析
|
||||
|
||||
### 1. 后端代码检查 ✅
|
||||
- **路由配置**: `government-backend/routes/government.js` 第11行
|
||||
```javascript
|
||||
router.get('/market-price', governmentController.getMarketPrice);
|
||||
```
|
||||
- **控制器方法**: `governmentController.getMarketPrice` 已正确实现
|
||||
- **应用路由注册**: `app.js` 第50行已正确注册
|
||||
```javascript
|
||||
app.use('/api/government', require('./routes/government'));
|
||||
```
|
||||
|
||||
### 2. 前端代码检查 ✅
|
||||
- **API调用**: `government-admin/src/views/MarketPrice.vue` 第94行
|
||||
```javascript
|
||||
const response = await axios.get('/api/government/market-price', {
|
||||
params: { type }
|
||||
})
|
||||
```
|
||||
- **代理配置**: `vite.config.js` 开发环境代理配置正确
|
||||
|
||||
### 3. Nginx配置问题 ❌
|
||||
**问题所在**: nginx配置中政府API的路径匹配错误
|
||||
|
||||
**原始错误配置**:
|
||||
```nginx
|
||||
location ^~ /government/api/ {
|
||||
proxy_pass http://localhost:5352/;
|
||||
}
|
||||
```
|
||||
|
||||
**问题分析**:
|
||||
- 前端请求路径: `/api/government/market-price`
|
||||
- nginx匹配路径: `/government/api/`
|
||||
- 路径不匹配导致404错误
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 修复nginx配置
|
||||
将政府API代理路径从 `/government/api/` 修改为 `/api/government/`:
|
||||
|
||||
```nginx
|
||||
# 政府API代理 - 修复路径匹配问题
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# CORS配置
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 部署步骤
|
||||
|
||||
1. **备份当前nginx配置**:
|
||||
```bash
|
||||
sudo cp /etc/nginx/conf.d/ningmuyun_one.conf /etc/nginx/conf.d/ningmuyun_one.conf.backup
|
||||
```
|
||||
|
||||
2. **应用修复配置**:
|
||||
```bash
|
||||
sudo cp nginx-fix-government-api.conf /etc/nginx/conf.d/
|
||||
# 或者手动编辑配置文件,将上述配置添加到 ad.ningmuyun.com server 块中
|
||||
```
|
||||
|
||||
3. **测试nginx配置**:
|
||||
```bash
|
||||
sudo nginx -t
|
||||
```
|
||||
|
||||
4. **重新加载nginx**:
|
||||
```bash
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
5. **验证修复**:
|
||||
```bash
|
||||
curl -k https://ad.ningmuyun.com/api/government/market-price?type=beef
|
||||
```
|
||||
|
||||
## 预期结果
|
||||
修复后,`https://ad.ningmuyun.com/api/government/market-price?type=beef` 应该返回正确的市场价格数据,而不是404错误。
|
||||
|
||||
## 相关文件
|
||||
- `government-backend/_etc_nginx_conf.d_ningmuyun_one.conf` - 主nginx配置文件
|
||||
- `government-backend/nginx-fix-government-api.conf` - 修复配置片段
|
||||
- `government-backend/routes/government.js` - 后端路由
|
||||
- `government-backend/controllers/governmentController.js` - 控制器实现
|
||||
- `government-admin/src/views/MarketPrice.vue` - 前端页面
|
||||
116
government-backend/test-after-restart.js
Normal file
116
government-backend/test-after-restart.js
Normal file
@@ -0,0 +1,116 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试重启后的API接口
|
||||
async function testAfterRestart() {
|
||||
console.log('🔍 测试nginx重启后的API接口...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '登录接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
expected: '应该返回401认证错误(接口正常)'
|
||||
},
|
||||
{
|
||||
name: '部门列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expected: '应该返回200或401'
|
||||
},
|
||||
{
|
||||
name: '数据中心接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expected: '应该返回200或401'
|
||||
},
|
||||
{
|
||||
name: '市场价格接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expected: '应该返回200或401'
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expected: '应该返回200或401'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method,
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 接口不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: nginx配置可能还有问题`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 当前nginx配置:');
|
||||
console.log(' location ^~ /api/government/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' }');
|
||||
console.log('');
|
||||
console.log('🔄 路径映射:');
|
||||
console.log(' /api/government/departments → http://localhost:5352/api/government/departments');
|
||||
console.log(' /api/government/positions → http://localhost:5352/api/government/positions');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testAfterRestart().catch(console.error);
|
||||
126
government-backend/test-final-fix.js
Normal file
126
government-backend/test-final-fix.js
Normal file
@@ -0,0 +1,126 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试最终修复效果
|
||||
async function testFinalFix() {
|
||||
console.log('🔍 测试最终修复效果...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '部门列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
expected: '应该返回200状态码和部门数据'
|
||||
},
|
||||
{
|
||||
name: '数据中心接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
expected: '应该返回200状态码和统计数据'
|
||||
},
|
||||
{
|
||||
name: '市场价格接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
expected: '应该返回200状态码和价格数据'
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
expected: '应该返回200状态码和岗位数据'
|
||||
},
|
||||
{
|
||||
name: '养殖户列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farmers',
|
||||
expected: '应该返回200状态码和养殖户数据'
|
||||
},
|
||||
{
|
||||
name: '养殖类型接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farm-types',
|
||||
expected: '应该返回200状态码和养殖类型数据'
|
||||
},
|
||||
{
|
||||
name: '登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
expected: '应该返回401认证错误(接口正常)'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 接口不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: nginx配置可能还有问题`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 最终nginx配置:');
|
||||
console.log(' location ^~ /api/government/auth/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/auth/;');
|
||||
console.log(' }');
|
||||
console.log(' location ^~ /api/government/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' }');
|
||||
console.log('');
|
||||
console.log('🔄 路径映射:');
|
||||
console.log(' /api/government/departments → http://localhost:5352/api/government/departments');
|
||||
console.log(' /api/government/data-center → http://localhost:5352/api/government/data-center');
|
||||
console.log(' /api/government/market-price → http://localhost:5352/api/government/market-price');
|
||||
console.log('');
|
||||
console.log('🔄 现在需要重启nginx使配置生效:');
|
||||
console.log(' sudo nginx -t # 检查配置语法');
|
||||
console.log(' sudo systemctl reload nginx # 重新加载配置');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testFinalFix().catch(console.error);
|
||||
94
government-backend/test-fixed-apis.js
Normal file
94
government-backend/test-fixed-apis.js
Normal file
@@ -0,0 +1,94 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试修复后的API接口
|
||||
async function testFixedAPIs() {
|
||||
console.log('🔍 测试修复后的政府端API接口...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '部门列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
expected: '应该返回200状态码和部门数据'
|
||||
},
|
||||
{
|
||||
name: '数据中心接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
expected: '应该返回200状态码和统计数据'
|
||||
},
|
||||
{
|
||||
name: '市场价格接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
expected: '应该返回200状态码和价格数据'
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
expected: '应该返回200状态码和岗位数据'
|
||||
},
|
||||
{
|
||||
name: '养殖户列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farmers',
|
||||
expected: '应该返回200状态码和养殖户数据'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const response = await axios.get(test.url, {
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
});
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 接口不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: nginx配置可能还有问题`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 修复说明:');
|
||||
console.log(' 问题: location规则缺少注释符号,导致被注释掉');
|
||||
console.log(' 修复: 添加了正确的注释和location规则');
|
||||
console.log('');
|
||||
console.log('🔄 现在需要重启nginx使配置生效:');
|
||||
console.log(' sudo nginx -t # 检查配置语法');
|
||||
console.log(' sudo systemctl reload nginx # 重新加载配置');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testFixedAPIs().catch(console.error);
|
||||
97
government-backend/test-government-apis.js
Normal file
97
government-backend/test-government-apis.js
Normal file
@@ -0,0 +1,97 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试政府端各种API接口
|
||||
async function testGovernmentAPIs() {
|
||||
console.log('🔍 测试政府端API接口...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '登录接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
expected: '应该返回登录成功或认证错误'
|
||||
},
|
||||
{
|
||||
name: '部门列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expected: '应该返回部门列表或认证错误'
|
||||
},
|
||||
{
|
||||
name: '市场价格接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expected: '应该返回市场价格数据或认证错误'
|
||||
},
|
||||
{
|
||||
name: '数据中心接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
method: 'GET',
|
||||
data: null,
|
||||
expected: '应该返回数据中心数据或认证错误'
|
||||
}
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
console.log(`📋 ${testCase.name}`);
|
||||
console.log(` URL: ${testCase.url}`);
|
||||
console.log(` 方法: ${testCase.method}`);
|
||||
console.log(` 预期: ${testCase.expected}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: testCase.method,
|
||||
url: testCase.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000
|
||||
};
|
||||
|
||||
if (testCase.data) {
|
||||
config.data = testCase.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
console.log(` ✅ 响应数据: ${JSON.stringify(response.data, null, 2).substring(0, 200)}...`);
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误信息: ${JSON.stringify(error.response.data, null, 2)}`);
|
||||
|
||||
// 分析错误类型
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: 404错误,可能是路径映射问题`);
|
||||
} else if (error.response.status === 401) {
|
||||
console.log(` 🔍 分析: 401错误,需要认证,这是正常的`);
|
||||
} else if (error.response.status === 500) {
|
||||
console.log(` 🔍 分析: 500错误,服务器内部错误`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 分析说明:');
|
||||
console.log(' - 如果所有接口都返回404,说明nginx配置有问题');
|
||||
console.log(' - 如果只有登录接口正常,其他返回404,说明路径映射有问题');
|
||||
console.log(' - 如果返回401认证错误,说明接口正常但需要登录');
|
||||
console.log(' - 如果返回500错误,说明后端服务有问题');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testGovernmentAPIs().catch(console.error);
|
||||
160
government-backend/test-location-order-fix.js
Normal file
160
government-backend/test-location-order-fix.js
Normal file
@@ -0,0 +1,160 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试location规则顺序修复效果
|
||||
async function testLocationOrderFix() {
|
||||
console.log('🔍 测试location规则顺序修复效果...\n');
|
||||
console.log('📝 修复说明:');
|
||||
console.log(' 调整了location规则的顺序,让API规则优先于静态文件规则');
|
||||
console.log(' 1. location ^~ /api/government/auth/ (最高优先级)');
|
||||
console.log(' 2. location ^~ /api/government/ (第二优先级)');
|
||||
console.log(' 3. location ^~ /government/ (静态文件,最低优先级)');
|
||||
console.log('');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '部门列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '数据中心接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '市场价格接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '养殖户列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farmers',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
shouldWork: true,
|
||||
expectedStatus: 401
|
||||
}
|
||||
];
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500;
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` 状态码: ${response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
if (test.expectedStatus) {
|
||||
if (response.status === test.expectedStatus) {
|
||||
console.log(` ✅ 成功: 返回预期状态码 ${test.expectedStatus}`);
|
||||
successCount++;
|
||||
} else {
|
||||
console.log(` ❌ 失败: 预期状态码 ${test.expectedStatus},实际 ${response.status}`);
|
||||
failCount++;
|
||||
}
|
||||
} else if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
successCount++;
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 失败: 接口返回 404`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` 状态码: ${error.response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
console.log(` ❌ 失败: ${error.response.statusText}`);
|
||||
failCount++;
|
||||
}
|
||||
} else {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log(` ✅ 成功: ${successCount} 个`);
|
||||
console.log(` ❌ 失败: ${failCount} 个`);
|
||||
console.log('');
|
||||
|
||||
if (failCount === 0) {
|
||||
console.log('🎉 所有测试通过!Location规则顺序修复成功!');
|
||||
} else {
|
||||
console.log('⚠️ 还有测试失败,请检查:');
|
||||
console.log(' 1. Nginx配置是否正确修改');
|
||||
console.log(' 2. Nginx服务是否已重启');
|
||||
console.log(' 3. 后端服务是否正常运行');
|
||||
console.log('');
|
||||
console.log('🔄 重启Nginx:');
|
||||
console.log(' sudo nginx -t');
|
||||
console.log(' sudo systemctl reload nginx');
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log('📝 修复后的nginx配置顺序:');
|
||||
console.log(' 1. location ^~ /api/government/auth/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/auth/;');
|
||||
console.log(' }');
|
||||
console.log(' 2. location ^~ /api/government/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' }');
|
||||
console.log(' 3. location ^~ /government/ {');
|
||||
console.log(' alias /data/vue/ningmuyun/government/dist/;');
|
||||
console.log(' }');
|
||||
console.log('');
|
||||
console.log('🔄 路径映射:');
|
||||
console.log(' /api/government/departments → http://localhost:5352/api/government/departments');
|
||||
console.log(' /api/government/data-center → http://localhost:5352/api/government/data-center');
|
||||
console.log(' /api/government/market-price → http://localhost:5352/api/government/market-price');
|
||||
console.log('');
|
||||
console.log('🚨 重要提醒:');
|
||||
console.log(' 修改nginx配置后,必须重启nginx服务才能生效!');
|
||||
console.log(' sudo nginx -t # 检查配置语法');
|
||||
console.log(' sudo systemctl reload nginx # 重新加载配置');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testLocationOrderFix().catch(console.error);
|
||||
66
government-backend/test-login-api.js
Normal file
66
government-backend/test-login-api.js
Normal file
@@ -0,0 +1,66 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试登录接口
|
||||
async function testLoginAPI() {
|
||||
console.log('🔍 测试政府端登录接口...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '本地后端直接测试',
|
||||
url: 'http://localhost:5352/api/auth/login',
|
||||
description: '直接访问后端服务'
|
||||
},
|
||||
{
|
||||
name: '生产环境nginx代理测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
description: '通过nginx代理访问'
|
||||
}
|
||||
];
|
||||
|
||||
const testData = {
|
||||
username: 'admin',
|
||||
password: 'admin123'
|
||||
};
|
||||
|
||||
for (const testCase of testCases) {
|
||||
console.log(`📋 ${testCase.name}`);
|
||||
console.log(` 描述: ${testCase.description}`);
|
||||
console.log(` URL: ${testCase.url}`);
|
||||
console.log(` 数据: ${JSON.stringify(testData)}`);
|
||||
|
||||
try {
|
||||
const response = await axios.post(testCase.url, testData, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
console.log(` ✅ 响应: ${JSON.stringify(response.data, null, 2)}`);
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误信息: ${JSON.stringify(error.response.data, null, 2)}`);
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(50));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 说明:');
|
||||
console.log(' - 如果本地测试成功但生产环境失败,说明nginx配置有问题');
|
||||
console.log(' - 如果两个都失败,说明后端服务有问题');
|
||||
console.log(' - 如果两个都成功,说明修复生效');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testLoginAPI().catch(console.error);
|
||||
114
government-backend/test-nginx-config.js
Normal file
114
government-backend/test-nginx-config.js
Normal file
@@ -0,0 +1,114 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试nginx配置问题
|
||||
async function testNginxConfig() {
|
||||
console.log('🔍 测试nginx配置问题...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '直接测试后端服务',
|
||||
url: 'http://localhost:5352/api/government/departments',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试部门接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
expected: '应该返回401认证错误'
|
||||
},
|
||||
{
|
||||
name: '测试重复路径接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/departments',
|
||||
expected: '应该返回200(如果nginx配置有问题)'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500;
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 接口不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: nginx配置问题,路径映射不正确`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 可能的问题:');
|
||||
console.log(' 1. nginx配置语法错误');
|
||||
console.log(' 2. nginx服务未重启');
|
||||
console.log(' 3. location规则冲突');
|
||||
console.log(' 4. proxy_pass配置错误');
|
||||
console.log('');
|
||||
console.log('🔄 建议的解决步骤:');
|
||||
console.log(' 1. 检查nginx配置语法: sudo nginx -t');
|
||||
console.log(' 2. 重启nginx服务: sudo systemctl reload nginx');
|
||||
console.log(' 3. 检查nginx错误日志: sudo tail -f /var/log/nginx/error.log');
|
||||
console.log(' 4. 检查nginx访问日志: sudo tail -f /var/log/nginx/ad.ningmuyun.com.access.log');
|
||||
console.log('');
|
||||
console.log('📝 当前nginx配置应该是:');
|
||||
console.log(' location ^~ /api/government/auth/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/auth/;');
|
||||
console.log(' }');
|
||||
console.log(' location ^~ /api/government/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' }');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testNginxConfig().catch(console.error);
|
||||
112
government-backend/test-nginx-debug.js
Normal file
112
government-backend/test-nginx-debug.js
Normal file
@@ -0,0 +1,112 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 调试nginx配置问题
|
||||
async function testNginxDebug() {
|
||||
console.log('🔍 调试nginx配置问题...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '直接测试后端服务',
|
||||
url: 'http://localhost:5352/api/government/departments',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试部门接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试数据中心接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试市场价格接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
expected: '应该返回200'
|
||||
},
|
||||
{
|
||||
name: '通过nginx测试登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
expected: '应该返回401认证错误'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 接口不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: nginx配置问题,路径映射不正确`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 调试完成!');
|
||||
console.log('');
|
||||
console.log('📝 当前nginx配置:');
|
||||
console.log(' location ^~ /api/government/auth/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/auth/;');
|
||||
console.log(' }');
|
||||
console.log(' location ^~ /api/government/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' }');
|
||||
console.log('');
|
||||
console.log('🔄 如果所有接口都返回404,可能需要:');
|
||||
console.log(' 1. 检查nginx配置语法: sudo nginx -t');
|
||||
console.log(' 2. 重启nginx服务: sudo systemctl reload nginx');
|
||||
console.log(' 3. 检查nginx错误日志: sudo tail -f /var/log/nginx/error.log');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testNginxDebug().catch(console.error);
|
||||
155
government-backend/test-nginx-fix-final.js
Normal file
155
government-backend/test-nginx-fix-final.js
Normal file
@@ -0,0 +1,155 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试nginx最终修复效果
|
||||
async function testNginxFixFinal() {
|
||||
console.log('🔍 测试nginx最终修复效果...\n');
|
||||
console.log('📝 修复说明:');
|
||||
console.log(' 将 proxy_pass http://localhost:5352');
|
||||
console.log(' 改为 proxy_pass http://localhost:5352/api/government/');
|
||||
console.log(' 这样可以正确代理到后端API路径\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '部门列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '数据中心接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '市场价格接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '养殖户列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farmers',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
shouldWork: true,
|
||||
expectedStatus: 401
|
||||
}
|
||||
];
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500;
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` 状态码: ${response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
if (test.expectedStatus) {
|
||||
if (response.status === test.expectedStatus) {
|
||||
console.log(` ✅ 成功: 返回预期状态码 ${test.expectedStatus}`);
|
||||
successCount++;
|
||||
} else {
|
||||
console.log(` ❌ 失败: 预期状态码 ${test.expectedStatus},实际 ${response.status}`);
|
||||
failCount++;
|
||||
}
|
||||
} else if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
successCount++;
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 失败: 接口返回 404`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` 状态码: ${error.response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
console.log(` ❌ 失败: ${error.response.statusText}`);
|
||||
failCount++;
|
||||
}
|
||||
} else {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log(` ✅ 成功: ${successCount} 个`);
|
||||
console.log(` ❌ 失败: ${failCount} 个`);
|
||||
console.log('');
|
||||
|
||||
if (failCount === 0) {
|
||||
console.log('🎉 所有测试通过!Nginx配置修复成功!');
|
||||
} else {
|
||||
console.log('⚠️ 还有测试失败,请检查:');
|
||||
console.log(' 1. Nginx配置是否正确修改');
|
||||
console.log(' 2. Nginx服务是否已重启');
|
||||
console.log(' 3. 后端服务是否正常运行');
|
||||
console.log('');
|
||||
console.log('🔄 重启Nginx:');
|
||||
console.log(' sudo nginx -t');
|
||||
console.log(' sudo systemctl reload nginx');
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log('📝 当前nginx配置:');
|
||||
console.log(' location ^~ /api/government/auth/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/auth/;');
|
||||
console.log(' }');
|
||||
console.log(' location ^~ /api/government/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' }');
|
||||
console.log('');
|
||||
console.log('🔄 路径映射:');
|
||||
console.log(' /api/government/departments → http://localhost:5352/api/government/departments');
|
||||
console.log(' /api/government/data-center → http://localhost:5352/api/government/data-center');
|
||||
console.log(' /api/government/market-price → http://localhost:5352/api/government/market-price');
|
||||
console.log('');
|
||||
console.log('🚨 重要提醒:');
|
||||
console.log(' 修改nginx配置后,必须重启nginx服务才能生效!');
|
||||
console.log(' sudo nginx -t # 检查配置语法');
|
||||
console.log(' sudo systemctl reload nginx # 重新加载配置');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testNginxFixFinal().catch(console.error);
|
||||
111
government-backend/test-nginx-fix.js
Normal file
111
government-backend/test-nginx-fix.js
Normal file
@@ -0,0 +1,111 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试nginx配置修复后的API接口
|
||||
async function testNginxFix() {
|
||||
console.log('🔍 测试nginx配置修复后的API接口...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '登录接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
expected: '应该返回401认证错误(接口正常)'
|
||||
},
|
||||
{
|
||||
name: '部门列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
method: 'GET',
|
||||
expected: '应该返回200状态码和部门数据'
|
||||
},
|
||||
{
|
||||
name: '数据中心接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
method: 'GET',
|
||||
expected: '应该返回200状态码和统计数据'
|
||||
},
|
||||
{
|
||||
name: '市场价格接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
method: 'GET',
|
||||
expected: '应该返回200状态码和价格数据'
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
method: 'GET',
|
||||
expected: '应该返回200状态码和岗位数据'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method,
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 接口不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: nginx配置可能还有问题`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 修复说明:');
|
||||
console.log(' 问题: location规则缩进不正确,被嵌套在错误的块内');
|
||||
console.log(' 修复: 调整了location规则的缩进和位置');
|
||||
console.log('');
|
||||
console.log('🔄 现在需要重启nginx使配置生效:');
|
||||
console.log(' sudo nginx -t # 检查配置语法');
|
||||
console.log(' sudo systemctl reload nginx # 重新加载配置');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testNginxFix().catch(console.error);
|
||||
150
government-backend/test-nginx-restart-fix.js
Normal file
150
government-backend/test-nginx-restart-fix.js
Normal file
@@ -0,0 +1,150 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试nginx重启后的修复效果
|
||||
async function testNginxRestartFix() {
|
||||
console.log('🔍 测试nginx重启后的修复效果...\n');
|
||||
console.log('📝 修复说明:');
|
||||
console.log(' 将 proxy_pass http://localhost:5352');
|
||||
console.log(' 改为 proxy_pass http://localhost:5352/api/government/');
|
||||
console.log(' 这样可以正确代理到后端API路径\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '部门列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '数据中心接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '市场价格接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '养殖户列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farmers',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
shouldWork: true,
|
||||
expectedStatus: 401
|
||||
}
|
||||
];
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500;
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` 状态码: ${response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
if (test.expectedStatus) {
|
||||
if (response.status === test.expectedStatus) {
|
||||
console.log(` ✅ 成功: 返回预期状态码 ${test.expectedStatus}`);
|
||||
successCount++;
|
||||
} else {
|
||||
console.log(` ❌ 失败: 预期状态码 ${test.expectedStatus},实际 ${response.status}`);
|
||||
failCount++;
|
||||
}
|
||||
} else if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
successCount++;
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 失败: 接口返回 404`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` 状态码: ${error.response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
console.log(` ❌ 失败: ${error.response.statusText}`);
|
||||
failCount++;
|
||||
}
|
||||
} else {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log(` ✅ 成功: ${successCount} 个`);
|
||||
console.log(` ❌ 失败: ${failCount} 个`);
|
||||
console.log('');
|
||||
|
||||
if (failCount === 0) {
|
||||
console.log('🎉 所有测试通过!Nginx配置修复成功!');
|
||||
} else {
|
||||
console.log('⚠️ 还有测试失败,请检查:');
|
||||
console.log(' 1. Nginx配置是否正确修改');
|
||||
console.log(' 2. Nginx服务是否已重启');
|
||||
console.log(' 3. 后端服务是否正常运行');
|
||||
console.log('');
|
||||
console.log('🔄 重启Nginx:');
|
||||
console.log(' sudo nginx -t');
|
||||
console.log(' sudo systemctl reload nginx');
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log('📝 当前nginx配置:');
|
||||
console.log(' location ^~ /api/government/auth/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/auth/;');
|
||||
console.log(' }');
|
||||
console.log(' location ^~ /api/government/ {');
|
||||
console.log(' proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' }');
|
||||
console.log('');
|
||||
console.log('🔄 路径映射:');
|
||||
console.log(' /api/government/departments → http://localhost:5352/api/government/departments');
|
||||
console.log(' /api/government/data-center → http://localhost:5352/api/government/data-center');
|
||||
console.log(' /api/government/market-price → http://localhost:5352/api/government/market-price');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testNginxRestartFix().catch(console.error);
|
||||
35
government-backend/test-nginx-syntax.js
Normal file
35
government-backend/test-nginx-syntax.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const { exec } = require('child_process');
|
||||
|
||||
// 测试nginx配置语法
|
||||
function testNginxSyntax() {
|
||||
console.log('🔍 测试nginx配置语法...\n');
|
||||
|
||||
exec('nginx -t', (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.log('❌ nginx配置语法错误:');
|
||||
console.log(stderr);
|
||||
console.log('\n🔍 可能的问题:');
|
||||
console.log(' 1. 缩进问题');
|
||||
console.log(' 2. 缺少分号');
|
||||
console.log(' 3. 括号不匹配');
|
||||
console.log(' 4. 隐藏字符');
|
||||
return;
|
||||
}
|
||||
|
||||
if (stderr) {
|
||||
console.log('⚠️ nginx配置警告:');
|
||||
console.log(stderr);
|
||||
}
|
||||
|
||||
console.log('✅ nginx配置语法正确');
|
||||
console.log(stdout);
|
||||
|
||||
console.log('\n🔄 建议的解决步骤:');
|
||||
console.log(' 1. 重启nginx服务: sudo systemctl reload nginx');
|
||||
console.log(' 2. 检查nginx错误日志: sudo tail -f /var/log/nginx/error.log');
|
||||
console.log(' 3. 检查nginx访问日志: sudo tail -f /var/log/nginx/access.log');
|
||||
});
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testNginxSyntax();
|
||||
115
government-backend/test-path-duplication-fix.js
Normal file
115
government-backend/test-path-duplication-fix.js
Normal file
@@ -0,0 +1,115 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试路径重复修复效果
|
||||
async function testPathDuplicationFix() {
|
||||
console.log('🔍 测试路径重复修复效果...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '正确路径 - 部门列表',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
expected: '应该返回200状态码'
|
||||
},
|
||||
{
|
||||
name: '重复路径 - 部门列表',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/departments',
|
||||
expected: '应该返回404错误(路径不存在)'
|
||||
},
|
||||
{
|
||||
name: '正确路径 - 数据中心',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
expected: '应该返回200状态码'
|
||||
},
|
||||
{
|
||||
name: '重复路径 - 数据中心',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/data-center',
|
||||
expected: '应该返回404错误(路径不存在)'
|
||||
},
|
||||
{
|
||||
name: '正确路径 - 市场价格',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
expected: '应该返回200状态码'
|
||||
},
|
||||
{
|
||||
name: '重复路径 - 市场价格',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/market-price?type=beef',
|
||||
expected: '应该返回404错误(路径不存在)'
|
||||
},
|
||||
{
|
||||
name: '正确路径 - 养殖类型',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farm-types',
|
||||
expected: '应该返回200状态码'
|
||||
},
|
||||
{
|
||||
name: '重复路径 - 养殖类型',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/farm-types',
|
||||
expected: '应该返回404错误(路径不存在)'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const response = await axios.get(test.url, {
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
});
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证(接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ✅ 正常: 路径不存在(符合预期)`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` ✅ 正常: 路径不存在(符合预期)`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
} else {
|
||||
console.log(` ❌ 请求配置错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 修复说明:');
|
||||
console.log(' 问题: proxy_pass配置导致路径重复');
|
||||
console.log(' 修复前: proxy_pass http://localhost:5352/api/government/;');
|
||||
console.log(' 修复后: proxy_pass http://localhost:5352/;');
|
||||
console.log('');
|
||||
console.log('🔄 路径映射:');
|
||||
console.log(' /api/government/departments → http://localhost:5352/api/government/departments');
|
||||
console.log(' /api/government/data-center → http://localhost:5352/api/government/data-center');
|
||||
console.log(' /api/government/market-price → http://localhost:5352/api/government/market-price');
|
||||
console.log('');
|
||||
console.log('🔄 现在需要重启nginx使配置生效:');
|
||||
console.log(' sudo nginx -t # 检查配置语法');
|
||||
console.log(' sudo systemctl reload nginx # 重新加载配置');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testPathDuplicationFix().catch(console.error);
|
||||
84
government-backend/test-path-fix.js
Normal file
84
government-backend/test-path-fix.js
Normal file
@@ -0,0 +1,84 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 测试路径修复效果
|
||||
async function testPathFix() {
|
||||
console.log('🔍 测试政府端API路径修复效果...\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '岗位列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
expected: '应该返回岗位列表数据'
|
||||
},
|
||||
{
|
||||
name: '部门列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
expected: '应该返回部门列表数据'
|
||||
},
|
||||
{
|
||||
name: '养殖户列表接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farmers',
|
||||
expected: '应该返回养殖户列表数据'
|
||||
},
|
||||
{
|
||||
name: '市场价格接口测试',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
expected: '应该返回市场价格数据'
|
||||
}
|
||||
];
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
console.log(` 预期: ${test.expected}`);
|
||||
|
||||
try {
|
||||
const response = await axios.get(test.url, {
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500; // 接受所有小于500的状态码
|
||||
}
|
||||
});
|
||||
|
||||
console.log(` ✅ 状态码: ${response.status}`);
|
||||
|
||||
if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 返回了正确的数据`);
|
||||
if (Array.isArray(response.data)) {
|
||||
console.log(` 📊 数据量: ${response.data.length} 条记录`);
|
||||
} else if (response.data && typeof response.data === 'object') {
|
||||
console.log(` 📊 数据类型: 对象,包含 ${Object.keys(response.data).length} 个字段`);
|
||||
}
|
||||
} else if (response.status === 401) {
|
||||
console.log(` ✅ 正常: 需要认证 (接口路径正确)`);
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 错误: 路径不存在`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` ❌ 状态码: ${error.response.status}`);
|
||||
console.log(` ❌ 错误: ${error.response.statusText}`);
|
||||
|
||||
if (error.response.status === 404) {
|
||||
console.log(` 🔍 分析: 可能是nginx配置问题,路径映射不正确`);
|
||||
}
|
||||
} else {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log('');
|
||||
console.log('📝 修复说明:');
|
||||
console.log(' - 修复前: proxy_pass http://localhost:5352/api/government/; (会重复路径)');
|
||||
console.log(' - 修复后: proxy_pass http://localhost:5352/; (正确映射)');
|
||||
console.log(' - 现在 /api/government/positions 应该正确映射到 /api/government/positions');
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
testPathFix().catch(console.error);
|
||||
149
government-backend/verify-nginx-fix.js
Normal file
149
government-backend/verify-nginx-fix.js
Normal file
@@ -0,0 +1,149 @@
|
||||
const axios = require('axios');
|
||||
|
||||
// 验证nginx修复效果
|
||||
async function verifyNginxFix() {
|
||||
console.log('🔍 验证 Nginx 修复效果...\n');
|
||||
console.log('📝 修改说明:');
|
||||
console.log(' 将 proxy_pass http://localhost:5352/api/government/');
|
||||
console.log(' 改为 proxy_pass http://localhost:5352');
|
||||
console.log(' 这样可以保持完整路径,避免路径重复\n');
|
||||
|
||||
const testCases = [
|
||||
{
|
||||
name: '部门列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/departments',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '数据中心接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/data-center',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '市场价格接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/market-price?type=beef',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '岗位列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/positions',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '养殖户列表接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/farmers',
|
||||
shouldWork: true
|
||||
},
|
||||
{
|
||||
name: '登录接口',
|
||||
url: 'https://ad.ningmuyun.com/api/government/auth/login',
|
||||
method: 'POST',
|
||||
data: { username: 'admin', password: 'admin123' },
|
||||
shouldWork: true,
|
||||
expectedStatus: 401
|
||||
},
|
||||
{
|
||||
name: '重复路径接口(应该404)',
|
||||
url: 'https://ad.ningmuyun.com/api/government/government/departments',
|
||||
shouldWork: false
|
||||
}
|
||||
];
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
for (const test of testCases) {
|
||||
console.log(`📋 ${test.name}`);
|
||||
console.log(` URL: ${test.url}`);
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: test.method || 'GET',
|
||||
url: test.url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 10000,
|
||||
validateStatus: function (status) {
|
||||
return status < 500;
|
||||
}
|
||||
};
|
||||
|
||||
if (test.data) {
|
||||
config.data = test.data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
|
||||
console.log(` 状态码: ${response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
if (test.expectedStatus) {
|
||||
if (response.status === test.expectedStatus) {
|
||||
console.log(` ✅ 成功: 返回预期状态码 ${test.expectedStatus}`);
|
||||
successCount++;
|
||||
} else {
|
||||
console.log(` ❌ 失败: 预期状态码 ${test.expectedStatus},实际 ${response.status}`);
|
||||
failCount++;
|
||||
}
|
||||
} else if (response.status === 200) {
|
||||
console.log(` ✅ 成功: 接口正常工作`);
|
||||
successCount++;
|
||||
} else if (response.status === 404) {
|
||||
console.log(` ❌ 失败: 接口返回 404`);
|
||||
failCount++;
|
||||
}
|
||||
} else {
|
||||
if (response.status === 404) {
|
||||
console.log(` ✅ 成功: 正确返回 404(重复路径应该不存在)`);
|
||||
successCount++;
|
||||
} else if (response.status === 200) {
|
||||
console.log(` ❌ 失败: 重复路径不应该工作`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
console.log(` 状态码: ${error.response.status}`);
|
||||
|
||||
if (test.shouldWork) {
|
||||
console.log(` ❌ 失败: ${error.response.statusText}`);
|
||||
failCount++;
|
||||
} else {
|
||||
if (error.response.status === 404) {
|
||||
console.log(` ✅ 成功: 正确返回 404`);
|
||||
successCount++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(` ❌ 网络错误: ${error.message}`);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' ' + '─'.repeat(60));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log('🎯 测试完成!');
|
||||
console.log(` ✅ 成功: ${successCount} 个`);
|
||||
console.log(` ❌ 失败: ${failCount} 个`);
|
||||
console.log('');
|
||||
|
||||
if (failCount === 0) {
|
||||
console.log('🎉 所有测试通过!Nginx 配置修复成功!');
|
||||
} else {
|
||||
console.log('⚠️ 还有测试失败,请检查:');
|
||||
console.log(' 1. Nginx 配置是否正确修改');
|
||||
console.log(' 2. Nginx 服务是否已重启');
|
||||
console.log(' 3. 浏览器缓存是否已清除');
|
||||
console.log('');
|
||||
console.log('🔄 重启 Nginx:');
|
||||
console.log(' sudo nginx -t');
|
||||
console.log(' sudo systemctl reload nginx');
|
||||
}
|
||||
}
|
||||
|
||||
// 运行验证
|
||||
verifyNginxFix().catch(console.error);
|
||||
126
government-backend/前端API路径修复说明.md
Normal file
126
government-backend/前端API路径修复说明.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# 前端API路径修复说明
|
||||
|
||||
## 问题描述
|
||||
- **错误URL**: `https://ad.ningmuyun.com/api/government/government/positions`
|
||||
- **问题**: URL中出现两个`government`,导致路径重复
|
||||
- **影响范围**: 政府端管理系统所有政府相关API接口
|
||||
|
||||
## 问题根因分析
|
||||
|
||||
### 1. 前端API配置问题 ❌
|
||||
在 `government-admin/src/utils/api.js` 中,政府相关的API接口缺少 `/api` 前缀:
|
||||
|
||||
**原始错误配置**:
|
||||
```javascript
|
||||
// 部门管理
|
||||
departments: {
|
||||
getList: (params) => instance.get('/government/departments', { params })
|
||||
},
|
||||
|
||||
// 岗位管理
|
||||
positions: {
|
||||
getList: (params) => instance.get('/government/positions', { params })
|
||||
},
|
||||
|
||||
// 养殖户管理
|
||||
farmers: {
|
||||
getList: (params) => instance.get('/government/farmers', { params }),
|
||||
create: (data) => instance.post('/government/farmers', data),
|
||||
// ... 其他方法
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 路径构造过程
|
||||
1. **前端baseURL**: `http://localhost:5352/api` (开发环境)
|
||||
2. **API调用**: `instance.get('/government/positions')`
|
||||
3. **实际请求**: `http://localhost:5352/api/government/positions` ✅ 正确
|
||||
|
||||
但在生产环境中:
|
||||
1. **前端baseURL**: `https://ad.ningmuyun.com` (生产环境)
|
||||
2. **API调用**: `instance.get('/government/positions')`
|
||||
3. **实际请求**: `https://ad.ningmuyun.com/government/positions` ❌ 缺少 `/api` 前缀
|
||||
4. **nginx匹配**: 匹配到 `location ^~ /government/` (静态文件规则)
|
||||
5. **重定向**: 可能被重写为 `/api/government/government/positions` ❌ 重复
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 修复前端API配置
|
||||
将所有政府相关API接口添加 `/api` 前缀:
|
||||
|
||||
```javascript
|
||||
// 部门管理
|
||||
departments: {
|
||||
getList: (params) => instance.get('/api/government/departments', { params })
|
||||
},
|
||||
|
||||
// 岗位管理
|
||||
positions: {
|
||||
getList: (params) => instance.get('/api/government/positions', { params })
|
||||
},
|
||||
|
||||
// 养殖户管理
|
||||
farmers: {
|
||||
getList: (params) => instance.get('/api/government/farmers', { params }),
|
||||
create: (data) => instance.post('/api/government/farmers', data),
|
||||
update: (id, data) => instance.put(`/api/government/farmers/${id}`, data),
|
||||
delete: (id) => instance.delete(`/api/government/farmers/${id}`),
|
||||
resetPassword: (id) => instance.post(`/api/government/farmers/${id}/reset-password`)
|
||||
},
|
||||
|
||||
// 养殖类型相关
|
||||
farmTypes: {
|
||||
getList: () => instance.get('/api/government/farm-types')
|
||||
},
|
||||
|
||||
// 养殖种类相关
|
||||
animalTypes: {
|
||||
getList: () => instance.get('/api/government/animal-types')
|
||||
},
|
||||
|
||||
// 智能项圈管理
|
||||
collars: {
|
||||
getList: (params) => instance.get('/api/government/collars', { params }),
|
||||
create: (data) => instance.post('/api/government/collars', data),
|
||||
update: (id, data) => instance.put(`/api/government/collars/${id}`, data),
|
||||
delete: (id) => instance.delete(`/api/government/collars/${id}`)
|
||||
}
|
||||
```
|
||||
|
||||
## 修复后的路径映射
|
||||
|
||||
| 前端调用 | 生产环境URL | nginx匹配 | 后端路由 | 状态 |
|
||||
|---------|------------|----------|---------|------|
|
||||
| `/api/government/departments` | `https://ad.ningmuyun.com/api/government/departments` | `^~ /api/government/` | `/api/government/departments` | ✅ 正确 |
|
||||
| `/api/government/positions` | `https://ad.ningmuyun.com/api/government/positions` | `^~ /api/government/` | `/api/government/positions` | ✅ 正确 |
|
||||
| `/api/government/farmers` | `https://ad.ningmuyun.com/api/government/farmers` | `^~ /api/government/` | `/api/government/farmers` | ✅ 正确 |
|
||||
|
||||
## 验证步骤
|
||||
|
||||
1. **重新构建前端项目**:
|
||||
```bash
|
||||
cd government-admin
|
||||
npm run build
|
||||
```
|
||||
|
||||
2. **部署到生产环境**:
|
||||
- 将构建后的文件部署到服务器
|
||||
- 确保nginx配置正确
|
||||
|
||||
3. **测试API接口**:
|
||||
```bash
|
||||
# 测试岗位接口
|
||||
curl https://ad.ningmuyun.com/api/government/positions
|
||||
|
||||
# 测试部门接口
|
||||
curl https://ad.ningmuyun.com/api/government/departments
|
||||
```
|
||||
|
||||
## 相关文件
|
||||
- `government-admin/src/utils/api.js` - 前端API配置文件
|
||||
- `government-backend/_etc_nginx_conf.d_ningmuyun_one.conf` - nginx配置文件
|
||||
|
||||
## 修复时间
|
||||
2024年12月19日
|
||||
|
||||
## 修复状态
|
||||
✅ 已修复 - 前端API配置已更新,添加了正确的 `/api` 前缀
|
||||
149
government-backend/检查nginx配置.md
Normal file
149
government-backend/检查nginx配置.md
Normal file
@@ -0,0 +1,149 @@
|
||||
# 检查 Nginx 配置的关键步骤
|
||||
|
||||
## 🔍 问题分析
|
||||
|
||||
根据您的反馈:
|
||||
- 修改 nginx 配置后,所有接口都返回 404
|
||||
- 只有登录接口正常(`/api/government/auth/login`)
|
||||
- 重复路径可以正常工作(`/api/government/government/departments`)
|
||||
|
||||
这说明:
|
||||
1. Nginx 已经重启并读取了新配置
|
||||
2. `/api/government/auth/` 规则工作正常
|
||||
3. `/api/government/` 规则可能有问题
|
||||
|
||||
## 🎯 关键问题
|
||||
|
||||
**重复路径能工作说明了什么?**
|
||||
|
||||
如果 `/api/government/government/departments` 能返回 200,说明:
|
||||
- 请求到达了 nginx
|
||||
- Nginx 将请求代理到了后端
|
||||
- 后端处理了 `/api/government/departments` 路径(去掉了一个 government)
|
||||
|
||||
这意味着当前的 proxy_pass 配置可能是:
|
||||
```nginx
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
```
|
||||
|
||||
这会导致:
|
||||
- 请求:`/api/government/government/departments`
|
||||
- Nginx 匹配:`location ^~ /api/government/`
|
||||
- 代理到:`http://localhost:5352/api/government/ + government/departments`
|
||||
- 实际请求:`http://localhost:5352/api/government/government/departments`
|
||||
|
||||
但是后端路由是 `/api/government/departments`,所以单个 government 的路径会 404。
|
||||
|
||||
## 🔧 正确的解决方案
|
||||
|
||||
需要修改 `proxy_pass` 配置,去掉路径重写:
|
||||
|
||||
```nginx
|
||||
# 错误的配置(当前)
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
# 这会导致路径重复:/api/government/departments → http://localhost:5352/api/government/departments
|
||||
}
|
||||
|
||||
# 正确的配置
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352;
|
||||
# 这会保持原始路径:/api/government/departments → http://localhost:5352/api/government/departments
|
||||
}
|
||||
```
|
||||
|
||||
## 📝 修改步骤
|
||||
|
||||
### 1. 修改 nginx 配置文件
|
||||
|
||||
找到 `_etc_nginx_conf.d_ningmuyun_one.conf` 文件中的这一段:
|
||||
|
||||
```nginx
|
||||
# 政府API代理 - 处理其他政府相关接口
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/; # ❌ 错误
|
||||
# ... 其他配置
|
||||
}
|
||||
```
|
||||
|
||||
修改为:
|
||||
|
||||
```nginx
|
||||
# 政府API代理 - 处理其他政府相关接口
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352; # ✅ 正确
|
||||
# ... 其他配置
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 重启 nginx
|
||||
|
||||
```bash
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
### 3. 验证修复
|
||||
|
||||
```bash
|
||||
# 应该返回 200
|
||||
curl -X GET https://ad.ningmuyun.com/api/government/departments
|
||||
|
||||
# 应该返回 404(因为路径不再重复)
|
||||
curl -X GET https://ad.ningmuyun.com/api/government/government/departments
|
||||
```
|
||||
|
||||
## 🎯 proxy_pass 尾部斜杠的区别
|
||||
|
||||
这是一个非常重要的 nginx 配置细节:
|
||||
|
||||
### 有尾部斜杠
|
||||
```nginx
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
}
|
||||
```
|
||||
- 请求:`/api/government/departments`
|
||||
- 匹配部分:`/api/government/`
|
||||
- 剩余部分:`departments`
|
||||
- 代理到:`http://localhost:5352/api/government/departments`
|
||||
|
||||
### 无尾部斜杠
|
||||
```nginx
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352;
|
||||
}
|
||||
```
|
||||
- 请求:`/api/government/departments`
|
||||
- 代理到:`http://localhost:5352/api/government/departments`(保持完整路径)
|
||||
|
||||
## 📊 当前配置应该是
|
||||
|
||||
```nginx
|
||||
# 政府认证API代理
|
||||
location ^~ /api/government/auth/ {
|
||||
proxy_pass http://localhost:5352/api/auth/; # ✅ 正确(需要去掉 government)
|
||||
# ... 其他配置
|
||||
}
|
||||
|
||||
# 政府API代理
|
||||
location ^~ /api/government/ {
|
||||
proxy_pass http://localhost:5352; # ✅ 正确(保持完整路径)
|
||||
# ... 其他配置
|
||||
}
|
||||
```
|
||||
|
||||
## 🚨 总结
|
||||
|
||||
修改配置文件,将:
|
||||
```nginx
|
||||
proxy_pass http://localhost:5352/api/government/;
|
||||
```
|
||||
|
||||
改为:
|
||||
```nginx
|
||||
proxy_pass http://localhost:5352;
|
||||
```
|
||||
|
||||
然后重启 nginx,问题应该就能解决了。
|
||||
|
||||
126
government-backend/登录接口404错误修复说明.md
Normal file
126
government-backend/登录接口404错误修复说明.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# 政府端登录接口404错误修复说明
|
||||
|
||||
## 问题描述
|
||||
- **错误URL**: `https://ad.ningmuyun.com/api/government/auth/login`
|
||||
- **错误状态**: 404 Not Found
|
||||
- **影响范围**: 政府端管理系统登录功能
|
||||
|
||||
## 问题根因分析
|
||||
|
||||
### 1. 前端API调用路径
|
||||
- **前端调用**: `https://ad.ningmuyun.com/api/government/auth/login`
|
||||
- **API定义**: `government-admin/src/utils/api.js` 第74行
|
||||
```javascript
|
||||
login: (data) => instance.post('/auth/login', data)
|
||||
```
|
||||
- **baseURL**: `http://localhost:5352/api` (开发环境)
|
||||
- **实际请求**: `http://localhost:5352/api/auth/login`
|
||||
|
||||
### 2. 后端路由配置 ✅
|
||||
- **认证路由**: `government-backend/routes/auth.js` 第6行
|
||||
```javascript
|
||||
router.post('/login', login)
|
||||
```
|
||||
- **应用路由注册**: `government-backend/app.js` 第39行
|
||||
```javascript
|
||||
app.use('/api/auth', require('./routes/auth'));
|
||||
```
|
||||
- **完整路径**: `/api/auth/login`
|
||||
|
||||
### 3. Nginx配置问题 ❌
|
||||
**问题所在**: nginx配置中缺少政府认证API的路径匹配
|
||||
|
||||
**原始配置问题**:
|
||||
- 只有 `/api/government/` 的代理配置
|
||||
- 缺少 `/api/government/auth/` 的专门代理配置
|
||||
- 导致 `/api/government/auth/login` 请求无法正确路由到后端的 `/api/auth/login`
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 修复nginx配置
|
||||
在 `government-backend/_etc_nginx_conf.d_ningmuyun_one.conf` 中添加政府认证API代理配置:
|
||||
|
||||
```nginx
|
||||
# 政府认证API代理 - 处理登录等认证接口
|
||||
location ^~ /api/government/auth/ {
|
||||
proxy_pass http://localhost:5352/api/auth/; # 政府后端认证服务端口
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# CORS配置
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com' always;
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
||||
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' 'https://ad.ningmuyun.com';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With';
|
||||
add_header 'Access-Control-Allow-Credentials' 'true';
|
||||
add_header 'Content-Type' 'text/plain; charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 路径映射说明
|
||||
- **前端请求**: `/api/government/auth/login`
|
||||
- **nginx匹配**: `location ^~ /api/government/auth/`
|
||||
- **代理转发**: `http://localhost:5352/api/auth/login`
|
||||
- **后端处理**: `/api/auth/login` (authController.login)
|
||||
|
||||
## 验证步骤
|
||||
|
||||
1. **重启nginx服务**:
|
||||
```bash
|
||||
sudo nginx -t # 检查配置语法
|
||||
sudo systemctl reload nginx # 重新加载配置
|
||||
```
|
||||
|
||||
2. **测试登录接口**:
|
||||
```bash
|
||||
curl -X POST https://ad.ningmuyun.com/api/government/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"test","password":"test"}'
|
||||
```
|
||||
|
||||
3. **检查前端登录功能**:
|
||||
- 访问政府端管理系统登录页面
|
||||
- 输入正确的用户名和密码
|
||||
- 确认登录成功
|
||||
|
||||
## 相关文件
|
||||
- `government-backend/_etc_nginx_conf.d_ningmuyun_one.conf` - nginx配置文件
|
||||
- `government-backend/routes/auth.js` - 认证路由
|
||||
- `government-backend/controllers/authController.js` - 认证控制器
|
||||
- `government-admin/src/utils/api.js` - 前端API配置
|
||||
- `government-admin/src/stores/auth.js` - 前端认证状态管理
|
||||
|
||||
## 修复时间
|
||||
2024年12月19日
|
||||
|
||||
## 修复状态
|
||||
✅ 已修复 - nginx配置已更新,添加了政府认证API代理配置
|
||||
|
||||
## 重要说明
|
||||
**配置优先级**: 在nginx中,更具体的location规则会优先匹配。因此:
|
||||
- `/api/government/auth/` 配置必须在 `/api/government/` 配置之前
|
||||
- 这样 `/api/government/auth/login` 会匹配到第一个规则,正确代理到 `/api/auth/login`
|
||||
- 其他 `/api/government/` 开头的请求会匹配到第二个规则
|
||||
|
||||
## 测试方法
|
||||
运行测试脚本验证修复效果:
|
||||
```bash
|
||||
cd government-backend
|
||||
node test-login-api.js
|
||||
```
|
||||
|
||||
## 重启nginx命令
|
||||
```bash
|
||||
sudo nginx -t # 检查配置语法
|
||||
sudo systemctl reload nginx # 重新加载配置
|
||||
```
|
||||
@@ -1,213 +0,0 @@
|
||||
# 政府端小程序架构说明
|
||||
|
||||
## 整体架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 政府端小程序架构 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 前端层 (Vue.js + uni-app) │
|
||||
│ ┌─────────────┬─────────────┬─────────────┬─────────────┐ │
|
||||
│ │ 首页组件 │ 登录组件 │ 看板组件 │ 管理组件 │ │
|
||||
│ │ Home.vue │ Login.vue │ Dashboard.vue│Supervision.vue│ │
|
||||
│ └─────────────┴─────────────┴─────────────┴─────────────┘ │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 服务层 (API Services) │
|
||||
│ ┌─────────────┬─────────────┬─────────────┬─────────────┐ │
|
||||
│ │ 认证服务 │ 看板服务 │ 监管服务 │ 审批服务 │ │
|
||||
│ │authService │dashboardSvc │supervisionSvc│approvalSvc │ │
|
||||
│ └─────────────┴─────────────┴─────────────┴─────────────┘ │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 工具层 (Utils) │
|
||||
│ ┌─────────────┬─────────────┬─────────────┬─────────────┐ │
|
||||
│ │ 认证工具 │ 请求工具 │ 路由守卫 │ 状态管理 │ │
|
||||
│ │ auth.js │ request.js │ router │ pinia │ │
|
||||
│ └─────────────┴─────────────┴─────────────┴─────────────┘ │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 后端API (Government Backend) │
|
||||
│ ┌─────────────┬─────────────┬─────────────┬─────────────┐ │
|
||||
│ │ 认证接口 │ 看板接口 │ 监管接口 │ 审批接口 │ │
|
||||
│ │ /api/auth │/api/visual │/api/superv │/api/approval│ │
|
||||
│ └─────────────┴─────────────┴─────────────┴─────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 组件架构
|
||||
|
||||
### 1. 页面组件 (Pages)
|
||||
```
|
||||
pages/
|
||||
├── index/
|
||||
│ └── index.vue # 首页
|
||||
├── login/
|
||||
│ └── login.vue # 登录页
|
||||
├── dashboard/
|
||||
│ └── dashboard.vue # 数据看板
|
||||
├── supervision/
|
||||
│ └── supervision.vue # 监管管理
|
||||
├── approval/
|
||||
│ └── approval.vue # 审批管理
|
||||
├── personnel/
|
||||
│ └── personnel.vue # 人员管理
|
||||
├── epidemic/
|
||||
│ └── epidemic.vue # 疫情监控
|
||||
├── service/
|
||||
│ └── service.vue # 服务管理
|
||||
├── warehouse/
|
||||
│ └── warehouse.vue # 仓库管理
|
||||
└── profile/
|
||||
└── profile.vue # 个人中心
|
||||
```
|
||||
|
||||
### 2. 业务组件 (Components)
|
||||
```
|
||||
components/
|
||||
├── Home.vue # 首页组件
|
||||
├── Login.vue # 登录组件
|
||||
├── Dashboard.vue # 数据看板组件
|
||||
├── Supervision.vue # 监管管理组件
|
||||
├── Approval.vue # 审批管理组件
|
||||
├── Personnel.vue # 人员管理组件
|
||||
├── Epidemic.vue # 疫情监控组件
|
||||
├── Service.vue # 服务管理组件
|
||||
├── Warehouse.vue # 仓库管理组件
|
||||
└── Profile.vue # 个人中心组件
|
||||
```
|
||||
|
||||
### 3. 服务层 (Services)
|
||||
```
|
||||
services/
|
||||
├── authService.js # 认证服务
|
||||
├── dashboardService.js # 看板服务
|
||||
├── supervisionService.js # 监管服务
|
||||
├── approvalService.js # 审批服务
|
||||
├── personnelService.js # 人员服务
|
||||
├── epidemicService.js # 疫情服务
|
||||
├── serviceService.js # 服务管理
|
||||
└── warehouseService.js # 仓库服务
|
||||
```
|
||||
|
||||
### 4. 工具层 (Utils)
|
||||
```
|
||||
utils/
|
||||
├── auth.js # 认证工具
|
||||
└── request.js # 请求工具
|
||||
```
|
||||
|
||||
## 数据流架构
|
||||
|
||||
### 1. 用户认证流程
|
||||
```
|
||||
用户输入 → Login组件 → authService → 后端API → 返回Token → 存储到本地 → 路由跳转
|
||||
```
|
||||
|
||||
### 2. 数据获取流程
|
||||
```
|
||||
组件挂载 → 调用Service → request工具 → 后端API → 数据处理 → 更新组件状态
|
||||
```
|
||||
|
||||
### 3. 状态管理流程
|
||||
```
|
||||
用户操作 → 组件事件 → 调用API → 更新数据 → 重新渲染 → 用户反馈
|
||||
```
|
||||
|
||||
## 技术栈架构
|
||||
|
||||
### 前端技术栈
|
||||
- **框架**: Vue 2.6 + uni-app
|
||||
- **状态管理**: Pinia
|
||||
- **路由**: Vue Router
|
||||
- **HTTP**: Axios
|
||||
- **样式**: Sass/SCSS
|
||||
- **构建**: Vue CLI + Vite
|
||||
|
||||
### 后端技术栈
|
||||
- **框架**: Express.js
|
||||
- **数据库**: MySQL (可选)
|
||||
- **认证**: JWT
|
||||
- **端口**: 5352
|
||||
|
||||
## 功能模块架构
|
||||
|
||||
### 1. 认证模块
|
||||
- 用户登录
|
||||
- Token管理
|
||||
- 权限控制
|
||||
- 路由守卫
|
||||
|
||||
### 2. 数据看板模块
|
||||
- 统计卡片
|
||||
- 图表展示
|
||||
- 实时数据
|
||||
- 预警信息
|
||||
|
||||
### 3. 管理模块
|
||||
- 监管管理
|
||||
- 审批管理
|
||||
- 人员管理
|
||||
- 疫情监控
|
||||
- 服务管理
|
||||
- 仓库管理
|
||||
|
||||
### 4. 个人中心模块
|
||||
- 用户信息
|
||||
- 设置管理
|
||||
- 退出登录
|
||||
|
||||
## 部署架构
|
||||
|
||||
### 开发环境
|
||||
```
|
||||
本地开发 → H5预览 → 微信开发者工具 → 真机调试
|
||||
```
|
||||
|
||||
### 生产环境
|
||||
```
|
||||
代码构建 → 服务器部署 → 域名配置 → 小程序发布
|
||||
```
|
||||
|
||||
## 安全架构
|
||||
|
||||
### 1. 前端安全
|
||||
- Token认证
|
||||
- 路由守卫
|
||||
- 输入验证
|
||||
- XSS防护
|
||||
|
||||
### 2. 后端安全
|
||||
- JWT认证
|
||||
- CORS配置
|
||||
- 请求验证
|
||||
- 错误处理
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 1. 前端优化
|
||||
- 组件懒加载
|
||||
- 图片压缩
|
||||
- 代码分割
|
||||
- 缓存策略
|
||||
|
||||
### 2. 网络优化
|
||||
- 请求合并
|
||||
- 数据缓存
|
||||
- 错误重试
|
||||
- 超时处理
|
||||
|
||||
## 扩展性设计
|
||||
|
||||
### 1. 组件扩展
|
||||
- 模块化设计
|
||||
- 可复用组件
|
||||
- 配置化开发
|
||||
- 主题定制
|
||||
|
||||
### 2. 功能扩展
|
||||
- 插件机制
|
||||
- 模块热插拔
|
||||
- API版本管理
|
||||
- 数据迁移
|
||||
|
||||
## 总结
|
||||
|
||||
政府端小程序采用现代化的前端架构,具有良好的可维护性、可扩展性和用户体验。通过模块化设计和组件化开发,实现了功能的快速迭代和团队协作开发。
|
||||
@@ -1,219 +0,0 @@
|
||||
# 政府端小程序完成报告
|
||||
|
||||
## 项目概述
|
||||
|
||||
基于养殖端小程序和政府端后端代码,成功创建了一个功能完整的政府端微信小程序。该项目采用Vue.js + uni-app技术栈,提供全面的政府管理功能。
|
||||
|
||||
## 完成情况
|
||||
|
||||
### ✅ 已完成功能
|
||||
|
||||
#### 1. 项目基础架构
|
||||
- [x] 完整的Vue 2.6 + uni-app项目结构
|
||||
- [x] 多端支持(微信小程序、H5、App)
|
||||
- [x] 响应式设计和移动端优化
|
||||
- [x] 模块化组件设计
|
||||
- [x] 完整的路由配置
|
||||
|
||||
#### 2. 用户认证系统
|
||||
- [x] 登录页面(用户名/密码)
|
||||
- [x] 用户信息管理
|
||||
- [x] Token认证机制
|
||||
- [x] 路由守卫和权限控制
|
||||
- [x] 退出登录功能
|
||||
|
||||
#### 3. 核心功能模块
|
||||
- [x] **数据看板** - 统计卡片、图表展示、实时数据
|
||||
- [x] **监管管理** - 记录管理、搜索筛选、状态跟踪
|
||||
- [x] **审批管理** - 审批流程、状态管理、操作记录
|
||||
- [x] **人员管理** - 人员信息、部门管理、联系方式
|
||||
- [x] **疫情监控** - 疫情数据、预警系统、风险等级
|
||||
- [x] **服务管理** - 服务项目、状态管理、分类管理
|
||||
- [x] **仓库管理** - 仓库信息、容量管理、管理员信息
|
||||
- [x] **个人中心** - 用户设置、头像编辑、系统配置
|
||||
|
||||
#### 4. 技术特性
|
||||
- [x] 完整的API服务层
|
||||
- [x] 统一的请求处理
|
||||
- [x] 错误处理和用户提示
|
||||
- [x] 加载状态管理
|
||||
- [x] 下拉刷新功能
|
||||
- [x] 搜索和筛选功能
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
government-mini-program/
|
||||
├── src/
|
||||
│ ├── components/ # 9个核心业务组件
|
||||
│ ├── pages/ # 9个页面文件
|
||||
│ ├── services/ # 8个API服务文件
|
||||
│ ├── utils/ # 2个工具类文件
|
||||
│ ├── styles/ # 样式文件
|
||||
│ ├── router/ # 路由配置
|
||||
│ ├── App.vue # 根组件
|
||||
│ └── main.js # 入口文件
|
||||
├── public/ # 静态资源
|
||||
├── static/ # 小程序静态资源
|
||||
├── 配置文件 # 项目配置文件
|
||||
├── 启动脚本 # 开发环境启动脚本
|
||||
└── 文档 # 完整的项目文档
|
||||
```
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 前端技术栈
|
||||
- **框架**: Vue 2.6 + uni-app
|
||||
- **状态管理**: Pinia
|
||||
- **路由**: Vue Router
|
||||
- **HTTP请求**: Axios
|
||||
- **样式**: Sass/SCSS
|
||||
- **构建工具**: Vue CLI + Vite
|
||||
|
||||
### 后端集成
|
||||
- **API基础地址**: http://localhost:5352/api
|
||||
- **认证方式**: JWT Token
|
||||
- **数据格式**: JSON
|
||||
- **错误处理**: 统一错误码
|
||||
|
||||
## 功能特色
|
||||
|
||||
### 1. 用户体验
|
||||
- 现代化的UI设计
|
||||
- 流畅的交互体验
|
||||
- 响应式布局
|
||||
- 移动端优化
|
||||
|
||||
### 2. 功能完整性
|
||||
- 8个核心功能模块
|
||||
- 完整的CRUD操作
|
||||
- 搜索和筛选功能
|
||||
- 状态管理
|
||||
|
||||
### 3. 技术先进性
|
||||
- 组件化开发
|
||||
- 模块化架构
|
||||
- 可维护性强
|
||||
- 扩展性好
|
||||
|
||||
## 文件统计
|
||||
|
||||
### 代码文件
|
||||
- **Vue组件**: 18个
|
||||
- **JavaScript文件**: 10个
|
||||
- **样式文件**: 3个
|
||||
- **配置文件**: 8个
|
||||
- **文档文件**: 5个
|
||||
|
||||
### 代码行数
|
||||
- **总代码行数**: 约3000行
|
||||
- **Vue组件**: 约2000行
|
||||
- **JavaScript**: 约800行
|
||||
- **样式**: 约200行
|
||||
|
||||
## 部署说明
|
||||
|
||||
### 开发环境
|
||||
```bash
|
||||
# 1. 安装依赖
|
||||
npm install
|
||||
|
||||
# 2. 启动后端服务
|
||||
cd ../government-backend
|
||||
npm start
|
||||
|
||||
# 3. 启动前端服务
|
||||
cd ../government-mini-program
|
||||
npm run dev:h5
|
||||
```
|
||||
|
||||
### 生产环境
|
||||
```bash
|
||||
# 1. 构建H5版本
|
||||
npm run build:h5
|
||||
|
||||
# 2. 构建微信小程序
|
||||
npm run build:mp-weixin
|
||||
```
|
||||
|
||||
## 使用指南
|
||||
|
||||
### 1. 快速开始
|
||||
1. 确保Node.js 16.20.2+已安装
|
||||
2. 启动后端服务(端口5352)
|
||||
3. 进入项目目录执行 `npm install`
|
||||
4. 执行 `npm run dev:h5` 启动开发服务器
|
||||
5. 访问 http://localhost:8080
|
||||
|
||||
### 2. 默认登录
|
||||
- 用户名: admin
|
||||
- 密码: 123456
|
||||
|
||||
### 3. 功能导航
|
||||
- 首页: 数据概览和快捷功能
|
||||
- 数据看板: 统计图表和数据分析
|
||||
- 监管管理: 监管记录和检查管理
|
||||
- 审批管理: 审批流程和状态跟踪
|
||||
- 人员管理: 工作人员信息管理
|
||||
- 疫情监控: 疫情数据和预警
|
||||
- 服务管理: 政府服务项目管理
|
||||
- 仓库管理: 物资仓库管理
|
||||
- 个人中心: 用户设置和个人信息
|
||||
|
||||
## 项目优势
|
||||
|
||||
### 1. 完整性
|
||||
- 功能模块完整
|
||||
- 代码结构清晰
|
||||
- 文档齐全
|
||||
- 可直接使用
|
||||
|
||||
### 2. 可维护性
|
||||
- 模块化设计
|
||||
- 组件化开发
|
||||
- 代码规范
|
||||
- 注释完整
|
||||
|
||||
### 3. 可扩展性
|
||||
- 易于添加新功能
|
||||
- 支持多端发布
|
||||
- 配置灵活
|
||||
- 架构合理
|
||||
|
||||
### 4. 用户体验
|
||||
- 界面美观
|
||||
- 操作流畅
|
||||
- 响应迅速
|
||||
- 交互友好
|
||||
|
||||
## 后续建议
|
||||
|
||||
### 1. 功能扩展
|
||||
- 添加更多数据可视化图表
|
||||
- 实现消息推送功能
|
||||
- 添加文件上传和下载
|
||||
- 集成地图定位功能
|
||||
|
||||
### 2. 性能优化
|
||||
- 实现虚拟滚动
|
||||
- 添加图片懒加载
|
||||
- 优化网络请求
|
||||
- 实现离线缓存
|
||||
|
||||
### 3. 用户体验
|
||||
- 添加暗黑模式
|
||||
- 实现多语言支持
|
||||
- 添加手势操作
|
||||
- 优化动画效果
|
||||
|
||||
## 总结
|
||||
|
||||
政府端小程序项目已成功完成,具备以下特点:
|
||||
|
||||
1. **功能完整**: 包含8个核心功能模块,满足政府管理需求
|
||||
2. **技术先进**: 采用现代化前端技术栈,代码质量高
|
||||
3. **易于使用**: 提供完整的文档和启动脚本
|
||||
4. **可扩展**: 模块化设计,易于维护和扩展
|
||||
5. **生产就绪**: 可直接用于生产环境
|
||||
|
||||
项目已准备好进行测试、部署和使用。所有核心功能都已实现,代码结构清晰,文档完整,可以立即投入使用。
|
||||
@@ -1,252 +0,0 @@
|
||||
# 政府端小程序项目总结
|
||||
|
||||
## 项目概述
|
||||
|
||||
基于养殖端小程序和政府端后端代码,成功创建了一个完整的政府端微信小程序,提供全面的政府管理功能。
|
||||
|
||||
## 已完成功能
|
||||
|
||||
### 1. 基础架构
|
||||
- ✅ 完整的Vue 2.6 + uni-app项目结构
|
||||
- ✅ 响应式设计,支持多端发布(微信小程序、H5、App)
|
||||
- ✅ 模块化组件设计,易于维护和扩展
|
||||
- ✅ 完整的路由配置和页面管理
|
||||
|
||||
### 2. 用户认证系统
|
||||
- ✅ 登录页面(用户名/密码登录)
|
||||
- ✅ 用户信息管理
|
||||
- ✅ Token认证机制
|
||||
- ✅ 路由守卫和权限控制
|
||||
|
||||
### 3. 核心功能模块
|
||||
|
||||
#### 数据看板 (Dashboard)
|
||||
- ✅ 数据概览卡片展示
|
||||
- ✅ 统计图表区域
|
||||
- ✅ 监管统计信息
|
||||
- ✅ 最近活动列表
|
||||
|
||||
#### 监管管理 (Supervision)
|
||||
- ✅ 监管记录列表
|
||||
- ✅ 搜索和筛选功能
|
||||
- ✅ 新增/编辑/删除监管记录
|
||||
- ✅ 状态管理(待处理、进行中、已完成、已逾期)
|
||||
|
||||
#### 审批管理 (Approval)
|
||||
- ✅ 审批记录列表
|
||||
- ✅ 审批通过/拒绝功能
|
||||
- ✅ 审批状态跟踪
|
||||
- ✅ 申请人信息管理
|
||||
|
||||
#### 人员管理 (Personnel)
|
||||
- ✅ 人员信息列表
|
||||
- ✅ 头像和基本信息展示
|
||||
- ✅ 部门角色管理
|
||||
- ✅ 联系方式管理
|
||||
|
||||
#### 疫情监控 (Epidemic)
|
||||
- ✅ 疫情预警系统
|
||||
- ✅ 疫情数据统计
|
||||
- ✅ 疫情记录管理
|
||||
- ✅ 风险等级显示
|
||||
|
||||
#### 服务管理 (Service)
|
||||
- ✅ 服务项目列表
|
||||
- ✅ 服务状态管理
|
||||
- ✅ 服务分类管理
|
||||
- ✅ 服务描述和详情
|
||||
|
||||
#### 仓库管理 (Warehouse)
|
||||
- ✅ 仓库信息管理
|
||||
- ✅ 容量和位置信息
|
||||
- ✅ 管理员信息
|
||||
- ✅ 仓库状态跟踪
|
||||
|
||||
#### 个人中心 (Profile)
|
||||
- ✅ 用户信息展示
|
||||
- ✅ 头像编辑功能
|
||||
- ✅ 设置菜单
|
||||
- ✅ 退出登录功能
|
||||
|
||||
### 4. 技术特性
|
||||
- ✅ 完整的API服务层
|
||||
- ✅ 统一的请求处理
|
||||
- ✅ 错误处理和用户提示
|
||||
- ✅ 加载状态管理
|
||||
- ✅ 下拉刷新功能
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
government-mini-program/
|
||||
├── src/
|
||||
│ ├── components/ # 核心组件
|
||||
│ │ ├── Home.vue # 首页
|
||||
│ │ ├── Login.vue # 登录
|
||||
│ │ ├── Dashboard.vue # 数据看板
|
||||
│ │ ├── Supervision.vue # 监管管理
|
||||
│ │ ├── Approval.vue # 审批管理
|
||||
│ │ ├── Personnel.vue # 人员管理
|
||||
│ │ ├── Epidemic.vue # 疫情监控
|
||||
│ │ ├── Service.vue # 服务管理
|
||||
│ │ ├── Warehouse.vue # 仓库管理
|
||||
│ │ └── Profile.vue # 个人中心
|
||||
│ ├── pages/ # 页面文件
|
||||
│ ├── services/ # API服务
|
||||
│ │ ├── authService.js
|
||||
│ │ ├── dashboardService.js
|
||||
│ │ ├── supervisionService.js
|
||||
│ │ ├── approvalService.js
|
||||
│ │ ├── personnelService.js
|
||||
│ │ ├── epidemicService.js
|
||||
│ │ ├── serviceService.js
|
||||
│ │ └── warehouseService.js
|
||||
│ ├── utils/ # 工具类
|
||||
│ │ ├── auth.js # 认证工具
|
||||
│ │ └── request.js # 请求工具
|
||||
│ ├── styles/ # 样式文件
|
||||
│ │ ├── variables.scss # 变量
|
||||
│ │ └── mixins.scss # 混入
|
||||
│ ├── router/ # 路由配置
|
||||
│ ├── App.vue # 根组件
|
||||
│ └── main.js # 入口文件
|
||||
├── public/ # 静态资源
|
||||
├── static/ # 小程序静态资源
|
||||
├── package.json # 项目配置
|
||||
├── pages.json # 页面配置
|
||||
├── manifest.json # 应用配置
|
||||
├── vue.config.js # Vue配置
|
||||
├── vite.config.js # Vite配置
|
||||
├── start-dev.bat # Windows启动脚本
|
||||
├── start-dev.sh # Linux/Mac启动脚本
|
||||
└── README.md # 项目说明
|
||||
```
|
||||
|
||||
## API接口集成
|
||||
|
||||
### 认证接口
|
||||
- `POST /api/auth/login` - 用户登录
|
||||
- `GET /api/auth/userinfo` - 获取用户信息
|
||||
|
||||
### 数据看板接口
|
||||
- `GET /api/visualization/data` - 获取可视化数据
|
||||
- `GET /api/supervision/stats` - 获取监管统计
|
||||
- `GET /api/approval/stats` - 获取审批统计
|
||||
- `GET /api/personnel/stats` - 获取人员统计
|
||||
- `GET /api/epidemic/stats` - 获取疫情统计
|
||||
- `GET /api/service/stats` - 获取服务统计
|
||||
- `GET /api/warehouse/stats` - 获取仓库统计
|
||||
|
||||
### 管理功能接口
|
||||
- 监管管理:`/api/supervision/*`
|
||||
- 审批管理:`/api/approval/*`
|
||||
- 人员管理:`/api/personnel/*`
|
||||
- 疫情监控:`/api/epidemic/*`
|
||||
- 服务管理:`/api/service/*`
|
||||
- 仓库管理:`/api/warehouse/*`
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 1. 环境准备
|
||||
```bash
|
||||
# 确保Node.js 16.20.2+已安装
|
||||
node --version
|
||||
|
||||
# 确保后端服务运行在 http://localhost:5352
|
||||
```
|
||||
|
||||
### 2. 安装和启动
|
||||
```bash
|
||||
# 进入项目目录
|
||||
cd government-mini-program
|
||||
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 启动开发服务器
|
||||
npm run dev:h5
|
||||
```
|
||||
|
||||
### 3. 微信小程序开发
|
||||
```bash
|
||||
# 构建微信小程序
|
||||
npm run build:mp-weixin
|
||||
|
||||
# 使用微信开发者工具打开 dist/mp-weixin 目录
|
||||
```
|
||||
|
||||
### 4. 默认登录信息
|
||||
- 用户名:admin
|
||||
- 密码:123456
|
||||
|
||||
## 特色功能
|
||||
|
||||
### 1. 响应式设计
|
||||
- 适配不同屏幕尺寸
|
||||
- 支持横屏和竖屏模式
|
||||
- 优化的移动端交互体验
|
||||
|
||||
### 2. 模块化架构
|
||||
- 组件化开发,易于维护
|
||||
- 服务层分离,便于测试
|
||||
- 统一的样式和主题管理
|
||||
|
||||
### 3. 用户体验优化
|
||||
- 加载状态提示
|
||||
- 错误处理和用户反馈
|
||||
- 下拉刷新和上拉加载
|
||||
- 搜索和筛选功能
|
||||
|
||||
### 4. 数据可视化
|
||||
- 统计卡片展示
|
||||
- 图表数据展示
|
||||
- 实时数据更新
|
||||
- 预警信息提示
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **前端框架**: Vue 2.6 + uni-app
|
||||
- **UI组件**: 自定义组件 + Vant Weapp
|
||||
- **状态管理**: Pinia
|
||||
- **路由管理**: Vue Router
|
||||
- **HTTP请求**: Axios
|
||||
- **样式预处理**: Sass/SCSS
|
||||
- **构建工具**: Vue CLI + Vite
|
||||
- **开发语言**: JavaScript ES6+
|
||||
|
||||
## 部署说明
|
||||
|
||||
### H5部署
|
||||
1. 执行 `npm run build:h5`
|
||||
2. 将 `dist` 目录上传到服务器
|
||||
3. 配置nginx或其他web服务器
|
||||
|
||||
### 微信小程序部署
|
||||
1. 使用微信开发者工具打开项目
|
||||
2. 配置小程序AppID
|
||||
3. 点击上传,提交审核
|
||||
4. 审核通过后发布
|
||||
|
||||
## 后续扩展建议
|
||||
|
||||
### 1. 功能扩展
|
||||
- 添加更多数据可视化图表
|
||||
- 实现消息推送功能
|
||||
- 添加文件上传和下载
|
||||
- 集成地图定位功能
|
||||
|
||||
### 2. 性能优化
|
||||
- 实现虚拟滚动
|
||||
- 添加图片懒加载
|
||||
- 优化网络请求
|
||||
- 实现离线缓存
|
||||
|
||||
### 3. 用户体验
|
||||
- 添加暗黑模式
|
||||
- 实现多语言支持
|
||||
- 添加手势操作
|
||||
- 优化动画效果
|
||||
|
||||
## 总结
|
||||
|
||||
政府端小程序已成功创建,具备完整的政府管理功能,包括数据看板、监管管理、审批管理、人员管理、疫情监控、服务管理和仓库管理等核心模块。项目采用现代化的前端技术栈,具有良好的可维护性和扩展性,可以直接用于生产环境。
|
||||
@@ -1,138 +0,0 @@
|
||||
# 政府端小程序快速开始指南
|
||||
|
||||
## 🚀 快速启动
|
||||
|
||||
### 1. 环境检查
|
||||
确保您的开发环境满足以下要求:
|
||||
- Node.js 16.20.2 或更高版本
|
||||
- npm 8.0.0 或更高版本
|
||||
- 微信开发者工具(用于小程序开发)
|
||||
|
||||
### 2. 后端服务
|
||||
确保政府端后端服务正在运行:
|
||||
```bash
|
||||
# 在 government-backend 目录下
|
||||
npm start
|
||||
# 服务将在 http://localhost:5352 启动
|
||||
```
|
||||
|
||||
### 3. 启动小程序
|
||||
```bash
|
||||
# 进入项目目录
|
||||
cd government-mini-program
|
||||
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 启动H5开发服务器
|
||||
npm run dev:h5
|
||||
```
|
||||
|
||||
### 4. 访问应用
|
||||
打开浏览器访问:http://localhost:8080
|
||||
|
||||
## 🔑 登录信息
|
||||
|
||||
使用以下默认账号登录:
|
||||
- **用户名**: admin
|
||||
- **密码**: 123456
|
||||
|
||||
## 📱 功能导航
|
||||
|
||||
### 主要功能模块
|
||||
1. **首页** - 数据概览和快捷功能
|
||||
2. **数据看板** - 统计图表和数据分析
|
||||
3. **监管管理** - 监管记录和检查管理
|
||||
4. **审批管理** - 审批流程和状态跟踪
|
||||
5. **人员管理** - 工作人员信息管理
|
||||
6. **疫情监控** - 疫情数据和预警
|
||||
7. **服务管理** - 政府服务项目管理
|
||||
8. **仓库管理** - 物资仓库管理
|
||||
9. **个人中心** - 用户设置和个人信息
|
||||
|
||||
### 快捷操作
|
||||
- **搜索**: 在列表页面使用搜索框快速查找
|
||||
- **筛选**: 点击筛选按钮进行条件筛选
|
||||
- **新增**: 点击右上角"+"按钮添加新记录
|
||||
- **编辑**: 点击列表项的编辑按钮
|
||||
- **删除**: 点击列表项的删除按钮
|
||||
|
||||
## 🛠️ 开发模式
|
||||
|
||||
### H5开发
|
||||
```bash
|
||||
npm run dev:h5
|
||||
```
|
||||
- 支持热重载
|
||||
- 自动打开浏览器
|
||||
- 支持移动端调试
|
||||
|
||||
### 微信小程序开发
|
||||
```bash
|
||||
# 构建小程序
|
||||
npm run build:mp-weixin
|
||||
|
||||
# 使用微信开发者工具打开 dist/mp-weixin 目录
|
||||
```
|
||||
|
||||
### 生产构建
|
||||
```bash
|
||||
# H5生产构建
|
||||
npm run build:h5
|
||||
|
||||
# 微信小程序生产构建
|
||||
npm run build:mp-weixin
|
||||
```
|
||||
|
||||
## 🔧 配置说明
|
||||
|
||||
### API配置
|
||||
在 `src/utils/request.js` 中配置API基础地址:
|
||||
```javascript
|
||||
const BASE_URL = process.env.NODE_ENV === 'development'
|
||||
? 'http://localhost:5352/api'
|
||||
: 'https://your-domain.com/api'
|
||||
```
|
||||
|
||||
### 微信小程序配置
|
||||
在 `manifest.json` 中配置小程序信息:
|
||||
```json
|
||||
{
|
||||
"mp-weixin": {
|
||||
"appid": "your-wechat-appid",
|
||||
"setting": {
|
||||
"urlCheck": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📋 常见问题
|
||||
|
||||
### Q: 无法连接到后端服务
|
||||
A: 确保后端服务正在运行,检查端口5352是否被占用
|
||||
|
||||
### Q: 登录失败
|
||||
A: 检查用户名密码是否正确,确保后端认证接口正常
|
||||
|
||||
### Q: 页面显示异常
|
||||
A: 检查浏览器控制台错误信息,确保所有依赖已正确安装
|
||||
|
||||
### Q: 微信小程序无法预览
|
||||
A: 确保已配置正确的AppID,检查网络连接
|
||||
|
||||
## 🎯 下一步
|
||||
|
||||
1. **自定义配置**: 根据实际需求修改API地址和配置
|
||||
2. **添加功能**: 在现有基础上扩展新的功能模块
|
||||
3. **样式调整**: 根据设计稿调整UI样式和布局
|
||||
4. **数据对接**: 连接真实的后端API接口
|
||||
5. **测试部署**: 进行功能测试和生产环境部署
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如有问题,请参考:
|
||||
- 项目README.md文件
|
||||
- 项目PROJECT_SUMMARY.md文件
|
||||
- 微信小程序官方文档
|
||||
- Vue.js官方文档
|
||||
@@ -1,197 +1,157 @@
|
||||
# 政府端小程序
|
||||
# 政府端微信小程序
|
||||
|
||||
基于Vue.js和uni-app开发的政府管理系统微信小程序,提供完整的政府监管、审批、人员管理等功能。
|
||||
## 项目简介
|
||||
|
||||
这是一个基于微信小程序开发的政府端管理系统,主要用于政府部门的养殖户管理和相关业务处理。
|
||||
|
||||
## 功能特性
|
||||
|
||||
### 核心功能
|
||||
- **数据看板**: 实时展示各类统计数据和分析图表
|
||||
- **监管管理**: 监管记录管理、检查任务分配、结果跟踪
|
||||
- **审批管理**: 各类申请审批流程管理
|
||||
- **人员管理**: 政府工作人员信息管理
|
||||
- **疫情监控**: 疫情数据监控和预警
|
||||
- **服务管理**: 政府服务项目管理
|
||||
- **仓库管理**: 物资仓库管理
|
||||
### 🏠 首页
|
||||
- **智能硬件设备管理**
|
||||
- 项圈仓库
|
||||
- 耳标仓库
|
||||
- 脚环仓库
|
||||
- 主机仓库
|
||||
|
||||
### 技术特性
|
||||
- 基于Vue 2.6 + uni-app框架
|
||||
- 支持微信小程序、H5、App多端发布
|
||||
- 响应式设计,适配各种屏幕尺寸
|
||||
- 模块化组件设计,易于维护和扩展
|
||||
- 完整的API接口集成
|
||||
- 用户认证和权限管理
|
||||
- **其它功能模块**
|
||||
- 牲畜身份认证
|
||||
- 无纸化检疫
|
||||
- 检疫证查询
|
||||
- 金融保险监管
|
||||
- 屠宰场监管
|
||||
- 检疫站监管
|
||||
- 无纸化防疫
|
||||
- 防疫站监管
|
||||
|
||||
### 👥 养殖户管理
|
||||
- 养殖户信息列表
|
||||
- 搜索功能(按姓名、手机号)
|
||||
- 添加/编辑/删除养殖户
|
||||
- 分页加载
|
||||
- 下拉刷新
|
||||
|
||||
### 👤 我的
|
||||
- 个人信息展示
|
||||
- 系统设置
|
||||
- 帮助中心
|
||||
- 关于我们
|
||||
- 退出登录
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **框架**:微信小程序原生开发
|
||||
- **语言**:JavaScript、WXML、WXSS
|
||||
- **工具**:微信开发者工具
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
government-mini-program/
|
||||
├── src/
|
||||
│ ├── components/ # 组件目录
|
||||
│ │ ├── Home.vue # 首页组件
|
||||
│ │ ├── Login.vue # 登录组件
|
||||
│ │ ├── Dashboard.vue # 数据看板
|
||||
│ │ ├── Supervision.vue # 监管管理
|
||||
│ │ ├── Approval.vue # 审批管理
|
||||
│ │ ├── Personnel.vue # 人员管理
|
||||
│ │ ├── Epidemic.vue # 疫情监控
|
||||
│ │ ├── Service.vue # 服务管理
|
||||
│ │ ├── Warehouse.vue # 仓库管理
|
||||
│ │ └── Profile.vue # 个人中心
|
||||
│ ├── pages/ # 页面目录
|
||||
│ ├── services/ # API服务
|
||||
│ ├── utils/ # 工具类
|
||||
│ ├── styles/ # 样式文件
|
||||
│ ├── router/ # 路由配置
|
||||
│ ├── App.vue # 根组件
|
||||
│ └── main.js # 入口文件
|
||||
├── public/ # 静态资源
|
||||
├── package.json # 项目配置
|
||||
├── pages.json # 页面配置
|
||||
├── manifest.json # 应用配置
|
||||
└── vue.config.js # Vue配置
|
||||
├── app.js # 小程序入口文件
|
||||
├── app.json # 全局配置
|
||||
├── app.wxss # 全局样式
|
||||
├── pages/ # 页面目录
|
||||
│ ├── index/ # 首页
|
||||
│ │ ├── index.js
|
||||
│ │ ├── index.json
|
||||
│ │ ├── index.wxml
|
||||
│ │ └── index.wxss
|
||||
│ ├── farmer/ # 养殖户管理
|
||||
│ │ ├── farmer.js
|
||||
│ │ ├── farmer.json
|
||||
│ │ ├── farmer.wxml
|
||||
│ │ └── farmer.wxss
|
||||
│ └── profile/ # 我的
|
||||
│ ├── profile.js
|
||||
│ ├── profile.json
|
||||
│ ├── profile.wxml
|
||||
│ └── profile.wxss
|
||||
├── images/ # 图片资源
|
||||
├── sitemap.json # 站点地图
|
||||
├── project.config.json # 项目配置
|
||||
└── package.json # 包管理文件
|
||||
```
|
||||
|
||||
## 开发环境
|
||||
## 快速开始
|
||||
|
||||
### 环境要求
|
||||
- Node.js 16.20.2+
|
||||
- npm 8.0.0+
|
||||
- 微信开发者工具
|
||||
### 1. 环境准备
|
||||
- 安装微信开发者工具
|
||||
- 注册微信小程序账号
|
||||
|
||||
### 安装依赖
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
### 2. 导入项目
|
||||
1. 打开微信开发者工具
|
||||
2. 选择"导入项目"
|
||||
3. 选择项目根目录
|
||||
4. 填写AppID(测试可使用测试号)
|
||||
5. 点击"确定"
|
||||
|
||||
### 开发模式
|
||||
```bash
|
||||
# H5开发
|
||||
npm run dev:h5
|
||||
### 3. 运行项目
|
||||
- 在微信开发者工具中点击"编译"
|
||||
- 在模拟器中查看效果
|
||||
- 使用真机调试测试实际效果
|
||||
|
||||
# 微信小程序开发
|
||||
npm run dev:mp-weixin
|
||||
```
|
||||
## 设计说明
|
||||
|
||||
### 构建生产版本
|
||||
```bash
|
||||
# H5构建
|
||||
npm run build:h5
|
||||
### 视觉设计
|
||||
- **主色调**:绿色 (#4CAF50)
|
||||
- **背景色**:浅灰色 (#f5f5f5)
|
||||
- **卡片样式**:圆角、阴影效果
|
||||
- **图标设计**:圆形背景、彩色配色
|
||||
|
||||
# 微信小程序构建
|
||||
npm run build:mp-weixin
|
||||
```
|
||||
### 布局特点
|
||||
- **顶部导航**:绿色背景,白色文字
|
||||
- **功能区域**:卡片式布局,网格排列
|
||||
- **底部导航**:3个标签页,图标+文字
|
||||
- **响应式**:适配不同屏幕尺寸
|
||||
|
||||
## API接口
|
||||
## 开发说明
|
||||
|
||||
### 认证接口
|
||||
- `POST /api/auth/login` - 用户登录
|
||||
- `GET /api/auth/userinfo` - 获取用户信息
|
||||
### 页面配置
|
||||
每个页面包含4个文件:
|
||||
- `.js` - 页面逻辑
|
||||
- `.json` - 页面配置
|
||||
- `.wxml` - 页面结构
|
||||
- `.wxss` - 页面样式
|
||||
|
||||
### 数据看板接口
|
||||
- `GET /api/visualization/data` - 获取可视化数据
|
||||
- `GET /api/supervision/stats` - 获取监管统计
|
||||
- `GET /api/approval/stats` - 获取审批统计
|
||||
### 数据管理
|
||||
- 使用Page的data属性管理页面数据
|
||||
- 通过setData方法更新数据
|
||||
- 支持下拉刷新和上拉加载
|
||||
|
||||
### 监管管理接口
|
||||
- `GET /api/supervision/list` - 获取监管列表
|
||||
- `POST /api/supervision` - 创建监管记录
|
||||
- `PUT /api/supervision/:id` - 更新监管记录
|
||||
- `DELETE /api/supervision/:id` - 删除监管记录
|
||||
|
||||
### 审批管理接口
|
||||
- `GET /api/approval/list` - 获取审批列表
|
||||
- `POST /api/approval` - 创建审批
|
||||
- `POST /api/approval/:id/approve` - 审批通过
|
||||
- `POST /api/approval/:id/reject` - 审批拒绝
|
||||
|
||||
### 人员管理接口
|
||||
- `GET /api/personnel/list` - 获取人员列表
|
||||
- `POST /api/personnel` - 创建人员
|
||||
- `PUT /api/personnel/:id` - 更新人员
|
||||
- `DELETE /api/personnel/:id` - 删除人员
|
||||
|
||||
### 疫情监控接口
|
||||
- `GET /api/epidemic/list` - 获取疫情列表
|
||||
- `POST /api/epidemic` - 创建疫情记录
|
||||
- `GET /api/epidemic/stats` - 获取疫情统计
|
||||
|
||||
### 服务管理接口
|
||||
- `GET /api/service/list` - 获取服务列表
|
||||
- `POST /api/service` - 创建服务
|
||||
- `PUT /api/service/:id` - 更新服务
|
||||
- `DELETE /api/service/:id` - 删除服务
|
||||
|
||||
### 仓库管理接口
|
||||
- `GET /api/warehouse/list` - 获取仓库列表
|
||||
- `POST /api/warehouse` - 创建仓库
|
||||
- `PUT /api/warehouse/:id` - 更新仓库
|
||||
- `DELETE /api/warehouse/:id` - 删除仓库
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 环境配置
|
||||
在项目根目录创建 `.env` 文件(或复制 `config.env` 为 `.env`):
|
||||
```
|
||||
# API基础地址
|
||||
VUE_APP_API_BASE_URL=http://localhost:5352/api
|
||||
|
||||
# 应用配置
|
||||
VUE_APP_TITLE=政府管理系统
|
||||
VUE_APP_VERSION=1.0.0
|
||||
```
|
||||
|
||||
### 微信小程序配置
|
||||
在 `manifest.json` 中配置小程序信息:
|
||||
```json
|
||||
{
|
||||
"mp-weixin": {
|
||||
"appid": "your-wechat-appid",
|
||||
"setting": {
|
||||
"urlCheck": false,
|
||||
"es6": true,
|
||||
"postcss": true,
|
||||
"minified": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 部署说明
|
||||
|
||||
### 微信小程序部署
|
||||
1. 使用微信开发者工具打开项目
|
||||
2. 配置小程序AppID
|
||||
3. 点击上传,提交审核
|
||||
4. 审核通过后发布
|
||||
|
||||
### H5部署
|
||||
1. 执行 `npm run build:h5`
|
||||
2. 将 `dist` 目录上传到服务器
|
||||
3. 配置nginx或其他web服务器
|
||||
|
||||
## 开发指南
|
||||
|
||||
### 添加新页面
|
||||
1. 在 `src/pages` 目录下创建页面文件夹
|
||||
2. 在 `pages.json` 中注册页面
|
||||
3. 在 `src/router/index.js` 中添加路由
|
||||
|
||||
### 添加新组件
|
||||
1. 在 `src/components` 目录下创建组件文件
|
||||
2. 在需要使用的页面中导入并使用
|
||||
|
||||
### 添加新API
|
||||
1. 在 `src/services` 目录下创建服务文件
|
||||
2. 在 `src/utils/request.js` 中添加请求方法
|
||||
3. 在组件中调用API
|
||||
### 事件处理
|
||||
- 使用bindtap绑定点击事件
|
||||
- 使用catchtap阻止事件冒泡
|
||||
- 支持表单输入和数据提交
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保后端API服务正常运行
|
||||
2. 检查网络请求配置和跨域设置
|
||||
3. 微信小程序需要配置合法域名
|
||||
4. 生产环境需要配置HTTPS
|
||||
1. **图标资源**:当前使用占位符图标,建议替换为实际设计图标
|
||||
2. **API接口**:功能模块目前显示"开发中"提示,需要对接实际后端API
|
||||
3. **权限控制**:根据实际需求添加用户权限验证
|
||||
4. **错误处理**:完善网络请求和异常情况的处理
|
||||
5. **性能优化**:图片压缩、代码分包等优化措施
|
||||
|
||||
## 许可证
|
||||
## 后续开发
|
||||
|
||||
MIT License
|
||||
### 功能完善
|
||||
- [ ] 对接后端API接口
|
||||
- [ ] 添加用户登录认证
|
||||
- [ ] 完善权限管理系统
|
||||
- [ ] 添加数据统计功能
|
||||
- [ ] 实现消息推送
|
||||
|
||||
### 体验优化
|
||||
- [ ] 添加加载动画
|
||||
- [ ] 优化页面切换效果
|
||||
- [ ] 完善错误提示
|
||||
- [ ] 添加操作反馈
|
||||
- [ ] 支持暗色模式
|
||||
|
||||
## 联系方式
|
||||
|
||||
如有问题或建议,请联系开发团队。
|
||||
|
||||
---
|
||||
|
||||
**版本**:v1.0.0
|
||||
**更新时间**:2024年1月
|
||||
|
||||
@@ -10,12 +10,10 @@ App({
|
||||
wx.login({
|
||||
success: res => {
|
||||
// 发送 res.code 到后台换取 openId, sessionKey, unionId
|
||||
console.log('登录成功', res.code)
|
||||
}
|
||||
})
|
||||
},
|
||||
globalData: {
|
||||
userInfo: null,
|
||||
baseUrl: 'http://localhost:5352/api'
|
||||
userInfo: null
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2,51 +2,46 @@
|
||||
"pages": [
|
||||
"pages/index/index",
|
||||
"pages/login/login",
|
||||
"pages/dashboard/dashboard",
|
||||
"pages/supervision/supervision",
|
||||
"pages/approval/approval",
|
||||
"pages/personnel/personnel",
|
||||
"pages/epidemic/epidemic",
|
||||
"pages/service/service",
|
||||
"pages/warehouse/warehouse",
|
||||
"pages/profile/profile"
|
||||
"pages/farmers/farmers",
|
||||
"pages/profile/profile",
|
||||
"pages/statistics/statistics",
|
||||
"pages/farmer/farmer",
|
||||
"pages/notification/notification",
|
||||
"pages/farmer/add/add",
|
||||
"pages/farmer/edit/edit",
|
||||
"pages/farmer/detail/detail",
|
||||
"pages/notification/detail/detail"
|
||||
],
|
||||
"window": {
|
||||
"backgroundTextStyle": "light",
|
||||
"navigationBarBackgroundColor": "#1890ff",
|
||||
"navigationBarTitleText": "政府管理系统",
|
||||
"navigationBarBackgroundColor": "#2c5aa0",
|
||||
"navigationBarTitleText": "政府监管端",
|
||||
"navigationBarTextStyle": "white",
|
||||
"backgroundColor": "#f6f6f6"
|
||||
"backgroundColor": "#f5f5f5"
|
||||
},
|
||||
"tabBar": {
|
||||
"color": "#7A7E83",
|
||||
"selectedColor": "#1890ff",
|
||||
"borderStyle": "black",
|
||||
"color": "#999999",
|
||||
"selectedColor": "#7CB342",
|
||||
"backgroundColor": "#ffffff",
|
||||
"borderStyle": "black",
|
||||
"list": [
|
||||
{
|
||||
"pagePath": "pages/index/index",
|
||||
"text": "首页",
|
||||
"iconPath": "images/home.png",
|
||||
"selectedIconPath": "images/home-active.png",
|
||||
"text": "首页"
|
||||
"selectedIconPath": "images/home-active.png"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/dashboard/dashboard",
|
||||
"iconPath": "images/dashboard.png",
|
||||
"selectedIconPath": "images/dashboard-active.png",
|
||||
"text": "看板"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/supervision/supervision",
|
||||
"iconPath": "images/supervision.png",
|
||||
"selectedIconPath": "images/supervision-active.png",
|
||||
"text": "监管"
|
||||
"pagePath": "pages/farmers/farmers",
|
||||
"text": "养殖户",
|
||||
"iconPath": "images/farmer.png",
|
||||
"selectedIconPath": "images/farmer-active.png"
|
||||
},
|
||||
{
|
||||
"pagePath": "pages/profile/profile",
|
||||
"text": "我的",
|
||||
"iconPath": "images/profile.png",
|
||||
"selectedIconPath": "images/profile-active.png",
|
||||
"text": "我的"
|
||||
"selectedIconPath": "images/profile-active.png"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -54,6 +49,5 @@
|
||||
"request": 10000,
|
||||
"downloadFile": 10000
|
||||
},
|
||||
"debug": true,
|
||||
"sitemapLocation": "sitemap.json"
|
||||
"debug": true
|
||||
}
|
||||
@@ -11,147 +11,29 @@
|
||||
|
||||
/* 全局样式 */
|
||||
page {
|
||||
background-color: #f6f6f6;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
/* 通用类 */
|
||||
.card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
line-height: 1.5;
|
||||
background-color: #f5f5f5;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', SimSun, sans-serif;
|
||||
}
|
||||
|
||||
/* 通用按钮样式 */
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 20rpx 40rpx;
|
||||
border-radius: 8rpx;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
border: none;
|
||||
transition: all 0.3s;
|
||||
padding: 20rpx 40rpx;
|
||||
}
|
||||
|
||||
.btn.primary {
|
||||
background: #1890ff;
|
||||
color: #fff;
|
||||
/* 卡片样式 */
|
||||
.card {
|
||||
background-color: #ffffff;
|
||||
border-radius: 12rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
margin: 20rpx;
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.btn.success {
|
||||
background: #52c41a;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn.warning {
|
||||
background: #faad14;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn.danger {
|
||||
background: #ff4d4f;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex.center {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flex.between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.flex.around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.flex.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80rpx 40rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 80rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
/* 列表项 */
|
||||
.list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx 20rpx;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.list-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.item-desc {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.item-arrow {
|
||||
font-size: 24rpx;
|
||||
color: #ccc;
|
||||
/* 分割线样式 */
|
||||
.divider {
|
||||
height: 2rpx;
|
||||
background-color: #e0e0e0;
|
||||
margin: 20rpx 0;
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
# API基础地址
|
||||
VUE_APP_API_BASE_URL=http://localhost:5352/api
|
||||
|
||||
# 应用配置
|
||||
VUE_APP_TITLE=政府管理系统
|
||||
VUE_APP_VERSION=1.0.0
|
||||
VUE_APP_DESCRIPTION=政府管理系统微信小程序
|
||||
|
||||
# 微信小程序配置
|
||||
VUE_APP_WECHAT_APPID=wx1b9c7cd2d0e0bfd3
|
||||
|
||||
# 开发环境配置
|
||||
NODE_ENV=development
|
||||
|
||||
# 端口配置
|
||||
PORT=8080
|
||||
|
||||
# 代理配置
|
||||
VUE_APP_PROXY_TARGET=http://localhost:5352
|
||||
68
government-mini-program/config/api.js
Normal file
68
government-mini-program/config/api.js
Normal file
@@ -0,0 +1,68 @@
|
||||
// API配置文件
|
||||
const config = {
|
||||
// 后端服务器配置
|
||||
baseURL: 'https://ad.ningmuyun.com/api',
|
||||
|
||||
// 请求超时时间
|
||||
timeout: 10000,
|
||||
|
||||
// API端点配置
|
||||
endpoints: {
|
||||
// 认证相关
|
||||
auth: {
|
||||
login: '/government/auth/login',
|
||||
logout: '/auth/logout',
|
||||
userInfo: '/auth/userinfo'
|
||||
},
|
||||
|
||||
// 养殖户管理
|
||||
farmer: {
|
||||
list: '/government/farmers',
|
||||
create: '/government/farmers',
|
||||
update: '/government/farmers',
|
||||
delete: '/government/farmers',
|
||||
detail: '/government/farmers',
|
||||
resetPassword: '/government/farmers/{id}/reset-password',
|
||||
farmTypes: '/government/farm-types',
|
||||
animalTypes: '/government/animal-types'
|
||||
},
|
||||
|
||||
// 数据统计
|
||||
statistics: {
|
||||
dataCenter: '/government/data-center',
|
||||
marketPrice: '/government/market-price'
|
||||
},
|
||||
|
||||
// 系统管理
|
||||
system: {
|
||||
health: '/health'
|
||||
},
|
||||
|
||||
// 设备管理
|
||||
device: {
|
||||
warnings: '/device-warning',
|
||||
smartCollars: '/government/collars',
|
||||
smartEarmarks: '/smart-earmark',
|
||||
smartHosts: '/smart-host'
|
||||
},
|
||||
|
||||
// 疫情管理
|
||||
epidemic: {
|
||||
records: '/epidemic-record',
|
||||
activities: '/epidemic-activity',
|
||||
vaccines: '/vaccine'
|
||||
},
|
||||
|
||||
// 监管任务
|
||||
supervision: {
|
||||
tasks: '/supervision'
|
||||
},
|
||||
|
||||
// 审批流程
|
||||
approval: {
|
||||
processes: '/approval-process'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
@@ -1,22 +0,0 @@
|
||||
# 环境配置示例文件
|
||||
# 复制此文件为 .env 并修改相应配置
|
||||
|
||||
# API基础地址
|
||||
VUE_APP_API_BASE_URL=http://localhost:5352/api
|
||||
|
||||
# 应用配置
|
||||
VUE_APP_TITLE=政府管理系统
|
||||
VUE_APP_VERSION=1.0.0
|
||||
VUE_APP_DESCRIPTION=政府管理系统微信小程序
|
||||
|
||||
# 微信小程序配置
|
||||
VUE_APP_WECHAT_APPID=your-wechat-appid-here
|
||||
|
||||
# 开发环境配置
|
||||
NODE_ENV=development
|
||||
|
||||
# 端口配置
|
||||
PORT=8080
|
||||
|
||||
# 代理配置
|
||||
VUE_APP_PROXY_TARGET=http://localhost:5352
|
||||
1
government-mini-program/images/empty.png
Normal file
1
government-mini-program/images/empty.png
Normal file
@@ -0,0 +1 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
1
government-mini-program/images/epidemic-station.png
Normal file
1
government-mini-program/images/epidemic-station.png
Normal file
@@ -0,0 +1 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
1
government-mini-program/images/epidemic.png
Normal file
1
government-mini-program/images/epidemic.png
Normal file
@@ -0,0 +1 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
1
government-mini-program/images/farmer-active.png
Normal file
1
government-mini-program/images/farmer-active.png
Normal file
@@ -0,0 +1 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
1
government-mini-program/images/farmer.png
Normal file
1
government-mini-program/images/farmer.png
Normal file
@@ -0,0 +1 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
1
government-mini-program/images/finance.png
Normal file
1
government-mini-program/images/finance.png
Normal file
@@ -0,0 +1 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
1
government-mini-program/images/help.png
Normal file
1
government-mini-program/images/help.png
Normal file
@@ -0,0 +1 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
1
government-mini-program/images/host.png
Normal file
1
government-mini-program/images/host.png
Normal file
@@ -0,0 +1 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
1
government-mini-program/images/identity.png
Normal file
1
government-mini-program/images/identity.png
Normal file
@@ -0,0 +1 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
1
government-mini-program/images/quarantine.png
Normal file
1
government-mini-program/images/quarantine.png
Normal file
@@ -0,0 +1 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
1
government-mini-program/images/settings.png
Normal file
1
government-mini-program/images/settings.png
Normal file
@@ -0,0 +1 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
1
government-mini-program/images/slaughter.png
Normal file
1
government-mini-program/images/slaughter.png
Normal file
@@ -0,0 +1 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
1
government-mini-program/images/station.png
Normal file
1
government-mini-program/images/station.png
Normal file
@@ -0,0 +1 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
1
government-mini-program/images/user-info.png
Normal file
1
government-mini-program/images/user-info.png
Normal file
@@ -0,0 +1 @@
|
||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||
@@ -1,80 +0,0 @@
|
||||
{
|
||||
"name": "政府端小程序",
|
||||
"appid": "wx1b9c7cd2d0e0bfd3",
|
||||
"description": "政府管理系统微信小程序",
|
||||
"versionName": "1.0.0",
|
||||
"versionCode": "100",
|
||||
"transformPx": false,
|
||||
"mp-weixin": {
|
||||
"appid": "wx1b9c7cd2d0e0bfd3",
|
||||
"setting": {
|
||||
"urlCheck": false,
|
||||
"es6": true,
|
||||
"postcss": true,
|
||||
"minified": true
|
||||
},
|
||||
"usingComponents": true,
|
||||
"permission": {
|
||||
"scope.userLocation": {
|
||||
"desc": "你的位置信息将用于政府监管和地图展示"
|
||||
}
|
||||
},
|
||||
"optimization": {
|
||||
"subPackages": true
|
||||
}
|
||||
},
|
||||
"vueVersion": "2",
|
||||
"splashscreen": {
|
||||
"alwaysShowBeforeRender": true,
|
||||
"autoclose": false,
|
||||
"waiting": true
|
||||
},
|
||||
"app-plus": {
|
||||
"usingComponents": true,
|
||||
"nvueStyle": "flex",
|
||||
"compilerVersion": 3,
|
||||
"splashscreen": {
|
||||
"alwaysShowBeforeRender": true,
|
||||
"autoclose": false,
|
||||
"waiting": true,
|
||||
"delay": 0
|
||||
},
|
||||
"modules": {},
|
||||
"distribute": {
|
||||
"android": {
|
||||
"permissions": [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_LOCATION_EXTRA_COMMANDS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.INTERNET\"/>"
|
||||
]
|
||||
},
|
||||
"ios": {}
|
||||
}
|
||||
},
|
||||
"quickapp": {},
|
||||
"h5": {
|
||||
"router": {
|
||||
"mode": "hash"
|
||||
},
|
||||
"optimization": {
|
||||
"treeShaking": {
|
||||
"enable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,392 +0,0 @@
|
||||
module.exports = (function() {
|
||||
var __MODS__ = {};
|
||||
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
|
||||
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
|
||||
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
|
||||
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
|
||||
__DEFINE__(1758268322045, function(require, module, exports) {
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.onNavigationBarSearchInputClicked = exports.onNavigationBarSearchInputConfirmed = exports.onNavigationBarSearchInputChanged = exports.onBackPress = exports.onNavigationBarButtonTap = exports.onTabItemTap = exports.onResize = exports.onPageScroll = exports.onAddToFavorites = exports.onShareTimeline = exports.onShareAppMessage = exports.onReachBottom = exports.onPullDownRefresh = exports.onUnload = exports.onReady = exports.onLoad = exports.onInit = exports.onUniNViewMessage = exports.onThemeChange = exports.onUnhandledRejection = exports.onPageNotFound = exports.onError = exports.onLaunch = exports.onHide = exports.onShow = exports.initUtsPackageName = exports.initUtsClassName = exports.initUtsIndexClassName = exports.initUtsProxyFunction = exports.initUtsProxyClass = void 0;
|
||||
var composition_api_1 = require("@vue/composition-api");
|
||||
var app = require("./app");
|
||||
var mp = require("./mp");
|
||||
var uts_1 = require("./uts");
|
||||
Object.defineProperty(exports, "initUtsProxyClass", { enumerable: true, get: function () { return uts_1.initUtsProxyClass; } });
|
||||
Object.defineProperty(exports, "initUtsProxyFunction", { enumerable: true, get: function () { return uts_1.initUtsProxyFunction; } });
|
||||
Object.defineProperty(exports, "initUtsIndexClassName", { enumerable: true, get: function () { return uts_1.initUtsIndexClassName; } });
|
||||
Object.defineProperty(exports, "initUtsClassName", { enumerable: true, get: function () { return uts_1.initUtsClassName; } });
|
||||
Object.defineProperty(exports, "initUtsPackageName", { enumerable: true, get: function () { return uts_1.initUtsPackageName; } });
|
||||
var lifecycles = [];
|
||||
var createLifeCycle = function (lifecycle) {
|
||||
lifecycles.push(lifecycle);
|
||||
var fn = (0, composition_api_1.createLifeCycle)(lifecycle);
|
||||
return function (callback, target) {
|
||||
return fn(callback, target);
|
||||
};
|
||||
};
|
||||
if (typeof plus === 'object') {
|
||||
app.init();
|
||||
}
|
||||
else if (typeof window === 'object' && 'document' in window) {
|
||||
}
|
||||
else {
|
||||
mp.init(lifecycles);
|
||||
}
|
||||
exports.onShow = createLifeCycle('onShow');
|
||||
exports.onHide = createLifeCycle('onHide');
|
||||
exports.onLaunch = createLifeCycle('onLaunch');
|
||||
exports.onError = createLifeCycle('onError');
|
||||
exports.onPageNotFound = createLifeCycle('onPageNotFound');
|
||||
exports.onUnhandledRejection = createLifeCycle('onUnhandledRejection');
|
||||
exports.onThemeChange = createLifeCycle('onThemeChange');
|
||||
exports.onUniNViewMessage = createLifeCycle('onUniNViewMessage');
|
||||
exports.onInit = createLifeCycle('onInit');
|
||||
exports.onLoad = createLifeCycle('onLoad');
|
||||
exports.onReady = createLifeCycle('onReady');
|
||||
exports.onUnload = createLifeCycle('onUnload');
|
||||
exports.onPullDownRefresh = createLifeCycle('onPullDownRefresh');
|
||||
exports.onReachBottom = createLifeCycle('onReachBottom');
|
||||
exports.onShareAppMessage = createLifeCycle('onShareAppMessage');
|
||||
exports.onShareTimeline = createLifeCycle('onShareTimeline');
|
||||
exports.onAddToFavorites = createLifeCycle('onAddToFavorites');
|
||||
exports.onPageScroll = createLifeCycle('onPageScroll');
|
||||
exports.onResize = createLifeCycle('onResize');
|
||||
exports.onTabItemTap = createLifeCycle('onTabItemTap');
|
||||
exports.onNavigationBarButtonTap = createLifeCycle('onNavigationBarButtonTap');
|
||||
exports.onBackPress = createLifeCycle('onBackPress');
|
||||
exports.onNavigationBarSearchInputChanged = createLifeCycle('onNavigationBarSearchInputChanged');
|
||||
exports.onNavigationBarSearchInputConfirmed = createLifeCycle('onNavigationBarSearchInputConfirmed');
|
||||
exports.onNavigationBarSearchInputClicked = createLifeCycle('onNavigationBarSearchInputClicked');
|
||||
|
||||
}, function(modId) {var map = {"./app":1758268322046,"./mp":1758268322047,"./uts":1758268322048}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322046, function(require, module, exports) {
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.init = void 0;
|
||||
var Vue = require("vue");
|
||||
function init() {
|
||||
var vueConstructor = (Vue.default ? Vue.default : Vue);
|
||||
var defaultMergeHook = vueConstructor.config.optionMergeStrategies.mounted;
|
||||
var onReadyFn;
|
||||
vueConstructor.config.optionMergeStrategies.mounted = function Le(parentVal, childVal) {
|
||||
var res = defaultMergeHook.call(this, parentVal, childVal);
|
||||
if (Array.isArray(res)) {
|
||||
var index = void 0;
|
||||
if (onReadyFn) {
|
||||
index = res.indexOf(onReadyFn);
|
||||
}
|
||||
else {
|
||||
index = res.findIndex(function (fn) { return fn.toString().includes('onReady'); });
|
||||
onReadyFn = res[index];
|
||||
}
|
||||
if (index !== -1) {
|
||||
res.splice(index, 1);
|
||||
res.push(onReadyFn);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
}
|
||||
exports.init = init;
|
||||
|
||||
}, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322047, function(require, module, exports) {
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.init = void 0;
|
||||
var vue_1 = require("vue");
|
||||
function updateLifeCycle(lifecycles, setupLifecycles, fn) {
|
||||
if (fn) {
|
||||
if (fn.lifecycles) {
|
||||
fn.lifecycles.forEach(function (item) {
|
||||
if (!setupLifecycles.includes(item)) {
|
||||
setupLifecycles.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
var fnString_1 = fn.toString();
|
||||
lifecycles.forEach(function (item) {
|
||||
if (!setupLifecycles.includes(item) && (new RegExp("\\b(".concat(item, ")\\b"))).test(fnString_1)) {
|
||||
setupLifecycles.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
function init(lifecycles) {
|
||||
var setup = vue_1.default.config.optionMergeStrategies.setup;
|
||||
var extend = vue_1.default.extend;
|
||||
vue_1.default.extend = function () {
|
||||
var extendedVue = extend.apply(this, arguments);
|
||||
var newOptions = extendedVue.options;
|
||||
var setup = newOptions.setup;
|
||||
if (setup && setup.lifecycles) {
|
||||
setup.lifecycles.forEach(function (item) {
|
||||
newOptions[item] = newOptions[item] || [function noop() { }];
|
||||
});
|
||||
}
|
||||
return extendedVue;
|
||||
};
|
||||
Object.defineProperty(vue_1.default.config.optionMergeStrategies, 'setup', {
|
||||
set: function (fn) {
|
||||
setup = fn;
|
||||
},
|
||||
get: function () {
|
||||
return function (to, from) {
|
||||
if (typeof setup === 'function') {
|
||||
var newSetup = setup.apply(this, arguments);
|
||||
newSetup.lifecycles = newSetup.lifecycles || [];
|
||||
updateLifeCycle(lifecycles, newSetup.lifecycles, from);
|
||||
updateLifeCycle(lifecycles, newSetup.lifecycles, to);
|
||||
return newSetup;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.init = init;
|
||||
|
||||
}, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322048, function(require, module, exports) {
|
||||
|
||||
exports.__esModule = true;
|
||||
exports.initUtsClassName = exports.initUtsIndexClassName = exports.initUtsPackageName = exports.initUtsProxyClass = exports.initUtsProxyFunction = exports.normalizeArg = void 0;
|
||||
var utils_1 = require("./utils");
|
||||
var callbackId = 1;
|
||||
var proxy;
|
||||
var callbacks = {};
|
||||
function normalizeArg(arg) {
|
||||
if (typeof arg === 'function') {
|
||||
// 查找该函数是否已缓存
|
||||
var oldId = Object.keys(callbacks).find(function (id) { return callbacks[id] === arg; });
|
||||
var id = oldId ? parseInt(oldId) : callbackId++;
|
||||
callbacks[id] = arg;
|
||||
return id;
|
||||
}
|
||||
else if ((0, utils_1.isPlainObject)(arg)) {
|
||||
Object.keys(arg).forEach(function (name) {
|
||||
;
|
||||
arg[name] = normalizeArg(arg[name]);
|
||||
});
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
exports.normalizeArg = normalizeArg;
|
||||
function initUtsInstanceMethod(async, opts, instanceId) {
|
||||
return initProxyFunction(async, opts, instanceId);
|
||||
}
|
||||
function getProxy() {
|
||||
if (!proxy) {
|
||||
proxy = uni.requireNativePlugin('UTS-Proxy');
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
function resolveSyncResult(res) {
|
||||
if (res.errMsg) {
|
||||
throw new Error(res.errMsg);
|
||||
}
|
||||
return res.params;
|
||||
}
|
||||
function invokePropGetter(args) {
|
||||
if (args.errMsg) {
|
||||
throw new Error(args.errMsg);
|
||||
}
|
||||
delete args.errMsg;
|
||||
return resolveSyncResult(getProxy().invokeSync(args, function () { }));
|
||||
}
|
||||
function initProxyFunction(async, _a, instanceId) {
|
||||
var pkg = _a.package, cls = _a["class"], propOrMethod = _a.name, method = _a.method, companion = _a.companion, methodParams = _a.params, errMsg = _a.errMsg;
|
||||
var invokeCallback = function (_a) {
|
||||
var id = _a.id, name = _a.name, params = _a.params, keepAlive = _a.keepAlive;
|
||||
var callback = callbacks[id];
|
||||
if (callback) {
|
||||
callback.apply(void 0, params);
|
||||
if (!keepAlive) {
|
||||
delete callbacks[id];
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.error("".concat(pkg).concat(cls, ".").concat(propOrMethod, " ").concat(name, " is not found"));
|
||||
}
|
||||
};
|
||||
var baseArgs = instanceId
|
||||
? { id: instanceId, name: propOrMethod, method: methodParams }
|
||||
: {
|
||||
package: pkg,
|
||||
"class": cls,
|
||||
name: method || propOrMethod,
|
||||
companion: companion,
|
||||
method: methodParams
|
||||
};
|
||||
return function () {
|
||||
var args = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
args[_i] = arguments[_i];
|
||||
}
|
||||
if (errMsg) {
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
var invokeArgs = (0, utils_1.extend)({}, baseArgs, {
|
||||
params: args.map(function (arg) { return normalizeArg(arg); })
|
||||
});
|
||||
if (async) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
getProxy().invokeAsync(invokeArgs, function (res) {
|
||||
if (res.type !== 'return') {
|
||||
invokeCallback(res);
|
||||
}
|
||||
else {
|
||||
if (res.errMsg) {
|
||||
reject(res.errMsg);
|
||||
}
|
||||
else {
|
||||
resolve(res.params);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
return resolveSyncResult(getProxy().invokeSync(invokeArgs, invokeCallback));
|
||||
};
|
||||
}
|
||||
function initUtsStaticMethod(async, opts) {
|
||||
if (opts.main && !opts.method) {
|
||||
if (typeof plus !== 'undefined' && plus.os.name === 'iOS') {
|
||||
opts.method = 's_' + opts.name;
|
||||
}
|
||||
}
|
||||
return initProxyFunction(async, opts, 0);
|
||||
}
|
||||
exports.initUtsProxyFunction = initUtsStaticMethod;
|
||||
function initUtsProxyClass(_a) {
|
||||
var pkg = _a.package, cls = _a["class"], constructorParams = _a.constructor.params, methods = _a.methods, props = _a.props, staticProps = _a.staticProps, staticMethods = _a.staticMethods, errMsg = _a.errMsg;
|
||||
var baseOptions = {
|
||||
package: pkg,
|
||||
"class": cls,
|
||||
errMsg: errMsg
|
||||
};
|
||||
var ProxyClass = /** @class */ (function () {
|
||||
function UtsClass() {
|
||||
var params = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
params[_i] = arguments[_i];
|
||||
}
|
||||
if (errMsg) {
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
var target = {};
|
||||
// 初始化实例 ID
|
||||
var instanceId = initProxyFunction(false, (0, utils_1.extend)({ name: 'constructor', params: constructorParams }, baseOptions), 0).apply(null, params);
|
||||
if (!instanceId) {
|
||||
throw new Error("new ".concat(cls, " is failed"));
|
||||
}
|
||||
return new Proxy(this, {
|
||||
get: function (_, name) {
|
||||
if (!target[name]) {
|
||||
//实例方法
|
||||
if ((0, utils_1.hasOwn)(methods, name)) {
|
||||
var _a = methods[name], async = _a.async, params_1 = _a.params;
|
||||
target[name] = initUtsInstanceMethod(!!async, (0, utils_1.extend)({
|
||||
name: name,
|
||||
params: params_1
|
||||
}, baseOptions), instanceId);
|
||||
}
|
||||
else if (props.includes(name)) {
|
||||
// 实例属性
|
||||
return invokePropGetter({
|
||||
id: instanceId,
|
||||
name: name,
|
||||
errMsg: errMsg
|
||||
});
|
||||
}
|
||||
}
|
||||
return target[name];
|
||||
}
|
||||
});
|
||||
}
|
||||
return UtsClass;
|
||||
}());
|
||||
var staticMethodCache = {};
|
||||
return new Proxy(ProxyClass, {
|
||||
get: function (target, name, receiver) {
|
||||
if ((0, utils_1.hasOwn)(staticMethods, name)) {
|
||||
if (!staticMethodCache[name]) {
|
||||
var _a = staticMethods[name], async = _a.async, params = _a.params;
|
||||
// 静态方法
|
||||
staticMethodCache[name] = initUtsStaticMethod(!!async, (0, utils_1.extend)({ name: name, companion: true, params: params }, baseOptions));
|
||||
}
|
||||
return staticMethodCache[name];
|
||||
}
|
||||
if (staticProps.includes(name)) {
|
||||
// 静态属性
|
||||
return invokePropGetter((0, utils_1.extend)({ name: name, companion: true }, baseOptions));
|
||||
}
|
||||
return Reflect.get(target, name, receiver);
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.initUtsProxyClass = initUtsProxyClass;
|
||||
function initUtsPackageName(name, is_uni_modules) {
|
||||
if (typeof plus !== 'undefined' && plus.os.name === 'Android') {
|
||||
return 'uts.sdk.' + (is_uni_modules ? 'modules.' : '') + name;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
exports.initUtsPackageName = initUtsPackageName;
|
||||
function initUtsIndexClassName(moduleName, is_uni_modules) {
|
||||
if (typeof plus === 'undefined') {
|
||||
return '';
|
||||
}
|
||||
return initUtsClassName(moduleName, plus.os.name === 'iOS' ? 'IndexSwift' : 'IndexKt', is_uni_modules);
|
||||
}
|
||||
exports.initUtsIndexClassName = initUtsIndexClassName;
|
||||
function initUtsClassName(moduleName, className, is_uni_modules) {
|
||||
if (typeof plus === 'undefined') {
|
||||
return '';
|
||||
}
|
||||
if (plus.os.name === 'Android') {
|
||||
return className;
|
||||
}
|
||||
if (plus.os.name === 'iOS') {
|
||||
return ('UTSSDK' +
|
||||
(is_uni_modules ? 'Modules' : '') +
|
||||
(0, utils_1.capitalize)(moduleName) +
|
||||
(0, utils_1.capitalize)(className));
|
||||
}
|
||||
return '';
|
||||
}
|
||||
exports.initUtsClassName = initUtsClassName;
|
||||
|
||||
}, function(modId) { var map = {"./utils":1758268322049}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322049, function(require, module, exports) {
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.capitalize = exports.isPlainObject = exports.hasOwn = exports.extend = void 0;
|
||||
exports.extend = Object.assign;
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
var hasOwn = function (val, key) { return hasOwnProperty.call(val, key); };
|
||||
exports.hasOwn = hasOwn;
|
||||
var objectToString = Object.prototype.toString;
|
||||
var toTypeString = function (value) {
|
||||
return objectToString.call(value);
|
||||
};
|
||||
var isPlainObject = function (val) {
|
||||
return toTypeString(val) === '[object Object]';
|
||||
};
|
||||
exports.isPlainObject = isPlainObject;
|
||||
var cacheStringFunction = function (fn) {
|
||||
var cache = Object.create(null);
|
||||
return (function (str) {
|
||||
var hit = cache[str];
|
||||
return hit || (cache[str] = fn(str));
|
||||
});
|
||||
};
|
||||
exports.capitalize = cacheStringFunction(function (str) { return str.charAt(0).toUpperCase() + str.slice(1); });
|
||||
|
||||
}, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
return __REQUIRE__(1758268322045);
|
||||
})()
|
||||
//miniprogram-npm-outsideDeps=["@vue/composition-api","vue"]
|
||||
//# sourceMappingURL=index.js.map
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,411 +0,0 @@
|
||||
module.exports = (function() {
|
||||
var __MODS__ = {};
|
||||
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
|
||||
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
|
||||
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
|
||||
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
|
||||
__DEFINE__(1758268322053, function(require, module, exports) {
|
||||
module.exports =
|
||||
{
|
||||
parallel : require('./parallel.js'),
|
||||
serial : require('./serial.js'),
|
||||
serialOrdered : require('./serialOrdered.js')
|
||||
};
|
||||
|
||||
}, function(modId) {var map = {"./parallel.js":1758268322054,"./serial.js":1758268322061,"./serialOrdered.js":1758268322062}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322054, function(require, module, exports) {
|
||||
var iterate = require('./lib/iterate.js')
|
||||
, initState = require('./lib/state.js')
|
||||
, terminator = require('./lib/terminator.js')
|
||||
;
|
||||
|
||||
// Public API
|
||||
module.exports = parallel;
|
||||
|
||||
/**
|
||||
* Runs iterator over provided array elements in parallel
|
||||
*
|
||||
* @param {array|object} list - array or object (named list) to iterate over
|
||||
* @param {function} iterator - iterator to run
|
||||
* @param {function} callback - invoked when all elements processed
|
||||
* @returns {function} - jobs terminator
|
||||
*/
|
||||
function parallel(list, iterator, callback)
|
||||
{
|
||||
var state = initState(list);
|
||||
|
||||
while (state.index < (state['keyedList'] || list).length)
|
||||
{
|
||||
iterate(list, iterator, state, function(error, result)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
callback(error, result);
|
||||
return;
|
||||
}
|
||||
|
||||
// looks like it's the last one
|
||||
if (Object.keys(state.jobs).length === 0)
|
||||
{
|
||||
callback(null, state.results);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
state.index++;
|
||||
}
|
||||
|
||||
return terminator.bind(state, callback);
|
||||
}
|
||||
|
||||
}, function(modId) { var map = {"./lib/iterate.js":1758268322055,"./lib/state.js":1758268322059,"./lib/terminator.js":1758268322060}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322055, function(require, module, exports) {
|
||||
var async = require('./async.js')
|
||||
, abort = require('./abort.js')
|
||||
;
|
||||
|
||||
// API
|
||||
module.exports = iterate;
|
||||
|
||||
/**
|
||||
* Iterates over each job object
|
||||
*
|
||||
* @param {array|object} list - array or object (named list) to iterate over
|
||||
* @param {function} iterator - iterator to run
|
||||
* @param {object} state - current job status
|
||||
* @param {function} callback - invoked when all elements processed
|
||||
*/
|
||||
function iterate(list, iterator, state, callback)
|
||||
{
|
||||
// store current index
|
||||
var key = state['keyedList'] ? state['keyedList'][state.index] : state.index;
|
||||
|
||||
state.jobs[key] = runJob(iterator, key, list[key], function(error, output)
|
||||
{
|
||||
// don't repeat yourself
|
||||
// skip secondary callbacks
|
||||
if (!(key in state.jobs))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// clean up jobs
|
||||
delete state.jobs[key];
|
||||
|
||||
if (error)
|
||||
{
|
||||
// don't process rest of the results
|
||||
// stop still active jobs
|
||||
// and reset the list
|
||||
abort(state);
|
||||
}
|
||||
else
|
||||
{
|
||||
state.results[key] = output;
|
||||
}
|
||||
|
||||
// return salvaged results
|
||||
callback(error, state.results);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs iterator over provided job element
|
||||
*
|
||||
* @param {function} iterator - iterator to invoke
|
||||
* @param {string|number} key - key/index of the element in the list of jobs
|
||||
* @param {mixed} item - job description
|
||||
* @param {function} callback - invoked after iterator is done with the job
|
||||
* @returns {function|mixed} - job abort function or something else
|
||||
*/
|
||||
function runJob(iterator, key, item, callback)
|
||||
{
|
||||
var aborter;
|
||||
|
||||
// allow shortcut if iterator expects only two arguments
|
||||
if (iterator.length == 2)
|
||||
{
|
||||
aborter = iterator(item, async(callback));
|
||||
}
|
||||
// otherwise go with full three arguments
|
||||
else
|
||||
{
|
||||
aborter = iterator(item, key, async(callback));
|
||||
}
|
||||
|
||||
return aborter;
|
||||
}
|
||||
|
||||
}, function(modId) { var map = {"./async.js":1758268322056,"./abort.js":1758268322058}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322056, function(require, module, exports) {
|
||||
var defer = require('./defer.js');
|
||||
|
||||
// API
|
||||
module.exports = async;
|
||||
|
||||
/**
|
||||
* Runs provided callback asynchronously
|
||||
* even if callback itself is not
|
||||
*
|
||||
* @param {function} callback - callback to invoke
|
||||
* @returns {function} - augmented callback
|
||||
*/
|
||||
function async(callback)
|
||||
{
|
||||
var isAsync = false;
|
||||
|
||||
// check if async happened
|
||||
defer(function() { isAsync = true; });
|
||||
|
||||
return function async_callback(err, result)
|
||||
{
|
||||
if (isAsync)
|
||||
{
|
||||
callback(err, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
defer(function nextTick_callback()
|
||||
{
|
||||
callback(err, result);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}, function(modId) { var map = {"./defer.js":1758268322057}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322057, function(require, module, exports) {
|
||||
module.exports = defer;
|
||||
|
||||
/**
|
||||
* Runs provided function on next iteration of the event loop
|
||||
*
|
||||
* @param {function} fn - function to run
|
||||
*/
|
||||
function defer(fn)
|
||||
{
|
||||
var nextTick = typeof setImmediate == 'function'
|
||||
? setImmediate
|
||||
: (
|
||||
typeof process == 'object' && typeof process.nextTick == 'function'
|
||||
? process.nextTick
|
||||
: null
|
||||
);
|
||||
|
||||
if (nextTick)
|
||||
{
|
||||
nextTick(fn);
|
||||
}
|
||||
else
|
||||
{
|
||||
setTimeout(fn, 0);
|
||||
}
|
||||
}
|
||||
|
||||
}, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322058, function(require, module, exports) {
|
||||
// API
|
||||
module.exports = abort;
|
||||
|
||||
/**
|
||||
* Aborts leftover active jobs
|
||||
*
|
||||
* @param {object} state - current state object
|
||||
*/
|
||||
function abort(state)
|
||||
{
|
||||
Object.keys(state.jobs).forEach(clean.bind(state));
|
||||
|
||||
// reset leftover jobs
|
||||
state.jobs = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up leftover job by invoking abort function for the provided job id
|
||||
*
|
||||
* @this state
|
||||
* @param {string|number} key - job id to abort
|
||||
*/
|
||||
function clean(key)
|
||||
{
|
||||
if (typeof this.jobs[key] == 'function')
|
||||
{
|
||||
this.jobs[key]();
|
||||
}
|
||||
}
|
||||
|
||||
}, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322059, function(require, module, exports) {
|
||||
// API
|
||||
module.exports = state;
|
||||
|
||||
/**
|
||||
* Creates initial state object
|
||||
* for iteration over list
|
||||
*
|
||||
* @param {array|object} list - list to iterate over
|
||||
* @param {function|null} sortMethod - function to use for keys sort,
|
||||
* or `null` to keep them as is
|
||||
* @returns {object} - initial state object
|
||||
*/
|
||||
function state(list, sortMethod)
|
||||
{
|
||||
var isNamedList = !Array.isArray(list)
|
||||
, initState =
|
||||
{
|
||||
index : 0,
|
||||
keyedList: isNamedList || sortMethod ? Object.keys(list) : null,
|
||||
jobs : {},
|
||||
results : isNamedList ? {} : [],
|
||||
size : isNamedList ? Object.keys(list).length : list.length
|
||||
}
|
||||
;
|
||||
|
||||
if (sortMethod)
|
||||
{
|
||||
// sort array keys based on it's values
|
||||
// sort object's keys just on own merit
|
||||
initState.keyedList.sort(isNamedList ? sortMethod : function(a, b)
|
||||
{
|
||||
return sortMethod(list[a], list[b]);
|
||||
});
|
||||
}
|
||||
|
||||
return initState;
|
||||
}
|
||||
|
||||
}, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322060, function(require, module, exports) {
|
||||
var abort = require('./abort.js')
|
||||
, async = require('./async.js')
|
||||
;
|
||||
|
||||
// API
|
||||
module.exports = terminator;
|
||||
|
||||
/**
|
||||
* Terminates jobs in the attached state context
|
||||
*
|
||||
* @this AsyncKitState#
|
||||
* @param {function} callback - final callback to invoke after termination
|
||||
*/
|
||||
function terminator(callback)
|
||||
{
|
||||
if (!Object.keys(this.jobs).length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// fast forward iteration index
|
||||
this.index = this.size;
|
||||
|
||||
// abort jobs
|
||||
abort(this);
|
||||
|
||||
// send back results we have so far
|
||||
async(callback)(null, this.results);
|
||||
}
|
||||
|
||||
}, function(modId) { var map = {"./abort.js":1758268322058,"./async.js":1758268322056}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322061, function(require, module, exports) {
|
||||
var serialOrdered = require('./serialOrdered.js');
|
||||
|
||||
// Public API
|
||||
module.exports = serial;
|
||||
|
||||
/**
|
||||
* Runs iterator over provided array elements in series
|
||||
*
|
||||
* @param {array|object} list - array or object (named list) to iterate over
|
||||
* @param {function} iterator - iterator to run
|
||||
* @param {function} callback - invoked when all elements processed
|
||||
* @returns {function} - jobs terminator
|
||||
*/
|
||||
function serial(list, iterator, callback)
|
||||
{
|
||||
return serialOrdered(list, iterator, null, callback);
|
||||
}
|
||||
|
||||
}, function(modId) { var map = {"./serialOrdered.js":1758268322062}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322062, function(require, module, exports) {
|
||||
var iterate = require('./lib/iterate.js')
|
||||
, initState = require('./lib/state.js')
|
||||
, terminator = require('./lib/terminator.js')
|
||||
;
|
||||
|
||||
// Public API
|
||||
module.exports = serialOrdered;
|
||||
// sorting helpers
|
||||
module.exports.ascending = ascending;
|
||||
module.exports.descending = descending;
|
||||
|
||||
/**
|
||||
* Runs iterator over provided sorted array elements in series
|
||||
*
|
||||
* @param {array|object} list - array or object (named list) to iterate over
|
||||
* @param {function} iterator - iterator to run
|
||||
* @param {function} sortMethod - custom sort function
|
||||
* @param {function} callback - invoked when all elements processed
|
||||
* @returns {function} - jobs terminator
|
||||
*/
|
||||
function serialOrdered(list, iterator, sortMethod, callback)
|
||||
{
|
||||
var state = initState(list, sortMethod);
|
||||
|
||||
iterate(list, iterator, state, function iteratorHandler(error, result)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
callback(error, result);
|
||||
return;
|
||||
}
|
||||
|
||||
state.index++;
|
||||
|
||||
// are we there yet?
|
||||
if (state.index < (state['keyedList'] || list).length)
|
||||
{
|
||||
iterate(list, iterator, state, iteratorHandler);
|
||||
return;
|
||||
}
|
||||
|
||||
// done here
|
||||
callback(null, state.results);
|
||||
});
|
||||
|
||||
return terminator.bind(state, callback);
|
||||
}
|
||||
|
||||
/*
|
||||
* -- Sort methods
|
||||
*/
|
||||
|
||||
/**
|
||||
* sort helper to sort array elements in ascending order
|
||||
*
|
||||
* @param {mixed} a - an item to compare
|
||||
* @param {mixed} b - an item to compare
|
||||
* @returns {number} - comparison result
|
||||
*/
|
||||
function ascending(a, b)
|
||||
{
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sort helper to sort array elements in descending order
|
||||
*
|
||||
* @param {mixed} a - an item to compare
|
||||
* @param {mixed} b - an item to compare
|
||||
* @returns {number} - comparison result
|
||||
*/
|
||||
function descending(a, b)
|
||||
{
|
||||
return -1 * ascending(a, b);
|
||||
}
|
||||
|
||||
}, function(modId) { var map = {"./lib/iterate.js":1758268322055,"./lib/state.js":1758268322059,"./lib/terminator.js":1758268322060}; return __REQUIRE__(map[modId], modId); })
|
||||
return __REQUIRE__(1758268322053);
|
||||
})()
|
||||
//miniprogram-npm-outsideDeps=[]
|
||||
//# sourceMappingURL=index.js.map
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,62 +0,0 @@
|
||||
module.exports = (function() {
|
||||
var __MODS__ = {};
|
||||
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
|
||||
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
|
||||
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
|
||||
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
|
||||
__DEFINE__(1758268322096, function(require, module, exports) {
|
||||
|
||||
|
||||
var bind = require('function-bind');
|
||||
var $TypeError = require('es-errors/type');
|
||||
|
||||
var $call = require('./functionCall');
|
||||
var $actualApply = require('./actualApply');
|
||||
|
||||
/** @type {(args: [Function, thisArg?: unknown, ...args: unknown[]]) => Function} TODO FIXME, find a way to use import('.') */
|
||||
module.exports = function callBindBasic(args) {
|
||||
if (args.length < 1 || typeof args[0] !== 'function') {
|
||||
throw new $TypeError('a function is required');
|
||||
}
|
||||
return $actualApply(bind, $call, args);
|
||||
};
|
||||
|
||||
}, function(modId) {var map = {"./functionCall":1758268322097,"./actualApply":1758268322098}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322097, function(require, module, exports) {
|
||||
|
||||
|
||||
/** @type {import('./functionCall')} */
|
||||
module.exports = Function.prototype.call;
|
||||
|
||||
}, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322098, function(require, module, exports) {
|
||||
|
||||
|
||||
var bind = require('function-bind');
|
||||
|
||||
var $apply = require('./functionApply');
|
||||
var $call = require('./functionCall');
|
||||
var $reflectApply = require('./reflectApply');
|
||||
|
||||
/** @type {import('./actualApply')} */
|
||||
module.exports = $reflectApply || bind.call($call, $apply);
|
||||
|
||||
}, function(modId) { var map = {"./functionApply":1758268322099,"./functionCall":1758268322097,"./reflectApply":1758268322100}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322099, function(require, module, exports) {
|
||||
|
||||
|
||||
/** @type {import('./functionApply')} */
|
||||
module.exports = Function.prototype.apply;
|
||||
|
||||
}, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322100, function(require, module, exports) {
|
||||
|
||||
|
||||
/** @type {import('./reflectApply')} */
|
||||
module.exports = typeof Reflect !== 'undefined' && Reflect && Reflect.apply;
|
||||
|
||||
}, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
return __REQUIRE__(1758268322096);
|
||||
})()
|
||||
//miniprogram-npm-outsideDeps=["function-bind","es-errors/type"]
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["index.js","functionCall.js","actualApply.js","functionApply.js","reflectApply.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;ACFA,ADGA;ACFA,ADGA;ACFA,ADGA;AELA,ADGA,ADGA;AELA,ADGA,ADGA;AELA,AFMA;AELA,ACHA,AHSA;AELA,ACHA,AHSA;AELA,ACHA,AHSA;AELA,ACHA,AHSA,AIZA;AFOA,ACHA,AHSA,AIZA;AFOA,AFMA,AIZA;AFOA,AFMA,AIZA;AFOA,AENA","file":"index.js","sourcesContent":["\n\nvar bind = require('function-bind');\nvar $TypeError = require('es-errors/type');\n\nvar $call = require('./functionCall');\nvar $actualApply = require('./actualApply');\n\n/** @type {(args: [Function, thisArg?: unknown, ...args: unknown[]]) => Function} TODO FIXME, find a way to use import('.') */\nmodule.exports = function callBindBasic(args) {\n\tif (args.length < 1 || typeof args[0] !== 'function') {\n\t\tthrow new $TypeError('a function is required');\n\t}\n\treturn $actualApply(bind, $call, args);\n};\n","\n\n/** @type {import('./functionCall')} */\nmodule.exports = Function.prototype.call;\n","\n\nvar bind = require('function-bind');\n\nvar $apply = require('./functionApply');\nvar $call = require('./functionCall');\nvar $reflectApply = require('./reflectApply');\n\n/** @type {import('./actualApply')} */\nmodule.exports = $reflectApply || bind.call($call, $apply);\n","\n\n/** @type {import('./functionApply')} */\nmodule.exports = Function.prototype.apply;\n","\n\n/** @type {import('./reflectApply')} */\nmodule.exports = typeof Reflect !== 'undefined' && Reflect && Reflect.apply;\n"]}
|
||||
@@ -1,221 +0,0 @@
|
||||
module.exports = (function() {
|
||||
var __MODS__ = {};
|
||||
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
|
||||
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
|
||||
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
|
||||
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
|
||||
__DEFINE__(1758268322101, function(require, module, exports) {
|
||||
var util = require('util');
|
||||
var Stream = require('stream').Stream;
|
||||
var DelayedStream = require('delayed-stream');
|
||||
|
||||
module.exports = CombinedStream;
|
||||
function CombinedStream() {
|
||||
this.writable = false;
|
||||
this.readable = true;
|
||||
this.dataSize = 0;
|
||||
this.maxDataSize = 2 * 1024 * 1024;
|
||||
this.pauseStreams = true;
|
||||
|
||||
this._released = false;
|
||||
this._streams = [];
|
||||
this._currentStream = null;
|
||||
this._insideLoop = false;
|
||||
this._pendingNext = false;
|
||||
}
|
||||
util.inherits(CombinedStream, Stream);
|
||||
|
||||
CombinedStream.create = function(options) {
|
||||
var combinedStream = new this();
|
||||
|
||||
options = options || {};
|
||||
for (var option in options) {
|
||||
combinedStream[option] = options[option];
|
||||
}
|
||||
|
||||
return combinedStream;
|
||||
};
|
||||
|
||||
CombinedStream.isStreamLike = function(stream) {
|
||||
return (typeof stream !== 'function')
|
||||
&& (typeof stream !== 'string')
|
||||
&& (typeof stream !== 'boolean')
|
||||
&& (typeof stream !== 'number')
|
||||
&& (!Buffer.isBuffer(stream));
|
||||
};
|
||||
|
||||
CombinedStream.prototype.append = function(stream) {
|
||||
var isStreamLike = CombinedStream.isStreamLike(stream);
|
||||
|
||||
if (isStreamLike) {
|
||||
if (!(stream instanceof DelayedStream)) {
|
||||
var newStream = DelayedStream.create(stream, {
|
||||
maxDataSize: Infinity,
|
||||
pauseStream: this.pauseStreams,
|
||||
});
|
||||
stream.on('data', this._checkDataSize.bind(this));
|
||||
stream = newStream;
|
||||
}
|
||||
|
||||
this._handleErrors(stream);
|
||||
|
||||
if (this.pauseStreams) {
|
||||
stream.pause();
|
||||
}
|
||||
}
|
||||
|
||||
this._streams.push(stream);
|
||||
return this;
|
||||
};
|
||||
|
||||
CombinedStream.prototype.pipe = function(dest, options) {
|
||||
Stream.prototype.pipe.call(this, dest, options);
|
||||
this.resume();
|
||||
return dest;
|
||||
};
|
||||
|
||||
CombinedStream.prototype._getNext = function() {
|
||||
this._currentStream = null;
|
||||
|
||||
if (this._insideLoop) {
|
||||
this._pendingNext = true;
|
||||
return; // defer call
|
||||
}
|
||||
|
||||
this._insideLoop = true;
|
||||
try {
|
||||
do {
|
||||
this._pendingNext = false;
|
||||
this._realGetNext();
|
||||
} while (this._pendingNext);
|
||||
} finally {
|
||||
this._insideLoop = false;
|
||||
}
|
||||
};
|
||||
|
||||
CombinedStream.prototype._realGetNext = function() {
|
||||
var stream = this._streams.shift();
|
||||
|
||||
|
||||
if (typeof stream == 'undefined') {
|
||||
this.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof stream !== 'function') {
|
||||
this._pipeNext(stream);
|
||||
return;
|
||||
}
|
||||
|
||||
var getStream = stream;
|
||||
getStream(function(stream) {
|
||||
var isStreamLike = CombinedStream.isStreamLike(stream);
|
||||
if (isStreamLike) {
|
||||
stream.on('data', this._checkDataSize.bind(this));
|
||||
this._handleErrors(stream);
|
||||
}
|
||||
|
||||
this._pipeNext(stream);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
CombinedStream.prototype._pipeNext = function(stream) {
|
||||
this._currentStream = stream;
|
||||
|
||||
var isStreamLike = CombinedStream.isStreamLike(stream);
|
||||
if (isStreamLike) {
|
||||
stream.on('end', this._getNext.bind(this));
|
||||
stream.pipe(this, {end: false});
|
||||
return;
|
||||
}
|
||||
|
||||
var value = stream;
|
||||
this.write(value);
|
||||
this._getNext();
|
||||
};
|
||||
|
||||
CombinedStream.prototype._handleErrors = function(stream) {
|
||||
var self = this;
|
||||
stream.on('error', function(err) {
|
||||
self._emitError(err);
|
||||
});
|
||||
};
|
||||
|
||||
CombinedStream.prototype.write = function(data) {
|
||||
this.emit('data', data);
|
||||
};
|
||||
|
||||
CombinedStream.prototype.pause = function() {
|
||||
if (!this.pauseStreams) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.pauseStreams && this._currentStream && typeof(this._currentStream.pause) == 'function') this._currentStream.pause();
|
||||
this.emit('pause');
|
||||
};
|
||||
|
||||
CombinedStream.prototype.resume = function() {
|
||||
if (!this._released) {
|
||||
this._released = true;
|
||||
this.writable = true;
|
||||
this._getNext();
|
||||
}
|
||||
|
||||
if(this.pauseStreams && this._currentStream && typeof(this._currentStream.resume) == 'function') this._currentStream.resume();
|
||||
this.emit('resume');
|
||||
};
|
||||
|
||||
CombinedStream.prototype.end = function() {
|
||||
this._reset();
|
||||
this.emit('end');
|
||||
};
|
||||
|
||||
CombinedStream.prototype.destroy = function() {
|
||||
this._reset();
|
||||
this.emit('close');
|
||||
};
|
||||
|
||||
CombinedStream.prototype._reset = function() {
|
||||
this.writable = false;
|
||||
this._streams = [];
|
||||
this._currentStream = null;
|
||||
};
|
||||
|
||||
CombinedStream.prototype._checkDataSize = function() {
|
||||
this._updateDataSize();
|
||||
if (this.dataSize <= this.maxDataSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
var message =
|
||||
'DelayedStream#maxDataSize of ' + this.maxDataSize + ' bytes exceeded.';
|
||||
this._emitError(new Error(message));
|
||||
};
|
||||
|
||||
CombinedStream.prototype._updateDataSize = function() {
|
||||
this.dataSize = 0;
|
||||
|
||||
var self = this;
|
||||
this._streams.forEach(function(stream) {
|
||||
if (!stream.dataSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.dataSize += stream.dataSize;
|
||||
});
|
||||
|
||||
if (this._currentStream && this._currentStream.dataSize) {
|
||||
this.dataSize += this._currentStream.dataSize;
|
||||
}
|
||||
};
|
||||
|
||||
CombinedStream.prototype._emitError = function(err) {
|
||||
this._reset();
|
||||
this.emit('error', err);
|
||||
};
|
||||
|
||||
}, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
return __REQUIRE__(1758268322101);
|
||||
})()
|
||||
//miniprogram-npm-outsideDeps=["util","stream","delayed-stream"]
|
||||
//# sourceMappingURL=index.js.map
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,120 +0,0 @@
|
||||
module.exports = (function() {
|
||||
var __MODS__ = {};
|
||||
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
|
||||
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
|
||||
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
|
||||
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
|
||||
__DEFINE__(1758268322103, function(require, module, exports) {
|
||||
var Stream = require('stream').Stream;
|
||||
var util = require('util');
|
||||
|
||||
module.exports = DelayedStream;
|
||||
function DelayedStream() {
|
||||
this.source = null;
|
||||
this.dataSize = 0;
|
||||
this.maxDataSize = 1024 * 1024;
|
||||
this.pauseStream = true;
|
||||
|
||||
this._maxDataSizeExceeded = false;
|
||||
this._released = false;
|
||||
this._bufferedEvents = [];
|
||||
}
|
||||
util.inherits(DelayedStream, Stream);
|
||||
|
||||
DelayedStream.create = function(source, options) {
|
||||
var delayedStream = new this();
|
||||
|
||||
options = options || {};
|
||||
for (var option in options) {
|
||||
delayedStream[option] = options[option];
|
||||
}
|
||||
|
||||
delayedStream.source = source;
|
||||
|
||||
var realEmit = source.emit;
|
||||
source.emit = function() {
|
||||
delayedStream._handleEmit(arguments);
|
||||
return realEmit.apply(source, arguments);
|
||||
};
|
||||
|
||||
source.on('error', function() {});
|
||||
if (delayedStream.pauseStream) {
|
||||
source.pause();
|
||||
}
|
||||
|
||||
return delayedStream;
|
||||
};
|
||||
|
||||
Object.defineProperty(DelayedStream.prototype, 'readable', {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return this.source.readable;
|
||||
}
|
||||
});
|
||||
|
||||
DelayedStream.prototype.setEncoding = function() {
|
||||
return this.source.setEncoding.apply(this.source, arguments);
|
||||
};
|
||||
|
||||
DelayedStream.prototype.resume = function() {
|
||||
if (!this._released) {
|
||||
this.release();
|
||||
}
|
||||
|
||||
this.source.resume();
|
||||
};
|
||||
|
||||
DelayedStream.prototype.pause = function() {
|
||||
this.source.pause();
|
||||
};
|
||||
|
||||
DelayedStream.prototype.release = function() {
|
||||
this._released = true;
|
||||
|
||||
this._bufferedEvents.forEach(function(args) {
|
||||
this.emit.apply(this, args);
|
||||
}.bind(this));
|
||||
this._bufferedEvents = [];
|
||||
};
|
||||
|
||||
DelayedStream.prototype.pipe = function() {
|
||||
var r = Stream.prototype.pipe.apply(this, arguments);
|
||||
this.resume();
|
||||
return r;
|
||||
};
|
||||
|
||||
DelayedStream.prototype._handleEmit = function(args) {
|
||||
if (this._released) {
|
||||
this.emit.apply(this, args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (args[0] === 'data') {
|
||||
this.dataSize += args[1].length;
|
||||
this._checkIfMaxDataSizeExceeded();
|
||||
}
|
||||
|
||||
this._bufferedEvents.push(args);
|
||||
};
|
||||
|
||||
DelayedStream.prototype._checkIfMaxDataSizeExceeded = function() {
|
||||
if (this._maxDataSizeExceeded) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.dataSize <= this.maxDataSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._maxDataSizeExceeded = true;
|
||||
var message =
|
||||
'DelayedStream#maxDataSize of ' + this.maxDataSize + ' bytes exceeded.'
|
||||
this.emit('error', new Error(message));
|
||||
};
|
||||
|
||||
}, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
return __REQUIRE__(1758268322103);
|
||||
})()
|
||||
//miniprogram-npm-outsideDeps=["stream","util"]
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["delayed_stream.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"index.js","sourcesContent":["var Stream = require('stream').Stream;\nvar util = require('util');\n\nmodule.exports = DelayedStream;\nfunction DelayedStream() {\n this.source = null;\n this.dataSize = 0;\n this.maxDataSize = 1024 * 1024;\n this.pauseStream = true;\n\n this._maxDataSizeExceeded = false;\n this._released = false;\n this._bufferedEvents = [];\n}\nutil.inherits(DelayedStream, Stream);\n\nDelayedStream.create = function(source, options) {\n var delayedStream = new this();\n\n options = options || {};\n for (var option in options) {\n delayedStream[option] = options[option];\n }\n\n delayedStream.source = source;\n\n var realEmit = source.emit;\n source.emit = function() {\n delayedStream._handleEmit(arguments);\n return realEmit.apply(source, arguments);\n };\n\n source.on('error', function() {});\n if (delayedStream.pauseStream) {\n source.pause();\n }\n\n return delayedStream;\n};\n\nObject.defineProperty(DelayedStream.prototype, 'readable', {\n configurable: true,\n enumerable: true,\n get: function() {\n return this.source.readable;\n }\n});\n\nDelayedStream.prototype.setEncoding = function() {\n return this.source.setEncoding.apply(this.source, arguments);\n};\n\nDelayedStream.prototype.resume = function() {\n if (!this._released) {\n this.release();\n }\n\n this.source.resume();\n};\n\nDelayedStream.prototype.pause = function() {\n this.source.pause();\n};\n\nDelayedStream.prototype.release = function() {\n this._released = true;\n\n this._bufferedEvents.forEach(function(args) {\n this.emit.apply(this, args);\n }.bind(this));\n this._bufferedEvents = [];\n};\n\nDelayedStream.prototype.pipe = function() {\n var r = Stream.prototype.pipe.apply(this, arguments);\n this.resume();\n return r;\n};\n\nDelayedStream.prototype._handleEmit = function(args) {\n if (this._released) {\n this.emit.apply(this, args);\n return;\n }\n\n if (args[0] === 'data') {\n this.dataSize += args[1].length;\n this._checkIfMaxDataSizeExceeded();\n }\n\n this._bufferedEvents.push(args);\n};\n\nDelayedStream.prototype._checkIfMaxDataSizeExceeded = function() {\n if (this._maxDataSizeExceeded) {\n return;\n }\n\n if (this.dataSize <= this.maxDataSize) {\n return;\n }\n\n this._maxDataSizeExceeded = true;\n var message =\n 'DelayedStream#maxDataSize of ' + this.maxDataSize + ' bytes exceeded.'\n this.emit('error', new Error(message));\n};\n"]}
|
||||
@@ -1,27 +0,0 @@
|
||||
module.exports = (function() {
|
||||
var __MODS__ = {};
|
||||
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
|
||||
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
|
||||
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
|
||||
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
|
||||
__DEFINE__(1758268322104, function(require, module, exports) {
|
||||
|
||||
|
||||
/** @type {import('.')} */
|
||||
var $defineProperty = Object.defineProperty || false;
|
||||
if ($defineProperty) {
|
||||
try {
|
||||
$defineProperty({}, 'a', { value: 1 });
|
||||
} catch (e) {
|
||||
// IE 8 has a broken defineProperty
|
||||
$defineProperty = false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = $defineProperty;
|
||||
|
||||
}, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
return __REQUIRE__(1758268322104);
|
||||
})()
|
||||
//miniprogram-npm-outsideDeps=[]
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["index.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"index.js","sourcesContent":["\n\n/** @type {import('.')} */\nvar $defineProperty = Object.defineProperty || false;\nif ($defineProperty) {\n\ttry {\n\t\t$defineProperty({}, 'a', { value: 1 });\n\t} catch (e) {\n\t\t// IE 8 has a broken defineProperty\n\t\t$defineProperty = false;\n\t}\n}\n\nmodule.exports = $defineProperty;\n"]}
|
||||
@@ -1,17 +0,0 @@
|
||||
module.exports = (function() {
|
||||
var __MODS__ = {};
|
||||
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
|
||||
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
|
||||
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
|
||||
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
|
||||
__DEFINE__(1758268322105, function(require, module, exports) {
|
||||
|
||||
|
||||
/** @type {import('.')} */
|
||||
module.exports = Error;
|
||||
|
||||
}, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
return __REQUIRE__(1758268322105);
|
||||
})()
|
||||
//miniprogram-npm-outsideDeps=[]
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["index.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA","file":"index.js","sourcesContent":["\n\n/** @type {import('.')} */\nmodule.exports = Error;\n"]}
|
||||
@@ -1,17 +0,0 @@
|
||||
module.exports = (function() {
|
||||
var __MODS__ = {};
|
||||
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
|
||||
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
|
||||
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
|
||||
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
|
||||
__DEFINE__(1758268322106, function(require, module, exports) {
|
||||
|
||||
|
||||
/** @type {import('.')} */
|
||||
module.exports = Object;
|
||||
|
||||
}, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
return __REQUIRE__(1758268322106);
|
||||
})()
|
||||
//miniprogram-npm-outsideDeps=[]
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["index.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA","file":"index.js","sourcesContent":["\n\n/** @type {import('.')} */\nmodule.exports = Object;\n"]}
|
||||
@@ -1,48 +0,0 @@
|
||||
module.exports = (function() {
|
||||
var __MODS__ = {};
|
||||
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
|
||||
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
|
||||
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
|
||||
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
|
||||
__DEFINE__(1758268322107, function(require, module, exports) {
|
||||
|
||||
|
||||
var GetIntrinsic = require('get-intrinsic');
|
||||
|
||||
var $defineProperty = GetIntrinsic('%Object.defineProperty%', true);
|
||||
|
||||
var hasToStringTag = require('has-tostringtag/shams')();
|
||||
var hasOwn = require('hasown');
|
||||
var $TypeError = require('es-errors/type');
|
||||
|
||||
var toStringTag = hasToStringTag ? Symbol.toStringTag : null;
|
||||
|
||||
/** @type {import('.')} */
|
||||
module.exports = function setToStringTag(object, value) {
|
||||
var overrideIfSet = arguments.length > 2 && !!arguments[2] && arguments[2].force;
|
||||
var nonConfigurable = arguments.length > 2 && !!arguments[2] && arguments[2].nonConfigurable;
|
||||
if (
|
||||
(typeof overrideIfSet !== 'undefined' && typeof overrideIfSet !== 'boolean')
|
||||
|| (typeof nonConfigurable !== 'undefined' && typeof nonConfigurable !== 'boolean')
|
||||
) {
|
||||
throw new $TypeError('if provided, the `overrideIfSet` and `nonConfigurable` options must be booleans');
|
||||
}
|
||||
if (toStringTag && (overrideIfSet || !hasOwn(object, toStringTag))) {
|
||||
if ($defineProperty) {
|
||||
$defineProperty(object, toStringTag, {
|
||||
configurable: !nonConfigurable,
|
||||
enumerable: false,
|
||||
value: value,
|
||||
writable: false
|
||||
});
|
||||
} else {
|
||||
object[toStringTag] = value; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
return __REQUIRE__(1758268322107);
|
||||
})()
|
||||
//miniprogram-npm-outsideDeps=["get-intrinsic","has-tostringtag/shams","hasown","es-errors/type"]
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["index.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"index.js","sourcesContent":["\n\nvar GetIntrinsic = require('get-intrinsic');\n\nvar $defineProperty = GetIntrinsic('%Object.defineProperty%', true);\n\nvar hasToStringTag = require('has-tostringtag/shams')();\nvar hasOwn = require('hasown');\nvar $TypeError = require('es-errors/type');\n\nvar toStringTag = hasToStringTag ? Symbol.toStringTag : null;\n\n/** @type {import('.')} */\nmodule.exports = function setToStringTag(object, value) {\n\tvar overrideIfSet = arguments.length > 2 && !!arguments[2] && arguments[2].force;\n\tvar nonConfigurable = arguments.length > 2 && !!arguments[2] && arguments[2].nonConfigurable;\n\tif (\n\t\t(typeof overrideIfSet !== 'undefined' && typeof overrideIfSet !== 'boolean')\n\t\t|| (typeof nonConfigurable !== 'undefined' && typeof nonConfigurable !== 'boolean')\n\t) {\n\t\tthrow new $TypeError('if provided, the `overrideIfSet` and `nonConfigurable` options must be booleans');\n\t}\n\tif (toStringTag && (overrideIfSet || !hasOwn(object, toStringTag))) {\n\t\tif ($defineProperty) {\n\t\t\t$defineProperty(object, toStringTag, {\n\t\t\t\tconfigurable: !nonConfigurable,\n\t\t\t\tenumerable: false,\n\t\t\t\tvalue: value,\n\t\t\t\twritable: false\n\t\t\t});\n\t\t} else {\n\t\t\tobject[toStringTag] = value; // eslint-disable-line no-param-reassign\n\t\t}\n\t}\n};\n"]}
|
||||
@@ -1,725 +0,0 @@
|
||||
module.exports = (function() {
|
||||
var __MODS__ = {};
|
||||
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
|
||||
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
|
||||
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
|
||||
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
|
||||
__DEFINE__(1758268322108, function(require, module, exports) {
|
||||
var url = require("url");
|
||||
var URL = url.URL;
|
||||
var http = require("http");
|
||||
var https = require("https");
|
||||
var Writable = require("stream").Writable;
|
||||
var assert = require("assert");
|
||||
var debug = require("./debug");
|
||||
|
||||
// Preventive platform detection
|
||||
// istanbul ignore next
|
||||
(function detectUnsupportedEnvironment() {
|
||||
var looksLikeNode = typeof process !== "undefined";
|
||||
var looksLikeBrowser = typeof window !== "undefined" && typeof document !== "undefined";
|
||||
var looksLikeV8 = isFunction(Error.captureStackTrace);
|
||||
if (!looksLikeNode && (looksLikeBrowser || !looksLikeV8)) {
|
||||
console.warn("The follow-redirects package should be excluded from browser builds.");
|
||||
}
|
||||
}());
|
||||
|
||||
// Whether to use the native URL object or the legacy url module
|
||||
var useNativeURL = false;
|
||||
try {
|
||||
assert(new URL(""));
|
||||
}
|
||||
catch (error) {
|
||||
useNativeURL = error.code === "ERR_INVALID_URL";
|
||||
}
|
||||
|
||||
// URL fields to preserve in copy operations
|
||||
var preservedUrlFields = [
|
||||
"auth",
|
||||
"host",
|
||||
"hostname",
|
||||
"href",
|
||||
"path",
|
||||
"pathname",
|
||||
"port",
|
||||
"protocol",
|
||||
"query",
|
||||
"search",
|
||||
"hash",
|
||||
];
|
||||
|
||||
// Create handlers that pass events from native requests
|
||||
var events = ["abort", "aborted", "connect", "error", "socket", "timeout"];
|
||||
var eventHandlers = Object.create(null);
|
||||
events.forEach(function (event) {
|
||||
eventHandlers[event] = function (arg1, arg2, arg3) {
|
||||
this._redirectable.emit(event, arg1, arg2, arg3);
|
||||
};
|
||||
});
|
||||
|
||||
// Error types with codes
|
||||
var InvalidUrlError = createErrorType(
|
||||
"ERR_INVALID_URL",
|
||||
"Invalid URL",
|
||||
TypeError
|
||||
);
|
||||
var RedirectionError = createErrorType(
|
||||
"ERR_FR_REDIRECTION_FAILURE",
|
||||
"Redirected request failed"
|
||||
);
|
||||
var TooManyRedirectsError = createErrorType(
|
||||
"ERR_FR_TOO_MANY_REDIRECTS",
|
||||
"Maximum number of redirects exceeded",
|
||||
RedirectionError
|
||||
);
|
||||
var MaxBodyLengthExceededError = createErrorType(
|
||||
"ERR_FR_MAX_BODY_LENGTH_EXCEEDED",
|
||||
"Request body larger than maxBodyLength limit"
|
||||
);
|
||||
var WriteAfterEndError = createErrorType(
|
||||
"ERR_STREAM_WRITE_AFTER_END",
|
||||
"write after end"
|
||||
);
|
||||
|
||||
// istanbul ignore next
|
||||
var destroy = Writable.prototype.destroy || noop;
|
||||
|
||||
// An HTTP(S) request that can be redirected
|
||||
function RedirectableRequest(options, responseCallback) {
|
||||
// Initialize the request
|
||||
Writable.call(this);
|
||||
this._sanitizeOptions(options);
|
||||
this._options = options;
|
||||
this._ended = false;
|
||||
this._ending = false;
|
||||
this._redirectCount = 0;
|
||||
this._redirects = [];
|
||||
this._requestBodyLength = 0;
|
||||
this._requestBodyBuffers = [];
|
||||
|
||||
// Attach a callback if passed
|
||||
if (responseCallback) {
|
||||
this.on("response", responseCallback);
|
||||
}
|
||||
|
||||
// React to responses of native requests
|
||||
var self = this;
|
||||
this._onNativeResponse = function (response) {
|
||||
try {
|
||||
self._processResponse(response);
|
||||
}
|
||||
catch (cause) {
|
||||
self.emit("error", cause instanceof RedirectionError ?
|
||||
cause : new RedirectionError({ cause: cause }));
|
||||
}
|
||||
};
|
||||
|
||||
// Perform the first request
|
||||
this._performRequest();
|
||||
}
|
||||
RedirectableRequest.prototype = Object.create(Writable.prototype);
|
||||
|
||||
RedirectableRequest.prototype.abort = function () {
|
||||
destroyRequest(this._currentRequest);
|
||||
this._currentRequest.abort();
|
||||
this.emit("abort");
|
||||
};
|
||||
|
||||
RedirectableRequest.prototype.destroy = function (error) {
|
||||
destroyRequest(this._currentRequest, error);
|
||||
destroy.call(this, error);
|
||||
return this;
|
||||
};
|
||||
|
||||
// Writes buffered data to the current native request
|
||||
RedirectableRequest.prototype.write = function (data, encoding, callback) {
|
||||
// Writing is not allowed if end has been called
|
||||
if (this._ending) {
|
||||
throw new WriteAfterEndError();
|
||||
}
|
||||
|
||||
// Validate input and shift parameters if necessary
|
||||
if (!isString(data) && !isBuffer(data)) {
|
||||
throw new TypeError("data should be a string, Buffer or Uint8Array");
|
||||
}
|
||||
if (isFunction(encoding)) {
|
||||
callback = encoding;
|
||||
encoding = null;
|
||||
}
|
||||
|
||||
// Ignore empty buffers, since writing them doesn't invoke the callback
|
||||
// https://github.com/nodejs/node/issues/22066
|
||||
if (data.length === 0) {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Only write when we don't exceed the maximum body length
|
||||
if (this._requestBodyLength + data.length <= this._options.maxBodyLength) {
|
||||
this._requestBodyLength += data.length;
|
||||
this._requestBodyBuffers.push({ data: data, encoding: encoding });
|
||||
this._currentRequest.write(data, encoding, callback);
|
||||
}
|
||||
// Error when we exceed the maximum body length
|
||||
else {
|
||||
this.emit("error", new MaxBodyLengthExceededError());
|
||||
this.abort();
|
||||
}
|
||||
};
|
||||
|
||||
// Ends the current native request
|
||||
RedirectableRequest.prototype.end = function (data, encoding, callback) {
|
||||
// Shift parameters if necessary
|
||||
if (isFunction(data)) {
|
||||
callback = data;
|
||||
data = encoding = null;
|
||||
}
|
||||
else if (isFunction(encoding)) {
|
||||
callback = encoding;
|
||||
encoding = null;
|
||||
}
|
||||
|
||||
// Write data if needed and end
|
||||
if (!data) {
|
||||
this._ended = this._ending = true;
|
||||
this._currentRequest.end(null, null, callback);
|
||||
}
|
||||
else {
|
||||
var self = this;
|
||||
var currentRequest = this._currentRequest;
|
||||
this.write(data, encoding, function () {
|
||||
self._ended = true;
|
||||
currentRequest.end(null, null, callback);
|
||||
});
|
||||
this._ending = true;
|
||||
}
|
||||
};
|
||||
|
||||
// Sets a header value on the current native request
|
||||
RedirectableRequest.prototype.setHeader = function (name, value) {
|
||||
this._options.headers[name] = value;
|
||||
this._currentRequest.setHeader(name, value);
|
||||
};
|
||||
|
||||
// Clears a header value on the current native request
|
||||
RedirectableRequest.prototype.removeHeader = function (name) {
|
||||
delete this._options.headers[name];
|
||||
this._currentRequest.removeHeader(name);
|
||||
};
|
||||
|
||||
// Global timeout for all underlying requests
|
||||
RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
|
||||
var self = this;
|
||||
|
||||
// Destroys the socket on timeout
|
||||
function destroyOnTimeout(socket) {
|
||||
socket.setTimeout(msecs);
|
||||
socket.removeListener("timeout", socket.destroy);
|
||||
socket.addListener("timeout", socket.destroy);
|
||||
}
|
||||
|
||||
// Sets up a timer to trigger a timeout event
|
||||
function startTimer(socket) {
|
||||
if (self._timeout) {
|
||||
clearTimeout(self._timeout);
|
||||
}
|
||||
self._timeout = setTimeout(function () {
|
||||
self.emit("timeout");
|
||||
clearTimer();
|
||||
}, msecs);
|
||||
destroyOnTimeout(socket);
|
||||
}
|
||||
|
||||
// Stops a timeout from triggering
|
||||
function clearTimer() {
|
||||
// Clear the timeout
|
||||
if (self._timeout) {
|
||||
clearTimeout(self._timeout);
|
||||
self._timeout = null;
|
||||
}
|
||||
|
||||
// Clean up all attached listeners
|
||||
self.removeListener("abort", clearTimer);
|
||||
self.removeListener("error", clearTimer);
|
||||
self.removeListener("response", clearTimer);
|
||||
self.removeListener("close", clearTimer);
|
||||
if (callback) {
|
||||
self.removeListener("timeout", callback);
|
||||
}
|
||||
if (!self.socket) {
|
||||
self._currentRequest.removeListener("socket", startTimer);
|
||||
}
|
||||
}
|
||||
|
||||
// Attach callback if passed
|
||||
if (callback) {
|
||||
this.on("timeout", callback);
|
||||
}
|
||||
|
||||
// Start the timer if or when the socket is opened
|
||||
if (this.socket) {
|
||||
startTimer(this.socket);
|
||||
}
|
||||
else {
|
||||
this._currentRequest.once("socket", startTimer);
|
||||
}
|
||||
|
||||
// Clean up on events
|
||||
this.on("socket", destroyOnTimeout);
|
||||
this.on("abort", clearTimer);
|
||||
this.on("error", clearTimer);
|
||||
this.on("response", clearTimer);
|
||||
this.on("close", clearTimer);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// Proxy all other public ClientRequest methods
|
||||
[
|
||||
"flushHeaders", "getHeader",
|
||||
"setNoDelay", "setSocketKeepAlive",
|
||||
].forEach(function (method) {
|
||||
RedirectableRequest.prototype[method] = function (a, b) {
|
||||
return this._currentRequest[method](a, b);
|
||||
};
|
||||
});
|
||||
|
||||
// Proxy all public ClientRequest properties
|
||||
["aborted", "connection", "socket"].forEach(function (property) {
|
||||
Object.defineProperty(RedirectableRequest.prototype, property, {
|
||||
get: function () { return this._currentRequest[property]; },
|
||||
});
|
||||
});
|
||||
|
||||
RedirectableRequest.prototype._sanitizeOptions = function (options) {
|
||||
// Ensure headers are always present
|
||||
if (!options.headers) {
|
||||
options.headers = {};
|
||||
}
|
||||
|
||||
// Since http.request treats host as an alias of hostname,
|
||||
// but the url module interprets host as hostname plus port,
|
||||
// eliminate the host property to avoid confusion.
|
||||
if (options.host) {
|
||||
// Use hostname if set, because it has precedence
|
||||
if (!options.hostname) {
|
||||
options.hostname = options.host;
|
||||
}
|
||||
delete options.host;
|
||||
}
|
||||
|
||||
// Complete the URL object when necessary
|
||||
if (!options.pathname && options.path) {
|
||||
var searchPos = options.path.indexOf("?");
|
||||
if (searchPos < 0) {
|
||||
options.pathname = options.path;
|
||||
}
|
||||
else {
|
||||
options.pathname = options.path.substring(0, searchPos);
|
||||
options.search = options.path.substring(searchPos);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Executes the next native request (initial or redirect)
|
||||
RedirectableRequest.prototype._performRequest = function () {
|
||||
// Load the native protocol
|
||||
var protocol = this._options.protocol;
|
||||
var nativeProtocol = this._options.nativeProtocols[protocol];
|
||||
if (!nativeProtocol) {
|
||||
throw new TypeError("Unsupported protocol " + protocol);
|
||||
}
|
||||
|
||||
// If specified, use the agent corresponding to the protocol
|
||||
// (HTTP and HTTPS use different types of agents)
|
||||
if (this._options.agents) {
|
||||
var scheme = protocol.slice(0, -1);
|
||||
this._options.agent = this._options.agents[scheme];
|
||||
}
|
||||
|
||||
// Create the native request and set up its event handlers
|
||||
var request = this._currentRequest =
|
||||
nativeProtocol.request(this._options, this._onNativeResponse);
|
||||
request._redirectable = this;
|
||||
for (var event of events) {
|
||||
request.on(event, eventHandlers[event]);
|
||||
}
|
||||
|
||||
// RFC7230§5.3.1: When making a request directly to an origin server, […]
|
||||
// a client MUST send only the absolute path […] as the request-target.
|
||||
this._currentUrl = /^\//.test(this._options.path) ?
|
||||
url.format(this._options) :
|
||||
// When making a request to a proxy, […]
|
||||
// a client MUST send the target URI in absolute-form […].
|
||||
this._options.path;
|
||||
|
||||
// End a redirected request
|
||||
// (The first request must be ended explicitly with RedirectableRequest#end)
|
||||
if (this._isRedirect) {
|
||||
// Write the request entity and end
|
||||
var i = 0;
|
||||
var self = this;
|
||||
var buffers = this._requestBodyBuffers;
|
||||
(function writeNext(error) {
|
||||
// Only write if this request has not been redirected yet
|
||||
// istanbul ignore else
|
||||
if (request === self._currentRequest) {
|
||||
// Report any write errors
|
||||
// istanbul ignore if
|
||||
if (error) {
|
||||
self.emit("error", error);
|
||||
}
|
||||
// Write the next buffer if there are still left
|
||||
else if (i < buffers.length) {
|
||||
var buffer = buffers[i++];
|
||||
// istanbul ignore else
|
||||
if (!request.finished) {
|
||||
request.write(buffer.data, buffer.encoding, writeNext);
|
||||
}
|
||||
}
|
||||
// End the request if `end` has been called on us
|
||||
else if (self._ended) {
|
||||
request.end();
|
||||
}
|
||||
}
|
||||
}());
|
||||
}
|
||||
};
|
||||
|
||||
// Processes a response from the current native request
|
||||
RedirectableRequest.prototype._processResponse = function (response) {
|
||||
// Store the redirected response
|
||||
var statusCode = response.statusCode;
|
||||
if (this._options.trackRedirects) {
|
||||
this._redirects.push({
|
||||
url: this._currentUrl,
|
||||
headers: response.headers,
|
||||
statusCode: statusCode,
|
||||
});
|
||||
}
|
||||
|
||||
// RFC7231§6.4: The 3xx (Redirection) class of status code indicates
|
||||
// that further action needs to be taken by the user agent in order to
|
||||
// fulfill the request. If a Location header field is provided,
|
||||
// the user agent MAY automatically redirect its request to the URI
|
||||
// referenced by the Location field value,
|
||||
// even if the specific status code is not understood.
|
||||
|
||||
// If the response is not a redirect; return it as-is
|
||||
var location = response.headers.location;
|
||||
if (!location || this._options.followRedirects === false ||
|
||||
statusCode < 300 || statusCode >= 400) {
|
||||
response.responseUrl = this._currentUrl;
|
||||
response.redirects = this._redirects;
|
||||
this.emit("response", response);
|
||||
|
||||
// Clean up
|
||||
this._requestBodyBuffers = [];
|
||||
return;
|
||||
}
|
||||
|
||||
// The response is a redirect, so abort the current request
|
||||
destroyRequest(this._currentRequest);
|
||||
// Discard the remainder of the response to avoid waiting for data
|
||||
response.destroy();
|
||||
|
||||
// RFC7231§6.4: A client SHOULD detect and intervene
|
||||
// in cyclical redirections (i.e., "infinite" redirection loops).
|
||||
if (++this._redirectCount > this._options.maxRedirects) {
|
||||
throw new TooManyRedirectsError();
|
||||
}
|
||||
|
||||
// Store the request headers if applicable
|
||||
var requestHeaders;
|
||||
var beforeRedirect = this._options.beforeRedirect;
|
||||
if (beforeRedirect) {
|
||||
requestHeaders = Object.assign({
|
||||
// The Host header was set by nativeProtocol.request
|
||||
Host: response.req.getHeader("host"),
|
||||
}, this._options.headers);
|
||||
}
|
||||
|
||||
// RFC7231§6.4: Automatic redirection needs to done with
|
||||
// care for methods not known to be safe, […]
|
||||
// RFC7231§6.4.2–3: For historical reasons, a user agent MAY change
|
||||
// the request method from POST to GET for the subsequent request.
|
||||
var method = this._options.method;
|
||||
if ((statusCode === 301 || statusCode === 302) && this._options.method === "POST" ||
|
||||
// RFC7231§6.4.4: The 303 (See Other) status code indicates that
|
||||
// the server is redirecting the user agent to a different resource […]
|
||||
// A user agent can perform a retrieval request targeting that URI
|
||||
// (a GET or HEAD request if using HTTP) […]
|
||||
(statusCode === 303) && !/^(?:GET|HEAD)$/.test(this._options.method)) {
|
||||
this._options.method = "GET";
|
||||
// Drop a possible entity and headers related to it
|
||||
this._requestBodyBuffers = [];
|
||||
removeMatchingHeaders(/^content-/i, this._options.headers);
|
||||
}
|
||||
|
||||
// Drop the Host header, as the redirect might lead to a different host
|
||||
var currentHostHeader = removeMatchingHeaders(/^host$/i, this._options.headers);
|
||||
|
||||
// If the redirect is relative, carry over the host of the last request
|
||||
var currentUrlParts = parseUrl(this._currentUrl);
|
||||
var currentHost = currentHostHeader || currentUrlParts.host;
|
||||
var currentUrl = /^\w+:/.test(location) ? this._currentUrl :
|
||||
url.format(Object.assign(currentUrlParts, { host: currentHost }));
|
||||
|
||||
// Create the redirected request
|
||||
var redirectUrl = resolveUrl(location, currentUrl);
|
||||
debug("redirecting to", redirectUrl.href);
|
||||
this._isRedirect = true;
|
||||
spreadUrlObject(redirectUrl, this._options);
|
||||
|
||||
// Drop confidential headers when redirecting to a less secure protocol
|
||||
// or to a different domain that is not a superdomain
|
||||
if (redirectUrl.protocol !== currentUrlParts.protocol &&
|
||||
redirectUrl.protocol !== "https:" ||
|
||||
redirectUrl.host !== currentHost &&
|
||||
!isSubdomain(redirectUrl.host, currentHost)) {
|
||||
removeMatchingHeaders(/^(?:(?:proxy-)?authorization|cookie)$/i, this._options.headers);
|
||||
}
|
||||
|
||||
// Evaluate the beforeRedirect callback
|
||||
if (isFunction(beforeRedirect)) {
|
||||
var responseDetails = {
|
||||
headers: response.headers,
|
||||
statusCode: statusCode,
|
||||
};
|
||||
var requestDetails = {
|
||||
url: currentUrl,
|
||||
method: method,
|
||||
headers: requestHeaders,
|
||||
};
|
||||
beforeRedirect(this._options, responseDetails, requestDetails);
|
||||
this._sanitizeOptions(this._options);
|
||||
}
|
||||
|
||||
// Perform the redirected request
|
||||
this._performRequest();
|
||||
};
|
||||
|
||||
// Wraps the key/value object of protocols with redirect functionality
|
||||
function wrap(protocols) {
|
||||
// Default settings
|
||||
var exports = {
|
||||
maxRedirects: 21,
|
||||
maxBodyLength: 10 * 1024 * 1024,
|
||||
};
|
||||
|
||||
// Wrap each protocol
|
||||
var nativeProtocols = {};
|
||||
Object.keys(protocols).forEach(function (scheme) {
|
||||
var protocol = scheme + ":";
|
||||
var nativeProtocol = nativeProtocols[protocol] = protocols[scheme];
|
||||
var wrappedProtocol = exports[scheme] = Object.create(nativeProtocol);
|
||||
|
||||
// Executes a request, following redirects
|
||||
function request(input, options, callback) {
|
||||
// Parse parameters, ensuring that input is an object
|
||||
if (isURL(input)) {
|
||||
input = spreadUrlObject(input);
|
||||
}
|
||||
else if (isString(input)) {
|
||||
input = spreadUrlObject(parseUrl(input));
|
||||
}
|
||||
else {
|
||||
callback = options;
|
||||
options = validateUrl(input);
|
||||
input = { protocol: protocol };
|
||||
}
|
||||
if (isFunction(options)) {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
options = Object.assign({
|
||||
maxRedirects: exports.maxRedirects,
|
||||
maxBodyLength: exports.maxBodyLength,
|
||||
}, input, options);
|
||||
options.nativeProtocols = nativeProtocols;
|
||||
if (!isString(options.host) && !isString(options.hostname)) {
|
||||
options.hostname = "::1";
|
||||
}
|
||||
|
||||
assert.equal(options.protocol, protocol, "protocol mismatch");
|
||||
debug("options", options);
|
||||
return new RedirectableRequest(options, callback);
|
||||
}
|
||||
|
||||
// Executes a GET request, following redirects
|
||||
function get(input, options, callback) {
|
||||
var wrappedRequest = wrappedProtocol.request(input, options, callback);
|
||||
wrappedRequest.end();
|
||||
return wrappedRequest;
|
||||
}
|
||||
|
||||
// Expose the properties on the wrapped protocol
|
||||
Object.defineProperties(wrappedProtocol, {
|
||||
request: { value: request, configurable: true, enumerable: true, writable: true },
|
||||
get: { value: get, configurable: true, enumerable: true, writable: true },
|
||||
});
|
||||
});
|
||||
return exports;
|
||||
}
|
||||
|
||||
function noop() { /* empty */ }
|
||||
|
||||
function parseUrl(input) {
|
||||
var parsed;
|
||||
// istanbul ignore else
|
||||
if (useNativeURL) {
|
||||
parsed = new URL(input);
|
||||
}
|
||||
else {
|
||||
// Ensure the URL is valid and absolute
|
||||
parsed = validateUrl(url.parse(input));
|
||||
if (!isString(parsed.protocol)) {
|
||||
throw new InvalidUrlError({ input });
|
||||
}
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function resolveUrl(relative, base) {
|
||||
// istanbul ignore next
|
||||
return useNativeURL ? new URL(relative, base) : parseUrl(url.resolve(base, relative));
|
||||
}
|
||||
|
||||
function validateUrl(input) {
|
||||
if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
|
||||
throw new InvalidUrlError({ input: input.href || input });
|
||||
}
|
||||
if (/^\[/.test(input.host) && !/^\[[:0-9a-f]+\](:\d+)?$/i.test(input.host)) {
|
||||
throw new InvalidUrlError({ input: input.href || input });
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
function spreadUrlObject(urlObject, target) {
|
||||
var spread = target || {};
|
||||
for (var key of preservedUrlFields) {
|
||||
spread[key] = urlObject[key];
|
||||
}
|
||||
|
||||
// Fix IPv6 hostname
|
||||
if (spread.hostname.startsWith("[")) {
|
||||
spread.hostname = spread.hostname.slice(1, -1);
|
||||
}
|
||||
// Ensure port is a number
|
||||
if (spread.port !== "") {
|
||||
spread.port = Number(spread.port);
|
||||
}
|
||||
// Concatenate path
|
||||
spread.path = spread.search ? spread.pathname + spread.search : spread.pathname;
|
||||
|
||||
return spread;
|
||||
}
|
||||
|
||||
function removeMatchingHeaders(regex, headers) {
|
||||
var lastValue;
|
||||
for (var header in headers) {
|
||||
if (regex.test(header)) {
|
||||
lastValue = headers[header];
|
||||
delete headers[header];
|
||||
}
|
||||
}
|
||||
return (lastValue === null || typeof lastValue === "undefined") ?
|
||||
undefined : String(lastValue).trim();
|
||||
}
|
||||
|
||||
function createErrorType(code, message, baseClass) {
|
||||
// Create constructor
|
||||
function CustomError(properties) {
|
||||
// istanbul ignore else
|
||||
if (isFunction(Error.captureStackTrace)) {
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
Object.assign(this, properties || {});
|
||||
this.code = code;
|
||||
this.message = this.cause ? message + ": " + this.cause.message : message;
|
||||
}
|
||||
|
||||
// Attach constructor and set default properties
|
||||
CustomError.prototype = new (baseClass || Error)();
|
||||
Object.defineProperties(CustomError.prototype, {
|
||||
constructor: {
|
||||
value: CustomError,
|
||||
enumerable: false,
|
||||
},
|
||||
name: {
|
||||
value: "Error [" + code + "]",
|
||||
enumerable: false,
|
||||
},
|
||||
});
|
||||
return CustomError;
|
||||
}
|
||||
|
||||
function destroyRequest(request, error) {
|
||||
for (var event of events) {
|
||||
request.removeListener(event, eventHandlers[event]);
|
||||
}
|
||||
request.on("error", noop);
|
||||
request.destroy(error);
|
||||
}
|
||||
|
||||
function isSubdomain(subdomain, domain) {
|
||||
assert(isString(subdomain) && isString(domain));
|
||||
var dot = subdomain.length - domain.length - 1;
|
||||
return dot > 0 && subdomain[dot] === "." && subdomain.endsWith(domain);
|
||||
}
|
||||
|
||||
function isString(value) {
|
||||
return typeof value === "string" || value instanceof String;
|
||||
}
|
||||
|
||||
function isFunction(value) {
|
||||
return typeof value === "function";
|
||||
}
|
||||
|
||||
function isBuffer(value) {
|
||||
return typeof value === "object" && ("length" in value);
|
||||
}
|
||||
|
||||
function isURL(value) {
|
||||
return URL && value instanceof URL;
|
||||
}
|
||||
|
||||
// Exports
|
||||
module.exports = wrap({ http: http, https: https });
|
||||
module.exports.wrap = wrap;
|
||||
|
||||
}, function(modId) {var map = {"http":1758268322109,"https":1758268322110,"./debug":1758268322111}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322109, function(require, module, exports) {
|
||||
module.exports = require("./").http;
|
||||
|
||||
}, function(modId) { var map = {"./":1758268322108}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322110, function(require, module, exports) {
|
||||
module.exports = require("./").https;
|
||||
|
||||
}, function(modId) { var map = {"./":1758268322108}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322111, function(require, module, exports) {
|
||||
var debug;
|
||||
|
||||
module.exports = function () {
|
||||
if (!debug) {
|
||||
try {
|
||||
/* eslint global-require: off */
|
||||
debug = require("debug")("follow-redirects");
|
||||
}
|
||||
catch (error) { /* */ }
|
||||
if (typeof debug !== "function") {
|
||||
debug = function () { /* */ };
|
||||
}
|
||||
}
|
||||
debug.apply(null, arguments);
|
||||
};
|
||||
|
||||
}, function(modId) { var map = {"debug":1758268322111}; return __REQUIRE__(map[modId], modId); })
|
||||
return __REQUIRE__(1758268322108);
|
||||
})()
|
||||
//miniprogram-npm-outsideDeps=["url","stream","assert"]
|
||||
//# sourceMappingURL=index.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,520 +0,0 @@
|
||||
module.exports = (function() {
|
||||
var __MODS__ = {};
|
||||
var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; };
|
||||
var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; };
|
||||
var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } };
|
||||
var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; };
|
||||
__DEFINE__(1758268322112, function(require, module, exports) {
|
||||
|
||||
|
||||
var CombinedStream = require('combined-stream');
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var http = require('http');
|
||||
var https = require('https');
|
||||
var parseUrl = require('url').parse;
|
||||
var fs = require('fs');
|
||||
var Stream = require('stream').Stream;
|
||||
var crypto = require('crypto');
|
||||
var mime = require('mime-types');
|
||||
var asynckit = require('asynckit');
|
||||
var setToStringTag = require('es-set-tostringtag');
|
||||
var hasOwn = require('hasown');
|
||||
var populate = require('./populate.js');
|
||||
|
||||
/**
|
||||
* Create readable "multipart/form-data" streams.
|
||||
* Can be used to submit forms
|
||||
* and file uploads to other web applications.
|
||||
*
|
||||
* @constructor
|
||||
* @param {object} options - Properties to be added/overriden for FormData and CombinedStream
|
||||
*/
|
||||
function FormData(options) {
|
||||
if (!(this instanceof FormData)) {
|
||||
return new FormData(options);
|
||||
}
|
||||
|
||||
this._overheadLength = 0;
|
||||
this._valueLength = 0;
|
||||
this._valuesToMeasure = [];
|
||||
|
||||
CombinedStream.call(this);
|
||||
|
||||
options = options || {}; // eslint-disable-line no-param-reassign
|
||||
for (var option in options) { // eslint-disable-line no-restricted-syntax
|
||||
this[option] = options[option];
|
||||
}
|
||||
}
|
||||
|
||||
// make it a Stream
|
||||
util.inherits(FormData, CombinedStream);
|
||||
|
||||
FormData.LINE_BREAK = '\r\n';
|
||||
FormData.DEFAULT_CONTENT_TYPE = 'application/octet-stream';
|
||||
|
||||
FormData.prototype.append = function (field, value, options) {
|
||||
options = options || {}; // eslint-disable-line no-param-reassign
|
||||
|
||||
// allow filename as single option
|
||||
if (typeof options === 'string') {
|
||||
options = { filename: options }; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
|
||||
var append = CombinedStream.prototype.append.bind(this);
|
||||
|
||||
// all that streamy business can't handle numbers
|
||||
if (typeof value === 'number' || value == null) {
|
||||
value = String(value); // eslint-disable-line no-param-reassign
|
||||
}
|
||||
|
||||
// https://github.com/felixge/node-form-data/issues/38
|
||||
if (Array.isArray(value)) {
|
||||
/*
|
||||
* Please convert your array into string
|
||||
* the way web server expects it
|
||||
*/
|
||||
this._error(new Error('Arrays are not supported.'));
|
||||
return;
|
||||
}
|
||||
|
||||
var header = this._multiPartHeader(field, value, options);
|
||||
var footer = this._multiPartFooter();
|
||||
|
||||
append(header);
|
||||
append(value);
|
||||
append(footer);
|
||||
|
||||
// pass along options.knownLength
|
||||
this._trackLength(header, value, options);
|
||||
};
|
||||
|
||||
FormData.prototype._trackLength = function (header, value, options) {
|
||||
var valueLength = 0;
|
||||
|
||||
/*
|
||||
* used w/ getLengthSync(), when length is known.
|
||||
* e.g. for streaming directly from a remote server,
|
||||
* w/ a known file a size, and not wanting to wait for
|
||||
* incoming file to finish to get its size.
|
||||
*/
|
||||
if (options.knownLength != null) {
|
||||
valueLength += Number(options.knownLength);
|
||||
} else if (Buffer.isBuffer(value)) {
|
||||
valueLength = value.length;
|
||||
} else if (typeof value === 'string') {
|
||||
valueLength = Buffer.byteLength(value);
|
||||
}
|
||||
|
||||
this._valueLength += valueLength;
|
||||
|
||||
// @check why add CRLF? does this account for custom/multiple CRLFs?
|
||||
this._overheadLength += Buffer.byteLength(header) + FormData.LINE_BREAK.length;
|
||||
|
||||
// empty or either doesn't have path or not an http response or not a stream
|
||||
if (!value || (!value.path && !(value.readable && hasOwn(value, 'httpVersion')) && !(value instanceof Stream))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// no need to bother with the length
|
||||
if (!options.knownLength) {
|
||||
this._valuesToMeasure.push(value);
|
||||
}
|
||||
};
|
||||
|
||||
FormData.prototype._lengthRetriever = function (value, callback) {
|
||||
if (hasOwn(value, 'fd')) {
|
||||
// take read range into a account
|
||||
// `end` = Infinity –> read file till the end
|
||||
//
|
||||
// TODO: Looks like there is bug in Node fs.createReadStream
|
||||
// it doesn't respect `end` options without `start` options
|
||||
// Fix it when node fixes it.
|
||||
// https://github.com/joyent/node/issues/7819
|
||||
if (value.end != undefined && value.end != Infinity && value.start != undefined) {
|
||||
// when end specified
|
||||
// no need to calculate range
|
||||
// inclusive, starts with 0
|
||||
callback(null, value.end + 1 - (value.start ? value.start : 0)); // eslint-disable-line callback-return
|
||||
|
||||
// not that fast snoopy
|
||||
} else {
|
||||
// still need to fetch file size from fs
|
||||
fs.stat(value.path, function (err, stat) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// update final size based on the range options
|
||||
var fileSize = stat.size - (value.start ? value.start : 0);
|
||||
callback(null, fileSize);
|
||||
});
|
||||
}
|
||||
|
||||
// or http response
|
||||
} else if (hasOwn(value, 'httpVersion')) {
|
||||
callback(null, Number(value.headers['content-length'])); // eslint-disable-line callback-return
|
||||
|
||||
// or request stream http://github.com/mikeal/request
|
||||
} else if (hasOwn(value, 'httpModule')) {
|
||||
// wait till response come back
|
||||
value.on('response', function (response) {
|
||||
value.pause();
|
||||
callback(null, Number(response.headers['content-length']));
|
||||
});
|
||||
value.resume();
|
||||
|
||||
// something else
|
||||
} else {
|
||||
callback('Unknown stream'); // eslint-disable-line callback-return
|
||||
}
|
||||
};
|
||||
|
||||
FormData.prototype._multiPartHeader = function (field, value, options) {
|
||||
/*
|
||||
* custom header specified (as string)?
|
||||
* it becomes responsible for boundary
|
||||
* (e.g. to handle extra CRLFs on .NET servers)
|
||||
*/
|
||||
if (typeof options.header === 'string') {
|
||||
return options.header;
|
||||
}
|
||||
|
||||
var contentDisposition = this._getContentDisposition(value, options);
|
||||
var contentType = this._getContentType(value, options);
|
||||
|
||||
var contents = '';
|
||||
var headers = {
|
||||
// add custom disposition as third element or keep it two elements if not
|
||||
'Content-Disposition': ['form-data', 'name="' + field + '"'].concat(contentDisposition || []),
|
||||
// if no content type. allow it to be empty array
|
||||
'Content-Type': [].concat(contentType || [])
|
||||
};
|
||||
|
||||
// allow custom headers.
|
||||
if (typeof options.header === 'object') {
|
||||
populate(headers, options.header);
|
||||
}
|
||||
|
||||
var header;
|
||||
for (var prop in headers) { // eslint-disable-line no-restricted-syntax
|
||||
if (hasOwn(headers, prop)) {
|
||||
header = headers[prop];
|
||||
|
||||
// skip nullish headers.
|
||||
if (header == null) {
|
||||
continue; // eslint-disable-line no-restricted-syntax, no-continue
|
||||
}
|
||||
|
||||
// convert all headers to arrays.
|
||||
if (!Array.isArray(header)) {
|
||||
header = [header];
|
||||
}
|
||||
|
||||
// add non-empty headers.
|
||||
if (header.length) {
|
||||
contents += prop + ': ' + header.join('; ') + FormData.LINE_BREAK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '--' + this.getBoundary() + FormData.LINE_BREAK + contents + FormData.LINE_BREAK;
|
||||
};
|
||||
|
||||
FormData.prototype._getContentDisposition = function (value, options) { // eslint-disable-line consistent-return
|
||||
var filename;
|
||||
|
||||
if (typeof options.filepath === 'string') {
|
||||
// custom filepath for relative paths
|
||||
filename = path.normalize(options.filepath).replace(/\\/g, '/');
|
||||
} else if (options.filename || (value && (value.name || value.path))) {
|
||||
/*
|
||||
* custom filename take precedence
|
||||
* formidable and the browser add a name property
|
||||
* fs- and request- streams have path property
|
||||
*/
|
||||
filename = path.basename(options.filename || (value && (value.name || value.path)));
|
||||
} else if (value && value.readable && hasOwn(value, 'httpVersion')) {
|
||||
// or try http response
|
||||
filename = path.basename(value.client._httpMessage.path || '');
|
||||
}
|
||||
|
||||
if (filename) {
|
||||
return 'filename="' + filename + '"';
|
||||
}
|
||||
};
|
||||
|
||||
FormData.prototype._getContentType = function (value, options) {
|
||||
// use custom content-type above all
|
||||
var contentType = options.contentType;
|
||||
|
||||
// or try `name` from formidable, browser
|
||||
if (!contentType && value && value.name) {
|
||||
contentType = mime.lookup(value.name);
|
||||
}
|
||||
|
||||
// or try `path` from fs-, request- streams
|
||||
if (!contentType && value && value.path) {
|
||||
contentType = mime.lookup(value.path);
|
||||
}
|
||||
|
||||
// or if it's http-reponse
|
||||
if (!contentType && value && value.readable && hasOwn(value, 'httpVersion')) {
|
||||
contentType = value.headers['content-type'];
|
||||
}
|
||||
|
||||
// or guess it from the filepath or filename
|
||||
if (!contentType && (options.filepath || options.filename)) {
|
||||
contentType = mime.lookup(options.filepath || options.filename);
|
||||
}
|
||||
|
||||
// fallback to the default content type if `value` is not simple value
|
||||
if (!contentType && value && typeof value === 'object') {
|
||||
contentType = FormData.DEFAULT_CONTENT_TYPE;
|
||||
}
|
||||
|
||||
return contentType;
|
||||
};
|
||||
|
||||
FormData.prototype._multiPartFooter = function () {
|
||||
return function (next) {
|
||||
var footer = FormData.LINE_BREAK;
|
||||
|
||||
var lastPart = this._streams.length === 0;
|
||||
if (lastPart) {
|
||||
footer += this._lastBoundary();
|
||||
}
|
||||
|
||||
next(footer);
|
||||
}.bind(this);
|
||||
};
|
||||
|
||||
FormData.prototype._lastBoundary = function () {
|
||||
return '--' + this.getBoundary() + '--' + FormData.LINE_BREAK;
|
||||
};
|
||||
|
||||
FormData.prototype.getHeaders = function (userHeaders) {
|
||||
var header;
|
||||
var formHeaders = {
|
||||
'content-type': 'multipart/form-data; boundary=' + this.getBoundary()
|
||||
};
|
||||
|
||||
for (header in userHeaders) { // eslint-disable-line no-restricted-syntax
|
||||
if (hasOwn(userHeaders, header)) {
|
||||
formHeaders[header.toLowerCase()] = userHeaders[header];
|
||||
}
|
||||
}
|
||||
|
||||
return formHeaders;
|
||||
};
|
||||
|
||||
FormData.prototype.setBoundary = function (boundary) {
|
||||
if (typeof boundary !== 'string') {
|
||||
throw new TypeError('FormData boundary must be a string');
|
||||
}
|
||||
this._boundary = boundary;
|
||||
};
|
||||
|
||||
FormData.prototype.getBoundary = function () {
|
||||
if (!this._boundary) {
|
||||
this._generateBoundary();
|
||||
}
|
||||
|
||||
return this._boundary;
|
||||
};
|
||||
|
||||
FormData.prototype.getBuffer = function () {
|
||||
var dataBuffer = new Buffer.alloc(0); // eslint-disable-line new-cap
|
||||
var boundary = this.getBoundary();
|
||||
|
||||
// Create the form content. Add Line breaks to the end of data.
|
||||
for (var i = 0, len = this._streams.length; i < len; i++) {
|
||||
if (typeof this._streams[i] !== 'function') {
|
||||
// Add content to the buffer.
|
||||
if (Buffer.isBuffer(this._streams[i])) {
|
||||
dataBuffer = Buffer.concat([dataBuffer, this._streams[i]]);
|
||||
} else {
|
||||
dataBuffer = Buffer.concat([dataBuffer, Buffer.from(this._streams[i])]);
|
||||
}
|
||||
|
||||
// Add break after content.
|
||||
if (typeof this._streams[i] !== 'string' || this._streams[i].substring(2, boundary.length + 2) !== boundary) {
|
||||
dataBuffer = Buffer.concat([dataBuffer, Buffer.from(FormData.LINE_BREAK)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the footer and return the Buffer object.
|
||||
return Buffer.concat([dataBuffer, Buffer.from(this._lastBoundary())]);
|
||||
};
|
||||
|
||||
FormData.prototype._generateBoundary = function () {
|
||||
// This generates a 50 character boundary similar to those used by Firefox.
|
||||
|
||||
// They are optimized for boyer-moore parsing.
|
||||
this._boundary = '--------------------------' + crypto.randomBytes(12).toString('hex');
|
||||
};
|
||||
|
||||
// Note: getLengthSync DOESN'T calculate streams length
|
||||
// As workaround one can calculate file size manually and add it as knownLength option
|
||||
FormData.prototype.getLengthSync = function () {
|
||||
var knownLength = this._overheadLength + this._valueLength;
|
||||
|
||||
// Don't get confused, there are 3 "internal" streams for each keyval pair so it basically checks if there is any value added to the form
|
||||
if (this._streams.length) {
|
||||
knownLength += this._lastBoundary().length;
|
||||
}
|
||||
|
||||
// https://github.com/form-data/form-data/issues/40
|
||||
if (!this.hasKnownLength()) {
|
||||
/*
|
||||
* Some async length retrievers are present
|
||||
* therefore synchronous length calculation is false.
|
||||
* Please use getLength(callback) to get proper length
|
||||
*/
|
||||
this._error(new Error('Cannot calculate proper length in synchronous way.'));
|
||||
}
|
||||
|
||||
return knownLength;
|
||||
};
|
||||
|
||||
// Public API to check if length of added values is known
|
||||
// https://github.com/form-data/form-data/issues/196
|
||||
// https://github.com/form-data/form-data/issues/262
|
||||
FormData.prototype.hasKnownLength = function () {
|
||||
var hasKnownLength = true;
|
||||
|
||||
if (this._valuesToMeasure.length) {
|
||||
hasKnownLength = false;
|
||||
}
|
||||
|
||||
return hasKnownLength;
|
||||
};
|
||||
|
||||
FormData.prototype.getLength = function (cb) {
|
||||
var knownLength = this._overheadLength + this._valueLength;
|
||||
|
||||
if (this._streams.length) {
|
||||
knownLength += this._lastBoundary().length;
|
||||
}
|
||||
|
||||
if (!this._valuesToMeasure.length) {
|
||||
process.nextTick(cb.bind(this, null, knownLength));
|
||||
return;
|
||||
}
|
||||
|
||||
asynckit.parallel(this._valuesToMeasure, this._lengthRetriever, function (err, values) {
|
||||
if (err) {
|
||||
cb(err);
|
||||
return;
|
||||
}
|
||||
|
||||
values.forEach(function (length) {
|
||||
knownLength += length;
|
||||
});
|
||||
|
||||
cb(null, knownLength);
|
||||
});
|
||||
};
|
||||
|
||||
FormData.prototype.submit = function (params, cb) {
|
||||
var request;
|
||||
var options;
|
||||
var defaults = { method: 'post' };
|
||||
|
||||
// parse provided url if it's string or treat it as options object
|
||||
if (typeof params === 'string') {
|
||||
params = parseUrl(params); // eslint-disable-line no-param-reassign
|
||||
/* eslint sort-keys: 0 */
|
||||
options = populate({
|
||||
port: params.port,
|
||||
path: params.pathname,
|
||||
host: params.hostname,
|
||||
protocol: params.protocol
|
||||
}, defaults);
|
||||
} else { // use custom params
|
||||
options = populate(params, defaults);
|
||||
// if no port provided use default one
|
||||
if (!options.port) {
|
||||
options.port = options.protocol === 'https:' ? 443 : 80;
|
||||
}
|
||||
}
|
||||
|
||||
// put that good code in getHeaders to some use
|
||||
options.headers = this.getHeaders(params.headers);
|
||||
|
||||
// https if specified, fallback to http in any other case
|
||||
if (options.protocol === 'https:') {
|
||||
request = https.request(options);
|
||||
} else {
|
||||
request = http.request(options);
|
||||
}
|
||||
|
||||
// get content length and fire away
|
||||
this.getLength(function (err, length) {
|
||||
if (err && err !== 'Unknown stream') {
|
||||
this._error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// add content length
|
||||
if (length) {
|
||||
request.setHeader('Content-Length', length);
|
||||
}
|
||||
|
||||
this.pipe(request);
|
||||
if (cb) {
|
||||
var onResponse;
|
||||
|
||||
var callback = function (error, responce) {
|
||||
request.removeListener('error', callback);
|
||||
request.removeListener('response', onResponse);
|
||||
|
||||
return cb.call(this, error, responce); // eslint-disable-line no-invalid-this
|
||||
};
|
||||
|
||||
onResponse = callback.bind(this, null);
|
||||
|
||||
request.on('error', callback);
|
||||
request.on('response', onResponse);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
return request;
|
||||
};
|
||||
|
||||
FormData.prototype._error = function (err) {
|
||||
if (!this.error) {
|
||||
this.error = err;
|
||||
this.pause();
|
||||
this.emit('error', err);
|
||||
}
|
||||
};
|
||||
|
||||
FormData.prototype.toString = function () {
|
||||
return '[object FormData]';
|
||||
};
|
||||
setToStringTag(FormData, 'FormData');
|
||||
|
||||
// Public API
|
||||
module.exports = FormData;
|
||||
|
||||
}, function(modId) {var map = {"./populate.js":1758268322113}; return __REQUIRE__(map[modId], modId); })
|
||||
__DEFINE__(1758268322113, function(require, module, exports) {
|
||||
|
||||
|
||||
// populates missing values
|
||||
module.exports = function (dst, src) {
|
||||
Object.keys(src).forEach(function (prop) {
|
||||
dst[prop] = dst[prop] || src[prop]; // eslint-disable-line no-param-reassign
|
||||
});
|
||||
|
||||
return dst;
|
||||
};
|
||||
|
||||
}, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); })
|
||||
return __REQUIRE__(1758268322112);
|
||||
})()
|
||||
//miniprogram-npm-outsideDeps=["combined-stream","util","path","http","https","url","fs","stream","crypto","mime-types","asynckit","es-set-tostringtag","hasown"]
|
||||
//# sourceMappingURL=index.js.map
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user