Initial LisgloSIPS V2 implementation

This commit is contained in:
hectorzhao
2026-06-22 10:56:38 +08:00
commit 5fa1bd35e9
303 changed files with 35644 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
LISGLOSIPS_ENTRYPOINT=/opt/lisglosips/current/server.mjs
HOST=127.0.0.1
PORT=3000
@@ -0,0 +1,13 @@
server_tokens off;
limit_req_zone $binary_remote_addr zone=lisglosips_api_per_ip:10m rate=20r/s;
limit_req_zone $binary_remote_addr zone=lisglosips_auth_per_ip:10m rate=5r/m;
limit_conn_zone $binary_remote_addr zone=lisglosips_conn_per_ip:10m;
limit_req_status 429;
limit_conn_status 429;
map $http_upgrade $lisglosips_connection_upgrade {
default upgrade;
'' close;
}
@@ -0,0 +1,15 @@
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Request-ID $request_id;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $lisglosips_connection_upgrade;
proxy_connect_timeout 3s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 8 16k;
@@ -0,0 +1,7 @@
add_header Strict-Transport-Security "max-age=300" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "same-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; frame-ancestors 'none'; form-action 'self'; object-src 'none'" always;
@@ -0,0 +1,8 @@
ssl_certificate /etc/lisglosips/pki/certs/server.crt;
ssl_certificate_key /etc/lisglosips/pki/private/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:LisgloSIPSTLS:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
+68
View File
@@ -0,0 +1,68 @@
upstream lisglosips_api {
server 127.0.0.1:3000 max_fails=3 fail_timeout=5s;
keepalive 32;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 308 https://$host$request_uri;
}
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name _;
include /etc/nginx/snippets/lisglosips-tls.conf;
root /opt/lisglosips/current/public;
index index.html;
client_max_body_size 2m;
max_ranges 1;
include /etc/nginx/snippets/lisglosips-security-headers.conf;
limit_conn lisglosips_conn_per_ip 30;
location = /healthz {
include /etc/nginx/snippets/lisglosips-proxy.conf;
proxy_pass http://lisglosips_api;
access_log off;
}
location = /api/auth/login {
include /etc/nginx/snippets/lisglosips-proxy.conf;
limit_req zone=lisglosips_auth_per_ip burst=3 nodelay;
proxy_pass http://lisglosips_api;
}
location /api/ {
include /etc/nginx/snippets/lisglosips-proxy.conf;
limit_req zone=lisglosips_api_per_ip burst=40 nodelay;
proxy_pass http://lisglosips_api;
}
location /_recordings/ {
internal;
alias /data/recordings/;
autoindex off;
include /etc/nginx/snippets/lisglosips-security-headers.conf;
add_header Accept-Ranges bytes always;
}
location /assets/ {
try_files $uri =404;
expires 7d;
include /etc/nginx/snippets/lisglosips-security-headers.conf;
add_header Cache-Control "public, immutable";
}
location / {
try_files $uri $uri/ /index.html;
expires -1;
include /etc/nginx/snippets/lisglosips-security-headers.conf;
add_header Cache-Control "no-store";
}
}
@@ -0,0 +1,22 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LisgloSIPS</title>
<style>
:root { color-scheme: light; font-family: Arial, sans-serif; background: #f5f6f8; color: #12121a; }
body { margin: 0; min-height: 100vh; display: grid; place-items: center; }
main { width: min(560px, calc(100% - 48px)); border-top: 4px solid #d9c3a0; padding: 32px 0; }
h1 { margin: 0 0 12px; font-size: 32px; letter-spacing: 0; }
p { margin: 0; color: #5d606b; line-height: 1.7; }
</style>
</head>
<body>
<main>
<h1>LisgloSIPS</h1>
<p>聆界SIP管理平台服务入口已就绪。</p>
</main>
</body>
</html>
+30
View File
@@ -0,0 +1,30 @@
import { createServer } from 'node:http';
const host = process.env.HOST || '127.0.0.1';
const port = Number.parseInt(process.env.PORT || '3000', 10);
const server = createServer((request, response) => {
response.setHeader('Content-Type', 'application/json; charset=utf-8');
response.setHeader('Cache-Control', 'no-store');
response.setHeader('X-Content-Type-Options', 'nosniff');
if (request.method === 'GET' && (request.url === '/healthz' || request.url === '/api/health')) {
response.writeHead(200);
response.end(JSON.stringify({ status: 'ok', service: 'lisglosips-api-placeholder' }));
return;
}
response.writeHead(404);
response.end(JSON.stringify({ status: 'not_found' }));
});
server.listen(port, host);
function shutdown() {
server.close((error) => process.exit(error ? 1 : 0));
setTimeout(() => process.exit(1), 10_000).unref();
}
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
+72
View File
@@ -0,0 +1,72 @@
#!/bin/sh
set -eu
PKI_ROOT=/etc/lisglosips/pki
CA_DIR="$PKI_ROOT/ca"
CERT_DIR="$PKI_ROOT/certs"
PRIVATE_DIR="$PKI_ROOT/private"
HOST_NAME=${HOST_NAME:-yanzi}
TAILSCALE_IP=${TAILSCALE_IP:-100.90.90.91}
umask 077
install -d -m 0700 "$CA_DIR"
install -d -m 0755 "$CERT_DIR"
install -d -m 0700 "$PRIVATE_DIR"
if [ ! -s "$CA_DIR/lisglosips-dev-ca.key" ] || [ ! -s "$CA_DIR/lisglosips-dev-ca.crt" ]; then
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
-subj '/CN=LisgloSIPS Development CA/O=LisgloSIPS' \
-keyout "$CA_DIR/lisglosips-dev-ca.key" \
-out "$CA_DIR/lisglosips-dev-ca.crt"
fi
cat >"$PKI_ROOT/server-cert.cnf" <<EOF
[req]
prompt = no
distinguished_name = dn
req_extensions = req_ext
[dn]
CN = lisglosips.local
O = LisgloSIPS
[req_ext]
subjectAltName = @alt_names
[alt_names]
DNS.1 = lisglosips.local
DNS.2 = $HOST_NAME
DNS.3 = grafana.lisglosips.local
DNS.4 = homer.lisglosips.local
IP.1 = $TAILSCALE_IP
IP.2 = 127.0.0.1
[server_ext]
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
EOF
openssl req -new -newkey rsa:3072 -nodes -sha256 \
-config "$PKI_ROOT/server-cert.cnf" \
-keyout "$PRIVATE_DIR/server.key" \
-out "$PKI_ROOT/server.csr"
openssl x509 -req -sha256 -days 397 \
-in "$PKI_ROOT/server.csr" \
-CA "$CA_DIR/lisglosips-dev-ca.crt" \
-CAkey "$CA_DIR/lisglosips-dev-ca.key" \
-CAcreateserial \
-extfile "$PKI_ROOT/server-cert.cnf" \
-extensions server_ext \
-out "$CERT_DIR/server.crt"
chown root:root "$CA_DIR/lisglosips-dev-ca.key" "$PKI_ROOT/server.csr" "$PKI_ROOT/server-cert.cnf"
chown root:www-data "$PRIVATE_DIR/server.key"
chown root:root "$CA_DIR/lisglosips-dev-ca.crt" "$CERT_DIR/server.crt"
chmod 0600 "$CA_DIR/lisglosips-dev-ca.key" "$PKI_ROOT/server.csr" "$PKI_ROOT/server-cert.cnf"
chmod 0640 "$PRIVATE_DIR/server.key"
chmod 0644 "$CA_DIR/lisglosips-dev-ca.crt" "$CERT_DIR/server.crt"
openssl verify -CAfile "$CA_DIR/lisglosips-dev-ca.crt" "$CERT_DIR/server.crt"
@@ -0,0 +1,42 @@
[Unit]
Description=LisgloSIPS %i service
After=network-online.target mysql.service redis-server.service
Wants=network-online.target
[Service]
Type=simple
User=lisglosips
Group=lisglosips
WorkingDirectory=/opt/lisglosips/current
Environment=NODE_ENV=production
EnvironmentFile=/etc/lisglosips/%i.env
ExecStart=/usr/bin/node ${LISGLOSIPS_ENTRYPOINT}
Restart=on-failure
RestartSec=3s
TimeoutStartSec=30s
TimeoutStopSec=30s
KillSignal=SIGTERM
UMask=0027
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
ProtectClock=true
ProtectHostname=true
RestrictSUIDSGID=true
RestrictRealtime=true
LockPersonality=true
CapabilityBoundingSet=
AmbientCapabilities=
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
StateDirectory=lisglosips
RuntimeDirectory=lisglosips
[Install]
WantedBy=multi-user.target
+6
View File
@@ -0,0 +1,6 @@
NODEJS_VERSION=22.22.2-1nodesource1
NODEJS_RUNTIME=v22.22.2
PNPM_VERSION=10.33.0
COREPACK_VERSION=0.34.6
NGINX_VERSION=1.24.0-2ubuntu7.12