- Backend: Email producer (LavinMQ), EmailService interface - Backend: CRUD API for email_templates and email_settings - Backend: avatar_url field in users table + UpdateMyProfile support - Backend: StorageService for pre-signed URLs - NestJS: Email consumer with Nodemailer and Handlebars - Frontend: Email Templates admin pages (list/edit) - Frontend: Updated profileApi.uploadAvatar with pre-signed URL flow - Frontend: New /post-job public page (company registration + job creation wizard) - Migrations: 027_create_email_system.sql, 028_add_avatar_url_to_users.sql
105 lines
2.5 KiB
Go
105 lines
2.5 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/joho/godotenv"
|
|
_ "github.com/lib/pq"
|
|
)
|
|
|
|
func main() {
|
|
pwd, _ := os.Getwd()
|
|
log.Printf("Current Working Directory: %s", pwd)
|
|
|
|
if err := godotenv.Load(".env"); err != nil {
|
|
// Try loading from parent if not in root
|
|
if err := godotenv.Load("../.env"); err != nil {
|
|
log.Println("No .env file found")
|
|
}
|
|
}
|
|
|
|
dbURL := os.Getenv("DATABASE_URL")
|
|
if dbURL == "" {
|
|
host := os.Getenv("DB_HOST")
|
|
port := os.Getenv("DB_PORT")
|
|
user := os.Getenv("DB_USER")
|
|
pass := os.Getenv("DB_PASSWORD")
|
|
name := os.Getenv("DB_NAME")
|
|
ssl := os.Getenv("DB_SSLMODE")
|
|
if host != "" {
|
|
dbURL = fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=%s", user, pass, host, port, name, ssl)
|
|
} else {
|
|
// Last resort
|
|
dbURL = "postgres://postgres:postgres@localhost:5432/gohorsejobs?sslmode=disable"
|
|
}
|
|
}
|
|
|
|
db, err := sql.Open("postgres", dbURL)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// List of migrations to run (in order)
|
|
migrations := []string{
|
|
"024_create_external_services_credentials.sql",
|
|
"025_create_chat_tables.sql",
|
|
"026_create_system_settings.sql",
|
|
"027_create_email_system.sql",
|
|
"028_add_avatar_url_to_users.sql",
|
|
}
|
|
|
|
for _, migFile := range migrations {
|
|
log.Printf("Processing migration: %s", migFile)
|
|
|
|
// Try multiple paths
|
|
paths := []string{
|
|
"migrations/" + migFile,
|
|
"backend/migrations/" + migFile,
|
|
"../migrations/" + migFile,
|
|
"/home/yamamoto/lab/gohorsejobs/backend/migrations/" + migFile,
|
|
}
|
|
|
|
var content []byte
|
|
var readErr error
|
|
|
|
for _, p := range paths {
|
|
content, readErr = os.ReadFile(p)
|
|
if readErr == nil {
|
|
log.Printf("Found migration at: %s", p)
|
|
break
|
|
}
|
|
}
|
|
|
|
if content == nil {
|
|
log.Fatalf("Could not find migration file %s. Last error: %v", migFile, readErr)
|
|
}
|
|
|
|
statements := strings.Split(string(content), ";")
|
|
for _, stmt := range statements {
|
|
trimmed := strings.TrimSpace(stmt)
|
|
if trimmed == "" {
|
|
continue
|
|
}
|
|
log.Printf("Executing: %s", trimmed)
|
|
_, err = db.Exec(trimmed)
|
|
if err != nil {
|
|
if strings.Contains(err.Error(), "already exists") {
|
|
log.Printf("Warning (ignored): %v", err)
|
|
} else {
|
|
log.Printf("FAILED executing: %s\nError: %v", trimmed, err)
|
|
// Verify if we should stop. For now, continue best effort or fail?
|
|
// Use fatal for critical schema errors not "already exists"
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
log.Printf("Migration %s applied successfully", migFile)
|
|
}
|
|
|
|
fmt.Println("All requested migrations applied.")
|
|
}
|