package database import ( "context" "fmt" "os" "time" "5803025048/W04/internal/auth" "github.com/google/uuid" "github.com/jackc/pgx/v5" ) // Connect membuat koneksi ke PostgreSQL menggunakan env vars individual func Connect() (*pgx.Conn, error) { host := getEnvOrDefault("DB_HOST", "localhost") port := getEnvOrDefault("DB_PORT", "5432") user := os.Getenv("DB_USER") password := os.Getenv("DB_PASSWORD") dbname := os.Getenv("DB_NAME") sslmode := getEnvOrDefault("DB_SSLMODE", "disable") connStr := fmt.Sprintf( "host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", host, port, user, password, dbname, sslmode, ) conn, err := pgx.Connect(context.Background(), connStr) if err != nil { return nil, fmt.Errorf("unable to connect to database: %v", err) } return conn, nil } // CreateTables membuat tabel users, sessions, dan snippets jika belum ada func CreateTables(conn *pgx.Conn) error { queries := []string{ `CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, username TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL )`, `CREATE TABLE IF NOT EXISTS sessions ( token TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() )`, `CREATE TABLE IF NOT EXISTS snippets ( id TEXT PRIMARY KEY, title TEXT NOT NULL, content TEXT NOT NULL, user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE )`, } for _, q := range queries { _, err := conn.Exec(context.Background(), q) if err != nil { return fmt.Errorf("failed to create table: %v", err) } } // Otomatis masukkan data sample 2 user & session if err := SeedSampleData(conn); err != nil { fmt.Fprintf(os.Stderr, "Warning: failed to seed sample data: %v\n", err) } return nil } // SeedSampleData memasukkan data sample 2 user dan session ke database dan mencetak infonya langsung ke konsol VS Code func SeedSampleData(conn *pgx.Conn) error { ctx := context.Background() samples := []struct { username string password string }{ {username: "zagar", password: "password123"}, {username: "zagarnashrullah", password: "password12345"}, {username: "jiminyu", password: "password12345678"}, {username: "yj", password: "password12345678910"}, } fmt.Println("✅ INSERT DATA SAMPLE KE DATABASE (lebih dari 2 USERS):") for i, s := range samples { userID := uuid.New().String() hash, err := auth.HashPassword(s.password) if err != nil { return fmt.Errorf("failed to hash password for %s: %w", s.username, err) } err = conn.QueryRow(ctx, "INSERT INTO users (id, username, password_hash) VALUES ($1, $2, $3) ON CONFLICT (username) DO UPDATE SET password_hash = EXCLUDED.password_hash RETURNING id", userID, s.username, hash, ).Scan(&userID) if err != nil { return fmt.Errorf("failed to insert user %s: %w", s.username, err) } token, err := auth.GenerateToken() if err != nil { return fmt.Errorf("failed to generate session token for %s: %w", s.username, err) } _, err = conn.Exec(ctx, "INSERT INTO sessions (token, user_id, created_at) VALUES ($1, $2, $3)", token, userID, time.Now(), ) if err != nil { return fmt.Errorf("failed to insert session for %s: %w", s.username, err) } fmt.Printf("\n--- [User %d: %s] ---\n", i+1, s.username) fmt.Printf(" User ID : %s\n", userID) fmt.Printf(" Username : %s\n", s.username) fmt.Printf(" Password Plain: %s\n", s.password) fmt.Printf(" Password Hash : %s\n", hash) fmt.Printf(" Session Token : %s\n", token) } return nil } func getEnvOrDefault(key, defaultVal string) string { if val := os.Getenv(key); val != "" { return val } return defaultVal }