add proxy support
This commit is contained in:
parent
72f5d249cf
commit
ebb413d693
86
README.md
86
README.md
|
|
@ -113,6 +113,14 @@ bootstrapApplication(AppComponent, {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"serve": {
|
||||||
|
"proxy": {
|
||||||
|
"/api/*": {
|
||||||
|
"target": "http://localhost:3000",
|
||||||
|
"changeOrigin": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"environments": {
|
"environments": {
|
||||||
"development": {
|
"development": {
|
||||||
"minifyNames": true,
|
"minifyNames": true,
|
||||||
|
|
@ -329,6 +337,84 @@ export class HighlightDirective {
|
||||||
|
|
||||||
## 🔧 Advanced Features
|
## 🔧 Advanced Features
|
||||||
|
|
||||||
|
### Development Server Proxy
|
||||||
|
|
||||||
|
The development server supports proxying HTTP requests to a backend server. This is useful when your frontend needs to communicate with an API during development.
|
||||||
|
|
||||||
|
**Configuration in quarc.json:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"serve": {
|
||||||
|
"proxy": {
|
||||||
|
"/api/*": {
|
||||||
|
"target": "http://192.168.1.100:8080",
|
||||||
|
"changeOrigin": true
|
||||||
|
},
|
||||||
|
"/auth/*": {
|
||||||
|
"target": "https://auth.example.com",
|
||||||
|
"changeOrigin": true,
|
||||||
|
"pathRewrite": {
|
||||||
|
"^/auth": "/api/v1/auth"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Proxy Options:**
|
||||||
|
|
||||||
|
- **Pattern matching**: Use wildcards (`*`) to match request paths
|
||||||
|
- `/api/*` matches `/api/users`, `/api/data`, etc.
|
||||||
|
- `/v1/*/data` matches `/v1/users/data`, `/v1/products/data`, etc.
|
||||||
|
|
||||||
|
- **target**: Backend server URL (required)
|
||||||
|
- Can be HTTP or HTTPS
|
||||||
|
- Include protocol and host, optionally port
|
||||||
|
|
||||||
|
- **changeOrigin**: Set to `true` to change the `Host` header to match the target (recommended for most cases)
|
||||||
|
|
||||||
|
- **pathRewrite**: Object with regex patterns to rewrite request paths
|
||||||
|
- Key: regex pattern to match
|
||||||
|
- Value: replacement string
|
||||||
|
|
||||||
|
**Example Use Cases:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"serve": {
|
||||||
|
"proxy": {
|
||||||
|
"/api/*": {
|
||||||
|
"target": "http://localhost:3000",
|
||||||
|
"changeOrigin": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When your app makes a request to `http://localhost:4200/api/users`, it will be proxied to `http://localhost:3000/api/users`.
|
||||||
|
|
||||||
|
**Perfect for ESP32 Development:**
|
||||||
|
|
||||||
|
When developing for ESP32, you can proxy API requests to the device:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"serve": {
|
||||||
|
"proxy": {
|
||||||
|
"/api/*": {
|
||||||
|
"target": "http://192.168.1.50",
|
||||||
|
"changeOrigin": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This allows you to develop your frontend locally while testing against the actual ESP32 backend.
|
||||||
|
|
||||||
### Lazy Loading
|
### Lazy Loading
|
||||||
|
|
||||||
Components are automatically lazy-loaded when using route-based code splitting:
|
Components are automatically lazy-loaded when using route-based code splitting:
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { BaseBuilder } from './base-builder';
|
||||||
import {
|
import {
|
||||||
StaticPath,
|
StaticPath,
|
||||||
StaticRemotePath,
|
StaticRemotePath,
|
||||||
|
ProxyConfig,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
class Server extends BaseBuilder {
|
class Server extends BaseBuilder {
|
||||||
|
|
@ -159,6 +160,82 @@ class Server extends BaseBuilder {
|
||||||
return this.config.serve?.staticPaths || [];
|
return this.config.serve?.staticPaths || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getProxyConfig(): ProxyConfig {
|
||||||
|
return this.config.serve?.proxy || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
private matchProxyPath(reqUrl: string): { pattern: string; config: { target: string; changeOrigin?: boolean; pathRewrite?: { [key: string]: string } } } | null {
|
||||||
|
const proxyConfig = this.getProxyConfig();
|
||||||
|
|
||||||
|
for (const [pattern, config] of Object.entries(proxyConfig)) {
|
||||||
|
const regexPattern = pattern.replace(/\*/g, '.*');
|
||||||
|
const regex = new RegExp(`^${regexPattern}`);
|
||||||
|
|
||||||
|
if (regex.test(reqUrl)) {
|
||||||
|
return { pattern, config };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private tryProxyRequest(reqUrl: string, req: http.IncomingMessage, res: http.ServerResponse): boolean {
|
||||||
|
const match = this.matchProxyPath(reqUrl);
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { pattern, config } = match;
|
||||||
|
let targetPath = reqUrl;
|
||||||
|
|
||||||
|
if (config.pathRewrite) {
|
||||||
|
for (const [from, to] of Object.entries(config.pathRewrite)) {
|
||||||
|
const fromRegex = new RegExp(from);
|
||||||
|
targetPath = targetPath.replace(fromRegex, to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetUrl = config.target + targetPath;
|
||||||
|
|
||||||
|
if (this.isVerbose()) {
|
||||||
|
console.log(`[Proxy] ${req.method} ${reqUrl} -> ${targetUrl}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedUrl = new URL(targetUrl);
|
||||||
|
const protocol = parsedUrl.protocol === 'https:' ? https : http;
|
||||||
|
|
||||||
|
const headers: http.OutgoingHttpHeaders = { ...req.headers };
|
||||||
|
|
||||||
|
if (config.changeOrigin) {
|
||||||
|
headers.host = parsedUrl.host;
|
||||||
|
}
|
||||||
|
|
||||||
|
const proxyReq = protocol.request(
|
||||||
|
targetUrl,
|
||||||
|
{
|
||||||
|
method: req.method,
|
||||||
|
headers,
|
||||||
|
},
|
||||||
|
(proxyRes) => {
|
||||||
|
if (this.isVerbose()) {
|
||||||
|
console.log(`[Proxy] Response: ${proxyRes.statusCode} for ${req.url}`);
|
||||||
|
}
|
||||||
|
res.writeHead(proxyRes.statusCode || 500, proxyRes.headers);
|
||||||
|
proxyRes.pipe(res);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
proxyReq.on('error', (err) => {
|
||||||
|
console.error(`[Proxy] Error for ${req.url}:`, err.message);
|
||||||
|
res.writeHead(502);
|
||||||
|
res.end('Bad Gateway');
|
||||||
|
});
|
||||||
|
|
||||||
|
req.pipe(proxyReq);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private proxyRequest(targetUrl: string, req: http.IncomingMessage, res: http.ServerResponse): void {
|
private proxyRequest(targetUrl: string, req: http.IncomingMessage, res: http.ServerResponse): void {
|
||||||
console.log(`[Proxy] ${req.method} ${req.url} -> ${targetUrl}`);
|
console.log(`[Proxy] ${req.method} ${req.url} -> ${targetUrl}`);
|
||||||
const parsedUrl = new URL(targetUrl);
|
const parsedUrl = new URL(targetUrl);
|
||||||
|
|
@ -245,6 +322,10 @@ class Server extends BaseBuilder {
|
||||||
this.httpServer = http.createServer((req, res) => {
|
this.httpServer = http.createServer((req, res) => {
|
||||||
const reqUrl = req.url || '/';
|
const reqUrl = req.url || '/';
|
||||||
|
|
||||||
|
if (this.tryProxyRequest(reqUrl, req, res)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.tryServeStaticPath(reqUrl, req, res)) {
|
if (this.tryServeStaticPath(reqUrl, req, res)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,14 @@ export interface StaticRemotePath {
|
||||||
|
|
||||||
export type StaticPath = StaticLocalPath | StaticRemotePath;
|
export type StaticPath = StaticLocalPath | StaticRemotePath;
|
||||||
|
|
||||||
|
export interface ProxyConfig {
|
||||||
|
[path: string]: {
|
||||||
|
target: string;
|
||||||
|
changeOrigin?: boolean;
|
||||||
|
pathRewrite?: { [key: string]: string };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface ActionsConfig {
|
export interface ActionsConfig {
|
||||||
prebuild?: string[];
|
prebuild?: string[];
|
||||||
postbuild?: string[];
|
postbuild?: string[];
|
||||||
|
|
@ -57,6 +65,7 @@ export interface BuildConfig {
|
||||||
export interface ServeConfig {
|
export interface ServeConfig {
|
||||||
actions?: ActionsConfig;
|
actions?: ActionsConfig;
|
||||||
staticPaths?: StaticPath[];
|
staticPaths?: StaticPath[];
|
||||||
|
proxy?: ProxyConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QuarcConfig {
|
export interface QuarcConfig {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue