mirror of
https://github.com/skidoodle/ncore-stats.git
synced 2026-04-28 07:47:36 +02:00
5084c70295
- 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.
175 lines
5.0 KiB
Go
175 lines
5.0 KiB
Go
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!")
|
|
}
|