package main import ( "net/http" "log" "sync" "regexp" "html/template" "database/sql" _ "github.com/go-sql-driver/mysql" "fmt" "encoding/json" // "io" "strconv" "bytes" ) type Page struct { tpl *template.Template Title string Name string } type Borrower struct { Id int `json:"id"` Credit int `json:"credit"` Income int `json:"income"` Num int `json:"num"` } type FeeTemplate struct { Id int `json:"id"` User int `json:"user"` Branch int `json:"branch"` Amount int `json:"amount"` Perc int `json:"perc"` Type string `json:"type"` Notes string `json:"notes"` Name string `json:"name"` Category string `json:"category"` Auto bool `json:"auto"` } type Fee struct { Id int `json:"id"` LoanId int `json:"loan_id"` Amount int `json:"amount"` Perc int `json:"perc"` Type string `json:"type"` Notes string `json:"notes"` Name string `json:"name"` Category string `json:"category"` } type LoanType struct { Id int `json:"id"` User int `json:"user"` Branch int `json:"branch"` Name string `json:"name"` } type Loan struct { Id int `json:id` EstimateId int `json:estimate_id` Type LoanType `json:"loanType"` Amount int `json:"loanAmount"` Term int `json:"term"` Ltv int `json:"ltv"` Dti int `json:"dti"` Hoi int `json:"hoi"` Interest int `json:"interest"` Lender string `json:"lender"` MiName string `json:"miName"` MiAmount int `json:"miAmount"` Fees []Fee `json:"fees"` Name string `json:"name"` } type Estimate struct { Id int `json:"id"` User int `json:"user"` Borrower Borrower `json:"borrower"` Transaction string `json:"transaction"` Price int `json:"price"` Property string `json:"property"` Occupancy string `json:"occupancy"` Zip string `json:"zip"` Pud bool `json:"pud"` Loans []Loan `json:"loans"` } var ( regexen = make(map[string]*regexp.Regexp) relock sync.Mutex address = "127.0.0.1:8001" ) var paths = map[string]string { "home": "home.tpl", "terms": "terms.tpl", "app": "app.tpl", } var pages = map[string]Page { "home": cache("home", "Home"), "terms": cache("terms", "Terms and Conditions"), "app": cache("app", "App"), } func cache(name string, title string) Page { var p = []string{"master.tpl", paths[name]} tpl := template.Must(template.ParseFiles(p...)) return Page{tpl: tpl, Title: title, Name: name, } } func (page Page) Render(w http.ResponseWriter) { err := page.tpl.Execute(w, page) if err != nil { log.Print(err) } } func match(path, pattern string, args *[]string) bool { relock.Lock() defer relock.Unlock() regex := regexen[pattern] if regex == nil { regex = regexp.MustCompile("^" + pattern + "$") regexen[pattern] = regex } matches := regex.FindStringSubmatch(path) if len(matches) <= 0 { return false } *args = matches[1:] return true } func getLoanType( db *sql.DB, user int, branch int, isUser bool) ([]LoanType, error) { var loans []LoanType // Should be changed to specify user rows, err := db.Query(`SELECT * FROM loan_type WHERE user_id = ? AND branch_id = ? ` + "OR (user_id = 0 AND branch_id = 0)", user, branch) if err != nil { return nil, fmt.Errorf("loan_type error: %v", err) } defer rows.Close() for rows.Next() { var loan LoanType if err := rows.Scan( &loan.Id, &loan.User, &loan.Branch, &loan.Name) err != nil { log.Printf("Error occured fetching loan: %v", err) return nil, fmt.Errorf("Error occured fetching loan: %v", err) } loans = append(loans, loan) } log.Printf("The loans: %v", loans) return loans, nil } func getEstimate(db *sql.DB, id int) (Estimate, error) { var estimate Estimate // Inner join should always be valid because a borrower is a required // foreign key. row := db.QueryRow( "SELECT * FROM estimate "+ "WHERE id = ? " + "INNER JOIN borrower ON estimate.borrower = borrower.id", id) if err := row.Scan( &estimate.Id, &estimate.User, &estimate.Borrower.Id, &estimate.Transaction, &estimate.Price, &estimate.Property, &estimate.Occupancy, &estimate.Zip, &estimate.Pud, ) err != nil { return estimate, fmt.Errorf("Estimate scanning error: %v", err) } return estimate, nil } func getFees(db *sql.DB, loan int) ([]Fee, error) { var fees []Fee rows, err := db.Query( "SELECT * FROM fees " + "WHERE loan_id = ?", loan) if err != nil { return nil, fmt.Errorf("Fee query error %v", err) } defer rows.Close() for rows.Next() { var fee Fee if err := rows.Scan( &fee.Id, &fee.LoanId, &fee.Amount, &fee.Perc, &fee.Type, &fee.Notes, &fee.Name, &fee.Category, ) err != nil { return nil, fmt.Errorf("Fees scanning error: %v", err) } fees = append(fees, fee) } return fees, nil } // Fetch fees from the database func getFeesTemp(db *sql.DB, user int) ([]FeeTemplate, error) { var fees []FeeTemplate rows, err := db.Query( "SELECT * FROM fee_template " + "WHERE user_id = ? OR user_id = 0", user) if err != nil { return nil, fmt.Errorf("Fee template query error %v", err) } defer rows.Close() for rows.Next() { var fee FeeTemplate if err := rows.Scan( &fee.Id, &fee.User, &fee.Branch, &fee.Amount, &fee.Perc, &fee.Type, &fee.Notes, &fee.Name, &fee.Category, &fee.Auto) err != nil { return nil, fmt.Errorf("FeesTemplate scanning error: %v", err) } fees = append(fees, fee) } est, err := getEstimate(db, 1) fmt.Printf("the estimate: %v,\nthe error %v\n", est, err) // getMi(db, getEstimate(db, 1), getBorrower(db, 1)) return fees, nil } func getLoans(db *sql.DB, estimate int) ([]Loan, error) { var loans []Loan query := `SELECT loan.id, loan.amount, loan.term, loan.interest, loan.ltv, loan.dti, loan.hoi, loan.mi_name, loan.mi_amount loan_type.id, loan_type.user_id, loan_type.branch_id, loan_type.name FROM loan INNER JOIN loan_type ON loan.type_id = loan_type(id) WHERE loan.estimate_id = ? ` rows, err := db.Query(query, estimate) if err != nil { return nil, fmt.Errorf("Loan query error %v", err) } defer rows.Close() for rows.Next() { var loan Loan if err := rows.Scan( &loan.Id, &loan.Amount, &loan.Term, &loan.Interest, &loan.Ltv, &loan.Dti, &loan.Hoi, &loan.MiName, &loan.MiAmount, &loan.Type.Id, &loan.Type.User, &loan.Type.Branch, &loan.Type.Name, ) err != nil { return loans, fmt.Errorf("Loans scanning error: %v", err) } loans = append(loans, loan) } est, err := getEstimate(db, 1) fmt.Printf("the loan: %v,\nthe error %v\n", est, err) // getMi(db, getEstimate(db, 1), getBorrower(db, 1)) return loans, nil } func getBorrower(db *sql.DB, id int) (Borrower, error) { var borrower Borrower row := db.QueryRow( "SELECT * FROM borrower " + "WHERE id = ? LIMIT 1", id) if err := row.Scan( &borrower.Id, &borrower.Credit, &borrower.Income, &borrower.Num, ) err != nil { return borrower, fmt.Errorf("Borrower scanning error: %v", err) } return borrower, nil } func getMi(db *sql.DB, estimate Estimate, borrower Borrower) { // body := map[string]string{ // "zipCode": estimate.Zip, // // "stateCode": "CA", // // "address": "", // "propertyTypeCode": "SFO", // "occupancyTypeCode": "PRS", // "loanPurposeCode": "PUR", // "loanAmount": "1000000", // "loanToValue": "LTV95", // "amortizationTerm": "A30", // "loanTypeCode": "FXD", // "duLpDecisionCode": "DAE", // "loanProgramCodes": [], // "debtToIncome": "5", // "wholesaleLoan": 0, // "coveragePercentageCode": "L30", // "productCode": "BPM", // "renewalTypeCode": "CON", // "numberOfBorrowers": 1, // "coBorrowerCreditScores": [], // "borrowerCreditScore": "740", // "masterPolicy": null, // "selfEmployedIndicator": false, // "armType": "", // "userId": 44504 // } var propertyCodes = map[string]string { "Single Family Attached": "SFO", "Single Family Detached": "SFO", "Condominium Lo-rise": "CON", "Condominium Hi-rise": "CON", } /* var occupancyCodes = map[string]string { "Primary Residence": "PRS", "Second Home": "SCH", "Condominium Lo-rise": "CON", "Condominium Hi-rise": "CON", } */ body, _ := json.Marshal(map[string]any{ "zipCode": estimate.Zip, // "stateCode": "CA", // "address": "", "propertyTypeCode": propertyCodes[estimate.Property], "occupancyTypeCode": "PRS", "loanPurposeCode": "PUR", "loanAmount": strconv.Itoa(500000 / 100), "loanToValue": "LTV95", "amortizationTerm": "A30", "loanTypeCode": "FXD", "duLpDecisionCode": "DAE", "debtToIncome": 5, "wholesaleLoan": 0, "coveragePercentageCode": "L30", "productCode": "BPM", "renewalTypeCode": "CON", "numberOfBorrowers": 1, "borrowerCreditScore": strconv.Itoa(borrower.Credit), "masterPolicy": nil, "selfEmployedIndicator": false, "armType": "", "userId": 44504, }) log.Println(bytes.NewBuffer(body)) } func validateEstimate() { return } func route(w http.ResponseWriter, r *http.Request) { var page Page var args []string p := r.URL.Path switch { case r.Method == "GET" && match(p, "/", &args): page = pages[ "home" ] case match(p, "/terms", &args): page = pages[ "terms" ] case match(p, "/app", &args): page = pages[ "app" ] case match(p, "/assets", &args): page = pages[ "app" ] default: http.NotFound(w, r) return } page.Render(w) } func api(w http.ResponseWriter, r *http.Request) { var args []string // var response string p := r.URL.Path db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/skouter", config["DBUser"], config["DBPass"])) w.Header().Set("Content-Type", "application/json; charset=UTF-8") err = db.Ping() if err != nil { print("Bad database configuration: %v", err) panic(err) // maybe os.Exit(1) instead } switch { case match(p, "/api/loans", &args): resp, err := getLoanType(db, 0, 0, true) if resp != nil { json.NewEncoder(w).Encode(resp) } else { json.NewEncoder(w).Encode(err) } case match(p, "/api/fees", &args): resp, err := getFeesTemp(db, 0) if resp != nil { json.NewEncoder(w).Encode(resp) } else { json.NewEncoder(w).Encode(err) } } } func main() { files := http.FileServer(http.Dir("")) http.Handle("/assets/", files) http.HandleFunc("/api/", api) http.HandleFunc("/", route) log.Fatal(http.ListenAndServe(address, nil)) }