skyrama-clone/backend/server/main.go

182 lines
4.9 KiB
Go
Raw Permalink Normal View History

2026-05-25 20:07:14 +03:00
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!")
}
}
}