agent-publish-server 1.0.24 → 1.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -2
- package/README_EN.md +22 -0
- package/dist/cli.js +0 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +69 -42
- package/dist/types.d.ts +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -91,6 +91,7 @@ agent-publish-server -c ./agent_config.json
|
|
|
91
91
|
interface AgentConfig {
|
|
92
92
|
port?: number; // 服务器端口(默认:8080)
|
|
93
93
|
dir?: string; // 静态文件目录(默认:"./")
|
|
94
|
+
redirect?: string; // 根路径重定向,如 "/app" 表示访问 / 时重定向到 /app
|
|
94
95
|
log?: boolean; // 启用访问日志(默认:true)
|
|
95
96
|
proxy?: {
|
|
96
97
|
// API代理配置
|
|
@@ -114,6 +115,25 @@ interface AgentConfig {
|
|
|
114
115
|
}
|
|
115
116
|
```
|
|
116
117
|
|
|
118
|
+
### 根路径重定向
|
|
119
|
+
|
|
120
|
+
通过 `redirect` 配置实现访问根路径时自动重定向到指定路径:
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"port": 3000,
|
|
125
|
+
"redirect": "/app",
|
|
126
|
+
"staticProxy": {
|
|
127
|
+
"/app": {
|
|
128
|
+
"target": "./dist",
|
|
129
|
+
"type": "static"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
这样配置后,访问 `http://localhost:3000/` 会自动重定向到 `http://localhost:3000/app`。
|
|
136
|
+
|
|
117
137
|
### 静态网页代理
|
|
118
138
|
|
|
119
139
|
除了 API 代理外,还支持静态网页代理功能,用于代理整个网站或应用。
|
|
@@ -228,8 +248,10 @@ agent-publish-server --log false
|
|
|
228
248
|
|
|
229
249
|
### 版本历史
|
|
230
250
|
|
|
231
|
-
- **v1.0.
|
|
232
|
-
- **v1.0.
|
|
251
|
+
- **v1.0.26**: 新增根路径重定向配置(redirect),支持访问 / 时自动跳转到指定路径
|
|
252
|
+
- **v1.0.25**: 修复 staticProxy static 类型不支持 SPA History 路由刷新的问题,添加自动 fallback 到 index.html 支持
|
|
253
|
+
- **v1.0.24**: 修复移动端兼容性问题,优化 HTTP 响应头设置,确保 iOS 和 Android 显示一致性
|
|
254
|
+
- **v1.0.23**: 完善双语文档支持,优化 package.json 关键词,提升 npm 包曝光度
|
|
233
255
|
- **v1.0.22**: 优化和完善 staticProxy 功能,提升稳定性
|
|
234
256
|
- **v1.0.18**: 增强 staticProxy 功能,支持静态文件代理和 HTTP 服务代理两种模式
|
|
235
257
|
- **v1.0.17**: 新增静态网页代理功能(staticProxy),支持与 API 代理同时使用
|
package/README_EN.md
CHANGED
|
@@ -77,9 +77,29 @@ agent-publish-server -c config.json
|
|
|
77
77
|
|
|
78
78
|
- **port**: Server port (default: 3000)
|
|
79
79
|
- **dir**: Static file directory (default: current directory)
|
|
80
|
+
- **redirect**: Root path redirect, e.g. "/app" redirects / to /app
|
|
80
81
|
- **proxy**: API proxy configuration
|
|
81
82
|
- **staticProxy**: Static proxy configuration
|
|
82
83
|
|
|
84
|
+
### Root Path Redirect
|
|
85
|
+
|
|
86
|
+
Use `redirect` to automatically redirect root path to a specified path:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"port": 3000,
|
|
91
|
+
"redirect": "/app",
|
|
92
|
+
"staticProxy": {
|
|
93
|
+
"/app": {
|
|
94
|
+
"target": "./dist",
|
|
95
|
+
"type": "static"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
With this config, accessing `http://localhost:3000/` will redirect to `http://localhost:3000/app`.
|
|
102
|
+
|
|
83
103
|
#### API Proxy Configuration (ProxyConfig)
|
|
84
104
|
|
|
85
105
|
```typescript
|
|
@@ -229,6 +249,8 @@ startServer(app, config.port);
|
|
|
229
249
|
|
|
230
250
|
## Version History
|
|
231
251
|
|
|
252
|
+
- **v1.0.26**: Added root path redirect configuration (redirect), supports automatic redirect when accessing /
|
|
253
|
+
- **v1.0.25**: Fixed staticProxy static type not supporting SPA History route refresh, added automatic fallback to index.html
|
|
232
254
|
- **v1.0.24**: Fixed mobile compatibility issues, optimized HTTP response headers for consistent iOS and Android display
|
|
233
255
|
- **v1.0.23**: Enhanced bilingual documentation support, optimized package.json keywords for better npm exposure
|
|
234
256
|
- **v1.0.22**: Optimized and improved staticProxy functionality, enhanced stability
|
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/server.d.ts
CHANGED
package/dist/server.js
CHANGED
|
@@ -11,12 +11,12 @@ const path_1 = __importDefault(require("path"));
|
|
|
11
11
|
const fs_1 = __importDefault(require("fs"));
|
|
12
12
|
function createServer(config) {
|
|
13
13
|
const app = (0, express_1.default)();
|
|
14
|
-
const staticDir = config.dir ||
|
|
14
|
+
const staticDir = config.dir || "./";
|
|
15
15
|
const enableLog = config.log !== false; // 默认为true
|
|
16
16
|
// 检查项目路径是否存在
|
|
17
17
|
const absoluteStaticDir = path_1.default.resolve(process.cwd(), staticDir);
|
|
18
18
|
if (!fs_1.default.existsSync(absoluteStaticDir)) {
|
|
19
|
-
throw new Error(
|
|
19
|
+
throw new Error("项目路径错误");
|
|
20
20
|
}
|
|
21
21
|
// 添加日志中间件
|
|
22
22
|
if (enableLog) {
|
|
@@ -24,12 +24,21 @@ function createServer(config) {
|
|
|
24
24
|
const timestamp = new Date().toISOString();
|
|
25
25
|
const method = req.method;
|
|
26
26
|
const url = req.url;
|
|
27
|
-
const userAgent = req.get(
|
|
28
|
-
const ip = req.ip || req.connection.remoteAddress ||
|
|
27
|
+
const userAgent = req.get("User-Agent") || "-";
|
|
28
|
+
const ip = req.ip || req.connection.remoteAddress || "-";
|
|
29
29
|
console.log(`[${timestamp}] ${ip} "${method} ${url}" "${userAgent}"`);
|
|
30
30
|
next();
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
|
+
// 根路径重定向配置
|
|
34
|
+
if (config.redirect) {
|
|
35
|
+
app.get("/", (req, res) => {
|
|
36
|
+
const redirectPath = config.redirect.startsWith("/")
|
|
37
|
+
? config.redirect
|
|
38
|
+
: `/${config.redirect}`;
|
|
39
|
+
res.redirect(302, redirectPath);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
33
42
|
// 配置API代理
|
|
34
43
|
if (config.proxy) {
|
|
35
44
|
Object.entries(config.proxy).forEach(([path, proxyConfig]) => {
|
|
@@ -39,41 +48,59 @@ function createServer(config) {
|
|
|
39
48
|
// 配置静态网页代理
|
|
40
49
|
if (config.staticProxy) {
|
|
41
50
|
Object.entries(config.staticProxy).forEach(([proxyPath, staticProxyConfig]) => {
|
|
42
|
-
const proxyType = staticProxyConfig.type ||
|
|
43
|
-
if (proxyType ===
|
|
51
|
+
const proxyType = staticProxyConfig.type || "http"; // 默认为http类型
|
|
52
|
+
if (proxyType === "static") {
|
|
44
53
|
// 静态文件代理
|
|
45
54
|
const staticPath = path_1.default.resolve(process.cwd(), staticProxyConfig.target);
|
|
46
55
|
if (!fs_1.default.existsSync(staticPath)) {
|
|
47
56
|
console.warn(`Warning: Static proxy path does not exist: ${staticPath}`);
|
|
48
57
|
return;
|
|
49
58
|
}
|
|
59
|
+
// 静态文件服务
|
|
50
60
|
app.use(proxyPath, express_1.default.static(staticPath, {
|
|
51
61
|
setHeaders: (res, filePath, stat) => {
|
|
52
62
|
// 设置通用响应头,提升移动端兼容性
|
|
53
|
-
res.setHeader(
|
|
54
|
-
res.setHeader(
|
|
55
|
-
res.setHeader(
|
|
63
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
64
|
+
res.setHeader("X-Frame-Options", "SAMEORIGIN");
|
|
65
|
+
res.setHeader("X-XSS-Protection", "1; mode=block");
|
|
56
66
|
// 针对HTML文件设置移动端优化响应头
|
|
57
|
-
if (filePath.endsWith(
|
|
58
|
-
res.setHeader(
|
|
59
|
-
res.setHeader(
|
|
60
|
-
res.setHeader(
|
|
61
|
-
res.setHeader(
|
|
67
|
+
if (filePath.endsWith(".html")) {
|
|
68
|
+
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
69
|
+
res.setHeader("Pragma", "no-cache");
|
|
70
|
+
res.setHeader("Expires", "0");
|
|
71
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
62
72
|
}
|
|
63
73
|
// 针对CSS文件确保正确的Content-Type
|
|
64
|
-
if (filePath.endsWith(
|
|
65
|
-
res.setHeader(
|
|
74
|
+
if (filePath.endsWith(".css")) {
|
|
75
|
+
res.setHeader("Content-Type", "text/css; charset=utf-8");
|
|
66
76
|
}
|
|
67
77
|
// 针对JS文件确保正确的Content-Type
|
|
68
|
-
if (filePath.endsWith(
|
|
69
|
-
res.setHeader(
|
|
78
|
+
if (filePath.endsWith(".js")) {
|
|
79
|
+
res.setHeader("Content-Type", "application/javascript; charset=utf-8");
|
|
70
80
|
}
|
|
71
81
|
// 针对图片文件设置适当的缓存
|
|
72
82
|
if (filePath.match(/\.(jpg|jpeg|png|gif|webp|svg)$/i)) {
|
|
73
|
-
res.setHeader(
|
|
83
|
+
res.setHeader("Cache-Control", "public, max-age=31536000");
|
|
74
84
|
}
|
|
75
|
-
}
|
|
85
|
+
},
|
|
76
86
|
}));
|
|
87
|
+
// SPA fallback:静态文件不存在时回退到 index.html
|
|
88
|
+
app.use(proxyPath, (req, res, next) => {
|
|
89
|
+
const reqPath = req.path === "/" ? "" : req.path;
|
|
90
|
+
const filePath = path_1.default.join(staticPath, reqPath);
|
|
91
|
+
const indexPath = path_1.default.join(staticPath, "index.html");
|
|
92
|
+
// 如果请求的文件存在,跳过(已被 express.static 处理)
|
|
93
|
+
if (fs_1.default.existsSync(filePath) && fs_1.default.statSync(filePath).isFile()) {
|
|
94
|
+
return next();
|
|
95
|
+
}
|
|
96
|
+
// 如果 index.html 存在,返回它(SPA fallback)
|
|
97
|
+
if (fs_1.default.existsSync(indexPath)) {
|
|
98
|
+
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
99
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
100
|
+
return res.sendFile(indexPath);
|
|
101
|
+
}
|
|
102
|
+
next();
|
|
103
|
+
});
|
|
77
104
|
}
|
|
78
105
|
else {
|
|
79
106
|
// HTTP服务代理
|
|
@@ -81,8 +108,8 @@ function createServer(config) {
|
|
|
81
108
|
target: staticProxyConfig.target,
|
|
82
109
|
changeOrigin: staticProxyConfig.changeOrigin !== false, // 默认为true
|
|
83
110
|
pathRewrite: {
|
|
84
|
-
[`^${proxyPath}`]:
|
|
85
|
-
}
|
|
111
|
+
[`^${proxyPath}`]: "", // 移除代理路径前缀
|
|
112
|
+
},
|
|
86
113
|
}));
|
|
87
114
|
}
|
|
88
115
|
});
|
|
@@ -91,39 +118,39 @@ function createServer(config) {
|
|
|
91
118
|
app.use(express_1.default.static(staticDir, {
|
|
92
119
|
setHeaders: (res, filePath, stat) => {
|
|
93
120
|
// 设置通用响应头,提升移动端兼容性
|
|
94
|
-
res.setHeader(
|
|
95
|
-
res.setHeader(
|
|
96
|
-
res.setHeader(
|
|
121
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
122
|
+
res.setHeader("X-Frame-Options", "SAMEORIGIN");
|
|
123
|
+
res.setHeader("X-XSS-Protection", "1; mode=block");
|
|
97
124
|
// 针对HTML文件设置移动端优化响应头
|
|
98
|
-
if (filePath.endsWith(
|
|
99
|
-
res.setHeader(
|
|
100
|
-
res.setHeader(
|
|
101
|
-
res.setHeader(
|
|
125
|
+
if (filePath.endsWith(".html")) {
|
|
126
|
+
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
127
|
+
res.setHeader("Pragma", "no-cache");
|
|
128
|
+
res.setHeader("Expires", "0");
|
|
102
129
|
// 确保正确的Content-Type
|
|
103
|
-
res.setHeader(
|
|
130
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
104
131
|
}
|
|
105
132
|
// 针对CSS文件确保正确的Content-Type
|
|
106
|
-
if (filePath.endsWith(
|
|
107
|
-
res.setHeader(
|
|
133
|
+
if (filePath.endsWith(".css")) {
|
|
134
|
+
res.setHeader("Content-Type", "text/css; charset=utf-8");
|
|
108
135
|
}
|
|
109
136
|
// 针对JS文件确保正确的Content-Type
|
|
110
|
-
if (filePath.endsWith(
|
|
111
|
-
res.setHeader(
|
|
137
|
+
if (filePath.endsWith(".js")) {
|
|
138
|
+
res.setHeader("Content-Type", "application/javascript; charset=utf-8");
|
|
112
139
|
}
|
|
113
140
|
// 针对图片文件设置适当的缓存
|
|
114
141
|
if (filePath.match(/\.(jpg|jpeg|png|gif|webp|svg)$/i)) {
|
|
115
|
-
res.setHeader(
|
|
142
|
+
res.setHeader("Cache-Control", "public, max-age=31536000");
|
|
116
143
|
}
|
|
117
|
-
}
|
|
144
|
+
},
|
|
118
145
|
}));
|
|
119
146
|
// 添加移动端兼容性中间件
|
|
120
147
|
app.use((req, res, next) => {
|
|
121
148
|
// 检测移动端User-Agent并设置相应响应头
|
|
122
|
-
const userAgent = req.get(
|
|
149
|
+
const userAgent = req.get("User-Agent") || "";
|
|
123
150
|
const isMobile = /Mobile|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
|
|
124
151
|
if (isMobile) {
|
|
125
152
|
// 为移动端设置额外的响应头
|
|
126
|
-
res.setHeader(
|
|
153
|
+
res.setHeader("Vary", "User-Agent");
|
|
127
154
|
}
|
|
128
155
|
next();
|
|
129
156
|
});
|
|
@@ -131,7 +158,7 @@ function createServer(config) {
|
|
|
131
158
|
// 只有当实际文件不存在时才回退到index.html
|
|
132
159
|
app.use((req, res, next) => {
|
|
133
160
|
// 排除API和已处理的代理请求
|
|
134
|
-
if (req.path.startsWith(
|
|
161
|
+
if (req.path.startsWith("/api/")) {
|
|
135
162
|
return next();
|
|
136
163
|
}
|
|
137
164
|
// 排除静态代理路径
|
|
@@ -146,7 +173,7 @@ function createServer(config) {
|
|
|
146
173
|
// 检查是否为目录
|
|
147
174
|
if (fs_1.default.existsSync(filePath) && fs_1.default.statSync(filePath).isDirectory()) {
|
|
148
175
|
// 检查目录中是否有index.html
|
|
149
|
-
const indexPath = path_1.default.join(filePath,
|
|
176
|
+
const indexPath = path_1.default.join(filePath, "index.html");
|
|
150
177
|
if (fs_1.default.existsSync(indexPath)) {
|
|
151
178
|
return res.sendFile(indexPath);
|
|
152
179
|
}
|
|
@@ -156,7 +183,7 @@ function createServer(config) {
|
|
|
156
183
|
return res.sendFile(filePath);
|
|
157
184
|
}
|
|
158
185
|
// 如果路径不存在,回退到index.html
|
|
159
|
-
res.sendFile(path_1.default.join(process.cwd(), staticDir,
|
|
186
|
+
res.sendFile(path_1.default.join(process.cwd(), staticDir, "index.html"));
|
|
160
187
|
});
|
|
161
188
|
return app;
|
|
162
189
|
}
|
|
@@ -172,7 +199,7 @@ function startServer(config) {
|
|
|
172
199
|
});
|
|
173
200
|
}
|
|
174
201
|
catch (error) {
|
|
175
|
-
console.error(
|
|
202
|
+
console.error("Failed to start server:", error);
|
|
176
203
|
reject(error);
|
|
177
204
|
}
|
|
178
205
|
});
|
package/dist/types.d.ts
CHANGED
|
@@ -5,12 +5,13 @@ export interface ProxyConfig {
|
|
|
5
5
|
}
|
|
6
6
|
export interface StaticProxyConfig {
|
|
7
7
|
target: string;
|
|
8
|
-
type?:
|
|
8
|
+
type?: "http" | "static";
|
|
9
9
|
changeOrigin?: boolean;
|
|
10
10
|
}
|
|
11
11
|
export interface AgentConfig {
|
|
12
12
|
port?: number;
|
|
13
13
|
dir?: string;
|
|
14
|
+
redirect?: string;
|
|
14
15
|
proxy?: Record<string, ProxyConfig>;
|
|
15
16
|
staticProxy?: Record<string, StaticProxyConfig>;
|
|
16
17
|
log?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-publish-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.26",
|
|
4
4
|
"description": "A powerful frontend development server with API proxy, static file serving, and dual-mode static proxy support. Built with Node.js + Express + http-proxy-middleware. 基于 Node.js + Express + http-proxy-middleware 的强大前端开发服务器,支持API代理、静态文件服务和双模式静态代理。",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|