mirror of
https://github.com/Dvorinka/ClubLogos.git
synced 2026-06-03 19:42:58 +00:00
working
This commit is contained in:
+1
-1
@@ -10,7 +10,7 @@ require (
|
|||||||
github.com/mattn/go-sqlite3 v1.14.19
|
github.com/mattn/go-sqlite3 v1.14.19
|
||||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c
|
||||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef
|
||||||
github.com/PuerkitoBio/goquery v1.9.2
|
golang.org/x/text v0.14.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
+29
-30
@@ -1,12 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
neturl "net/url"
|
neturl "net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -18,34 +18,18 @@ import (
|
|||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/PuerkitoBio/goquery"
|
"golang.org/x/text/unicode/norm"
|
||||||
"golang.org/x/text/unicode/norm"
|
|
||||||
"unicode"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================== Club Handlers ====================
|
// ==================== Club Handlers ====================
|
||||||
|
|
||||||
func searchClubs(c *gin.Context) {
|
func searchClubs(c *gin.Context) {
|
||||||
q := strings.TrimSpace(c.Query("q"))
|
|
||||||
if q == "" {
|
|
||||||
q := strings.TrimSpace(c.Query("q"))
|
q := strings.TrimSpace(c.Query("q"))
|
||||||
if q == "" {
|
if q == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "query parameter 'q' is required"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "query parameter 'q' is required"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
clubs, err := scrapeFotbalSearch(q)
|
|
||||||
if err != nil || len(clubs) == 0 {
|
|
||||||
nq := removeDiacritics(strings.ToLower(q))
|
|
||||||
if nq != strings.ToLower(q) {
|
|
||||||
if c2, err2 := scrapeFotbalSearch(nq); err2 == nil && len(c2) > 0 {
|
|
||||||
c.JSON(http.StatusOK, c2)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, getDemoClubs(q))
|
|
||||||
clubs, err := scrapeFotbalSearch(q)
|
clubs, err := scrapeFotbalSearch(q)
|
||||||
if err != nil || len(clubs) == 0 {
|
if err != nil || len(clubs) == 0 {
|
||||||
nq := removeDiacritics(strings.ToLower(q))
|
nq := removeDiacritics(strings.ToLower(q))
|
||||||
@@ -69,8 +53,6 @@ func getClub(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
club, err := fetchClubByID(id)
|
|
||||||
if err != nil || club == nil {
|
|
||||||
club, err := fetchClubByID(id)
|
club, err := fetchClubByID(id)
|
||||||
if err != nil || club == nil {
|
if err != nil || club == nil {
|
||||||
c.JSON(http.StatusNotFound, gin.H{"error": "club not found"})
|
c.JSON(http.StatusNotFound, gin.H{"error": "club not found"})
|
||||||
@@ -513,7 +495,6 @@ func getLogoWithMetadata(c *gin.Context) {
|
|||||||
// List all logos
|
// List all logos
|
||||||
func listLogos(c *gin.Context) {
|
func listLogos(c *gin.Context) {
|
||||||
q := strings.TrimSpace(c.Query("q"))
|
q := strings.TrimSpace(c.Query("q"))
|
||||||
sport := strings.ToLower(strings.TrimSpace(c.DefaultQuery("sport", c.DefaultQuery("type", ""))))
|
|
||||||
sortParam := c.DefaultQuery("sort", "name")
|
sortParam := c.DefaultQuery("sort", "name")
|
||||||
limitStr := c.Query("limit")
|
limitStr := c.Query("limit")
|
||||||
pageStr := c.Query("page")
|
pageStr := c.Query("page")
|
||||||
@@ -610,7 +591,9 @@ func listLogos(c *gin.Context) {
|
|||||||
args2 = append(args2, limit)
|
args2 = append(args2, limit)
|
||||||
if pageStr != "" {
|
if pageStr != "" {
|
||||||
if page, err := strconv.Atoi(pageStr); err == nil {
|
if page, err := strconv.Atoi(pageStr); err == nil {
|
||||||
if page < 1 { page = 1 }
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
offset := (page - 1) * limit
|
offset := (page - 1) * limit
|
||||||
limitClause2 += " OFFSET ?"
|
limitClause2 += " OFFSET ?"
|
||||||
args2 = append(args2, offset)
|
args2 = append(args2, offset)
|
||||||
@@ -643,10 +626,16 @@ func listLogos(c *gin.Context) {
|
|||||||
&logo.PrimaryFormat,
|
&logo.PrimaryFormat,
|
||||||
&logo.CreatedAt,
|
&logo.CreatedAt,
|
||||||
&logo.UpdatedAt,
|
&logo.UpdatedAt,
|
||||||
); err != nil { continue }
|
); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
logo.HasSVG = hasSVG2 == 1
|
logo.HasSVG = hasSVG2 == 1
|
||||||
logo.HasPNG = hasPNG2 == 1
|
logo.HasPNG = hasPNG2 == 1
|
||||||
if logo.HasPNG { logo.LogoURL = fmt.Sprintf("%s/logos/%s?format=png", baseURL, logo.ID) } else if logo.HasSVG { logo.LogoURL = fmt.Sprintf("%s/logos/%s?format=svg", baseURL, logo.ID) }
|
if logo.HasPNG {
|
||||||
|
logo.LogoURL = fmt.Sprintf("%s/logos/%s?format=png", baseURL, logo.ID)
|
||||||
|
} else if logo.HasSVG {
|
||||||
|
logo.LogoURL = fmt.Sprintf("%s/logos/%s?format=svg", baseURL, logo.ID)
|
||||||
|
}
|
||||||
nameN := removeDiacritics(strings.ToLower(logo.ClubName))
|
nameN := removeDiacritics(strings.ToLower(logo.ClubName))
|
||||||
cityN := removeDiacritics(strings.ToLower(logo.ClubCity))
|
cityN := removeDiacritics(strings.ToLower(logo.ClubCity))
|
||||||
if strings.Contains(nameN, normQ) || strings.Contains(cityN, normQ) || strings.Contains(strings.ToLower(logo.ID), strings.ToLower(q)) {
|
if strings.Contains(nameN, normQ) || strings.Contains(cityN, normQ) || strings.Contains(strings.ToLower(logo.ID), strings.ToLower(q)) {
|
||||||
@@ -706,12 +695,22 @@ func uploadLogo(c *gin.Context) {
|
|||||||
|
|
||||||
if clubName == "" {
|
if clubName == "" {
|
||||||
if club, err := fetchClubByID(id); err == nil && club != nil {
|
if club, err := fetchClubByID(id); err == nil && club != nil {
|
||||||
if club.Name != "" { clubName = club.Name }
|
if club.Name != "" {
|
||||||
if clubType == "" && club.Type != "" { clubType = club.Type }
|
clubName = club.Name
|
||||||
if clubCity == "" && club.City != "" { clubCity = club.City }
|
}
|
||||||
if clubWebsite == "" && club.Website != "" { clubWebsite = club.Website }
|
if clubType == "" && club.Type != "" {
|
||||||
|
clubType = club.Type
|
||||||
|
}
|
||||||
|
if clubCity == "" && club.City != "" {
|
||||||
|
clubCity = club.City
|
||||||
|
}
|
||||||
|
if clubWebsite == "" && club.Website != "" {
|
||||||
|
clubWebsite = club.Website
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if clubName == "" {
|
||||||
|
clubName = "Club " + id
|
||||||
}
|
}
|
||||||
if clubName == "" { clubName = "Club " + id }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get uploaded file
|
// Get uploaded file
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ func setupRoutes(r *gin.Engine) {
|
|||||||
clubs := r.Group("/clubs")
|
clubs := r.Group("/clubs")
|
||||||
{
|
{
|
||||||
clubs.GET("/search", searchClubs)
|
clubs.GET("/search", searchClubs)
|
||||||
clubs.GET("/search-with-logos", searchClubsWithLogos)
|
|
||||||
clubs.GET("/:id", getClub)
|
clubs.GET("/:id", getClub)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,75 +56,3 @@ if (document.readyState === 'loading') {
|
|||||||
initThemeToggle()
|
initThemeToggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global light/dark theme handling for Czech Clubs Logos frontend
|
|
||||||
|
|
||||||
const THEME_KEY = 'clublogos-theme'
|
|
||||||
|
|
||||||
function getPreferredTheme() {
|
|
||||||
try {
|
|
||||||
const stored = localStorage.getItem(THEME_KEY)
|
|
||||||
if (stored === 'light' || stored === 'dark') return stored
|
|
||||||
} catch (_) {}
|
|
||||||
|
|
||||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
|
|
||||||
return 'light'
|
|
||||||
}
|
|
||||||
return 'dark'
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyTheme(theme) {
|
|
||||||
const root = document.documentElement
|
|
||||||
const mode = theme === 'light' ? 'light' : 'dark'
|
|
||||||
|
|
||||||
root.classList.remove('theme-light', 'theme-dark', 'dark')
|
|
||||||
if (mode === 'light') {
|
|
||||||
root.classList.add('theme-light')
|
|
||||||
} else {
|
|
||||||
root.classList.add('theme-dark', 'dark')
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
localStorage.setItem(THEME_KEY, mode)
|
|
||||||
} catch (_) {}
|
|
||||||
|
|
||||||
const toggle = document.getElementById('themeToggle')
|
|
||||||
if (toggle) {
|
|
||||||
if (mode === 'light') {
|
|
||||||
toggle.innerHTML = `
|
|
||||||
<span class="inline-flex items-center gap-1">
|
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z" />
|
|
||||||
</svg>
|
|
||||||
<span class="hidden sm:inline">Tmavý režim</span>
|
|
||||||
</span>
|
|
||||||
`
|
|
||||||
} else {
|
|
||||||
toggle.innerHTML = `
|
|
||||||
<span class="inline-flex items-center gap-1">
|
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v2m0 14v2m9-9h-2M5 12H3m15.364-6.364l-1.414 1.414M8.05 17.95l-1.414 1.414m0-12.728L8.05 8.05m9.9 9.9l-1.414-1.414M12 8a4 4 0 100 8 4 4 0 000-8z" />
|
|
||||||
</svg>
|
|
||||||
<span class="hidden sm:inline">Světlý režim</span>
|
|
||||||
</span>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupThemeToggle() {
|
|
||||||
const toggle = document.getElementById('themeToggle')
|
|
||||||
if (!toggle) return
|
|
||||||
|
|
||||||
toggle.addEventListener('click', () => {
|
|
||||||
const isLight = document.documentElement.classList.contains('theme-light')
|
|
||||||
applyTheme(isLight ? 'dark' : 'light')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
const initial = getPreferredTheme()
|
|
||||||
applyTheme(initial)
|
|
||||||
setupThemeToggle()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
module club
|
module club
|
||||||
|
|
||||||
go 1.25.1
|
go 1.25.1
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
|
|
||||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
|
|
||||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
|
|
||||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
|
|
||||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
|
|||||||
Reference in New Issue
Block a user