Files
w04_29_FarrelRevalino_database/internal/database/database.go
T
2026-09-28 18:27:16 +07:00

133 lines
3.6 KiB
Go

package database
import (
"context"
"fmt"
"os"
"time"
"5803025029/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: "Farrel", password: "123"},
{username: "Angel", password: "12345"},
{username: "Agus", password: "12345678"},
{username: "Budi", password: "1234567890"},
}
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
}