mirror of
https://github.com/skidoodle/ncore-stats.git
synced 2026-04-28 15:57:37 +02:00
Refactor database handling and API endpoints
- Removed fsnotify dependency and related file watching logic. - Introduced SQLite database for storing user profiles and history. - Created new API endpoints for fetching latest profiles and user history. - Migrated existing JSON data to the new database structure. - Simplified HTML and JavaScript for fetching and displaying profile data. - Improved error handling and logging throughout the application.
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
// ProfileData struct to match the structure in data.json
|
||||
type ProfileData struct {
|
||||
Owner string `json:"owner"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Rank int `json:"rank"`
|
||||
Upload string `json:"upload"`
|
||||
CurrentUpload string `json:"current_upload"`
|
||||
CurrentDownload string `json:"current_download"`
|
||||
Points int `json:"points"`
|
||||
SeedingCount int `json:"seeding_count"`
|
||||
}
|
||||
|
||||
var (
|
||||
db *sql.DB
|
||||
dbFile = "../data/ncore_stats.db"
|
||||
jsonFile = "../data/data.json"
|
||||
profilesFile = "../data/profiles.json"
|
||||
)
|
||||
|
||||
// initDB is the same function from the main app to set up the database.
|
||||
func initDB() {
|
||||
var err error
|
||||
if err := os.MkdirAll("./data", 0755); err != nil {
|
||||
log.Fatalf("Failed to create data directory: %v", err)
|
||||
}
|
||||
db, err = sql.Open("sqlite3", dbFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open database: %v", err)
|
||||
}
|
||||
// Create users table
|
||||
usersTableSQL := `
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"display_name" TEXT NOT NULL UNIQUE,
|
||||
"profile_id" TEXT NOT NULL
|
||||
);`
|
||||
if _, err = db.Exec(usersTableSQL); err != nil {
|
||||
log.Fatalf("Failed to create users table: %v", err)
|
||||
}
|
||||
// Create profile_history table
|
||||
profileHistoryTableSQL := `
|
||||
CREATE TABLE IF NOT EXISTS profile_history (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"timestamp" DATETIME NOT NULL,
|
||||
"rank" INTEGER,
|
||||
"upload" TEXT,
|
||||
"current_upload" TEXT,
|
||||
"current_download" TEXT,
|
||||
"points" INTEGER,
|
||||
"seeding_count" INTEGER,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
);`
|
||||
if _, err = db.Exec(profileHistoryTableSQL); err != nil {
|
||||
log.Fatalf("Failed to create profile_history table: %v", err)
|
||||
}
|
||||
log.Println("Database initialized successfully.")
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.Println("Starting data migration...")
|
||||
|
||||
// 1. Initialize the database and tables
|
||||
initDB()
|
||||
defer db.Close()
|
||||
|
||||
// 2. Begin a transaction
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to begin transaction: %v", err)
|
||||
}
|
||||
// Defer a rollback. If the transaction is committed, this is a no-op.
|
||||
// If something fails, this will undo all changes.
|
||||
defer tx.Rollback()
|
||||
|
||||
// --- Migrate Users ---
|
||||
log.Println("Reading profiles.json...")
|
||||
profFile, err := os.Open(profilesFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open profiles file: %v", err)
|
||||
}
|
||||
defer profFile.Close()
|
||||
|
||||
var profiles map[string]string
|
||||
if err := json.NewDecoder(profFile).Decode(&profiles); err != nil {
|
||||
log.Fatalf("Failed to decode profiles.json: %v", err)
|
||||
}
|
||||
|
||||
// This map will hold the new database ID for each user display name
|
||||
displayNameToID := make(map[string]int64)
|
||||
|
||||
userStmt, err := tx.Prepare("INSERT INTO users(display_name, profile_id) VALUES(?, ?)")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to prepare user insert statement: %v", err)
|
||||
}
|
||||
defer userStmt.Close()
|
||||
|
||||
log.Println("Migrating users to the database...")
|
||||
for name, id := range profiles {
|
||||
res, err := userStmt.Exec(name, id)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to insert user %s: %v", name, err)
|
||||
}
|
||||
newID, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get last insert ID for user %s: %v", name, err)
|
||||
}
|
||||
displayNameToID[name] = newID
|
||||
log.Printf(" > Migrated user: %s (New DB ID: %d)", name, newID)
|
||||
}
|
||||
log.Println("User migration complete.")
|
||||
|
||||
// --- Migrate History ---
|
||||
log.Println("Reading data.json...")
|
||||
histFile, err := os.Open(jsonFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open data file: %v", err)
|
||||
}
|
||||
defer histFile.Close()
|
||||
|
||||
byteValue, _ := io.ReadAll(histFile)
|
||||
var history []ProfileData
|
||||
if err := json.Unmarshal(byteValue, &history); err != nil {
|
||||
log.Fatalf("Failed to decode data.json: %v", err)
|
||||
}
|
||||
|
||||
historyStmt, err := tx.Prepare(`
|
||||
INSERT INTO profile_history(user_id, timestamp, rank, upload, current_upload, current_download, points, seeding_count)
|
||||
VALUES(?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to prepare history insert statement: %v", err)
|
||||
}
|
||||
defer historyStmt.Close()
|
||||
|
||||
log.Println("Migrating profile history to the database...")
|
||||
for i, record := range history {
|
||||
userID, ok := displayNameToID[record.Owner]
|
||||
if !ok {
|
||||
log.Printf("WARNING: Skipping history record for '%s' as they were not found in profiles.json.", record.Owner)
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := historyStmt.Exec(userID, record.Timestamp, record.Rank, record.Upload, record.CurrentUpload, record.CurrentDownload, record.Points, record.SeedingCount)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to insert history record for %s: %v", record.Owner, err)
|
||||
}
|
||||
if (i+1)%100 == 0 { // Log progress every 100 records
|
||||
log.Printf(" > Migrated %d history records...", i+1)
|
||||
}
|
||||
}
|
||||
log.Println("History migration complete.")
|
||||
|
||||
// 3. Commit the transaction
|
||||
if err := tx.Commit(); err != nil {
|
||||
log.Fatalf("Failed to commit transaction: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println("\n✅ MIGRATION COMPLETED SUCCESSFULLY!")
|
||||
}
|
||||
Reference in New Issue
Block a user