Switching to node
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
node_modules
|
node_modules
|
||||||
|
package-lock.json
|
||||||
1047
UI/_/code/quill.js
Normal file
1047
UI/_/code/quill.js
Normal file
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
@@ -1,12 +1,13 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Admin</title>
|
<title>Admin</title>
|
||||||
<link rel="icon" href="shield.png">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="stylesheet" href="">
|
<link rel="icon" href="/_/icons/shield.png">
|
||||||
<script type="module" src="index.js"></script>
|
<link rel="stylesheet" href="/_/code/shared.css">
|
||||||
</head>
|
<script src="/_/code/quill.js"></script>
|
||||||
<body style="background: #38614d">
|
<script type="module" src="75820185/index.js"></script>
|
||||||
|
</head>
|
||||||
</body>
|
<body>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
5
go.mod
5
go.mod
@@ -1,5 +0,0 @@
|
|||||||
module Admin
|
|
||||||
|
|
||||||
go 1.22.0
|
|
||||||
|
|
||||||
require github.com/gorilla/websocket v1.5.3 // indirect
|
|
||||||
2
go.sum
2
go.sum
@@ -1,2 +0,0 @@
|
|||||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
|
||||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
|
||||||
203
index.js
Normal file
203
index.js
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
// server.js
|
||||||
|
const fs = require("fs");
|
||||||
|
const http = require("http");
|
||||||
|
const https = require("https");
|
||||||
|
const tls = require("tls");
|
||||||
|
const httpProxy = require("http-proxy");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
// ---------------------------
|
||||||
|
// Request Log Store
|
||||||
|
// ---------------------------
|
||||||
|
let requests = [];
|
||||||
|
|
||||||
|
// Mutex not required in Node (single-threaded)
|
||||||
|
function recordRequest(info) {
|
||||||
|
requests.push(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------
|
||||||
|
// IP Helpers
|
||||||
|
// ---------------------------
|
||||||
|
function isLocalIP(ip) {
|
||||||
|
if (ip === "::1" || ip === "127.0.0.1") return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanIP(remoteAddr) {
|
||||||
|
if (!remoteAddr) return "";
|
||||||
|
let ip = remoteAddr.split(":")[0].replace("[", "").replace("]", "");
|
||||||
|
if (ip.startsWith("::ffff:")) ip = ip.replace("::ffff:", "");
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------
|
||||||
|
// Logging Middleware
|
||||||
|
// ---------------------------
|
||||||
|
function loggingMiddleware(next) {
|
||||||
|
return (req, res) => {
|
||||||
|
const rawIP = req.socket.remoteAddress;
|
||||||
|
const ip = cleanIP(rawIP);
|
||||||
|
|
||||||
|
let path = req.url.startsWith("/") ? req.url.slice(1) : req.url;
|
||||||
|
if (path === "") path = "/";
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const h = now.getHours();
|
||||||
|
const hour12 = h % 12 === 0 ? 12 : h % 12;
|
||||||
|
const ampm = h < 12 ? "am" : "pm";
|
||||||
|
const timestamp = `${now.getMonth() + 1}.${now.getDate()} ${hour12}:${String(now.getMinutes()).padStart(2,"0")}:${String(now.getSeconds()).padStart(2,"0")}${ampm}`;
|
||||||
|
|
||||||
|
if (isLocalIP(ip)) {
|
||||||
|
console.log(`${req.headers.host} ${ip} ${path}`);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
`\x1b[33m${timestamp}\x1b[0m ${ip} \x1b[94m${req.headers.host} ${path}\x1b[0m`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
recordRequest({
|
||||||
|
Timestamp: new Date().toISOString(),
|
||||||
|
Host: req.headers.host || "",
|
||||||
|
IP: ip,
|
||||||
|
Path: path,
|
||||||
|
Method: req.method
|
||||||
|
});
|
||||||
|
|
||||||
|
next(req, res);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------
|
||||||
|
// Proxy helper
|
||||||
|
// ---------------------------
|
||||||
|
const proxy = httpProxy.createProxyServer({});
|
||||||
|
|
||||||
|
function serveProxy(req, res, port) {
|
||||||
|
proxy.web(req, res, { target: `http://localhost:${port}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy.on("error", (err, req, res) => {
|
||||||
|
console.error("Proxy error:", err.message);
|
||||||
|
|
||||||
|
if (res.headersSent) {
|
||||||
|
res.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.writeHead(502, { "Content-Type": "text/plain" });
|
||||||
|
res.end("⚠️ target is unavailable. Please try again later.");
|
||||||
|
});
|
||||||
|
|
||||||
|
proxy.on("proxyReq", (proxyReq, req, res) => {
|
||||||
|
proxyReq.setHeader("X-Forwarded-Proto", "https");
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------------------------
|
||||||
|
// Outside Requests
|
||||||
|
// ---------------------------
|
||||||
|
function getPortForHost(host) {
|
||||||
|
host = host.toLowerCase();
|
||||||
|
if (host.endsWith(".parchment.page")) host = "parchment.page";
|
||||||
|
|
||||||
|
switch (host) {
|
||||||
|
case "america.sun.museum":
|
||||||
|
return 8000
|
||||||
|
case "thefiveprinciples.org":
|
||||||
|
return 3001
|
||||||
|
case "americanforum.net":
|
||||||
|
return 3002
|
||||||
|
case "hyperia.so":
|
||||||
|
return 3003
|
||||||
|
|
||||||
|
case "pma.aryan.so":
|
||||||
|
case "aryan.so":
|
||||||
|
case "apply.aryan.so":
|
||||||
|
return 3004
|
||||||
|
|
||||||
|
case "parchment.page":
|
||||||
|
return 3005
|
||||||
|
case "government.forum":
|
||||||
|
return 3006
|
||||||
|
|
||||||
|
case "noahkurtis.com":
|
||||||
|
return 3007
|
||||||
|
case "comalyr.com":
|
||||||
|
return 3008
|
||||||
|
|
||||||
|
case "blockcatcher.sun.museum":
|
||||||
|
return 3009
|
||||||
|
|
||||||
|
case "git.sun.museum":
|
||||||
|
return 4000
|
||||||
|
case "log.sun.museum":
|
||||||
|
return 4001
|
||||||
|
case "admin.sun.museum":
|
||||||
|
return 8080
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handler(req, res) {
|
||||||
|
const port = getPortForHost(req.headers.host);
|
||||||
|
if (port) return serveProxy(req, res, port);
|
||||||
|
res.writeHead(200, {"Content-Type":"text/plain"});
|
||||||
|
res.end("Hello, World! You're from outside.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------
|
||||||
|
// Begin
|
||||||
|
// ---------------------------
|
||||||
|
function startServer() {
|
||||||
|
const certs = {};
|
||||||
|
|
||||||
|
function loadCert(domain) {
|
||||||
|
const base = `/etc/letsencrypt/live/${domain}`;
|
||||||
|
const cert = {
|
||||||
|
key: fs.readFileSync(`${base}/privkey.pem`, "utf8"),
|
||||||
|
cert: fs.readFileSync(`${base}/fullchain.pem`, "utf8"),
|
||||||
|
};
|
||||||
|
certs[domain] = cert;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadCert("parchment.page-0001");
|
||||||
|
loadCert("hyperia.so-0001");
|
||||||
|
|
||||||
|
const httpsOptions = {
|
||||||
|
SNICallback: (servername, cb) => {
|
||||||
|
servername = servername.toLowerCase();
|
||||||
|
|
||||||
|
if (certs[servername]) {
|
||||||
|
return cb(null, tls.createSecureContext(certs[servername]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (servername.endsWith(".parchment.page")) {
|
||||||
|
return cb(null, tls.createSecureContext(certs["parchment.page-0001"]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return cb(null, tls.createSecureContext(certs["hyperia.so-0001"]));
|
||||||
|
},
|
||||||
|
minVersion: "TLSv1.2",
|
||||||
|
};
|
||||||
|
|
||||||
|
const server = https.createServer(httpsOptions, loggingMiddleware(handler));
|
||||||
|
|
||||||
|
server.on("upgrade", (req, socket, head) => {
|
||||||
|
const port = getPortForHost(req.headers.host);
|
||||||
|
if (!port) return socket.destroy();
|
||||||
|
proxy.ws(req, socket, head, { target: `http://localhost:${port}` });
|
||||||
|
socket.on("error", (err) => {
|
||||||
|
console.error("WebSocket error:", err.message);
|
||||||
|
socket.destroy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(3000, () => {
|
||||||
|
console.log("🔒 HTTPS server running on port 3000");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
startServer()
|
||||||
|
|
||||||
190
main.go
190
main.go
@@ -1,190 +0,0 @@
|
|||||||
// main.go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
"Admin/src"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RequestInfo struct {
|
|
||||||
Timestamp string `json:"timestamp"`
|
|
||||||
Host string `json:"host"`
|
|
||||||
IP string `json:"ip"`
|
|
||||||
Path string `json:"path"`
|
|
||||||
Method string `json:"method"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Global store for request logs
|
|
||||||
var (
|
|
||||||
requests []RequestInfo
|
|
||||||
mu sync.Mutex
|
|
||||||
)
|
|
||||||
|
|
||||||
func isLocalIP(ipStr string) bool {
|
|
||||||
if ipStr == "[::1]" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func loggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ip := r.RemoteAddr
|
|
||||||
cleanIP, _, _ := strings.Cut(ip, ":") // Split at first colon, ignore port
|
|
||||||
cleanIP = strings.Trim(cleanIP, "[]") // Remove IPv6 brackets
|
|
||||||
cleanIP = strings.TrimPrefix(cleanIP, "::ffff:")
|
|
||||||
|
|
||||||
path := r.URL.Path[1:]
|
|
||||||
if path == "" {
|
|
||||||
path = "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
now := time.Now()
|
|
||||||
formattedTime := fmt.Sprintf("%d.%d %d:%02d:%02d%s",
|
|
||||||
now.Month(), now.Day(), now.Hour()%12, now.Minute(), now.Second(),
|
|
||||||
map[bool]string{true: "am", false: "pm"}[now.Hour() < 12])
|
|
||||||
|
|
||||||
ipWithPort := r.RemoteAddr
|
|
||||||
ipWithPort = ipWithPort[:strings.LastIndex(ipWithPort, ":")] // Trim port for isLocalIP
|
|
||||||
if isLocalIP(ipWithPort) {
|
|
||||||
log.Printf("%s %s %s", r.Host, cleanIP, path)
|
|
||||||
} else {
|
|
||||||
log.Printf("\033[33m%s\033[0m %s \033[94m%s %s\033[0m", formattedTime, cleanIP, r.Host, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add request info to global store
|
|
||||||
mu.Lock()
|
|
||||||
requests = append(requests, RequestInfo{
|
|
||||||
Timestamp: time.Now().Format(time.RFC3339), // e.g., "2025-03-26T12:34:56Z"
|
|
||||||
Host: r.Host,
|
|
||||||
IP: cleanIP,
|
|
||||||
Path: path,
|
|
||||||
Method: r.Method,
|
|
||||||
})
|
|
||||||
mu.Unlock()
|
|
||||||
|
|
||||||
next(w, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func rootHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ip := r.RemoteAddr
|
|
||||||
ip = ip[:strings.LastIndex(ip, ":")] // Trim port
|
|
||||||
if isLocalIP(ip) {
|
|
||||||
src.UIServer(w, r)
|
|
||||||
} else {
|
|
||||||
outsideHandler(w, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func serveProxy(w http.ResponseWriter, r *http.Request, port int) {
|
|
||||||
target, _ := url.Parse(fmt.Sprintf("http://localhost:%d", port))
|
|
||||||
proxy := httputil.NewSingleHostReverseProxy(target)
|
|
||||||
proxy.ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func outsideHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
host := r.Host
|
|
||||||
if strings.HasSuffix(host, ".parchment.page") {
|
|
||||||
host = "parchment.page"
|
|
||||||
}
|
|
||||||
|
|
||||||
switch host {
|
|
||||||
case "america.sun.museum":
|
|
||||||
serveProxy(w, r, 8000)
|
|
||||||
|
|
||||||
case "thefiveprinciples.org":
|
|
||||||
serveProxy(w, r, 3001)
|
|
||||||
|
|
||||||
case "americanforum.net":
|
|
||||||
serveProxy(w, r, 3002)
|
|
||||||
|
|
||||||
case "hyperia.so":
|
|
||||||
serveProxy(w, r, 3003)
|
|
||||||
|
|
||||||
case "pma.aryan.so", "aryan.so", "apply.aryan.so":
|
|
||||||
serveProxy(w, r, 3004)
|
|
||||||
|
|
||||||
case "parchment.page":
|
|
||||||
serveProxy(w, r, 3005)
|
|
||||||
|
|
||||||
case "government.forum":
|
|
||||||
serveProxy(w, r, 3006)
|
|
||||||
|
|
||||||
case "noahkurtis.com":
|
|
||||||
serveProxy(w, r, 3007)
|
|
||||||
|
|
||||||
case "comalyr.com":
|
|
||||||
serveProxy(w, r, 3008)
|
|
||||||
|
|
||||||
case "blockcatcher.sun.museum":
|
|
||||||
serveProxy(w, r, 3009)
|
|
||||||
|
|
||||||
case "git.sun.museum":
|
|
||||||
serveProxy(w, r, 4000)
|
|
||||||
|
|
||||||
case "admin.sun.museum":
|
|
||||||
serveProxy(w, r, 8080)
|
|
||||||
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(w, "Hello, World! You're from outside.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.SetFlags(0)
|
|
||||||
|
|
||||||
// Your handler with logging middleware
|
|
||||||
http.HandleFunc("/", loggingMiddleware(rootHandler))
|
|
||||||
|
|
||||||
// Load multiple certificates
|
|
||||||
certs := map[string]*tls.Certificate{}
|
|
||||||
|
|
||||||
loadCert := func(domain string) {
|
|
||||||
cert, err := tls.LoadX509KeyPair(
|
|
||||||
"/etc/letsencrypt/live/"+domain+"/fullchain.pem",
|
|
||||||
"/etc/letsencrypt/live/"+domain+"/privkey.pem",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to load certificate for %s: %v", domain, err)
|
|
||||||
}
|
|
||||||
certs[domain] = &cert
|
|
||||||
}
|
|
||||||
|
|
||||||
// Example domains
|
|
||||||
loadCert("parchment.page-0001") // Separate to allow wildcards
|
|
||||||
loadCert("hyperia.so-0001")
|
|
||||||
|
|
||||||
// Configure TLS with SNI
|
|
||||||
tlsConfig := &tls.Config{
|
|
||||||
GetCertificate: func(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
|
||||||
serverName := strings.ToLower(hello.ServerName)
|
|
||||||
if cert, ok := certs[serverName]; ok {
|
|
||||||
return cert, nil
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(serverName, ".parchment.page") {
|
|
||||||
return certs["parchment.page-0001"], nil
|
|
||||||
}
|
|
||||||
// fallback: return any default cert
|
|
||||||
return certs["hyperia.so-0001"], nil
|
|
||||||
},
|
|
||||||
MinVersion: tls.VersionTLS12,
|
|
||||||
}
|
|
||||||
|
|
||||||
server := &http.Server{
|
|
||||||
Addr: ":3000",
|
|
||||||
Handler: nil, // uses default http handler
|
|
||||||
TLSConfig: tlsConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("🔒 HTTPS server running on port 3000")
|
|
||||||
log.Fatal(server.ListenAndServeTLS("", "")) // certs handled by tlsConfig
|
|
||||||
}
|
|
||||||
8
package.json
Normal file
8
package.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"start": "node index.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"http-proxy": "^1.18.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@ wildcard domains, and one for all the other websites.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* 1. Shut down Admin and run 'npm run start' (in this ssl folder) */
|
/* 1. Shut down Admin and run 'npm run start' (in this ssl folder) */
|
||||||
/* 2.1 Run this command to support Parchment wildcard certs (AWS Authentication)
|
/* 2.1 Run this command to support Parchment wildcard certs (AWS Authentication)
|
||||||
|
|
||||||
@@ -22,14 +21,15 @@ sudo certbot certonly \
|
|||||||
-d hyperia.so \
|
-d hyperia.so \
|
||||||
-d aryan.so \
|
-d aryan.so \
|
||||||
-d comalyr.com \
|
-d comalyr.com \
|
||||||
|
-d noahkurtis.com \
|
||||||
-d admin.sun.museum \
|
-d admin.sun.museum \
|
||||||
-d america.sun.museum \
|
-d america.sun.museum \
|
||||||
-d git.sun.museum \
|
-d git.sun.museum \
|
||||||
|
-d log.sun.museum \
|
||||||
-d blockcatcher.sun.museum \
|
-d blockcatcher.sun.museum \
|
||||||
-d americanforum.net \
|
-d americanforum.net \
|
||||||
-d thefiveprinciples.org \
|
-d thefiveprinciples.org \
|
||||||
-d government.forum
|
-d government.forum
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user