| package database |
|
|
| import ( |
| "database/sql" |
| "fmt" |
| "log" |
|
|
| _ "modernc.org/sqlite" |
| ) |
|
|
| type DB struct { |
| *sql.DB |
| } |
|
|
| |
| |
| func Connect(dbPath string) (*DB, error) { |
| db, err := sql.Open("sqlite", dbPath) |
| if err != nil { |
| return nil, fmt.Errorf("failed to open sqlite: %w", err) |
| } |
|
|
| |
| pragmas := []string{ |
| "PRAGMA journal_mode=WAL;", |
| "PRAGMA foreign_keys=ON;", |
| "PRAGMA busy_timeout=5000;", |
| } |
| for _, p := range pragmas { |
| if _, err := db.Exec(p); err != nil { |
| return nil, fmt.Errorf("pragma failed (%s): %w", p, err) |
| } |
| } |
|
|
| if err := db.Ping(); err != nil { |
| return nil, fmt.Errorf("failed to ping sqlite: %w", err) |
| } |
|
|
| log.Printf("SQLite connected: %s", dbPath) |
| return &DB{db}, nil |
| } |
|
|
| |
| func (db *DB) Migrate() error { |
| query := ` |
| CREATE TABLE IF NOT EXISTS short_links ( |
| id INTEGER PRIMARY KEY AUTOINCREMENT, |
| original_url TEXT NOT NULL, |
| creepy_slug TEXT NOT NULL UNIQUE, |
| full_short_url TEXT NOT NULL, |
| destruction_level INTEGER NOT NULL DEFAULT 3 |
| CHECK (destruction_level BETWEEN 1 AND 5), |
| click_count INTEGER NOT NULL DEFAULT 0, |
| created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| last_clicked_at DATETIME |
| ); |
| |
| CREATE UNIQUE INDEX IF NOT EXISTS idx_short_links_slug |
| ON short_links(creepy_slug); |
| |
| CREATE INDEX IF NOT EXISTS idx_short_links_created_at |
| ON short_links(created_at DESC); |
| ` |
|
|
| if _, err := db.Exec(query); err != nil { |
| return fmt.Errorf("migration failed: %w", err) |
| } |
|
|
| log.Println("SQLite migration complete") |
| return nil |
| } |