mirror of
https://github.com/Alexander-D-Karpov/webring.git
synced 2026-03-16 22:07:41 +03:00
203 lines
5.2 KiB
Go
203 lines
5.2 KiB
Go
package dashboard
|
|
|
|
import (
|
|
"database/sql"
|
|
"html/template"
|
|
"log"
|
|
"math"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"sync"
|
|
"webring/internal/favicon"
|
|
|
|
"webring/internal/models"
|
|
|
|
"github.com/gorilla/mux"
|
|
)
|
|
|
|
var (
|
|
templates *template.Template
|
|
templatesMu sync.RWMutex
|
|
)
|
|
|
|
func InitTemplates(t *template.Template) {
|
|
templatesMu.Lock()
|
|
defer templatesMu.Unlock()
|
|
templates = t
|
|
}
|
|
|
|
func RegisterHandlers(r *mux.Router, db *sql.DB) {
|
|
dashboardRouter := r.PathPrefix("/dashboard").Subrouter()
|
|
dashboardRouter.Use(basicAuthMiddleware)
|
|
|
|
dashboardRouter.HandleFunc("", dashboardHandler(db)).Methods("GET")
|
|
dashboardRouter.HandleFunc("/add", addSiteHandler(db)).Methods("POST")
|
|
dashboardRouter.HandleFunc("/remove/{id}", removeSiteHandler(db)).Methods("POST")
|
|
dashboardRouter.HandleFunc("/update/{id}", updateSiteHandler(db)).Methods("POST")
|
|
}
|
|
|
|
func basicAuthMiddleware(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
user, pass, ok := r.BasicAuth()
|
|
if !ok || user != os.Getenv("DASHBOARD_USER") || pass != os.Getenv("DASHBOARD_PASSWORD") {
|
|
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
func dashboardHandler(db *sql.DB) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
templatesMu.RLock()
|
|
t := templates
|
|
templatesMu.RUnlock()
|
|
|
|
if t == nil {
|
|
log.Println("Templates not initialized")
|
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
sites, err := getAllSites(db)
|
|
if err != nil {
|
|
log.Printf("Error fetching sites: %v", err)
|
|
http.Error(w, "Error fetching sites", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
err = t.ExecuteTemplate(w, "dashboard.html", sites)
|
|
if err != nil {
|
|
log.Printf("Error rendering template: %v", err)
|
|
http.Error(w, "Error rendering template", http.StatusInternalServerError)
|
|
}
|
|
}
|
|
}
|
|
|
|
func addSiteHandler(db *sql.DB) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
idStr := r.FormValue("id")
|
|
name := r.FormValue("name")
|
|
url := r.FormValue("url")
|
|
|
|
if idStr == "" || name == "" || url == "" {
|
|
http.Error(w, "ID, Name, and URL are required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
id, err := strconv.Atoi(idStr)
|
|
if err != nil {
|
|
http.Error(w, "Invalid ID", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
result, err := db.Exec("INSERT INTO sites (id, name, url) VALUES ($1, $2, $3)", id, name, url)
|
|
if err != nil {
|
|
http.Error(w, "Error adding site", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
insertedID, _ := result.LastInsertId()
|
|
|
|
// Start a goroutine to fetch and store the favicon
|
|
go func() {
|
|
mediaFolder := os.Getenv("MEDIA_FOLDER")
|
|
if mediaFolder == "" {
|
|
mediaFolder = "media"
|
|
}
|
|
|
|
faviconPath, err := favicon.GetAndStoreFavicon(url, mediaFolder, int(insertedID))
|
|
if err != nil {
|
|
log.Printf("Error retrieving favicon for %s: %v", url, err)
|
|
return
|
|
}
|
|
|
|
_, err = db.Exec("UPDATE sites SET favicon = $1 WHERE id = $2", faviconPath, insertedID)
|
|
if err != nil {
|
|
log.Printf("Error updating favicon for site %d: %v", insertedID, err)
|
|
}
|
|
}()
|
|
|
|
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
|
}
|
|
}
|
|
|
|
func removeSiteHandler(db *sql.DB) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
id := mux.Vars(r)["id"]
|
|
_, err := db.Exec("DELETE FROM sites WHERE id = $1", id)
|
|
if err != nil {
|
|
http.Error(w, "Error removing site", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
|
}
|
|
}
|
|
|
|
func updateSiteHandler(db *sql.DB) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
id := mux.Vars(r)["id"]
|
|
name := r.FormValue("name")
|
|
url := r.FormValue("url")
|
|
|
|
if name == "" || url == "" {
|
|
http.Error(w, "Name and URL are required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
_, err := db.Exec("UPDATE sites SET name = $1, url = $2 WHERE id = $3", name, url, id)
|
|
if err != nil {
|
|
http.Error(w, "Error updating site", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
go func() {
|
|
mediaFolder := os.Getenv("MEDIA_FOLDER")
|
|
if mediaFolder == "" {
|
|
mediaFolder = "media"
|
|
}
|
|
|
|
siteId, _ := strconv.Atoi(id)
|
|
faviconPath, err := favicon.GetAndStoreFavicon(url, mediaFolder, siteId)
|
|
if err != nil {
|
|
log.Printf("Error retrieving favicon for %s: %v", url, err)
|
|
return
|
|
}
|
|
|
|
_, err = db.Exec("UPDATE sites SET favicon = $1 WHERE id = $2", faviconPath, id)
|
|
if err != nil {
|
|
log.Printf("Error updating favicon for site %d: %v", id, err)
|
|
}
|
|
}()
|
|
|
|
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
|
}
|
|
}
|
|
|
|
func getAllSites(db *sql.DB) ([]models.Site, error) {
|
|
rows, err := db.Query("SELECT id, name, url, is_up, last_check, favicon FROM sites ORDER BY id")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func(rows *sql.Rows) {
|
|
err := rows.Close()
|
|
if err != nil {
|
|
log.Printf("Error closing rows: %v", err)
|
|
}
|
|
}(rows)
|
|
|
|
var sites []models.Site
|
|
for rows.Next() {
|
|
var site models.Site
|
|
err := rows.Scan(&site.ID, &site.Name, &site.URL, &site.IsUp, &site.LastCheck, &site.Favicon)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
site.LastCheck = math.Round(site.LastCheck * 1000)
|
|
sites = append(sites, site)
|
|
}
|
|
return sites, nil
|
|
}
|