init
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
12
UI/index.html
Normal file
12
UI/index.html
Normal 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
0
UI/index.js
Normal file
BIN
UI/lightning.png
Normal file
BIN
UI/lightning.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
UI/shield.png
Normal file
BIN
UI/shield.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.3 KiB |
5
go.mod
Normal file
5
go.mod
Normal 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
2
go.sum
Normal 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
114
main.go
Normal 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)
|
||||
}
|
||||
}
|
||||
96
src/betaSignups.go
Normal file
96
src/betaSignups.go
Normal 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
14
src/uiserver.go
Normal 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
32
ssl/index.js
Normal 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
58
ssl/package-lock.json
generated
Normal 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
16
ssl/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user