diff --git a/main.go b/main.go index 394e70f..b2c78f3 100644 --- a/main.go +++ b/main.go @@ -618,57 +618,234 @@ func getClubInfo(w http.ResponseWriter, r *http.Request) { Category: category, Competitions: competitions, } + w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(clubInfo) } func main() { - r := mux.NewRouter() - r.HandleFunc("/club/{type}/{id}", getClubInfo).Methods("GET") - r.HandleFunc("/club/{type}/{id}/table", getClubTables).Methods("GET") - r.HandleFunc("/club/search", getClubSearch).Methods("GET") - r.HandleFunc("/club/{id:[0-9a-fA-F-]+}", func(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - http.Redirect(w, r, "/club/football/"+vars["id"], http.StatusMovedPermanently) - }).Methods("GET") - r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{"status":"ok","message":"FACR Scraper API is running"}`)) - }) - port := ":8080" - fmt.Printf("Server running on http://localhost%s\n", port) - log.Fatal(http.ListenAndServe(port, r)) + r := mux.NewRouter() + r.HandleFunc("/club/{type}/{id}", getClubInfo).Methods("GET") + r.HandleFunc("/club/{type}/{id}/table", getClubTables).Methods("GET") + r.HandleFunc("/club/search", getClubSearch).Methods("GET") + r.HandleFunc("/club/{id:[0-9a-fA-F-]+}", func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + http.Redirect(w, r, "/club/football/"+vars["id"], http.StatusMovedPermanently) + }).Methods("GET") + r.HandleFunc("/", docsHandler) + port := ":8080" + fmt.Printf("Server running on http://localhost%s\n", port) + log.Fatal(http.ListenAndServe(port, r)) +} + +// docsHandler serves a simple HTML API documentation at the root endpoint. +func docsHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + io.WriteString(w, ` + + + + + FACR Scraper API Docs + + + + + + + +
+

FACR Scraper API

+

Status: ok — server is running.

+
+ +
+

Search Clubs

+

GET /club/search?q=QUERY

+

Find clubs on fotbal.cz. Supports football and futsal clubs.

+

Example: /club/search?q=Sparta

+
+ Response shape +
{
+  "query": "Sparta",
+  "count": 2,
+  "results": [
+    {
+      "name": "AC Sparta Praha",
+      "club_id": "",
+      "club_type": "football",
+      "url": "https://www.fotbal.cz/...",
+      "logo_url": "https://.../logo.png",
+      "category": "Muži",
+      "address": "..."
+    }
+  ]
+}
+
+
+ +
+

Club Info + Matches

+

GET /club/{type}/{id}

+ +

Example: /club/football/{id}

+
+ Response shape +
{
+  "name": "AC Sparta Praha",
+  "club_id": "00000000-0000-0000-0000-000000000000",
+  "club_type": "football",
+  "club_internal_id": "123456",
+  "url": "https://www.fotbal.cz/...",
+  "logo_url": "https://is1.fotbal.cz/media/kluby/.../logo.jpg",
+  "address": "Milady Horákové 98, 160 00 Praha 6",
+  "category": "Muži A",
+  "competitions": [
+    {
+      "id": "12345",
+      "code": "1. LIGA",
+      "name": "Fortuna Liga",
+      "team_count": "16",
+      "matches_link": "https://www.fotbal.cz/...",
+      "matches": [
+        {
+          "date_time": "12.08.2023 18:00",
+          "home": "AC Sparta Praha",
+          "home_id": "00000000-0000-0000-0000-000000000000",
+          "home_logo_url": "https://.../sparta.png",
+          "away": "SK Slavia Praha",
+          "away_id": "11111111-1111-1111-1111-111111111111",
+          "away_logo_url": "https://.../slavia.png",
+          "score": "2:1",
+          "venue": "Stadion Letná",
+          "match_id": "match12345",
+          "report_url": "https://www.fotbal.cz/..."
+        }
+      ]
+    }
+  ]
+}
+
+
+ +
+

Club Tables (Standings)

+

GET /club/{type}/{id}/table

+

Returns standings (overall table) for each competition of the club.

+

Example: /club/football/{id}/table

+
+ Response shape +
{
+  "name": "AC Sparta Praha",
+  "club_id": "00000000-0000-0000-0000-000000000000",
+  "club_type": "football",
+  "club_internal_id": "123456",
+  "url": "https://www.fotbal.cz/...",
+  "logo_url": "https://is1.fotbal.cz/media/kluby/.../logo.jpg",
+  "competitions": [
+    {
+      "id": "12345",
+      "code": "1. LIGA",
+      "name": "Fortuna Liga",
+      "team_count": "16",
+      "matches_link": "https://www.fotbal.cz/...",
+      "table": {
+        "overall": [
+          {
+            "rank": "1",
+            "team": "AC Sparta Praha",
+            "team_id": "00000000-0000-0000-0000-000000000000",
+            "team_logo_url": "https://.../sparta.png",
+            "played": "10",
+            "wins": "8",
+            "draws": "2",
+            "losses": "0",
+            "score": "25:5",
+            "points": "26"
+          },
+          {
+            "rank": "2",
+            "team": "SK Slavia Praha",
+            "team_id": "11111111-1111-1111-1111-111111111111",
+            "team_logo_url": "https://.../slavia.png",
+            "played": "10",
+            "wins": "7",
+            "draws": "2",
+            "losses": "1",
+            "score": "20:8",
+            "points": "23"
+          }
+        ]
+      }
+    }
+  ]
+}
+
+
+ +
+

Shortcuts

+

GET /club/{id} → redirects to /club/football/{id}

+
+ + + +`) } -// containsFold returns true if substr is within s, case-insensitive. func containsFold(s, substr string) bool { - s = strings.ToLower(strings.TrimSpace(s)) - substr = strings.ToLower(strings.TrimSpace(substr)) - if substr == "" { - return false - } - return strings.Contains(s, substr) + s = strings.ToLower(strings.TrimSpace(s)) + substr = strings.ToLower(strings.TrimSpace(substr)) + if substr == "" { + return false + } + return strings.Contains(s, substr) } // extractUUIDFromHref finds the first UUID-like token in an href and returns it. func extractUUIDFromHref(href string) string { - href = strings.TrimSpace(href) - if href == "" { - return "" - } - re := regexp.MustCompile(`[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}`) - if m := re.FindString(href); m != "" { - return m - } - // Fallback: some links may end with ID after slash; take last path token if it looks like hex+hyphenated - parts := strings.Split(href, "/") - if len(parts) > 0 { - cand := parts[len(parts)-1] - if re.MatchString(cand) { - return cand - } - } - return "" + href = strings.TrimSpace(href) + if href == "" { + return "" + } + re := regexp.MustCompile(`[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}`) + if m := re.FindString(href); m != "" { + return m + } + // Fallback: some links may end with ID after slash; take last path token if it looks like hex+hyphenated + parts := strings.Split(href, "/") + if len(parts) > 0 { + cand := parts[len(parts)-1] + if re.MatchString(cand) { + return cand + } + } + return "" } type Match struct {