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 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.24**: 修复移动端兼容性问题,优化HTTP响应头设置,确保iOS和Android显示一致性
232
- - **v1.0.23**: 完善双语文档支持,优化package.json关键词,提升npm包曝光度
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
@@ -1,3 +1,3 @@
1
- import { AgentConfig } from './types';
1
+ import { AgentConfig } from "./types";
2
2
  export declare function createServer(config: AgentConfig): import("express-serve-static-core").Express;
3
3
  export declare function startServer(config: AgentConfig): Promise<void>;
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('User-Agent') || '-';
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 || 'http'; // 默认为http类型
43
- if (proxyType === 'static') {
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('X-Content-Type-Options', 'nosniff');
54
- res.setHeader('X-Frame-Options', 'SAMEORIGIN');
55
- res.setHeader('X-XSS-Protection', '1; mode=block');
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('.html')) {
58
- res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
59
- res.setHeader('Pragma', 'no-cache');
60
- res.setHeader('Expires', '0');
61
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
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('.css')) {
65
- res.setHeader('Content-Type', 'text/css; charset=utf-8');
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('.js')) {
69
- res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
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('Cache-Control', 'public, max-age=31536000');
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('X-Content-Type-Options', 'nosniff');
95
- res.setHeader('X-Frame-Options', 'SAMEORIGIN');
96
- res.setHeader('X-XSS-Protection', '1; mode=block');
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('.html')) {
99
- res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
100
- res.setHeader('Pragma', 'no-cache');
101
- res.setHeader('Expires', '0');
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('Content-Type', 'text/html; charset=utf-8');
130
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
104
131
  }
105
132
  // 针对CSS文件确保正确的Content-Type
106
- if (filePath.endsWith('.css')) {
107
- res.setHeader('Content-Type', 'text/css; charset=utf-8');
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('.js')) {
111
- res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
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('Cache-Control', 'public, max-age=31536000');
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('User-Agent') || '';
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('Vary', 'User-Agent');
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('/api/')) {
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, 'index.html');
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, 'index.html'));
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('Failed to start server:', 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?: 'http' | 'static';
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.24",
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",