This commit is contained in:
metacryst
2025-04-08 11:53:18 -05:00
commit 5ba9bae02c
14 changed files with 351 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
node_modules

12
UI/index.html Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Admin</title>
<link rel="icon" href="shield.png">
<link rel="stylesheet" href="">
<script type="module" src="index.js"></script>
</head>
<body style="background: #38614d">
</body>
</html>

0
UI/index.js Normal file
View File

BIN
UI/lightning.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
UI/shield.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

5
go.mod Normal file
View File

@@ -0,0 +1,5 @@
module Admin
go 1.22.0
require github.com/gorilla/websocket v1.5.3 // indirect

2
go.sum Normal file
View File

@@ -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=

114
main.go Normal file
View File

@@ -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)
}
}

1
readme.md Normal file
View File

@@ -0,0 +1 @@
```sudo go run *.go```

96
src/betaSignups.go Normal file
View File

@@ -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)
}

14
src/uiserver.go Normal file
View File

@@ -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)
}

32
ssl/index.js Normal file
View File

@@ -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

58
ssl/package-lock.json generated Normal file
View File

@@ -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=="
}
}
}

16
ssl/package.json Normal file
View File

@@ -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"
}
}