fix some stuff lol

This commit is contained in:
skidoodle 2024-10-19 14:32:16 +02:00
parent 9acfc490f0
commit 20d6510053
Signed by: albert
GPG key ID: A06E3070D7D55BF2
10 changed files with 131 additions and 30 deletions

5
.dockerignore Normal file
View file

@ -0,0 +1,5 @@
.git
.github
.docker-compose*
Dockerfile
*.md

2
.env
View file

@ -1,2 +0,0 @@
NICK=
PASS=

3
.gitignore vendored
View file

@ -1 +1,2 @@
.env.local .env**
data/**

View file

@ -1,5 +1,7 @@
FROM golang:alpine as builder FROM golang:alpine AS builder
WORKDIR /build WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . . COPY . .
RUN go build -o trackncore . RUN go build -o trackncore .
@ -7,8 +9,7 @@ FROM alpine:latest
RUN apk --no-cache add ca-certificates RUN apk --no-cache add ca-certificates
WORKDIR /app/ WORKDIR /app/
COPY --from=builder /build/trackncore . COPY --from=builder /build/trackncore .
COPY --from=builder /build/.env . COPY --from=builder /build/data .
COPY --from=builder /build/profiles.json .
COPY --from=builder /build/index.html . COPY --from=builder /build/index.html .
EXPOSE 3000 EXPOSE 3000
CMD ["./trackncore"] CMD ["./trackncore"]

View file

@ -7,6 +7,9 @@ services:
- "3000:3000" - "3000:3000"
volumes: volumes:
- data:/app/data - data:/app/data
environment:
- NICK=${NICK}
- PASSWORD=${PASS}
volumes: volumes:
data: data:

View file

@ -7,6 +7,8 @@ services:
- "3000:3000" - "3000:3000"
volumes: volumes:
- data:/app/data - data:/app/data
environment:
- NICK=${NICK}
- PASSWORD=${PASS}
volumes: volumes:
data: data:

2
go.mod
View file

@ -6,10 +6,12 @@ toolchain go1.23.2
require ( require (
github.com/PuerkitoBio/goquery v1.10.0 github.com/PuerkitoBio/goquery v1.10.0
github.com/fsnotify/fsnotify v1.7.0
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
) )
require ( require (
github.com/andybalholm/cascadia v1.3.2 // indirect github.com/andybalholm/cascadia v1.3.2 // indirect
golang.org/x/net v0.30.0 // indirect golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.26.0 // indirect
) )

4
go.sum
View file

@ -2,6 +2,8 @@ github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbav
github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4= github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@ -26,6 +28,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=

130
main.go
View file

@ -13,6 +13,7 @@ import (
"time" "time"
"github.com/PuerkitoBio/goquery" "github.com/PuerkitoBio/goquery"
"github.com/fsnotify/fsnotify"
"github.com/joho/godotenv" "github.com/joho/godotenv"
) )
@ -24,13 +25,13 @@ type ProfileData struct {
CurrentUpload string `json:"current_upload"` CurrentUpload string `json:"current_upload"`
CurrentDownload string `json:"current_download"` CurrentDownload string `json:"current_download"`
Points int `json:"points"` Points int `json:"points"`
SeedingCount int `json:"seeding_count"` SeedingCount int `json:"seeding_count"`
} }
var ( var (
profiles = map[string]string{} profiles = map[string]string{}
jsonFile = "./data/data.json" jsonFile = "./data/data.json"
profilesFile = "profiles.json" profilesFile = "./data/profiles.json"
baseUrl = "https://ncore.pro/profile.php?id=" baseUrl = "https://ncore.pro/profile.php?id="
nick string nick string
pass string pass string
@ -44,46 +45,64 @@ func init() {
nick = os.Getenv("NICK") nick = os.Getenv("NICK")
pass = os.Getenv("PASS") pass = os.Getenv("PASS")
if _, err := os.Stat(profilesFile); os.IsNotExist(err) {
log.Printf("File %s does not exist, creating an empty one", profilesFile)
err := os.WriteFile(profilesFile, []byte("{}"), 0644)
if err != nil {
log.Fatalf("Failed to create profiles file: %v", err)
}
}
loadProfiles()
client = &http.Client{}
}
func loadProfiles() {
file, err := os.Open(profilesFile) file, err := os.Open(profilesFile)
if err != nil { if err != nil {
log.Fatal(err) log.Fatalf("Failed to open profiles file: %v", err)
} }
defer file.Close() defer file.Close()
jsonBytes, _ := io.ReadAll(file) jsonBytes, err := io.ReadAll(file)
if err != nil {
log.Fatalf("Failed to read profiles file: %v", err)
}
var tempProfiles map[string]string = make(map[string]string) var tempProfiles map[string]string
json.Unmarshal(jsonBytes, &tempProfiles) err = json.Unmarshal(jsonBytes, &tempProfiles)
if err != nil {
log.Fatalf("Failed to unmarshal profiles JSON: %v", err)
}
for k, v := range tempProfiles { for k, v := range tempProfiles {
profiles[k] = baseUrl + v profiles[k] = baseUrl + v
} }
log.Println("Profiles loaded successfully.")
client = &http.Client{}
} }
func fetchProfile(url string, displayName string) (*ProfileData, error) { func fetchProfile(url string, displayName string) (*ProfileData, error) {
req, err := http.NewRequest("GET", url, nil) req, err := http.NewRequest("GET", url, nil)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("error creating request for %s: %v", displayName, err)
} }
req.Header.Set("Cookie", fmt.Sprintf("nick=%s; pass=%s", nick, pass)) req.Header.Set("Cookie", fmt.Sprintf("nick=%s; pass=%s", nick, pass))
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("error fetching profile for %s: %v", displayName, err)
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to fetch profile: %s", displayName) return nil, fmt.Errorf("failed to fetch profile %s: received status %d", displayName, resp.StatusCode)
} }
doc, err := goquery.NewDocumentFromReader(resp.Body) doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("error parsing profile document for %s: %v", displayName, err)
} }
profile := &ProfileData{ profile := &ProfileData{
@ -130,19 +149,27 @@ func fetchProfile(url string, displayName string) (*ProfileData, error) {
func readExistingProfiles() ([]ProfileData, error) { func readExistingProfiles() ([]ProfileData, error) {
if _, err := os.Stat(jsonFile); os.IsNotExist(err) { if _, err := os.Stat(jsonFile); os.IsNotExist(err) {
log.Printf("File %s does not exist, returning an empty profile list.", jsonFile)
return []ProfileData{}, nil return []ProfileData{}, nil
} }
file, err := os.Open(jsonFile) file, err := os.Open(jsonFile)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("error opening %s: %v", jsonFile, err)
} }
defer file.Close() defer file.Close()
var existingProfiles []ProfileData var existingProfiles []ProfileData
byteValue, _ := io.ReadAll(file) byteValue, err := io.ReadAll(file)
if err != nil {
return nil, fmt.Errorf("error reading %s: %v", jsonFile, err)
}
err = json.Unmarshal(byteValue, &existingProfiles) err = json.Unmarshal(byteValue, &existingProfiles)
return existingProfiles, err if err != nil {
return nil, fmt.Errorf("error unmarshalling profile data: %v", err)
}
return existingProfiles, nil
} }
func logToJSON(profile *ProfileData) error { func logToJSON(profile *ProfileData) error {
@ -155,32 +182,88 @@ func logToJSON(profile *ProfileData) error {
file, err := os.OpenFile(jsonFile, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644) file, err := os.OpenFile(jsonFile, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil { if err != nil {
return err return fmt.Errorf("error opening file for writing: %v", err)
} }
defer file.Close() defer file.Close()
enc := json.NewEncoder(file) enc := json.NewEncoder(file)
enc.SetIndent("", " ") enc.SetIndent("", " ")
return enc.Encode(existingProfiles) err = enc.Encode(existingProfiles)
if err != nil {
return fmt.Errorf("error encoding JSON data: %v", err)
}
log.Printf("Profile for %s logged successfully.", profile.Owner)
return nil
} }
func dataHandler(w http.ResponseWriter, r *http.Request) { func dataHandler(w http.ResponseWriter, r *http.Request) {
existingProfiles, err := readExistingProfiles() existingProfiles, err := readExistingProfiles()
if err != nil { if err != nil {
http.Error(w, "Could not read data", http.StatusInternalServerError) http.Error(w, "Could not read data", http.StatusInternalServerError)
log.Printf("Error reading profiles: %v", err)
return return
} }
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(existingProfiles) err = json.NewEncoder(w).Encode(existingProfiles)
if err != nil {
log.Printf("Error encoding profiles to JSON: %v", err)
}
} }
func serveHTML(w http.ResponseWriter, r *http.Request) { func serveHTML(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "index.html") http.ServeFile(w, r, "index.html")
} }
func watchProfilesFile() {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatalf("Error creating file watcher: %v", err)
}
defer watcher.Close()
err = watcher.Add(profilesFile)
if err != nil {
log.Fatalf("Error adding file to watcher: %v", err)
}
debounce := time.AfterFunc(0, func() {})
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
log.Println("Profiles file changed, reloading profiles...")
debounce.Stop() // Stop any running debounce timer
debounce = time.AfterFunc(500*time.Millisecond, func() {
loadProfiles()
for displayName, url := range profiles {
profile, err := fetchProfile(url, displayName)
if err != nil {
log.Printf("Error fetching profile for %s after file change: %v", displayName, err)
continue
}
if err := logToJSON(profile); err != nil {
log.Printf("Error logging profile for %s: %v", displayName, err)
}
}
})
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Printf("File watcher error: %v", err)
}
}
}
func main() { func main() {
ticker := time.NewTicker(time.Minute * 30) ticker := time.NewTicker(time.Hour * 24)
defer ticker.Stop() defer ticker.Stop()
go func() { go func() {
@ -188,19 +271,22 @@ func main() {
for displayName, url := range profiles { for displayName, url := range profiles {
profile, err := fetchProfile(url, displayName) profile, err := fetchProfile(url, displayName)
if err != nil { if err != nil {
log.Println(err) log.Printf("Error fetching profile %s: %v", displayName, err)
continue continue
} }
if err := logToJSON(profile); err != nil { if err := logToJSON(profile); err != nil {
log.Println(err) log.Printf("Error logging profile for %s: %v", displayName, err)
} }
} }
<-ticker.C <-ticker.C
} }
}() }()
go watchProfilesFile()
http.HandleFunc("/data", dataHandler) http.HandleFunc("/data", dataHandler)
http.HandleFunc("/", serveHTML) http.HandleFunc("/", serveHTML)
log.Println("Server is starting on port 3000...")
log.Fatal(http.ListenAndServe(":3000", nil)) log.Fatal(http.ListenAndServe(":3000", nil))
} }

View file

@ -1 +0,0 @@
{}