-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathserver.js
More file actions
executable file
·136 lines (118 loc) · 4.35 KB
/
server.js
File metadata and controls
executable file
·136 lines (118 loc) · 4.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import express from 'express';
import { createProxyMiddleware } from 'http-proxy-middleware';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const app = express();
const PORT = process.env.PORT;
const API_TARGET = process.env.API_TARGET;
if (!PORT) {
console.error('ERROR: PORT environment variable is required!');
console.error('Please set PORT in your .env file or environment');
process.exit(1);
}
if (!API_TARGET) {
console.error('ERROR: API_TARGET environment variable is required!');
console.error('Please set API_TARGET in your .env file or environment');
process.exit(1);
}
//security headers
app.use((req, res, next) => {
res.header('Content-Security-Policy',
"default-src 'self'; " +
"script-src 'self' 'unsafe-inline'; " +
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; " +
"font-src 'self' https://fonts.gstatic.com; " +
"img-src 'self' https://*.sono.wtf https://sono.wtf http://*.sono.wtf http://sono.wtf https://raw.githubusercontent.com https://avatars.githubusercontent.com https://user-images.githubusercontent.com https://github.com https://github-production-user-asset-6210df.s3.amazonaws.com data: blob:; " +
"media-src 'self' https://*.sono.wtf https://sono.wtf http://*.sono.wtf http://sono.wtf https://github.com http://localhost:9000 blob:; " +
"connect-src 'self' https://lrclib.net https://api.github.com https://*.sono.wtf https://sono.wtf; " +
"frame-src https://*.sono.wtf https://sono.wtf;"
);
next();
});
//cors headers
app.use((req, res, next) => {
const allowedOrigins = [
'https://web.sono.wtf',
'https://sono.wtf',
'https://www.sono.wtf'
];
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.header('Access-Control-Allow-Origin', origin);
}
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization, X-Password-Encrypted');
res.header('Access-Control-Allow-Credentials', 'true');
if (req.method === 'OPTIONS') {
return res.status(204).send();
}
next();
});
//internal health check endpoint
app.get('/health', (req, res) => {
res.status(200).json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});
//proxy middleware for /api routes
app.use('/api', createProxyMiddleware({
target: API_TARGET,
changeOrigin: true,
secure: true,
followRedirects: false,
ws: true,
onProxyReq: (proxyReq, req, res) => {
const targetUrl = new URL(API_TARGET);
proxyReq.setHeader('Host', targetUrl.host);
},
onError: (err, req, res) => {
console.error('API Proxy Error:', err.message);
res.status(500).json({ error: 'API proxy server error' });
}
}));
//api health proxy
app.use('/api-health', createProxyMiddleware({
target: API_TARGET,
changeOrigin: false,
secure: true,
followRedirects: false,
pathRewrite: {
'^/api-health': '/health'
},
onProxyReq: (proxyReq, req, res) => {
proxyReq.setHeader('Host', req.headers.host);
},
onError: (err, req, res) => {
console.error('API Health Proxy Error:', err.message);
res.status(500).json({ error: 'API health check proxy error' });
}
}));
//body parsers
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
//serve static files from dist
app.use(express.static(path.join(__dirname, 'dist')));
//serve favicon from public folder
app.use('/favicon.png', express.static(path.join(__dirname, 'public/images/favicon.png')));
//catch-all to serve "index.html" for SPA routing (after all other routes)
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});
//start server
app.listen(PORT, () => {
const HOST_PORT = process.env.HOST_PORT || PORT;
console.log(`SonoWeb Server running on http://localhost:${PORT}`);
console.log(`Serving Vue app from: ${path.join(__dirname, 'dist')}`);
console.log(`Proxying /api/* to ${API_TARGET}/api/v1/*`);
console.log(`Proxying /api-health to ${API_TARGET}/health`);
console.log(`Container internal port: ${PORT}, External access port: ${HOST_PORT}`);
});
//shutdown handling
process.on('SIGTERM', () => {
console.log('Server shutting down gracefully');
process.exit(0);
});