package main import ( "context" "database/sql" "fmt" "os" tea "github.com/charmbracelet/bubbletea" _ "github.com/go-sql-driver/mysql" // MySQL Driver "golang.org/x/crypto/bcrypt" // CHANGE THIS to match your go.mod name + /dba "skyrama_clone_backend/backend/db" ) // ============================================================================ // 1. STATE DEFINITIONS // ============================================================================ type sessionState int const ( stateLogin sessionState = iota stateHomepage ) // ============================================================================ // 2. THE MASTER MODEL (The Router) // ============================================================================ type mainModel struct { state sessionState queries *db.Queries // <-- NEW: Holds our database queries login loginModel homepage homepageModel } func initialModel(q *db.Queries) mainModel { return mainModel{ state: stateLogin, queries: q, login: initialModell(), homepage: initialHomepageModel(), } } func (m mainModel) Init() tea.Cmd { return m.login.Init() } // ============================================================================ // 3. THE MASTER UPDATE (Traffic Cop & Authentication) // ============================================================================ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if keyMsg, ok := msg.(tea.KeyMsg); ok && keyMsg.String() == "ctrl+c" { return m, tea.Quit } //var cmd tea.Cmd var cmds []tea.Cmd ctx := context.Background() switch m.state { case stateLogin: newLoginModel, loginCmd := m.login.Update(msg) m.login = newLoginModel.(loginModel) cmds = append(cmds, loginCmd) // Did the user press Enter on the Submit button? if m.login.isSubmitted { username := m.login.inputs[0].Value() password := m.login.inputs[1].Value() // 1. Fetch the user from the database user, err := m.queries.GetUserByUsername(ctx, username) if err != nil { m.login.errorMessage = "User not found." m.login.isSubmitted = false // Reset so they can try again return m, nil } // 2. Verify the password hash err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)) if err != nil { m.login.errorMessage = "Invalid password." m.login.isSubmitted = false // Reset so they can try again return m, nil } // 3. Fetch their Airport Data // Note: user.ID is int32 in sqlc (based on schema) airport, err := m.queries.GetAirportByUserId(ctx, user.ID) if err != nil { m.login.errorMessage = "No airport found for this user." m.login.isSubmitted = false return m, nil } // 4. Fetch their Fleet Count // Note: airport.ID is uint32, GetPlayerFleet expects the correct type fleet, _ := m.queries.GetPlayerFleet(ctx, airport.ID) // 5. SUCCESS! Push real data to the homepage and switch screens m.homepage.playerName = user.Username m.homepage.cash = int(airport.Cash) m.homepage.fleetCount = len(fleet) m.state = stateHomepage cmds = append(cmds, m.homepage.Init()) } else if m.login.isCanceled { return m, tea.Quit } case stateHomepage: newHomepageModel, homeCmd := m.homepage.Update(msg) m.homepage = newHomepageModel.(homepageModel) cmds = append(cmds, homeCmd) if m.homepage.selected != "" { if m.homepage.selected == "🚪 Logout / Quit" { return m, tea.Quit } // Placeholder for other menus return m, tea.Quit } } return m, tea.Batch(cmds...) } func (m mainModel) View() string { switch m.state { case stateLogin: return m.login.View() case stateHomepage: return m.homepage.View() default: return "Error: Unknown state" } } // ============================================================================ // MAIN EXECUTION WITH DATABASE POOL // ============================================================================ func main() { // 1. Connect to MySQL Database // Ensure ?parseTime=true is present! dsn := "skyramaUser:Skyrama1234.@tcp(127.0.0.1:3306)/skyrama_clone?parseTime=true" dbConn, err := sql.Open("mysql", dsn) if err != nil { fmt.Printf("Error opening database pool: %v\n", err) os.Exit(1) } defer dbConn.Close() if err := dbConn.Ping(); err != nil { fmt.Printf("Database unreachable: %v\n", err) os.Exit(1) } // 2. Initialize sqlc queries queries := db.New(dbConn) // 3. Start the Bubble Tea UI, passing the database into the model p := tea.NewProgram(initialModel(queries), tea.WithAltScreen()) finalModel, err := p.Run() if err != nil { fmt.Printf("Fatal error starting SkyRama: %v", err) os.Exit(1) } fmt.Print("\033[H\033[2J") // Clear terminal on exit if m, ok := finalModel.(mainModel); ok { if m.state == stateHomepage && m.homepage.selected != "" && m.homepage.selected != "🚪 Logout / Quit" { fmt.Printf("You clicked '%s', which we will build next!\n", m.homepage.selected) } else { fmt.Println("Gracefully disconnected from SkyRama servers. Goodbye!") } } }