migrate to SQLite and prepare for MCP deployment

Switch from PostgreSQL to SQLite (modernc.org/sqlite, pure Go) for
simpler deployment on the MCP platform. Fix URL normalization to
preserve query parameters so sites like YouTube deduplicate correctly.
Add Dockerfile, Makefile, and MCP service definition. Add pg2sqlite
migration tool. Support $PORT env var for MCP port assignment.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-27 16:18:37 -07:00
parent 25e38814a2
commit 0fa85cb300
14 changed files with 363 additions and 126 deletions

View File

@@ -2,48 +2,38 @@ package links
import (
"context"
"database/sql"
"fmt"
"git.wntrmute.dev/kyle/goutils/config"
"github.com/jackc/pgx/v4/pgxpool"
_ "modernc.org/sqlite"
)
const (
// The keys are all set up as constants to avoid typos and
// runtime surprises. I've prefixed the names with k, which
// perhaps counterintuitively is not for 'kyle' but for 'key'.
kDriver = "DB_ENGINE"
kName = "DB_NAME"
kUser = "DB_USER"
kPass = "DB_PASSWORD"
kHost = "DB_HOST"
kPort = "DB_PORT"
defaultDriver = "postgres"
defaultName = "kls"
defaultUser = "kls"
defaultPort = "5432"
kDBPath = "DB_PATH"
defaultPath = "kls.db"
)
func connString(user, pass, host, name, port string) string {
return fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=verify-full",
user, pass, host, port, name)
}
const createTable = `CREATE TABLE IF NOT EXISTS urls (
id TEXT PRIMARY KEY,
url TEXT NOT NULL,
nurl TEXT NOT NULL,
short TEXT NOT NULL UNIQUE,
created_at TEXT NOT NULL
)`
// Connect will try to open a connection to the database using the
// standard configuration vars, etc.
func Connect(ctx context.Context) (*pgxpool.Pool, error) {
driver := config.GetDefault(kDriver, defaultDriver)
if driver != defaultDriver {
return nil, fmt.Errorf("database: unsupported driver %s", driver)
// Connect opens the SQLite database and ensures the schema exists.
func Connect(ctx context.Context) (*sql.DB, error) {
path := config.GetDefault(kDBPath, defaultPath)
db, err := sql.Open("sqlite", path)
if err != nil {
return nil, fmt.Errorf("database: %w", err)
}
user := config.GetDefault(kUser, defaultUser)
pass := config.Get(kPass)
host := config.Get(kHost)
name := config.GetDefault(kName, defaultName)
port := config.GetDefault(kPort, defaultPort)
cstr := connString(user, pass, host, name, port)
if _, err := db.ExecContext(ctx, createTable); err != nil {
db.Close()
return nil, fmt.Errorf("database: schema init: %w", err)
}
return pgxpool.Connect(ctx, cstr)
return db, nil
}