Migrations and DB optimization
This commit is contained in:
parent
4ad17de80a
commit
2ade3e1380
4 changed files with 95 additions and 35 deletions
|
@ -29,6 +29,7 @@ func RegisterUserHandler(c echo.Context) error {
|
||||||
user := lib.User{Name: name, Email: email, Password: hashedPassword}
|
user := lib.User{Name: name, Email: email, Password: hashedPassword}
|
||||||
println("User: ", user.Name, user.Email, user.Password)
|
println("User: ", user.Name, user.Email, user.Password)
|
||||||
if err := lib.SaveUser(lib.GetDBPool(), &user); err != nil {
|
if err := lib.SaveUser(lib.GetDBPool(), &user); err != nil {
|
||||||
|
println("Error saving user: ", err.Error())
|
||||||
return c.JSON(http.StatusInternalServerError, "Failed to save user")
|
return c.JSON(http.StatusInternalServerError, "Failed to save user")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
89
lib/db.go
89
lib/db.go
|
@ -2,6 +2,8 @@ package lib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v4/pgxpool"
|
"github.com/jackc/pgx/v4/pgxpool"
|
||||||
|
@ -9,11 +11,24 @@ import (
|
||||||
|
|
||||||
var dbPool *pgxpool.Pool
|
var dbPool *pgxpool.Pool
|
||||||
|
|
||||||
|
// Migration represents a database migration.
|
||||||
|
type Migration struct {
|
||||||
|
Version string
|
||||||
|
SQL string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateNewID generates a DB with the format "prefix_randomstring".
|
||||||
|
func GenerateNewID(prefix string) string {
|
||||||
|
randomBytes := make([]byte, 16)
|
||||||
|
if _, err := rand.Read(randomBytes); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s_%s", prefix, hex.EncodeToString(randomBytes))
|
||||||
|
}
|
||||||
|
|
||||||
// Initializes the global database connection pool.
|
// Initializes the global database connection pool.
|
||||||
func InitializeDBPool(host, user, password, dbname string, port int) error {
|
func InitializeDBPool(host, user, password, dbname string, port int) error {
|
||||||
// Construct the connection string using the provided parameters
|
|
||||||
connString := fmt.Sprintf("postgres://%s:%s@%s:%d/%s", user, password, host, port, dbname)
|
connString := fmt.Sprintf("postgres://%s:%s@%s:%d/%s", user, password, host, port, dbname)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
dbPool, err = pgxpool.Connect(context.Background(), connString)
|
dbPool, err = pgxpool.Connect(context.Background(), connString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -23,13 +38,20 @@ func InitializeDBPool(host, user, password, dbname string, port int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitializeSchema(dbPool *pgxpool.Pool) error {
|
func InitializeSchema(dbPool *pgxpool.Pool) error {
|
||||||
const schemaSQL = `
|
migrations := []Migration{
|
||||||
|
{
|
||||||
|
Version: "1_create_users_table",
|
||||||
|
SQL: `
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
id SERIAL PRIMARY KEY,
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
name VARCHAR(255) NOT NULL,
|
name VARCHAR(255) NOT NULL,
|
||||||
email VARCHAR(255) UNIQUE NOT NULL,
|
email VARCHAR(255) UNIQUE NOT NULL,
|
||||||
password VARCHAR(255) NOT NULL
|
password VARCHAR(255) NOT NULL
|
||||||
);
|
);`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Version: "2_create_rooms_table",
|
||||||
|
SQL: `
|
||||||
CREATE TABLE IF NOT EXISTS rooms (
|
CREATE TABLE IF NOT EXISTS rooms (
|
||||||
id TEXT NOT NULL PRIMARY KEY,
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
@ -38,14 +60,63 @@ func InitializeSchema(dbPool *pgxpool.Pool) error {
|
||||||
topicName TEXT,
|
topicName TEXT,
|
||||||
visible BOOLEAN NOT NULL DEFAULT false,
|
visible BOOLEAN NOT NULL DEFAULT false,
|
||||||
scale TEXT NOT NULL DEFAULT '0.5,1,2,3,5'
|
scale TEXT NOT NULL DEFAULT '0.5,1,2,3,5'
|
||||||
);
|
);`,
|
||||||
`
|
},
|
||||||
// TODO: Add more tables to the schema
|
{
|
||||||
|
Version: "3_add_foreign_key_to_rooms",
|
||||||
|
SQL: `
|
||||||
|
ALTER TABLE rooms
|
||||||
|
ADD CONSTRAINT fk_user_id
|
||||||
|
FOREIGN KEY (userId) REFERENCES users(id);`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Version: "4_create_index_on_users_email",
|
||||||
|
SQL: `
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Version: "5_create_index_on_rooms_userid",
|
||||||
|
SQL: `
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rooms_userid ON rooms(userId);
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
_, err := dbPool.Exec(context.Background(), schemaSQL)
|
// Ensure the migrations table exists
|
||||||
|
_, err := dbPool.Exec(context.Background(), `
|
||||||
|
CREATE TABLE IF NOT EXISTS migrations (
|
||||||
|
version VARCHAR(255) PRIMARY KEY
|
||||||
|
);
|
||||||
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply each migration
|
||||||
|
for _, migration := range migrations {
|
||||||
|
// Check if this migration has already been applied
|
||||||
|
var exists bool
|
||||||
|
err := dbPool.QueryRow(context.Background(), "SELECT EXISTS(SELECT 1 FROM migrations WHERE version = $1)", migration.Version).Scan(&exists)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
// Apply the migration
|
||||||
|
_, err = dbPool.Exec(context.Background(), migration.SQL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark this migration as applied
|
||||||
|
_, err = dbPool.Exec(context.Background(), "INSERT INTO migrations (version) VALUES ($1)", migration.Version)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
lib/room.go
14
lib/room.go
|
@ -2,10 +2,7 @@ package lib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v4/pgxpool"
|
"github.com/jackc/pgx/v4/pgxpool"
|
||||||
|
@ -21,15 +18,6 @@ type Room struct {
|
||||||
Scale string
|
Scale string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateNewID generates a new room ID with the format "room_randomstring".
|
|
||||||
func GenerateNewID() string {
|
|
||||||
randomBytes := make([]byte, 16)
|
|
||||||
if _, err := rand.Read(randomBytes); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("room_%s", hex.EncodeToString(randomBytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateRoom creates a new room in the database.
|
// CreateRoom creates a new room in the database.
|
||||||
func CreateRoom(dbPool *pgxpool.Pool, userID, roomName string) (*Room, error) {
|
func CreateRoom(dbPool *pgxpool.Pool, userID, roomName string) (*Room, error) {
|
||||||
if dbPool == nil {
|
if dbPool == nil {
|
||||||
|
@ -37,7 +25,7 @@ func CreateRoom(dbPool *pgxpool.Pool, userID, roomName string) (*Room, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a new ID for the room
|
// Generate a new ID for the room
|
||||||
newRoomID := GenerateNewID()
|
newRoomID := GenerateNewID("room")
|
||||||
|
|
||||||
// Insert the new room into the database
|
// Insert the new room into the database
|
||||||
_, err := dbPool.Exec(context.Background(), "INSERT INTO rooms (id, userid, roomname, topicname, visible, scale) VALUES ($1, $2, $3, $4, $5, $6)", newRoomID, userID, roomName, "My First Topic", false, "0.5,1,2,3,5")
|
_, err := dbPool.Exec(context.Background(), "INSERT INTO rooms (id, userid, roomname, topicname, visible, scale) VALUES ($1, $2, $3, $4, $5, $6)", newRoomID, userID, roomName, "My First Topic", false, "0.5,1,2,3,5")
|
||||||
|
|
|
@ -65,7 +65,7 @@ func SaveUser(dbPool *pgxpool.Pool, user *User) error {
|
||||||
return errors.New("database connection pool is not initialized")
|
return errors.New("database connection pool is not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
commandTag, err := dbPool.Exec(context.Background(), "INSERT INTO users (name, email, password) VALUES ($1, $2, $3)", user.Name, user.Email, user.Password)
|
commandTag, err := dbPool.Exec(context.Background(), "INSERT INTO users (id, name, email, password) VALUES ($1, $2, $3, $4)", GenerateNewID("user"), user.Name, user.Email, user.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue