commit ee9309da688d2eee9385868755d638ff184071af Author: skidoodle Date: Fri May 31 12:56:54 2024 +0200 initial commit diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5ad2434 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM golang:alpine as builder +WORKDIR /app +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN go build -o iphistory . + +FROM alpine:latest +RUN apk --no-cache add ca-certificates +WORKDIR /root/ +COPY --from=builder /app/iphistory . +EXPOSE 3000 +CMD ["./iphistory"] diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..50b0632 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,15 @@ +version: '3.9' + +services: + iphistory: + image: ghcr.io/skidoodle/iphistory:main + container_name: iphistory + restart: unless-stopped + ports: + - "8080:8080" + volumes: + - data:/app + +volumes: + data: + driver: local diff --git a/genhistory.py b/genhistory.py new file mode 100644 index 0000000..f50fe5b --- /dev/null +++ b/genhistory.py @@ -0,0 +1,27 @@ +import random +from datetime import datetime, timedelta +import sys + +def generate_example_data(num_entries): + data = [] + for _ in range(num_entries): + timestamp = datetime.now() - timedelta(days=random.randint(0, 365), + hours=random.randint(0, 23), + minutes=random.randint(0, 59), + seconds=random.randint(0, 59)) + ip_address = f"{random.randint(0, 255)}.{random.randint(0, 255)}.{random.randint(0, 255)}.{random.randint(0, 255)}" + entry = f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')} CEST - {ip_address}" + data.append(entry) + return data + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: python genhistory.py ") + sys.exit(1) + + num_entries = int(sys.argv[1]) + example_data = generate_example_data(num_entries) + + with open('ip_history.txt', 'w') as file: + for entry in example_data: + file.write(entry + '\n') diff --git a/index.html b/index.html new file mode 100644 index 0000000..4669123 --- /dev/null +++ b/index.html @@ -0,0 +1,221 @@ + + + + + + IP History + + + + +

IP History

+ + + + + + + + +
TimestampIP Address
+ + + + + + diff --git a/ip_history.txt b/ip_history.txt new file mode 100644 index 0000000..e69de29 diff --git a/main.go b/main.go new file mode 100644 index 0000000..f0be464 --- /dev/null +++ b/main.go @@ -0,0 +1,160 @@ +package main + +import ( + "bufio" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "strings" + "time" +) + +const ( + url = "https://one.one.one.one/cdn-cgi/trace" + filePath = "ip_history.txt" + checkInterval = 30 * time.Second + maxRetries = 3 + retryDelay = 10 * time.Second +) + +func FetchPublicIP() (string, error) { + var ip string + var err error + + for attempt := 1; attempt <= maxRetries; attempt++ { + ip, err = fetchIP() + if err == nil { + return ip, nil + } + fmt.Printf("Attempt %d: Error fetching IP: %v\n", attempt, err) + time.Sleep(retryDelay) + } + return "", err +} + +func fetchIP() (string, error) { + resp, err := http.Get(url) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + + for _, line := range strings.Split(string(body), "\n") { + if strings.HasPrefix(line, "ip=") { + return strings.TrimPrefix(line, "ip="), nil + } + } + return "", fmt.Errorf("IP address not found") +} + +func ReadIPHistory() ([]string, error) { + file, err := os.Open(filePath) + if os.IsNotExist(err) { + return []string{}, nil + } else if err != nil { + return nil, err + } + defer file.Close() + + var history []string + scanner := bufio.NewScanner(file) + for scanner.Scan() { + history = append(history, scanner.Text()) + } + return history, scanner.Err() +} + +func WriteIPHistory(history []string) error { + file, err := os.Create(filePath) + if err != nil { + return err + } + defer file.Close() + + writer := bufio.NewWriter(file) + for _, entry := range history { + _, err := writer.WriteString(entry + "\n") + if err != nil { + return err + } + } + return writer.Flush() +} + +func TrackIP(ipChan <-chan string) { + var lastLoggedIP string + for ip := range ipChan { + history, err := ReadIPHistory() + if err != nil { + fmt.Println("Error reading IP history:", err) + continue + } + + entry := fmt.Sprintf("%s - %s", time.Now().Format("2006-01-02 15:04:05 MST"), ip) + if len(history) == 0 || !strings.Contains(history[0], ip) { + history = append([]string{entry}, history...) + if err := WriteIPHistory(history); err != nil { + fmt.Println("Error writing IP history:", err) + } + fmt.Printf("📄 New IP logged: %s\n", entry) + lastLoggedIP = ip + } else { + if lastLoggedIP != ip { + fmt.Println("🤷 IP is already up to date") + lastLoggedIP = ip + } + } + } +} + +func ServeWeb() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "index.html") + }) + + http.HandleFunc("/history", func(w http.ResponseWriter, r *http.Request) { + history, err := ReadIPHistory() + if err != nil { + http.Error(w, "Error reading IP history", http.StatusInternalServerError) + return + } + + jsonData, err := json.Marshal(history) + if err != nil { + http.Error(w, "Error converting history to JSON", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + w.Write(jsonData) + }) + + fmt.Println("Starting server on :8080") + http.ListenAndServe(":8080", nil) +} + +func main() { + ipChan := make(chan string) + + go func() { + for { + ip, err := FetchPublicIP() + if err != nil { + fmt.Println("Error fetching public IP:", err) + } else { + ipChan <- ip + } + time.Sleep(checkInterval) + } + }() + + go TrackIP(ipChan) + ServeWeb() +}