00c1839635
- Replace go-sqlite3 with go-sql-driver/mysql (pure Go, no CGO needed)
- Add joho/godotenv for .env file loading
- Rewrite database/db.go with MySQL-compatible DDL (BIGINT AUTO_INCREMENT, InnoDB, utf8mb4)
- Convert all SQLite-specific SQL: INSERT OR IGNORE → INSERT IGNORE,
INSERT OR REPLACE → INSERT ... ON DUPLICATE KEY UPDATE,
datetime('now') → NOW(), date arithmetic → DATE_SUB()
- Add MySQL config fields to config.go (host/port/user/password/database)
- Add .env.example with connection parameter template
- Update Dockerfile: remove CGO/gcc/musl dependency, smaller build
- Update docker-compose.yml: add MySQL service container with healthcheck
- Update CLAUDE.md documentation
116 lines
3.1 KiB
Go
116 lines
3.1 KiB
Go
package database
|
|
|
|
import (
|
|
"database/sql"
|
|
"log"
|
|
|
|
_ "github.com/go-sql-driver/mysql"
|
|
|
|
"github.com/HoHD/PR-Helper/models"
|
|
)
|
|
|
|
type DB struct {
|
|
conn *sql.DB
|
|
}
|
|
|
|
func New(dsn string) (*DB, error) {
|
|
conn, err := sql.Open("mysql", dsn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := conn.Ping(); err != nil {
|
|
return nil, err
|
|
}
|
|
db := &DB{conn: conn}
|
|
if err := db.migrate(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := db.seedDefaults(); err != nil {
|
|
return nil, err
|
|
}
|
|
return db, nil
|
|
}
|
|
|
|
func (db *DB) Close() error {
|
|
return db.conn.Close()
|
|
}
|
|
|
|
func (db *DB) Conn() *sql.DB {
|
|
return db.conn
|
|
}
|
|
|
|
func (db *DB) migrate() error {
|
|
stmts := []string{
|
|
`CREATE TABLE IF NOT EXISTS settings (
|
|
key VARCHAR(255) PRIMARY KEY,
|
|
value TEXT NOT NULL
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,
|
|
`CREATE TABLE IF NOT EXISTS users (
|
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
email VARCHAR(255) NOT NULL UNIQUE,
|
|
password_hash TEXT NOT NULL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,
|
|
`CREATE TABLE IF NOT EXISTS user_settings (
|
|
user_id BIGINT NOT NULL,
|
|
key VARCHAR(255) NOT NULL,
|
|
value TEXT NOT NULL,
|
|
PRIMARY KEY (user_id, key),
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,
|
|
`CREATE TABLE IF NOT EXISTS repositories (
|
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
user_id BIGINT,
|
|
url TEXT NOT NULL,
|
|
local_path TEXT NOT NULL,
|
|
size_bytes BIGINT DEFAULT 0,
|
|
cloned_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
last_used DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,
|
|
`CREATE TABLE IF NOT EXISTS analyses (
|
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
user_id BIGINT,
|
|
repo_id BIGINT,
|
|
type VARCHAR(50) NOT NULL,
|
|
base_ref TEXT NOT NULL,
|
|
head_ref TEXT NOT NULL,
|
|
result LONGTEXT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (repo_id) REFERENCES repositories(id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,
|
|
`CREATE TABLE IF NOT EXISTS review_notes (
|
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
user_id BIGINT,
|
|
analysis_id BIGINT,
|
|
scope VARCHAR(50) NOT NULL,
|
|
scope_key TEXT NOT NULL,
|
|
content TEXT NOT NULL,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (analysis_id) REFERENCES analyses(id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`,
|
|
}
|
|
for _, s := range stmts {
|
|
if _, err := db.conn.Exec(s); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (db *DB) seedDefaults() error {
|
|
for key, val := range models.DefaultSettings {
|
|
_, err := db.conn.Exec(
|
|
`INSERT IGNORE INTO settings (key, value) VALUES (?, ?)`, key, val,
|
|
)
|
|
if err != nil {
|
|
log.Printf("warning: failed to seed setting %s: %v", key, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|