You've already forked speedtest-go
cd20f44d20
- IP detection: Cloudflare IPv6, ULA IPv6, proxy header chain, offline GeoIP DB - Database: add SQLite (pure Go, no CGo) and MSSQL backends - API: add JSON result sharing endpoint and ID obfuscation - Frontend: add modern CSS design, design switcher, favicon - Compatibility: ?cors parameter support, human-friendly distance rounding - Update Go to 1.21, add modernc.org/sqlite and maxminddb deps
96 lines
3.0 KiB
Go
96 lines
3.0 KiB
Go
package sqlite
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
|
|
"github.com/librespeed/speedtest-go/database/schema"
|
|
|
|
_ "modernc.org/sqlite"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
type SQLite struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
func Open(databaseFile string) *SQLite {
|
|
conn, err := sql.Open("sqlite", databaseFile)
|
|
if err != nil {
|
|
log.Fatalf("Cannot open SQLite database: %s", err)
|
|
}
|
|
|
|
// Enable WAL mode for better concurrent performance
|
|
if _, err := conn.Exec("PRAGMA journal_mode=WAL"); err != nil {
|
|
log.Warnf("Failed to set SQLite journal mode to WAL: %s", err)
|
|
}
|
|
|
|
// Create table if not exists (matching the PHP SQLite auto-creation behavior)
|
|
stmt := `CREATE TABLE IF NOT EXISTS speedtest_users (
|
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
ip TEXT NOT NULL,
|
|
ispinfo TEXT,
|
|
extra TEXT,
|
|
ua TEXT NOT NULL,
|
|
lang TEXT NOT NULL,
|
|
dl TEXT,
|
|
ul TEXT,
|
|
ping TEXT,
|
|
jitter TEXT,
|
|
log TEXT,
|
|
uuid TEXT
|
|
);`
|
|
if _, err := conn.Exec(stmt); err != nil {
|
|
log.Fatalf("Failed to create speedtest_users table: %s", err)
|
|
}
|
|
|
|
return &SQLite{db: conn}
|
|
}
|
|
|
|
func (p *SQLite) Insert(data *schema.TelemetryData) error {
|
|
var existingID int
|
|
// Check for duplicate UUID first
|
|
err := p.db.QueryRow(`SELECT id FROM speedtest_users WHERE uuid = ?`, data.UUID).Scan(&existingID)
|
|
if err == nil {
|
|
// Record with this UUID already exists - skip insert
|
|
return nil
|
|
}
|
|
|
|
stmt := `INSERT INTO speedtest_users (ip, ispinfo, extra, ua, lang, dl, ul, ping, jitter, log, uuid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`
|
|
_, err = p.db.Exec(stmt, data.IPAddress, data.ISPInfo, data.Extra, data.UserAgent, data.Language, data.Download, data.Upload, data.Ping, data.Jitter, data.Log, data.UUID)
|
|
return err
|
|
}
|
|
|
|
func (p *SQLite) FetchByUUID(uuid string) (*schema.TelemetryData, error) {
|
|
var record schema.TelemetryData
|
|
row := p.db.QueryRow(`SELECT * FROM speedtest_users WHERE uuid = ?`, uuid)
|
|
if row != nil {
|
|
var id int
|
|
if err := row.Scan(&id, &record.Timestamp, &record.IPAddress, &record.ISPInfo, &record.Extra, &record.UserAgent, &record.Language, &record.Download, &record.Upload, &record.Ping, &record.Jitter, &record.Log, &record.UUID); err != nil {
|
|
return nil, fmt.Errorf("sqlite fetch by uuid: %w", err)
|
|
}
|
|
}
|
|
return &record, nil
|
|
}
|
|
|
|
func (p *SQLite) FetchLast100() ([]schema.TelemetryData, error) {
|
|
var records []schema.TelemetryData
|
|
rows, err := p.db.Query(`SELECT * FROM speedtest_users ORDER BY timestamp DESC LIMIT 100;`)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("sqlite fetch last 100: %w", err)
|
|
}
|
|
if rows != nil {
|
|
defer rows.Close()
|
|
for rows.Next() {
|
|
var record schema.TelemetryData
|
|
var id int
|
|
if err := rows.Scan(&id, &record.Timestamp, &record.IPAddress, &record.ISPInfo, &record.Extra, &record.UserAgent, &record.Language, &record.Download, &record.Upload, &record.Ping, &record.Jitter, &record.Log, &record.UUID); err != nil {
|
|
return nil, fmt.Errorf("sqlite scan row: %w", err)
|
|
}
|
|
records = append(records, record)
|
|
}
|
|
}
|
|
return records, nil
|
|
}
|