6de2455917
- Added GLOBAL_MARKETS_TITLE to all translation files - Updated footer with 12 markets (4 active + 8 upcoming) - Translated market section to: zh-cn, zh-tw, ja, ko, th, vi, id, ms, hi - Built and deployed to production - CloudFront invalidation: I3RTMXVFDJXWLG3SYX208OP1CC
93 lines
2.4 KiB
Go
93 lines
2.4 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
|
|
_ "github.com/lib/pq"
|
|
)
|
|
|
|
// Connect opens a PostgreSQL connection, verifies it, and runs AutoMigrate.
|
|
func Connect(url string) (*sql.DB, error) {
|
|
db, err := sql.Open("postgres", url)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("db open: %w", err)
|
|
}
|
|
|
|
db.SetMaxOpenConns(25)
|
|
db.SetMaxIdleConns(10)
|
|
db.SetConnMaxLifetime(5 * time.Minute)
|
|
|
|
if err := db.Ping(); err != nil {
|
|
db.Close()
|
|
return nil, fmt.Errorf("db ping: %w", err)
|
|
}
|
|
|
|
if err := autoMigrate(db); err != nil {
|
|
db.Close()
|
|
return nil, fmt.Errorf("db migrate: %w", err)
|
|
}
|
|
|
|
return db, nil
|
|
}
|
|
|
|
func autoMigrate(db *sql.DB) error {
|
|
stmts := []string{
|
|
`CREATE TABLE IF NOT EXISTS users (
|
|
id TEXT PRIMARY KEY,
|
|
email TEXT NOT NULL UNIQUE,
|
|
name TEXT NOT NULL DEFAULT '',
|
|
role TEXT NOT NULL DEFAULT 'user',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
last_login TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
)`,
|
|
|
|
`CREATE TABLE IF NOT EXISTS audit_logs (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE SET NULL,
|
|
action TEXT NOT NULL,
|
|
resource TEXT NOT NULL DEFAULT '',
|
|
ip TEXT NOT NULL DEFAULT '',
|
|
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
details TEXT NOT NULL DEFAULT ''
|
|
)`,
|
|
|
|
`CREATE TABLE IF NOT EXISTS modules (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL UNIQUE,
|
|
enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
icon TEXT NOT NULL DEFAULT '',
|
|
status TEXT NOT NULL DEFAULT 'stopped'
|
|
)`,
|
|
|
|
`CREATE TABLE IF NOT EXISTS settings (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT NOT NULL DEFAULT '',
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
)`,
|
|
|
|
// password_hash added after initial schema; idempotent on re-run.
|
|
`ALTER TABLE users ADD COLUMN IF NOT EXISTS password_hash TEXT NOT NULL DEFAULT ''`,
|
|
|
|
// Indexes for common query patterns.
|
|
`CREATE INDEX IF NOT EXISTS idx_audit_logs_user_id ON audit_logs(user_id)`,
|
|
`CREATE INDEX IF NOT EXISTS idx_audit_logs_timestamp ON audit_logs(timestamp DESC)`,
|
|
}
|
|
|
|
for _, s := range stmts {
|
|
if _, err := db.Exec(s); err != nil {
|
|
return fmt.Errorf("exec %q: %w", s[:min(40, len(s))], err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func min(a, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|