bxo 0.0.10-dev.2 ā 0.0.10-dev.4
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/example/deployment-examples.ts +136 -0
- package/example/hostname-example.ts +127 -0
- package/example/ip-detection-example.ts +194 -0
- package/example/nginx.conf +48 -0
- package/example/websocket-example.ts +20 -4
- package/package.json +1 -1
- package/src/index.ts +22 -5
- package/test-default-content-type.ts +0 -75
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import BXO from "../src";
|
|
2
|
+
|
|
3
|
+
// Example configurations for different deployment scenarios
|
|
4
|
+
|
|
5
|
+
// 1. Development - Local only
|
|
6
|
+
export const developmentConfig = new BXO({
|
|
7
|
+
serve: {
|
|
8
|
+
hostname: "127.0.0.1", // Only localhost
|
|
9
|
+
port: 3000,
|
|
10
|
+
development: true
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// 2. Local Network - Accessible from other devices on same network
|
|
15
|
+
export const localNetworkConfig = new BXO({
|
|
16
|
+
serve: {
|
|
17
|
+
hostname: "0.0.0.0", // All interfaces
|
|
18
|
+
port: 3000,
|
|
19
|
+
development: true
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// 3. Production - External access with security
|
|
24
|
+
export const productionConfig = new BXO({
|
|
25
|
+
serve: {
|
|
26
|
+
hostname: "0.0.0.0", // All interfaces
|
|
27
|
+
port: 8080,
|
|
28
|
+
development: false
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// 4. Docker Container - Internal network
|
|
33
|
+
export const dockerConfig = new BXO({
|
|
34
|
+
serve: {
|
|
35
|
+
hostname: "0.0.0.0", // All interfaces (required for Docker)
|
|
36
|
+
port: 3000,
|
|
37
|
+
development: false
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// 5. Cloud Deployment (AWS, GCP, Azure)
|
|
42
|
+
export const cloudConfig = new BXO({
|
|
43
|
+
serve: {
|
|
44
|
+
hostname: "0.0.0.0", // All interfaces
|
|
45
|
+
port: process.env.PORT || 8080, // Use environment port
|
|
46
|
+
development: false
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// 6. Specific IP binding
|
|
51
|
+
export const specificIPConfig = new BXO({
|
|
52
|
+
serve: {
|
|
53
|
+
hostname: "192.168.1.100", // Specific IP
|
|
54
|
+
port: 3000
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// 7. Domain binding (if you have a specific domain)
|
|
59
|
+
export const domainConfig = new BXO({
|
|
60
|
+
serve: {
|
|
61
|
+
hostname: "api.yourdomain.com", // Specific domain
|
|
62
|
+
port: 80
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Example usage function
|
|
67
|
+
async function runExample() {
|
|
68
|
+
console.log("š BXO Deployment Configuration Examples\n");
|
|
69
|
+
|
|
70
|
+
// Choose which configuration to use
|
|
71
|
+
const app = localNetworkConfig; // Change this to test different configs
|
|
72
|
+
|
|
73
|
+
// Add some routes
|
|
74
|
+
app.get("/", (ctx) => {
|
|
75
|
+
return ctx.json({
|
|
76
|
+
message: "BXO Server Running",
|
|
77
|
+
hostname: app.server?.hostname || "default",
|
|
78
|
+
port: app.server?.port,
|
|
79
|
+
environment: process.env.NODE_ENV || "development",
|
|
80
|
+
accessible: app.server?.hostname === "0.0.0.0" ? "External networks" : "Local only"
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
app.get("/health", (ctx) => {
|
|
85
|
+
return ctx.json({
|
|
86
|
+
status: "healthy",
|
|
87
|
+
timestamp: new Date().toISOString(),
|
|
88
|
+
hostname: app.server?.hostname,
|
|
89
|
+
port: app.server?.port
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// WebSocket example with hostname info
|
|
94
|
+
app.ws("/ws", {
|
|
95
|
+
open(ws) {
|
|
96
|
+
const data = ws.data;
|
|
97
|
+
console.log(`š WebSocket connected from ${data.ip} to hostname: ${app.server?.hostname}`);
|
|
98
|
+
ws.send(`Connected to server on ${app.server?.hostname}:${app.server?.port}`);
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
message(ws, message) {
|
|
102
|
+
const data = ws.data;
|
|
103
|
+
console.log(`š¬ Message from ${data.ip}: ${message}`);
|
|
104
|
+
ws.send(`Echo from ${app.server?.hostname}: ${message}`);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
app.start();
|
|
109
|
+
|
|
110
|
+
console.log(`ā
Server started successfully!`);
|
|
111
|
+
console.log(`š Hostname: ${app.server?.hostname}`);
|
|
112
|
+
console.log(`š Port: ${app.server?.port}`);
|
|
113
|
+
console.log(`š Access URLs:`);
|
|
114
|
+
|
|
115
|
+
if (app.server?.hostname === "0.0.0.0") {
|
|
116
|
+
console.log(` ⢠http://localhost:${app.server.port}`);
|
|
117
|
+
console.log(` ⢠http://127.0.0.1:${app.server.port}`);
|
|
118
|
+
console.log(` ⢠http://your-server-ip:${app.server.port}`);
|
|
119
|
+
console.log(` ⢠http://your-domain.com:${app.server.port}`);
|
|
120
|
+
} else if (app.server?.hostname === "127.0.0.1") {
|
|
121
|
+
console.log(` ⢠http://localhost:${app.server.port}`);
|
|
122
|
+
console.log(` ⢠http://127.0.0.1:${app.server.port}`);
|
|
123
|
+
} else {
|
|
124
|
+
console.log(` ⢠http://${app.server?.hostname}:${app.server?.port}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
console.log(`\nš Configuration Guide:`);
|
|
128
|
+
console.log(` ⢠Development: hostname: "127.0.0.1" (localhost only)`);
|
|
129
|
+
console.log(` ⢠Local Network: hostname: "0.0.0.0" (all interfaces)`);
|
|
130
|
+
console.log(` ⢠Production: hostname: "0.0.0.0" + reverse proxy`);
|
|
131
|
+
console.log(` ⢠Docker: hostname: "0.0.0.0" (required for container networking)`);
|
|
132
|
+
console.log(` ⢠Cloud: hostname: "0.0.0.0" + environment port`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Run the example
|
|
136
|
+
runExample().catch(console.error);
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import BXO from "../src";
|
|
2
|
+
|
|
3
|
+
async function main() {
|
|
4
|
+
console.log("š BXO Hostname Configuration Examples\n");
|
|
5
|
+
|
|
6
|
+
// Example 1: Serve on all interfaces (0.0.0.0) - accessible from external networks
|
|
7
|
+
console.log("1. Serving on all interfaces (0.0.0.0) - accessible from external networks");
|
|
8
|
+
const app1 = new BXO({
|
|
9
|
+
serve: {
|
|
10
|
+
hostname: "0.0.0.0", // Listen on all network interfaces
|
|
11
|
+
port: 3000
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
app1.get("/", (ctx) => {
|
|
16
|
+
return ctx.json({
|
|
17
|
+
message: "Hello from BXO!",
|
|
18
|
+
hostname: "0.0.0.0",
|
|
19
|
+
accessible: "From any network interface",
|
|
20
|
+
examples: [
|
|
21
|
+
"http://localhost:3000",
|
|
22
|
+
"http://127.0.0.1:3000",
|
|
23
|
+
"http://your-server-ip:3000",
|
|
24
|
+
"http://your-domain.com:3000"
|
|
25
|
+
]
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Example 2: Serve on specific hostname/IP
|
|
30
|
+
console.log("2. Serving on specific hostname/IP");
|
|
31
|
+
const app2 = new BXO({
|
|
32
|
+
serve: {
|
|
33
|
+
hostname: "127.0.0.1", // Only localhost
|
|
34
|
+
port: 3001
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
app2.get("/", (ctx) => {
|
|
39
|
+
return ctx.json({
|
|
40
|
+
message: "Hello from BXO!",
|
|
41
|
+
hostname: "127.0.0.1",
|
|
42
|
+
accessible: "Only from localhost",
|
|
43
|
+
examples: [
|
|
44
|
+
"http://localhost:3001",
|
|
45
|
+
"http://127.0.0.1:3001"
|
|
46
|
+
]
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Example 3: Production configuration with external access
|
|
51
|
+
console.log("3. Production configuration with external access");
|
|
52
|
+
const app3 = new BXO({
|
|
53
|
+
serve: {
|
|
54
|
+
hostname: "0.0.0.0", // Listen on all interfaces
|
|
55
|
+
port: 8080,
|
|
56
|
+
// Additional production options
|
|
57
|
+
development: false,
|
|
58
|
+
// You can add more Bun.serve options here
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
app3.get("/", (ctx) => {
|
|
63
|
+
return ctx.json({
|
|
64
|
+
message: "Production BXO Server",
|
|
65
|
+
hostname: "0.0.0.0",
|
|
66
|
+
port: 8080,
|
|
67
|
+
accessible: "From external networks",
|
|
68
|
+
deployment: "Production ready"
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Example 4: Custom hostname for specific deployment
|
|
73
|
+
console.log("4. Custom hostname for specific deployment");
|
|
74
|
+
const app4 = new BXO({
|
|
75
|
+
serve: {
|
|
76
|
+
hostname: "192.168.1.100", // Specific IP address
|
|
77
|
+
port: 4000
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
app4.get("/", (ctx) => {
|
|
82
|
+
return ctx.json({
|
|
83
|
+
message: "BXO on specific IP",
|
|
84
|
+
hostname: "192.168.1.100",
|
|
85
|
+
accessible: "From network 192.168.1.x",
|
|
86
|
+
examples: [
|
|
87
|
+
"http://192.168.1.100:4000"
|
|
88
|
+
]
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Start the server you want to test
|
|
93
|
+
const selectedApp = app1; // Change this to app1, app2, app3, or app4
|
|
94
|
+
|
|
95
|
+
selectedApp.start();
|
|
96
|
+
|
|
97
|
+
console.log(`š Server started!`);
|
|
98
|
+
console.log(`š Hostname: ${selectedApp.server?.hostname || 'default'}`);
|
|
99
|
+
console.log(`š Port: ${selectedApp.server?.port}`);
|
|
100
|
+
console.log(`š Accessible at:`);
|
|
101
|
+
|
|
102
|
+
if (selectedApp === app1) {
|
|
103
|
+
console.log(` ⢠http://localhost:3000`);
|
|
104
|
+
console.log(` ⢠http://127.0.0.1:3000`);
|
|
105
|
+
console.log(` ⢠http://your-server-ip:3000`);
|
|
106
|
+
console.log(` ⢠http://your-domain.com:3000`);
|
|
107
|
+
} else if (selectedApp === app2) {
|
|
108
|
+
console.log(` ⢠http://localhost:3001`);
|
|
109
|
+
console.log(` ⢠http://127.0.0.1:3001`);
|
|
110
|
+
} else if (selectedApp === app3) {
|
|
111
|
+
console.log(` ⢠http://localhost:8080`);
|
|
112
|
+
console.log(` ⢠http://your-server-ip:8080`);
|
|
113
|
+
console.log(` ⢠http://your-domain.com:8080`);
|
|
114
|
+
} else if (selectedApp === app4) {
|
|
115
|
+
console.log(` ⢠http://192.168.1.100:4000`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.log(`\nš Configuration Options:`);
|
|
119
|
+
console.log(` ⢠hostname: "0.0.0.0" - Listen on all interfaces (external access)`);
|
|
120
|
+
console.log(` ⢠hostname: "127.0.0.1" - Only localhost access`);
|
|
121
|
+
console.log(` ⢠hostname: "192.168.1.100" - Specific IP address`);
|
|
122
|
+
console.log(` ⢠hostname: "your-domain.com" - Specific domain`);
|
|
123
|
+
console.log(` ⢠port: 3000 - Custom port number`);
|
|
124
|
+
console.log(` ⢠development: false - Production mode`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import BXO from "../src";
|
|
2
|
+
|
|
3
|
+
async function main() {
|
|
4
|
+
const app = new BXO({ serve: { port: 3000, hostname: '0.0.0.0' } });
|
|
5
|
+
|
|
6
|
+
// Enhanced IP detection function
|
|
7
|
+
function getClientIP(req: Request): string {
|
|
8
|
+
// Check proxy headers first
|
|
9
|
+
const forwardedFor = req.headers.get("x-forwarded-for");
|
|
10
|
+
const realIP = req.headers.get("x-real-ip");
|
|
11
|
+
const clientIP = req.headers.get("x-client-ip");
|
|
12
|
+
const cfConnectingIP = req.headers.get("cf-connecting-ip"); // Cloudflare
|
|
13
|
+
|
|
14
|
+
if (forwardedFor) {
|
|
15
|
+
// X-Forwarded-For can contain multiple IPs: "client, proxy1, proxy2"
|
|
16
|
+
// The first IP is usually the original client
|
|
17
|
+
return forwardedFor.split(',')[0].trim();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (realIP) return realIP;
|
|
21
|
+
if (clientIP) return clientIP;
|
|
22
|
+
if (cfConnectingIP) return cfConnectingIP;
|
|
23
|
+
|
|
24
|
+
// If no proxy headers, try to get from connection
|
|
25
|
+
// Note: This is a workaround since Bun doesn't expose connection IP directly
|
|
26
|
+
// In production, you should always use a reverse proxy that sets proper headers
|
|
27
|
+
|
|
28
|
+
// For localhost development, return localhost
|
|
29
|
+
const host = req.headers.get("host");
|
|
30
|
+
if (host?.includes("localhost") || host?.includes("127.0.0.1")) {
|
|
31
|
+
return "127.0.0.1";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return "unknown";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// HTTP route with enhanced IP detection
|
|
38
|
+
app.get("/api/ip", (ctx) => {
|
|
39
|
+
const ip = getClientIP(ctx.request);
|
|
40
|
+
|
|
41
|
+
return ctx.json({
|
|
42
|
+
ip: ip,
|
|
43
|
+
headers: {
|
|
44
|
+
"x-forwarded-for": ctx.headers["x-forwarded-for"],
|
|
45
|
+
"x-real-ip": ctx.headers["x-real-ip"],
|
|
46
|
+
"x-client-ip": ctx.headers["x-client-ip"],
|
|
47
|
+
"cf-connecting-ip": ctx.headers["cf-connecting-ip"],
|
|
48
|
+
"host": ctx.headers["host"]
|
|
49
|
+
},
|
|
50
|
+
allHeaders: ctx.headers,
|
|
51
|
+
message: ip === "unknown" ?
|
|
52
|
+
"IP detection failed. In production, use a reverse proxy (nginx, Cloudflare, etc.) that sets X-Forwarded-For headers." :
|
|
53
|
+
"IP detected successfully!"
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// WebSocket route with enhanced IP detection
|
|
58
|
+
app.ws("/ws", {
|
|
59
|
+
open(ws) {
|
|
60
|
+
// For WebSocket, we need to get IP from the original request
|
|
61
|
+
// This is a workaround since BXO's WebSocket data might show "unknown"
|
|
62
|
+
const ip = ws.data.ip !== "unknown" ? ws.data.ip : "127.0.0.1";
|
|
63
|
+
|
|
64
|
+
console.log(`WebSocket client connected from IP: ${ip}`);
|
|
65
|
+
ws.send(`Hello! Your IP is: ${ip}`);
|
|
66
|
+
},
|
|
67
|
+
message(ws, message) {
|
|
68
|
+
const ip = ws.data.ip !== "unknown" ? ws.data.ip : "127.0.0.1";
|
|
69
|
+
console.log(`Message from ${ip}: ${message}`);
|
|
70
|
+
ws.send(`Echo: ${message} (from IP: ${ip})`);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Debug route to see all headers
|
|
75
|
+
app.get("/debug", (ctx) => {
|
|
76
|
+
return ctx.json({
|
|
77
|
+
message: "Debug information",
|
|
78
|
+
headers: ctx.headers,
|
|
79
|
+
cookies: ctx.cookies,
|
|
80
|
+
userAgent: ctx.headers["user-agent"]
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Home page with test interface
|
|
85
|
+
app.get("/", (ctx) => {
|
|
86
|
+
return ctx.html(`
|
|
87
|
+
<!DOCTYPE html>
|
|
88
|
+
<html>
|
|
89
|
+
<head>
|
|
90
|
+
<title>BXO IP Detection Test</title>
|
|
91
|
+
<style>
|
|
92
|
+
body { font-family: Arial, sans-serif; margin: 40px; }
|
|
93
|
+
button { padding: 10px 20px; margin: 10px; background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; }
|
|
94
|
+
button:hover { background: #0056b3; }
|
|
95
|
+
.result { background: #f8f9fa; padding: 20px; margin: 20px 0; border-radius: 5px; }
|
|
96
|
+
pre { background: #e9ecef; padding: 10px; border-radius: 3px; overflow-x: auto; }
|
|
97
|
+
</style>
|
|
98
|
+
</head>
|
|
99
|
+
<body>
|
|
100
|
+
<h1>BXO IP Detection Test</h1>
|
|
101
|
+
|
|
102
|
+
<div>
|
|
103
|
+
<button onclick="checkIP()">Check My IP</button>
|
|
104
|
+
<button onclick="checkDebug()">Debug Headers</button>
|
|
105
|
+
<button onclick="connectWebSocket()">Connect WebSocket</button>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div id="result" class="result" style="display: none;">
|
|
109
|
+
<h3>Result:</h3>
|
|
110
|
+
<pre id="resultContent"></pre>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<div id="wsMessages" class="result" style="display: none;">
|
|
114
|
+
<h3>WebSocket Messages:</h3>
|
|
115
|
+
<div id="wsContent"></div>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<script>
|
|
119
|
+
let ws = null;
|
|
120
|
+
|
|
121
|
+
async function checkIP() {
|
|
122
|
+
try {
|
|
123
|
+
const res = await fetch('/api/ip');
|
|
124
|
+
const data = await res.json();
|
|
125
|
+
showResult(data);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
showResult({ error: error.message });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function checkDebug() {
|
|
132
|
+
try {
|
|
133
|
+
const res = await fetch('/debug');
|
|
134
|
+
const data = await res.json();
|
|
135
|
+
showResult(data);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
showResult({ error: error.message });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function connectWebSocket() {
|
|
142
|
+
if (ws) {
|
|
143
|
+
ws.close();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
ws = new WebSocket('ws://localhost:3000/ws');
|
|
147
|
+
|
|
148
|
+
ws.onopen = function() {
|
|
149
|
+
addWSMessage('Connected to WebSocket');
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
ws.onmessage = function(event) {
|
|
153
|
+
addWSMessage('Received: ' + event.data);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
ws.onclose = function() {
|
|
157
|
+
addWSMessage('Disconnected from WebSocket');
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
ws.onerror = function(error) {
|
|
161
|
+
addWSMessage('Error: ' + error);
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function showResult(data) {
|
|
166
|
+
document.getElementById('result').style.display = 'block';
|
|
167
|
+
document.getElementById('resultContent').textContent = JSON.stringify(data, null, 2);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function addWSMessage(message) {
|
|
171
|
+
document.getElementById('wsMessages').style.display = 'block';
|
|
172
|
+
const div = document.createElement('div');
|
|
173
|
+
div.textContent = new Date().toLocaleTimeString() + ': ' + message;
|
|
174
|
+
document.getElementById('wsContent').appendChild(div);
|
|
175
|
+
}
|
|
176
|
+
</script>
|
|
177
|
+
</body>
|
|
178
|
+
</html>
|
|
179
|
+
`);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
console.log("š BXO IP Detection Example");
|
|
183
|
+
console.log("š” Server running on http://localhost:3000");
|
|
184
|
+
console.log("š Visit http://localhost:3000 to test IP detection");
|
|
185
|
+
console.log("\nš” Tips for getting real IP addresses:");
|
|
186
|
+
console.log("1. Use a reverse proxy (nginx, Apache) that sets X-Forwarded-For headers");
|
|
187
|
+
console.log("2. Deploy behind Cloudflare (sets CF-Connecting-IP header)");
|
|
188
|
+
console.log("3. Use AWS ALB/ELB (sets X-Forwarded-For header)");
|
|
189
|
+
console.log("4. For local development, IP will show as 127.0.0.1 or unknown");
|
|
190
|
+
|
|
191
|
+
app.start();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Nginx configuration for BXO with proper IP forwarding
|
|
2
|
+
# This ensures X-Forwarded-For headers are set correctly
|
|
3
|
+
|
|
4
|
+
server {
|
|
5
|
+
listen 80;
|
|
6
|
+
server_name your-domain.com;
|
|
7
|
+
|
|
8
|
+
# Forward all requests to BXO server
|
|
9
|
+
location / {
|
|
10
|
+
proxy_pass http://127.0.0.1:3000;
|
|
11
|
+
|
|
12
|
+
# Set headers for IP detection
|
|
13
|
+
proxy_set_header Host $host;
|
|
14
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
15
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
16
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
17
|
+
|
|
18
|
+
# WebSocket support
|
|
19
|
+
proxy_http_version 1.1;
|
|
20
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
21
|
+
proxy_set_header Connection "upgrade";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# HTTPS version (recommended for production)
|
|
26
|
+
server {
|
|
27
|
+
listen 443 ssl;
|
|
28
|
+
server_name your-domain.com;
|
|
29
|
+
|
|
30
|
+
# SSL configuration
|
|
31
|
+
ssl_certificate /path/to/your/certificate.crt;
|
|
32
|
+
ssl_certificate_key /path/to/your/private.key;
|
|
33
|
+
|
|
34
|
+
location / {
|
|
35
|
+
proxy_pass http://127.0.0.1:3000;
|
|
36
|
+
|
|
37
|
+
# Set headers for IP detection
|
|
38
|
+
proxy_set_header Host $host;
|
|
39
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
40
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
41
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
42
|
+
|
|
43
|
+
# WebSocket support
|
|
44
|
+
proxy_http_version 1.1;
|
|
45
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
46
|
+
proxy_set_header Connection "upgrade";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import BXO from "../src";
|
|
2
2
|
|
|
3
3
|
async function main() {
|
|
4
|
-
|
|
4
|
+
// Configure hostname for external access
|
|
5
|
+
const app = new BXO({
|
|
6
|
+
serve: {
|
|
7
|
+
hostname: "0.0.0.0", // Listen on all interfaces for external access
|
|
8
|
+
port: 3000
|
|
9
|
+
}
|
|
10
|
+
});
|
|
5
11
|
|
|
6
12
|
// HTTP routes
|
|
7
13
|
app.get("/", (ctx) => {
|
|
@@ -252,9 +258,19 @@ async function main() {
|
|
|
252
258
|
});
|
|
253
259
|
|
|
254
260
|
app.start();
|
|
255
|
-
console.log(`š Server is running
|
|
256
|
-
console.log(
|
|
257
|
-
console.log(
|
|
261
|
+
console.log(`š Server is running:`);
|
|
262
|
+
console.log(`š Hostname: ${app.server?.hostname || 'default'}`);
|
|
263
|
+
console.log(`š Port: ${app.server?.port}`);
|
|
264
|
+
console.log(`š HTTP: http://localhost:${app.server?.port}`);
|
|
265
|
+
console.log(`š WebSocket: ws://localhost:${app.server?.port}/ws`);
|
|
266
|
+
console.log(`š¬ Chat WebSocket: ws://localhost:${app.server?.port}/chat/:room`);
|
|
267
|
+
|
|
268
|
+
if (app.server?.hostname === "0.0.0.0") {
|
|
269
|
+
console.log(`\nš External Access Available:`);
|
|
270
|
+
console.log(` ⢠http://your-server-ip:${app.server.port}`);
|
|
271
|
+
console.log(` ⢠http://your-domain.com:${app.server.port}`);
|
|
272
|
+
console.log(` ⢠ws://your-server-ip:${app.server.port}/ws`);
|
|
273
|
+
}
|
|
258
274
|
console.log(`\nš Features demonstrated:`);
|
|
259
275
|
console.log(` ⢠ws.data.id - Short, unique client identifier`);
|
|
260
276
|
console.log(` ⢠ws.data.connectionId - Detailed connection ID`);
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -82,6 +82,7 @@ export type Context<P extends string = string, S extends RouteSchema | undefined
|
|
|
82
82
|
};
|
|
83
83
|
json: <T>(data: T, status?: number) => Response;
|
|
84
84
|
text: (data: string, status?: number) => Response;
|
|
85
|
+
html: (data: string, status?: number) => Response;
|
|
85
86
|
status: <T extends number>(status: T, data: InferResponse<S, T>) => Response;
|
|
86
87
|
redirect: (url: string, status?: 301 | 302 | 303 | 307 | 308) => Response;
|
|
87
88
|
};
|
|
@@ -195,11 +196,14 @@ function parseQuery<T extends z.ZodTypeAny>(searchParams: URLSearchParams, schem
|
|
|
195
196
|
function parseQuery(searchParams: URLSearchParams, schema?: z.ZodTypeAny): any {
|
|
196
197
|
const out: QueryObject = {};
|
|
197
198
|
for (const [k, v] of searchParams.entries()) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
199
|
+
// Handle array notation like fields[] -> fields
|
|
200
|
+
const key = k.endsWith('[]') ? k.slice(0, -2) : k;
|
|
201
|
+
|
|
202
|
+
if (key in out) {
|
|
203
|
+
const existing = out[key];
|
|
204
|
+
if (Array.isArray(existing)) out[key] = [...existing, v];
|
|
205
|
+
else out[key] = [existing as string, v];
|
|
206
|
+
} else out[key] = v;
|
|
203
207
|
}
|
|
204
208
|
if (schema) {
|
|
205
209
|
return (schema as any).parse ? (schema as any).parse(out) : out;
|
|
@@ -808,6 +812,19 @@ export default class BXO {
|
|
|
808
812
|
headers: { "Content-Type": "text/plain" }
|
|
809
813
|
});
|
|
810
814
|
},
|
|
815
|
+
html: (data, status = 200) => {
|
|
816
|
+
if (route.schema?.response?.[status]) {
|
|
817
|
+
const sch = route.schema.response[status]!;
|
|
818
|
+
const res = (sch as any).safeParse ? (sch as any).safeParse(data) : { success: true };
|
|
819
|
+
if (!res.success) {
|
|
820
|
+
return new Response(JSON.stringify({ error: "Invalid response", issues: res.error?.issues ?? [] }), { status: 500, headers: { "Content-Type": "application/json" } });
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return new Response(String(data), {
|
|
824
|
+
status,
|
|
825
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
826
|
+
});
|
|
827
|
+
},
|
|
811
828
|
status: (status, data) => {
|
|
812
829
|
// Response validation if declared
|
|
813
830
|
if (route.schema?.response?.[status]) {
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import BXO from "./src";
|
|
2
|
-
|
|
3
|
-
async function testDefaultContentType() {
|
|
4
|
-
const bxo = new BXO({ serve: { port: 0 } });
|
|
5
|
-
|
|
6
|
-
// Test route that returns a string without explicit content type
|
|
7
|
-
bxo.get("/test-string", (ctx) => {
|
|
8
|
-
return "Hello World";
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
// Test route that returns a string with explicit content type
|
|
12
|
-
bxo.get("/test-string-explicit", (ctx) => {
|
|
13
|
-
return new Response("Hello World", {
|
|
14
|
-
headers: { "Content-Type": "text/plain" }
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
// Test route that uses ctx.json (should be application/json)
|
|
19
|
-
bxo.get("/test-json", (ctx) => {
|
|
20
|
-
return ctx.json({ message: "Hello World" });
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
// Test route that uses ctx.text (should be text/plain)
|
|
24
|
-
bxo.get("/test-text", (ctx) => {
|
|
25
|
-
return ctx.text("Hello World");
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
bxo.start();
|
|
29
|
-
|
|
30
|
-
const baseUrl = `http://localhost:${bxo.server?.port}`;
|
|
31
|
-
|
|
32
|
-
console.log("Testing default content type...");
|
|
33
|
-
|
|
34
|
-
// Test 1: String response should default to text/html
|
|
35
|
-
try {
|
|
36
|
-
const response1 = await fetch(`${baseUrl}/test-string`);
|
|
37
|
-
const contentType1 = response1.headers.get('Content-Type');
|
|
38
|
-
console.log(`ā String response Content-Type: ${contentType1} (should be text/html)`);
|
|
39
|
-
} catch (error) {
|
|
40
|
-
console.error("ā Error testing string response:", error);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Test 2: Explicit content type should be preserved
|
|
44
|
-
try {
|
|
45
|
-
const response2 = await fetch(`${baseUrl}/test-string-explicit`);
|
|
46
|
-
const contentType2 = response2.headers.get('Content-Type');
|
|
47
|
-
console.log(`ā Explicit Content-Type: ${contentType2} (should be text/plain)`);
|
|
48
|
-
} catch (error) {
|
|
49
|
-
console.error("ā Error testing explicit content type:", error);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Test 3: ctx.json should be application/json
|
|
53
|
-
try {
|
|
54
|
-
const response3 = await fetch(`${baseUrl}/test-json`);
|
|
55
|
-
const contentType3 = response3.headers.get('Content-Type');
|
|
56
|
-
console.log(`ā ctx.json Content-Type: ${contentType3} (should be application/json)`);
|
|
57
|
-
} catch (error) {
|
|
58
|
-
console.error("ā Error testing ctx.json:", error);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Test 4: ctx.text should be text/plain
|
|
62
|
-
try {
|
|
63
|
-
const response4 = await fetch(`${baseUrl}/test-text`);
|
|
64
|
-
const contentType4 = response4.headers.get('Content-Type');
|
|
65
|
-
console.log(`ā ctx.text Content-Type: ${contentType4} (should be text/plain)`);
|
|
66
|
-
} catch (error) {
|
|
67
|
-
console.error("ā Error testing ctx.text:", error);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Close the server
|
|
71
|
-
bxo.server?.stop();
|
|
72
|
-
console.log("Test completed!");
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
testDefaultContentType().catch(console.error);
|