commit 5ba9bae02c96ab66b07249348c0c1ff0bf9405a7 Author: metacryst Date: Tue Apr 8 11:53:18 2025 -0500 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/UI/index.html b/UI/index.html new file mode 100644 index 0000000..a609c39 --- /dev/null +++ b/UI/index.html @@ -0,0 +1,12 @@ + + + + Admin + + + + + + + + \ No newline at end of file diff --git a/UI/index.js b/UI/index.js new file mode 100644 index 0000000..e69de29 diff --git a/UI/lightning.png b/UI/lightning.png new file mode 100644 index 0000000..39ff084 Binary files /dev/null and b/UI/lightning.png differ diff --git a/UI/shield.png b/UI/shield.png new file mode 100644 index 0000000..1602e53 Binary files /dev/null and b/UI/shield.png differ diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f4e2ed2 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module Admin + +go 1.22.0 + +require github.com/gorilla/websocket v1.5.3 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..25a9fc4 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= diff --git a/main.go b/main.go new file mode 100644 index 0000000..6d294aa --- /dev/null +++ b/main.go @@ -0,0 +1,114 @@ +// main.go +package main + +import ( + "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("%s %s \033[33m%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 outsideHandler(w http.ResponseWriter, r *http.Request) { + if r.Host == "america.sun.museum" { + target, _ := url.Parse("http://localhost:8000") + proxy := httputil.NewSingleHostReverseProxy(target) + r.Host = target.Host + proxy.ServeHTTP(w, r) + return + } else + if r.Host == "admin.sun.museum" { + src.BetaSignupHandler(w, r) + return + } + + fmt.Fprintf(w, "Hello, World! You're from outside.") +} + +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 main() { + log.SetFlags(0) + http.HandleFunc("/", loggingMiddleware(rootHandler)) + + certFile := "/etc/letsencrypt/live/parchment.page/fullchain.pem" + keyFile := "/etc/letsencrypt/live/parchment.page/privkey.pem" + log.Println("Server running on port 3000") + err := http.ListenAndServeTLS(":3000", certFile, keyFile, nil) + if err != nil { + log.Fatalf("Server failed: %v", err) + } +} \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..61e86fa --- /dev/null +++ b/readme.md @@ -0,0 +1 @@ +```sudo go run *.go``` \ No newline at end of file diff --git a/src/betaSignups.go b/src/betaSignups.go new file mode 100644 index 0000000..d01b605 --- /dev/null +++ b/src/betaSignups.go @@ -0,0 +1,96 @@ +package src + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "os" + "path/filepath" + "time" +) + +type SignupRequest struct { + Email string `json:"email"` +} + +func BetaSignupHandler(w http.ResponseWriter, r *http.Request) { + log.Printf("\033[33m%s \033[0m ", "betahandler") + + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type") + + if r.Method == http.MethodOptions { + w.WriteHeader(http.StatusOK) + return + } + + if r.Method != http.MethodPost { + log.Printf("\033[33m%s %s\033[0m ", "Method not allowed: ", r.Method) + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + // Parse JSON request + var req SignupRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, "Invalid request", http.StatusBadRequest) + return + } + + // Get home directory + homeDir, err := os.UserHomeDir() + if err != nil { + log.Printf("Error getting home directory: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + // Check if email folder exists + folderPath := filepath.Join(homeDir, "Admin", "beta_signups", req.Email) + if _, err := os.Stat(folderPath); !os.IsNotExist(err) { + log.Printf("\033[31memail already exists!\033[0m") + http.Error(w, "Email already exists", http.StatusBadRequest) + return + } + + // Create directory + err = os.MkdirAll(folderPath, 0755) + if err != nil { + log.Printf("Error creating directory: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + // Format date and time + now := time.Now() + formattedDate := now.Format("1.2.2006") // M.D format + formattedTime := now.Format("3:04pm") // h:mma format + csvLine := fmt.Sprintf("\n%s,%s %s", req.Email, formattedDate, formattedTime) + + // Append to CSV file + csvFilePath := filepath.Join(homeDir, "Admin", "beta_signups.csv") + err = os.MkdirAll(filepath.Dir(csvFilePath), 0755) + if err != nil { + log.Printf("Error creating CSV directory: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + f, err := os.OpenFile(csvFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Printf("Error opening CSV file: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + defer f.Close() + + if _, err := f.WriteString(csvLine); err != nil { + log.Printf("Error appending to CSV: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) +} \ No newline at end of file diff --git a/src/uiserver.go b/src/uiserver.go new file mode 100644 index 0000000..7c9a2ca --- /dev/null +++ b/src/uiserver.go @@ -0,0 +1,14 @@ +package src + +import ( + "net/http" +) + +func UIServer(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/" { + http.ServeFile(w, r, "./UI/index.html") + return + } + + http.FileServer(http.Dir("./UI")).ServeHTTP(w, r) +} \ No newline at end of file diff --git a/ssl/index.js b/ssl/index.js new file mode 100644 index 0000000..1c0e33f --- /dev/null +++ b/ssl/index.js @@ -0,0 +1,32 @@ +/* 1. Shut down Admin and run this file 'npm run start' */ + +/* 2. Run this command */ +// sudo certbot certonly \ +// --dns-route53 \ +// -d '*.parchment.page' \ +// -d quill.sun.museum \ +// -d admin.sun.museum \ +// -d scraper.sun.museum \ +// -d ai.sun.museum \ +// -d america.sun.museum + + +import http from 'http'; +import httpProxy from 'http-proxy'; +const proxy = httpProxy.createProxyServer({}); + +const server = http.createServer((req, res) => { + // Forward all requests from port 3000 to port 80 + proxy.web(req, res, { target: 'http://localhost:80' }); +}); + +server.listen(3000, () => { + console.log('Proxy server is running on port 3000 and forwarding requests to port 80'); +}); + + +/* 2024-12-30 Notes*/ +// Switched to AWS certification so Parchment Users can have wildcard domains (*.parchment.page) +// AWS must now be used for all domains +// pip3 install certbot-dns-route53 +// https://chatgpt.com/c/66fc4450-a570-8005-a177-1d1eb6c738e8 \ No newline at end of file diff --git a/ssl/package-lock.json b/ssl/package-lock.json new file mode 100644 index 0000000..ac4b590 --- /dev/null +++ b/ssl/package-lock.json @@ -0,0 +1,58 @@ +{ + "name": "ssl", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ssl", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "http-proxy": "^1.18.1" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + } + } +} diff --git a/ssl/package.json b/ssl/package.json new file mode 100644 index 0000000..842ad14 --- /dev/null +++ b/ssl/package.json @@ -0,0 +1,16 @@ +{ + "name": "ssl", + "version": "1.0.0", + "type": "module", + "description": "", + "main": "index.js", + "scripts": { + "start": "node index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "http-proxy": "^1.18.1" + } +}