summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2022-11-23 16:59:45 +0100
committerTor Andersson <tor@ccxvii.net>2022-12-21 14:14:58 +0100
commit00916460c8261473b2afce60853c406f10aee6c0 (patch)
treeb42faf394c239f0a77080d68bd595900b82a4fd1
parent83835ea22d7e169f80d8740ce8a542103257d173 (diff)
downloadserver-00916460c8261473b2afce60853c406f10aee6c0.tar.gz
Simplify server.
Only listen to HTTP. Use reverse proxy server to handle SSL, compression, etc.
-rw-r--r--INSTALL.md59
-rw-r--r--package.json1
-rw-r--r--server.js88
3 files changed, 53 insertions, 95 deletions
diff --git a/INSTALL.md b/INSTALL.md
index e83cb31..57364a2 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -19,41 +19,22 @@ sqlite3 db < schema.sql
sqlite3 db < public/julius-caesar/title.sql
```
-Redirect port 80 and 443 to 8080 and 8443:
-
-```
-sudo iptables -A PREROUTING -t nat -p tcp --dport 80 -j REDIRECT --to-ports 8080
-sudo iptables -A PREROUTING -t nat -p tcp --dport 443 -j REDIRECT --to-ports 8443
-```
-
-Create SSL certificate with Let's Encrypt certbot, or self-signed with OpenSSL:
-
-```
-openssl req -nodes -new -x509 -keyout key.pem -out cert.pem
-```
-
Configure the server using the .env file:
```
NODE_ENV=production
-SITE_NAME=YOUR_SITE_NAME
-SITE_HOST=YOUR_DOMAIN
-SITE_URL=https://YOUR_DOMAIN
+SITE_NAME=Example
+SITE_URL=https://example.com
+HTTP_HOST=localhost
HTTP_PORT=8080
-HTTPS_PORT=8443
-SSL_KEY=/etc/letsencrypt/live/YOUR_DOMAIN/privkey.com
-SSL_CERT=/etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem
-
-MAIL_FROM=YOUR_SITE_NAME <notifications@YOUR_DOMAIN>
+MAIL_FROM=Example <notifications@example.com>
MAIL_HOST=localhost
MAIL_PORT=25
```
-If the HTTPS_PORT is missing, the server will only serve HTTP.
-
If MAIL_HOST/PORT/FROM are not present, the server will not send notification emails.
Start the server:
@@ -61,3 +42,35 @@ Start the server:
```
node server.js
```
+
+To use SSL you should run the site behind a reverse proxy server, such as Nginx.
+Here is an example Nginx configuration:
+
+```
+server {
+ listen 80;
+ server_name example.com www.example.com;
+ return 301 https://$host$request_uri;
+}
+
+server {
+ listen 443 ssl;
+ server_name example.com www.example.com;
+ ssl_certificate /path/to/ssl/certificate/fullchain.cer;
+ ssl_certificate_key /path/to/ssl/certificate/example.com.key;
+ root /path/to/server/public;
+ location / {
+ try_files $uri @rally;
+ }
+ location @rally {
+ proxy_pass http://127.0.0.1:8080;
+ proxy_http_version 1.1;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_read_timeout 3600s;
+ proxy_send_timeout 3600s;
+ }
+}
+```
diff --git a/package.json b/package.json
index 3fce0d4..812e862 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,6 @@
"description": "Rally the Troops!",
"dependencies": {
"better-sqlite3": "^7.6.2",
- "compression": "^1.7.4",
"dotenv": "^10.0.0",
"express": "^4.18.1",
"nodemailer": "^6.8.0",
diff --git a/server.js b/server.js
index a6f8b85..8ba605f 100644
--- a/server.js
+++ b/server.js
@@ -3,43 +3,26 @@
const fs = require('fs')
const crypto = require('crypto')
const http = require('http')
-const https = require('https')
const { WebSocketServer } = require('ws')
const express = require('express')
const url = require('url')
-const compression = require('compression')
const sqlite3 = require('better-sqlite3')
require('dotenv').config()
-let DEBUG = process.env.DEBUG || 0
+const DEBUG = process.env.DEBUG || 0
-let HTTP_PORT = process.env.HTTP_PORT || 8080
-let HTTPS_PORT = process.env.HTTPS_PORT
+const HTTP_HOST = process.env.HTTP_HOST || "localhost"
+const HTTP_PORT = process.env.HTTP_PORT || 8080
-let SITE_HOST = process.env.SITE_HOST || "localhost"
-let SITE_NAME = process.env.SITE_NAME || "Untitled"
-let SITE_URL = process.env.SITE_URL
-if (!SITE_URL) {
- if (HTTPS_PORT)
- SITE_URL = "https://" + SITE_HOST + ":" + HTTPS_PORT
- else
- SITE_URL = "http://" + SITE_HOST + ":" + HTTP_PORT
-}
+const SITE_NAME = process.env.SITE_NAME || "Localhost"
+const SITE_URL = process.env.SITE_URL || "http://" + HTTP_HOST + ":" + HTTP_PORT
// Time intervals in julian days
const HOURS = 1 / 24
const MINUTES = 1 / (24 * 60)
-var stat_start = Date.now() / 60000
-var stat_http_reqs = 0
-var stat_pug_reqs = 0
-var stat_ws_reqs = 0
-var stat_total1 = 0
-var stat_total2 = 0
-
function LOG_STATS() {
-
// Count clients connected to join page events
let num_joins = 0
for (let id in join_clients)
@@ -53,16 +36,11 @@ function LOG_STATS() {
num_sockets += game_clients[id].length
}
- let elapsed = Date.now() / 60000 - stat_start
- stat_total1 += stat_http_reqs - stat_pug_reqs
- stat_total2 += stat_pug_reqs + stat_ws_reqs
-
- console.log(`>>> STATS: games=${num_games} sockets=${num_sockets} joins=${num_joins} http=${stat_http_reqs} pug=${stat_pug_reqs} ws=${stat_ws_reqs} http-req/min=${Math.round(stat_total1 / elapsed)} server-req/min=${Math.round(stat_total2 / elapsed)}`)
-
- stat_http_reqs = stat_pug_reqs = stat_ws_reqs = 0
+ if (num_games > 0 || num_sockets > 0 || num_joins > 0)
+ console.log(`>>> games=${num_games} sockets=${num_sockets} joins=${num_joins}`)
}
-setInterval(LOG_STATS, 30 * 1000)
+setInterval(LOG_STATS, 60 * 1000)
/*
* Main database.
@@ -129,8 +107,6 @@ const login_sql_delete = SQL("delete from logins where sid = ?")
const login_sql_touch = SQL("update logins set expires = julianday() + 28 where sid = ? and expires < julianday() + 27")
function make_cookie(sid, age) {
- if (SITE_HOST !== "localhost")
- return `${COOKIE}${sid}; Path=/; Domain=${SITE_HOST}; Max-Age=${age}; HttpOnly`
return `${COOKIE}${sid}; Path=/; Max-Age=${age}; HttpOnly`
}
@@ -177,37 +153,13 @@ app.set('x-powered-by', false)
app.set('etag', false)
app.set('view engine', 'pug')
-app.use(function (req, res, next) {
- stat_http_reqs++
- return next()
-})
-
-app.use(compression())
app.use(express.static('public', { redirect: false, etag: false, cacheControl: false, setHeaders: set_static_headers }))
app.use(express.urlencoded({extended:false}))
-let wss
-
-if (HTTPS_PORT) {
- let https_server = https.createServer({
- key: fs.readFileSync(process.env.SSL_KEY || "key.pem"),
- cert: fs.readFileSync(process.env.SSL_CERT || "cert.pem")
- }, app)
- wss = new WebSocketServer({server: https_server})
- https_server.listen(HTTPS_PORT, "0.0.0.0", () => console.log("Listening to HTTPS on *:" + HTTPS_PORT))
- https_server.keepAliveTimeout = 0
-
- // Force HTTPS by redirecting HTTP.
- let redirect_app = express()
- redirect_app.all("*", (req, res) => res.redirect(308, SITE_URL + req.url))
- let redirect_server = http.createServer(redirect_app)
- redirect_server.listen(HTTP_PORT, "0.0.0.0", () => console.log("Redirecting from HTTP on *:" + HTTP_PORT))
-} else {
- let http_server = http.createServer(app)
- wss = new WebSocketServer({server: http_server})
- http_server.keepAliveTimeout = 0
- http_server.listen(HTTP_PORT, "0.0.0.0", () => console.log("Listening to HTTP on *:" + HTTP_PORT))
-}
+let http_server = http.createServer(app)
+let wss = new WebSocketServer({server: http_server})
+http_server.keepAliveTimeout = 0
+http_server.listen(HTTP_PORT, HTTP_HOST, () => console.log(`Listening to HTTP on ${HTTP_HOST}:${HTTP_PORT}`))
/*
* MISC FUNCTIONS
@@ -391,7 +343,9 @@ app.use(function (req, res, next) {
req.user_agent = parse_user_agent(req)
if (req.user_agent === "MSIE")
return res.redirect("/msie.html")
- let ip = req.ip || req.connection.remoteAddress || "0.0.0.0"
+
+ let ip = req.headers["x-real-ip"] || req.ip || req.connection.remoteAddress || "0.0.0.0"
+
res.setHeader('Cache-Control', 'no-store')
let sid = login_cookie(req)
if (sid) {
@@ -412,8 +366,6 @@ app.use(function (req, res, next) {
ip = String(ip).padEnd(15)
console.log(time, ip, ua, name, req.method, req.url)
- stat_pug_reqs++
-
return next()
})
@@ -1482,7 +1434,6 @@ function update_join_clients_deleted(game_id) {
res.write("retry: 15000\n")
res.write("event: deleted\n")
res.write("data: The game doesn't exist.\n\n")
- res.flush()
}
}
}
@@ -1495,7 +1446,6 @@ function update_join_clients_game(game_id) {
res.write("retry: 15000\n")
res.write("event: game\n")
res.write("data: " + JSON.stringify(game) + "\n\n")
- res.flush()
}
}
}
@@ -1511,7 +1461,6 @@ function update_join_clients_players(game_id) {
res.write("data: " + JSON.stringify(players) + "\n\n")
res.write("event: ready\n")
res.write("data: " + ready + "\n\n")
- res.flush()
}
}
}
@@ -1545,6 +1494,7 @@ app.get('/join-events/:game_id', must_be_logged_in, function (req, res) {
res.setHeader("Content-Type", "text/event-stream")
res.setHeader("Connection", "keep-alive")
+ res.setHeader("X-Accel-Buffering", "no")
if (!game) {
return res.send("event: deleted\ndata: The game doesn't exist.\n\n")
@@ -1569,7 +1519,6 @@ app.get('/join-events/:game_id', must_be_logged_in, function (req, res) {
res.write("data: " + JSON.stringify(game) + "\n\n")
res.write("event: players\n")
res.write("data: " + JSON.stringify(players) + "\n\n")
- res.flush()
})
app.post('/join/:game_id/:role', must_be_logged_in, function (req, res) {
@@ -2254,7 +2203,7 @@ wss.on('connection', (socket, req, client) => {
if (user_id)
socket.user = SQL_SELECT_USER_INFO.get(user_id)
- socket.ip = req.ip || req.connection.remoteAddress || "0.0.0.0"
+ socket.ip = req.headers["x-real-ip"] || req.ip || req.connection.remoteAddress || "0.0.0.0"
socket.title_id = req.query.title || "unknown"
socket.game_id = req.query.game | 0
socket.role = req.query.role
@@ -2264,8 +2213,6 @@ wss.on('connection', (socket, req, client) => {
SLOG(socket, "OPEN " + socket.seen)
try {
- stat_ws_reqs++
-
let title_id = SQL_SELECT_GAME_TITLE.get(socket.game_id)
if (title_id !== socket.title_id)
return socket.close(1000, "Invalid game ID.")
@@ -2303,7 +2250,6 @@ wss.on('connection', (socket, req, client) => {
})
socket.on('message', (data) => {
- stat_ws_reqs++
try {
let [ cmd, arg ] = JSON.parse(data)
if (socket.role !== "Observer")