Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
 
 
 
 
 
 

3530 lines
70 KiB

  1. package main
  2. import (
  3. "bytes"
  4. "database/sql"
  5. "encoding/base64"
  6. "encoding/json"
  7. "errors"
  8. "fmt"
  9. _ "github.com/go-sql-driver/mysql"
  10. "html/template"
  11. "io"
  12. "log"
  13. "math"
  14. "net/http"
  15. "net/http/httputil"
  16. "net/mail"
  17. "net/url"
  18. "os"
  19. "os/exec"
  20. "regexp"
  21. "strconv"
  22. "strings"
  23. "sync"
  24. "time"
  25. // pdf "github.com/SebastiaanKlippert/go-wkhtmltopdf"
  26. "github.com/brianvoe/gofakeit/v6"
  27. "github.com/disintegration/gift"
  28. "github.com/dustin/go-humanize"
  29. "github.com/golang-jwt/jwt/v4"
  30. "github.com/stripe/stripe-go/v76"
  31. "github.com/stripe/stripe-go/v76/customer"
  32. "github.com/stripe/stripe-go/v76/subscription"
  33. "github.com/stripe/stripe-go/v76/invoice"
  34. "github.com/stripe/stripe-go/v76/paymentintent"
  35. "image"
  36. _ "image/jpeg"
  37. "image/png"
  38. )
  39. type Config struct {
  40. DBName string
  41. DBUsername string
  42. DBPassword string
  43. }
  44. type Address struct {
  45. Id int `json:"id"`
  46. Full string `json:"full"`
  47. Street string `json:"street"`
  48. City string `json:"city"`
  49. Region string `json:"region"`
  50. Country string `json:"country"`
  51. Zip string `json:"zip"`
  52. }
  53. type Branch struct {
  54. Id int `json:"id"`
  55. Name string `json:"name"`
  56. Type string `json:"type"`
  57. Letterhead []byte `json:"letterhead"`
  58. Num string `json:"num"`
  59. Phone string `json:"phone"`
  60. Address Address `json:"address"`
  61. }
  62. type Subscription struct {
  63. Id int `json:"id"`
  64. UserId int `json:"userId"`
  65. StripeId string `json:"stripeId"`
  66. CustomerId string `json:"customerId"`
  67. Start int `json:"start"`
  68. End int `json:"end"`
  69. ClientSecret string `json:"clientSecret,omitempty"`
  70. PaymentStatus string `json:"paymentStatus"`
  71. }
  72. type User struct {
  73. Id int `json:"id"`
  74. Email string `json:"email"`
  75. FirstName string `json:"firstName"`
  76. LastName string `json:"lastName"`
  77. Phone string `json:"phone"`
  78. Address Address `json:"address"`
  79. Branch Branch `json:"branch"`
  80. License License `json:"license"`
  81. Sub Subscription `json:"sub"`
  82. Status string `json:"status"`
  83. Country string `json:"country"`
  84. Title string `json:"title"`
  85. Verified bool `json:"verified"`
  86. Role string `json:"role"`
  87. Password string `json:"password,omitempty"`
  88. CustomerId string `json:"customerId"`
  89. }
  90. type License struct {
  91. Id int `json:"id"`
  92. UserId int `json:"userId"`
  93. Type string `json:"type"`
  94. Num string `json:"num"`
  95. }
  96. type UserClaims struct {
  97. Id int `json:"id"`
  98. Role string `json:"role"`
  99. Exp string `json:"exp"`
  100. }
  101. type Page struct {
  102. tpl *template.Template
  103. Title string
  104. Name string
  105. }
  106. type Borrower struct {
  107. Id int `json:"id"`
  108. Credit int `json:"credit"`
  109. Income int `json:"income"`
  110. Num int `json:"num"`
  111. }
  112. type FeeTemplate struct {
  113. Id int `json:"id"`
  114. User int `json:"user"`
  115. Branch int `json:"branch"`
  116. Amount int `json:"amount"`
  117. Perc float32 `json:"perc"`
  118. Type string `json:"type"`
  119. Notes string `json:"notes"`
  120. Name string `json:"name"`
  121. Category string `json:"category"`
  122. Auto bool `json:"auto"`
  123. }
  124. type Fee struct {
  125. Id int `json:"id"`
  126. LoanId int `json:"loan_id"`
  127. Amount int `json:"amount"`
  128. Perc float32 `json:"perc"`
  129. Type string `json:"type"`
  130. Notes string `json:"notes"`
  131. Name string `json:"name"`
  132. Category string `json:"category"`
  133. }
  134. type LoanType struct {
  135. Id int `json:"id"`
  136. User int `json:"user"`
  137. Branch int `json:"branch"`
  138. Name string `json:"name"`
  139. }
  140. type Loan struct {
  141. Id int `json:"id"`
  142. EstimateId int `json:"estimateId"`
  143. Type LoanType `json:"type"`
  144. Amount int `json:"amount"`
  145. Amortization string `json:"amortization"`
  146. Term int `json:"term"`
  147. Ltv float32 `json:"ltv"`
  148. Dti float32 `json:"dti"`
  149. Hoi int `json:"hoi"`
  150. Hazard int `json:"hazard"`
  151. Tax int `json:"tax"`
  152. Interest float32 `json:"interest"`
  153. Mi MI `json:"mi"`
  154. Fees []Fee `json:"fees"`
  155. Credits []Fee // Fees with negative amounts for internal use
  156. Name string `json:"title"`
  157. Result Result `json:"result"`
  158. }
  159. type MI struct {
  160. Type string `json:"user"`
  161. Label string `json:"label"`
  162. Lender string `json:"lender"`
  163. Rate float32 `json:"rate"`
  164. Premium int `json:"premium"`
  165. Upfront int `json:"upfront"`
  166. Monthly bool `json:"monthly"`
  167. FiveYearTotal float32 `json:"fiveYearTotal"`
  168. InitialAllInPremium float32 `json:"initialAllInPremium"`
  169. InitialAllInRate float32 `json:"initialAllInRate"`
  170. InitialAmount float32 `json:"initialAmount"`
  171. }
  172. type Result struct {
  173. Id int `json:"id"`
  174. LoanId int `json:"loanId"`
  175. LoanPayment int `json:"loanPayment"`
  176. TotalMonthly int `json:"totalMonthly"`
  177. TotalFees int `json:"totalFees"`
  178. TotalCredits int `json:"totalCredits"`
  179. CashToClose int `json:"cashToClose"`
  180. }
  181. type Estimate struct {
  182. Id int `json:"id"`
  183. User int `json:"user"`
  184. Borrower Borrower `json:"borrower"`
  185. Transaction string `json:"transaction"`
  186. Price int `json:"price"`
  187. Property string `json:"property"`
  188. Occupancy string `json:"occupancy"`
  189. Zip string `json:"zip"`
  190. Pud bool `json:"pud"`
  191. Loans []Loan `json:"loans"`
  192. }
  193. type ETemplate struct {
  194. Id int `json:"id"`
  195. Estimate Estimate `json:"estimate"`
  196. UserId int `json:"userId"`
  197. BranchId int `json:"branchId"`
  198. }
  199. type Report struct {
  200. Title string
  201. Name string
  202. Avatar string
  203. Letterhead string
  204. User User
  205. Estimate Estimate
  206. }
  207. type Password struct {
  208. Old string `json:"old"`
  209. New string `json:"new"`
  210. }
  211. type Endpoint func(http.ResponseWriter, *sql.DB, *http.Request)
  212. var (
  213. regexen = make(map[string]*regexp.Regexp)
  214. relock sync.Mutex
  215. address = "localhost:8001"
  216. mainAddress = "localhost:8002"
  217. )
  218. var paths = map[string]string{
  219. "home": "views/home.tpl",
  220. "terms": "views/terms.tpl",
  221. "app": "views/app.tpl",
  222. "comparison": "views/report/comparison.tpl",
  223. }
  224. var pages = map[string]Page{
  225. "home": cache("home", "Home"),
  226. "terms": cache("terms", "Terms and Conditions"),
  227. "report": cachePdf("comparison"),
  228. "app": cache("app", "App"),
  229. }
  230. var roles = map[string]int{
  231. "User": 1,
  232. "Manager": 2,
  233. "Admin": 3,
  234. }
  235. var propertyTypes = []string{
  236. "Single Detached",
  237. "Single Attached",
  238. "Condo Lo-rise",
  239. "Condo Hi-rise",
  240. }
  241. var feeTypes = []string{
  242. "Government",
  243. "Title",
  244. "Required",
  245. "Lender",
  246. "Other",
  247. }
  248. // Used to validate claim in JWT token body. Checks if user id is greater than
  249. // zero and time format is valid
  250. func (c UserClaims) Valid() error {
  251. if c.Id < 1 {
  252. return errors.New("Invalid id")
  253. }
  254. t, err := time.Parse(time.UnixDate, c.Exp)
  255. if err != nil {
  256. return err
  257. }
  258. if t.Before(time.Now()) {
  259. return errors.New("Token expired.")
  260. }
  261. return err
  262. }
  263. func cache(name string, title string) Page {
  264. var p = []string{"views/master.tpl", paths[name]}
  265. tpl := template.Must(template.ParseFiles(p...))
  266. return Page{tpl: tpl, Title: title, Name: name}
  267. }
  268. func cachePdf(name string) Page {
  269. // Money is stored in cents, so it must be converted to dollars in reports
  270. dollars := func(cents int) string {
  271. return humanize.Commaf(float64(cents) / 100)
  272. }
  273. // For calculating down payments
  274. diff := func(a, b int) string {
  275. return humanize.Commaf(float64(b-a) / 100)
  276. }
  277. sortFees := func(ftype string, fees []Fee) []Fee {
  278. result := make([]Fee, 0)
  279. for i := range fees {
  280. if fees[i].Type != ftype {
  281. continue
  282. }
  283. result = append(result, fees[i])
  284. }
  285. return result
  286. }
  287. fm := template.FuncMap{
  288. "dollars": dollars,
  289. "diff": diff,
  290. "sortFees": sortFees}
  291. var p = []string{"views/report/master.tpl",
  292. "views/report/header.tpl",
  293. "views/report/summary.tpl",
  294. "views/report/comparison.tpl"}
  295. tpl := template.Must(template.New("master.tpl").Funcs(fm).ParseFiles(p...))
  296. return Page{tpl: tpl, Title: "", Name: name}
  297. }
  298. func (page Page) Render(w http.ResponseWriter) {
  299. err := page.tpl.Execute(w, page)
  300. if err != nil {
  301. log.Print(err)
  302. }
  303. }
  304. func match(path, pattern string, args *[]string) bool {
  305. relock.Lock()
  306. defer relock.Unlock()
  307. regex := regexen[pattern]
  308. if regex == nil {
  309. regex = regexp.MustCompile("^" + pattern + "$")
  310. regexen[pattern] = regex
  311. }
  312. matches := regex.FindStringSubmatch(path)
  313. if len(matches) <= 0 {
  314. return false
  315. }
  316. *args = matches[1:]
  317. return true
  318. }
  319. func createToken(id int, role string) (*http.Cookie, error) {
  320. token := jwt.NewWithClaims(jwt.SigningMethodHS256,
  321. UserClaims{Id: id, Role: role,
  322. Exp: time.Now().Add(time.Minute * 30).Format(time.UnixDate)})
  323. tokenStr, err := token.SignedString([]byte(os.Getenv("JWT_SECRET")))
  324. if err != nil {
  325. log.Println("Token could not be signed: ", err, tokenStr)
  326. return nil, errors.New("Token generation error.")
  327. }
  328. cookie := http.Cookie{Name: "skouter",
  329. Value: tokenStr,
  330. Path: "/",
  331. Expires: time.Now().Add(time.Hour * 24)}
  332. return &cookie, nil
  333. }
  334. func (estimate *Estimate) makeResults() []Result {
  335. var results []Result
  336. amortize := func(principle float64, rate float64, periods float64) int {
  337. exp := math.Pow(1+rate, periods)
  338. return int(principle * rate * exp / (exp - 1))
  339. }
  340. for i := range estimate.Loans {
  341. var loan = &estimate.Loans[i]
  342. var result Result = Result{}
  343. // Monthly payments use amortized loan payment formula plus monthly MI,
  344. // plus all other recurring fees
  345. result.LoanPayment = amortize(float64(loan.Amount),
  346. float64(loan.Interest/100/12),
  347. float64(loan.Term*12))
  348. result.TotalMonthly = result.LoanPayment + loan.Hoi + loan.Tax + loan.Hazard
  349. if loan.Mi.Monthly {
  350. result.TotalMonthly = result.TotalMonthly +
  351. int(loan.Mi.Rate/100/12*float32(loan.Amount))
  352. } else {
  353. loan.Mi.Upfront = int(loan.Mi.Rate / 100 * float32(loan.Amount))
  354. }
  355. for i := range loan.Fees {
  356. if loan.Fees[i].Amount > 0 {
  357. result.TotalFees = result.TotalFees + loan.Fees[i].Amount
  358. } else {
  359. result.TotalCredits = result.TotalCredits + loan.Fees[i].Amount
  360. }
  361. }
  362. result.CashToClose =
  363. result.TotalFees + result.TotalCredits + (estimate.Price - loan.Amount)
  364. result.LoanId = loan.Id
  365. loan.Result = result
  366. results = append(results, result)
  367. }
  368. return results
  369. }
  370. func summarize(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  371. var estimate Estimate
  372. err := json.NewDecoder(r.Body).Decode(&estimate)
  373. if err != nil {
  374. http.Error(w, "Invalid estimate.", 422)
  375. return
  376. }
  377. estimate.makeResults()
  378. json.NewEncoder(w).Encode(estimate)
  379. }
  380. func getLoanType(db *sql.DB, id int) (LoanType, error) {
  381. types, err := getLoanTypes(db, id, 0, 0)
  382. if err != nil {
  383. return LoanType{Id: id}, err
  384. }
  385. if len(types) == 0 {
  386. return LoanType{Id: id}, errors.New("No type with that id")
  387. }
  388. return types[0], nil
  389. }
  390. func getLoanTypes(db *sql.DB, id int, user int, branch int) (
  391. []LoanType, error) {
  392. var loans []LoanType
  393. var query = `SELECT
  394. id,
  395. coalesce(user_id, 0),
  396. coalesce(branch_id, 0),
  397. name
  398. FROM loan_type WHERE loan_type.id = CASE @e := ? WHEN 0 THEN id ELSE @e END
  399. OR
  400. loan_type.user_id = CASE @e := ? WHEN 0 THEN id ELSE @e END
  401. OR
  402. loan_type.branch_id = CASE @e := ? WHEN 0 THEN id ELSE @e END`
  403. // Should be changed to specify user
  404. rows, err := db.Query(query, id, user, branch)
  405. if err != nil {
  406. return nil, fmt.Errorf("loan_type error: %v", err)
  407. }
  408. defer rows.Close()
  409. for rows.Next() {
  410. var loan LoanType
  411. if err := rows.Scan(
  412. &loan.Id,
  413. &loan.User,
  414. &loan.Branch,
  415. &loan.Name); err != nil {
  416. log.Printf("Error occured fetching loan: %v", err)
  417. return nil, fmt.Errorf("Error occured fetching loan: %v", err)
  418. }
  419. loans = append(loans, loan)
  420. }
  421. return loans, nil
  422. }
  423. func getFees(db *sql.DB, loan int) ([]Fee, error) {
  424. var fees []Fee
  425. query := `SELECT id, loan_id, amount, perc, type, notes, name, category
  426. FROM fee
  427. WHERE loan_id = ?`
  428. rows, err := db.Query(query, loan)
  429. if err != nil {
  430. return nil, fmt.Errorf("Fee query error %v", err)
  431. }
  432. defer rows.Close()
  433. for rows.Next() {
  434. var fee Fee
  435. if err := rows.Scan(
  436. &fee.Id,
  437. &fee.LoanId,
  438. &fee.Amount,
  439. &fee.Perc,
  440. &fee.Type,
  441. &fee.Notes,
  442. &fee.Name,
  443. &fee.Category,
  444. ); err != nil {
  445. return nil, fmt.Errorf("Fees scanning error: %v", err)
  446. }
  447. fees = append(fees, fee)
  448. }
  449. return fees, nil
  450. }
  451. func fetchFeesTemp(db *sql.DB, user int, branch int) ([]FeeTemplate, error) {
  452. var fees []FeeTemplate
  453. query := `SELECT
  454. id, user_id, COALESCE(branch_id, 0), amount, perc, type, notes, name,
  455. category, auto
  456. FROM fee_template
  457. WHERE user_id = ? OR branch_id = ?
  458. `
  459. rows, err := db.Query(query, user, branch)
  460. if err != nil {
  461. return nil, fmt.Errorf("Fee template query error %v", err)
  462. }
  463. defer rows.Close()
  464. for rows.Next() {
  465. var fee FeeTemplate
  466. if err := rows.Scan(
  467. &fee.Id,
  468. &fee.User,
  469. &fee.Branch,
  470. &fee.Amount,
  471. &fee.Perc,
  472. &fee.Type,
  473. &fee.Notes,
  474. &fee.Name,
  475. &fee.Category,
  476. &fee.Auto); err != nil {
  477. return nil, fmt.Errorf("FeesTemplate scanning error: %v", err)
  478. }
  479. fees = append(fees, fee)
  480. }
  481. return fees, nil
  482. }
  483. // Fetch fees from the database
  484. func getFeesTemp(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  485. var fees []FeeTemplate
  486. claims, err := getClaims(r)
  487. if err != nil {
  488. w.WriteHeader(500)
  489. return
  490. }
  491. user, err := queryUser(db, claims.Id)
  492. if err != nil {
  493. w.WriteHeader(422)
  494. return
  495. }
  496. fees, err = fetchFeesTemp(db, claims.Id, user.Branch.Id)
  497. json.NewEncoder(w).Encode(fees)
  498. }
  499. // Fetch fees from the database
  500. func createFeesTemp(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  501. var fee FeeTemplate
  502. var query string
  503. var row *sql.Row
  504. var err error
  505. claims, err := getClaims(r)
  506. // var id int // Inserted estimate's id
  507. err = json.NewDecoder(r.Body).Decode(&fee)
  508. if err != nil {
  509. w.WriteHeader(422)
  510. return
  511. }
  512. query = `INSERT INTO fee_template
  513. (
  514. user_id,
  515. branch_id,
  516. amount,
  517. perc,
  518. type,
  519. notes,
  520. name,
  521. auto
  522. )
  523. VALUES (?, NULL, ?, ?, ?, ?, ?, ?)
  524. RETURNING id
  525. `
  526. row = db.QueryRow(query,
  527. claims.Id,
  528. fee.Amount,
  529. fee.Perc,
  530. fee.Type,
  531. fee.Notes,
  532. fee.Name,
  533. fee.Auto,
  534. )
  535. err = row.Scan(&fee.Id)
  536. if err != nil {
  537. w.WriteHeader(500)
  538. return
  539. }
  540. json.NewEncoder(w).Encode(fee)
  541. }
  542. // Fetch fees from the database
  543. func deleteFeeTemp(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  544. var fee FeeTemplate
  545. var query string
  546. var err error
  547. // claims, err := getClaims(r)
  548. // var id int // Inserted estimate's id
  549. err = json.NewDecoder(r.Body).Decode(&fee)
  550. if err != nil {
  551. w.WriteHeader(422)
  552. return
  553. }
  554. query = `DELETE FROM fee_template WHERE id = ?`
  555. _, err = db.Exec(query, fee.Id)
  556. if err != nil {
  557. w.WriteHeader(500)
  558. return
  559. }
  560. }
  561. func deleteEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  562. var estimate Estimate
  563. var err error
  564. err = json.NewDecoder(r.Body).Decode(&estimate)
  565. if err != nil {
  566. w.WriteHeader(422)
  567. return
  568. }
  569. claims, err := getClaims(r)
  570. err = estimate.del(db, claims.Id)
  571. if err != nil {
  572. http.Error(w, err.Error(), 500)
  573. return
  574. }
  575. }
  576. func deleteET(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  577. var et ETemplate
  578. var err error
  579. err = json.NewDecoder(r.Body).Decode(&et)
  580. if err != nil {
  581. w.WriteHeader(422)
  582. return
  583. }
  584. claims, err := getClaims(r)
  585. err = et.del(db, claims.Id)
  586. if err != nil {
  587. http.Error(w, err.Error(), 500)
  588. return
  589. }
  590. }
  591. func getMi(db *sql.DB, loan int) (MI, error) {
  592. var mi MI
  593. query := `SELECT
  594. type, label, lender, rate, premium, upfront, five_year_total,
  595. initial_premium, initial_rate, initial_amount
  596. FROM mi WHERE loan_id = ?`
  597. rows, err := db.Query(query, loan)
  598. if err != nil {
  599. return mi, err
  600. }
  601. defer rows.Close()
  602. if !rows.Next() {
  603. return mi, nil
  604. }
  605. if err := rows.Scan(
  606. &mi.Type,
  607. &mi.Label,
  608. &mi.Lender,
  609. &mi.Rate,
  610. &mi.Premium,
  611. &mi.Upfront,
  612. &mi.FiveYearTotal,
  613. &mi.InitialAllInPremium,
  614. &mi.InitialAllInRate,
  615. &mi.InitialAmount,
  616. ); err != nil {
  617. return mi, err
  618. }
  619. return mi, nil
  620. }
  621. func (estimate *Estimate) getBorrower(db *sql.DB) error {
  622. query := `SELECT id, credit_score, monthly_income, num FROM borrower
  623. WHERE estimate_id = ? LIMIT 1`
  624. row := db.QueryRow(query, estimate.Id)
  625. if err := row.Scan(
  626. &estimate.Borrower.Id,
  627. &estimate.Borrower.Credit,
  628. &estimate.Borrower.Income,
  629. &estimate.Borrower.Num,
  630. ); err != nil {
  631. return fmt.Errorf("Borrower scanning error: %v", err)
  632. }
  633. return nil
  634. }
  635. // Query Lender APIs and parse responses into MI structs
  636. func fetchMi(db *sql.DB, estimate *Estimate, pos int) []MI {
  637. var loan Loan = estimate.Loans[pos]
  638. var ltv = func(l float32) string {
  639. switch {
  640. case l > 95:
  641. return "LTV97"
  642. case l > 90:
  643. return "LTV95"
  644. case l > 85:
  645. return "LTV90"
  646. default:
  647. return "LTV85"
  648. }
  649. }
  650. var term = func(t int) string {
  651. switch {
  652. case t <= 10:
  653. return "A10"
  654. case t <= 15:
  655. return "A15"
  656. case t <= 20:
  657. return "A20"
  658. case t <= 25:
  659. return "A25"
  660. case t <= 30:
  661. return "A30"
  662. default:
  663. return "A40"
  664. }
  665. }
  666. var propertyCodes = map[string]string{
  667. "Single Attached": "SFO",
  668. "Single Detached": "SFO",
  669. "Condo Lo-rise": "CON",
  670. "Condo Hi-rise": "CON",
  671. }
  672. var purposeCodes = map[string]string{
  673. "Purchase": "PUR",
  674. "Refinance": "RRT",
  675. }
  676. body, err := json.Marshal(map[string]any{
  677. "zipCode": estimate.Zip,
  678. "stateCode": "CA",
  679. "address": "",
  680. "propertyTypeCode": propertyCodes[estimate.Property],
  681. "occupancyTypeCode": "PRS",
  682. "loanPurposeCode": purposeCodes[estimate.Transaction],
  683. "loanAmount": loan.Amount,
  684. "loanToValue": ltv(loan.Ltv),
  685. "amortizationTerm": term(loan.Term),
  686. "loanTypeCode": "FXD",
  687. "duLpDecisionCode": "DAE",
  688. "loanProgramCodes": []any{},
  689. "debtToIncome": loan.Dti,
  690. "wholesaleLoan": 0,
  691. "coveragePercentageCode": "L30",
  692. "productCode": "BPM",
  693. "renewalTypeCode": "CON",
  694. "numberOfBorrowers": 1,
  695. "coBorrowerCreditScores": []any{},
  696. "borrowerCreditScore": strconv.Itoa(estimate.Borrower.Credit),
  697. "masterPolicy": nil,
  698. "selfEmployedIndicator": false,
  699. "armType": "",
  700. "userId": 44504,
  701. })
  702. /*
  703. if err != nil {
  704. log.Printf("Could not marshal NationalMI body: \n%v\n%v\n",
  705. bytes.NewBuffer(body), err)
  706. func queryAddress(db *sql.DB, id int) ( Address, error ) {
  707. var address Address = Address{Id: id}
  708. var err error
  709. row := db.QueryRow(
  710. `SELECT id, full_address, street, city, region, country, zip
  711. FROM address WHERE id = ?`, id)
  712. err = row.Scan(
  713. &address.Id,
  714. &address.Full,
  715. &address.Street,
  716. &address.City,
  717. &address.Region,
  718. &address.Country,
  719. &address.Zip,
  720. )
  721. return address, err
  722. }
  723. }
  724. */
  725. req, err := http.NewRequest("POST",
  726. "https://rate-gps.nationalmi.com/rates/productRateQuote",
  727. bytes.NewBuffer(body))
  728. req.Header.Add("Content-Type", "application/json")
  729. req.AddCookie(&http.Cookie{
  730. Name: "nmirategps_email",
  731. Value: os.Getenv("NationalMIEmail")})
  732. resp, err := http.DefaultClient.Do(req)
  733. var res map[string]interface{}
  734. var result []MI
  735. if resp.StatusCode != 200 {
  736. log.Printf("the status: %v\nthe resp: %v\n the req: %v\n the body: %v\n",
  737. resp.Status, resp, req.Body, bytes.NewBuffer(body))
  738. } else {
  739. json.NewDecoder(resp.Body).Decode(&res)
  740. // estimate.Loans[pos].Mi = res
  741. // Parse res into result here
  742. }
  743. log.Println(err)
  744. return result
  745. }
  746. // Make comparison PDF
  747. func generatePDF(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  748. }
  749. func login(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  750. var id int
  751. var role string
  752. var err error
  753. var user User
  754. json.NewDecoder(r.Body).Decode(&user)
  755. row := db.QueryRow(
  756. `SELECT id, role FROM user WHERE email = ? AND password = sha2(?, 256)`,
  757. user.Email, user.Password,
  758. )
  759. err = row.Scan(&id, &role)
  760. if err != nil {
  761. http.Error(w, "Invalid Credentials.", http.StatusUnauthorized)
  762. return
  763. }
  764. cookie, err := createToken(id, role)
  765. if err != nil {
  766. http.Error(w, err.Error(), 500)
  767. }
  768. http.SetCookie(w, cookie)
  769. _, err = w.Write([]byte(cookie.Value))
  770. if err != nil {
  771. http.Error(w, "Could not complete token write.",
  772. http.StatusInternalServerError)
  773. }
  774. }
  775. func getToken(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  776. claims, err := getClaims(r)
  777. // Will verify existing signature and expiry time
  778. token := jwt.NewWithClaims(jwt.SigningMethodHS256,
  779. UserClaims{Id: claims.Id, Role: claims.Role,
  780. Exp: time.Now().Add(time.Minute * 30).Format(time.UnixDate)})
  781. tokenStr, err := token.SignedString([]byte(os.Getenv("JWT_SECRET")))
  782. if err != nil {
  783. log.Println("Token could not be signed: ", err, tokenStr)
  784. http.Error(w, "Token generation error.", http.StatusInternalServerError)
  785. return
  786. }
  787. cookie := http.Cookie{Name: "skouter",
  788. Value: tokenStr,
  789. Path: "/",
  790. Expires: time.Now().Add(time.Hour * 24)}
  791. http.SetCookie(w, &cookie)
  792. _, err = w.Write([]byte(tokenStr))
  793. if err != nil {
  794. http.Error(w,
  795. "Could not complete token write.",
  796. http.StatusInternalServerError)
  797. }
  798. }
  799. func getClaims(r *http.Request) (UserClaims, error) {
  800. claims := new(UserClaims)
  801. _, tokenStr, found := strings.Cut(r.Header.Get("Authorization"), " ")
  802. if !found {
  803. return *claims, errors.New("Token not found")
  804. }
  805. // Pull token payload into UserClaims
  806. _, err := jwt.ParseWithClaims(tokenStr, claims,
  807. func(token *jwt.Token) (any, error) {
  808. return []byte(os.Getenv("JWT_SECRET")), nil
  809. })
  810. if err != nil {
  811. return *claims, err
  812. }
  813. if err = claims.Valid(); err != nil {
  814. return *claims, err
  815. }
  816. return *claims, nil
  817. }
  818. func guard(r *http.Request, required int) bool {
  819. claims, err := getClaims(r)
  820. if err != nil {
  821. return false
  822. }
  823. if roles[claims.Role] < required {
  824. return false
  825. }
  826. return true
  827. }
  828. // Inserts an address and returns it's ID along with any errors.
  829. func insertAddress(db *sql.DB, address Address) (int, error) {
  830. var query string
  831. var row *sql.Row
  832. var err error
  833. var id int // Inserted user's id
  834. query = `INSERT INTO address
  835. (
  836. full_address,
  837. street,
  838. city,
  839. region,
  840. country,
  841. zip
  842. )
  843. VALUES (?, ?, ?, ?, ?, ?)
  844. RETURNING id
  845. `
  846. row = db.QueryRow(query,
  847. address.Full,
  848. address.Street,
  849. address.City,
  850. address.Region,
  851. address.Country,
  852. address.Zip,
  853. )
  854. err = row.Scan(&id)
  855. return id, err
  856. }
  857. // Inserts an address and returns it's ID along with any errors.
  858. func insertBranch(db *sql.DB, branch Branch) (int, error) {
  859. var query string
  860. var row *sql.Row
  861. var err error
  862. var id int // Inserted user's id
  863. query = `INSERT INTO branch
  864. (
  865. name,
  866. type,
  867. letterhead,
  868. num,
  869. phone,
  870. address
  871. )
  872. VALUES (?, ?, ?, ?, ?, ?)
  873. RETURNING id
  874. `
  875. row = db.QueryRow(query,
  876. branch.Name,
  877. branch.Type,
  878. branch.Letterhead,
  879. branch.Num,
  880. branch.Phone,
  881. branch.Address.Id,
  882. )
  883. err = row.Scan(&id)
  884. return id, err
  885. }
  886. // Inserts an address and returns it's ID along with any errors.
  887. func insertLicense(db *sql.DB, license License) (int, error) {
  888. var query string
  889. var row *sql.Row
  890. var err error
  891. var id int // Inserted license's id
  892. query = `INSERT INTO license
  893. (
  894. user_id,
  895. type,
  896. num
  897. )
  898. VALUES (?, ?, ?)
  899. RETURNING id
  900. `
  901. row = db.QueryRow(query,
  902. license.UserId,
  903. license.Type,
  904. license.Num,
  905. )
  906. err = row.Scan(&id)
  907. return id, err
  908. }
  909. func queryLicense(db *sql.DB, user int) (License, error) {
  910. var license License = License{UserId: user}
  911. var err error
  912. row := db.QueryRow(
  913. `SELECT id, type, num FROM license WHERE user_id = ?`,
  914. user)
  915. err = row.Scan(
  916. &license.Id,
  917. &license.Type,
  918. &license.Num,
  919. )
  920. return license, err
  921. }
  922. func queryAddress(db *sql.DB, id int) (Address, error) {
  923. var address Address = Address{Id: id}
  924. var err error
  925. row := db.QueryRow(
  926. `SELECT id, full_address, street, city, region, country, zip
  927. FROM address WHERE id = ?`, id)
  928. err = row.Scan(
  929. &address.Id,
  930. &address.Full,
  931. &address.Street,
  932. &address.City,
  933. &address.Region,
  934. &address.Country,
  935. &address.Zip,
  936. )
  937. return address, err
  938. }
  939. func queryBranch(db *sql.DB, id int) (Branch, error) {
  940. var branch Branch = Branch{Id: id}
  941. var err error
  942. row := db.QueryRow(
  943. `SELECT id, name, type, letterhead, num, phone, address
  944. FROM branch WHERE id = ?`, id)
  945. err = row.Scan(
  946. &branch.Id,
  947. &branch.Name,
  948. &branch.Type,
  949. &branch.Letterhead,
  950. &branch.Num,
  951. &branch.Phone,
  952. &branch.Address.Id,
  953. )
  954. return branch, err
  955. }
  956. func queryUser(db *sql.DB, id int) (User, error) {
  957. var user User
  958. var query string
  959. var err error
  960. query = `SELECT
  961. u.id,
  962. u.email,
  963. u.first_name,
  964. u.last_name,
  965. coalesce(u.branch_id, 0),
  966. u.country,
  967. u.title,
  968. coalesce(u.status, ''),
  969. coalesce(u.customer_id, ''),
  970. u.verified,
  971. u.role,
  972. u.address,
  973. u.phone
  974. FROM user u WHERE u.id = ?
  975. `
  976. row := db.QueryRow(query, id)
  977. if err != nil {
  978. return user, err
  979. }
  980. err = row.Scan(
  981. &user.Id,
  982. &user.Email,
  983. &user.FirstName,
  984. &user.LastName,
  985. &user.Branch.Id,
  986. &user.Country,
  987. &user.Title,
  988. &user.Status,
  989. &user.CustomerId,
  990. &user.Verified,
  991. &user.Role,
  992. &user.Address.Id,
  993. &user.Phone,
  994. )
  995. if err != nil {
  996. return user, err
  997. }
  998. user.Address, err = queryAddress(db, user.Address.Id)
  999. if err != nil {
  1000. return user, err
  1001. }
  1002. user.Branch, err = queryBranch(db, user.Branch.Id)
  1003. if err != nil {
  1004. return user, err
  1005. }
  1006. return user, nil
  1007. }
  1008. // Can probably be deleted.
  1009. func queryUsers(db *sql.DB, id int) ([]User, error) {
  1010. var users []User
  1011. var query string
  1012. var rows *sql.Rows
  1013. var err error
  1014. query = `SELECT
  1015. u.id,
  1016. u.email,
  1017. u.first_name,
  1018. u.last_name,
  1019. coalesce(u.branch_id, 0),
  1020. u.country,
  1021. u.title,
  1022. coalesce(u.status, ''),
  1023. u.verified,
  1024. u.role,
  1025. u.address,
  1026. u.phone
  1027. FROM user u WHERE u.id = CASE @e := ? WHEN 0 THEN u.id ELSE @e END
  1028. `
  1029. rows, err = db.Query(query, id)
  1030. if err != nil {
  1031. return users, err
  1032. }
  1033. defer rows.Close()
  1034. for rows.Next() {
  1035. var user User
  1036. if err := rows.Scan(
  1037. &user.Id,
  1038. &user.Email,
  1039. &user.FirstName,
  1040. &user.LastName,
  1041. &user.Branch.Id,
  1042. &user.Country,
  1043. &user.Title,
  1044. &user.Status,
  1045. &user.Verified,
  1046. &user.Role,
  1047. &user.Address.Id,
  1048. &user.Phone,
  1049. ); err != nil {
  1050. return users, err
  1051. }
  1052. user.Address, err = queryAddress(db, user.Address.Id)
  1053. if err != nil {
  1054. return users, err
  1055. }
  1056. user.Branch, err = queryBranch(db, user.Branch.Id)
  1057. if err != nil {
  1058. return users, err
  1059. }
  1060. users = append(users, user)
  1061. }
  1062. // Prevents runtime panics
  1063. if len(users) == 0 {
  1064. return users, errors.New("User not found.")
  1065. }
  1066. return users, nil
  1067. }
  1068. func querySub(db *sql.DB, id int) (Subscription, error) {
  1069. var query string
  1070. var err error
  1071. var s Subscription
  1072. query = `SELECT
  1073. id,
  1074. stripe_id,
  1075. user_id,
  1076. customer_id,
  1077. current_period_end,
  1078. current_period_start,
  1079. client_secret,
  1080. payment_status
  1081. FROM subscription WHERE id = ?
  1082. `
  1083. row := db.QueryRow(query, id)
  1084. err = row.Scan(
  1085. &s.Id,
  1086. &s.StripeId,
  1087. &s.CustomerId,
  1088. &s.End,
  1089. &s.Start,
  1090. &s.ClientSecret,
  1091. &s.PaymentStatus,
  1092. )
  1093. return s, err
  1094. }
  1095. func (user *User) querySub(db *sql.DB) error {
  1096. var query string
  1097. var err error
  1098. query = `SELECT
  1099. id,
  1100. stripe_id,
  1101. user_id,
  1102. customer_id,
  1103. current_period_end,
  1104. current_period_start,
  1105. client_secret,
  1106. payment_status
  1107. FROM subscription WHERE user_id = ?
  1108. `
  1109. row := db.QueryRow(query, user.Id)
  1110. err = row.Scan(
  1111. &user.Sub.Id,
  1112. &user.Sub.StripeId,
  1113. &user.Sub.UserId,
  1114. &user.Sub.CustomerId,
  1115. &user.Sub.End,
  1116. &user.Sub.Start,
  1117. &user.Sub.ClientSecret,
  1118. &user.Sub.PaymentStatus,
  1119. )
  1120. return err
  1121. }
  1122. func (estimate *Estimate) insertResults(db *sql.DB) error {
  1123. var query string
  1124. var row *sql.Row
  1125. var err error
  1126. var id int
  1127. query = `INSERT INTO estimate_result
  1128. (
  1129. loan_id,
  1130. loan_payment,
  1131. total_monthly,
  1132. total_fees,
  1133. total_credits,
  1134. cash_to_close
  1135. )
  1136. VALUES (?, ?, ?, ?, ?, ?)
  1137. RETURNING id
  1138. `
  1139. for i := range estimate.Loans {
  1140. r := estimate.Loans[i].Result
  1141. r.LoanId = estimate.Loans[i].Id
  1142. row = db.QueryRow(query,
  1143. r.LoanId,
  1144. r.LoanPayment,
  1145. r.TotalMonthly,
  1146. r.TotalFees,
  1147. r.TotalCredits,
  1148. r.CashToClose,
  1149. )
  1150. err = row.Scan(&id)
  1151. if err != nil {
  1152. return err
  1153. }
  1154. r.Id = id
  1155. }
  1156. return nil
  1157. }
  1158. // Insert user returning it's ID or any error
  1159. func insertUser(db *sql.DB, user User) (int, error) {
  1160. var query string
  1161. var row *sql.Row
  1162. var err error
  1163. var id int // Inserted user's id
  1164. user.Address.Id, err = insertAddress(db, user.Address)
  1165. if err != nil {
  1166. return 0, err
  1167. }
  1168. query = `INSERT INTO user
  1169. (
  1170. email,
  1171. first_name,
  1172. last_name,
  1173. password,
  1174. role,
  1175. title,
  1176. status,
  1177. verified,
  1178. address,
  1179. country,
  1180. branch_id,
  1181. phone,
  1182. created,
  1183. last_login
  1184. )
  1185. VALUES (?, ?, ?, sha2(?, 256), ?, ?, ?, ?, ?, ?,
  1186. CASE @b := ? WHEN 0 THEN NULL ELSE @b END,
  1187. ?, NOW(), NOW())
  1188. RETURNING id
  1189. `
  1190. row = db.QueryRow(query,
  1191. user.Email,
  1192. user.FirstName,
  1193. user.LastName,
  1194. user.Password,
  1195. user.Role,
  1196. user.Title,
  1197. user.Status,
  1198. user.Verified,
  1199. user.Address.Id,
  1200. user.Country,
  1201. user.Branch.Id,
  1202. user.Phone,
  1203. )
  1204. err = row.Scan(&id)
  1205. if err != nil {
  1206. return 0, err
  1207. }
  1208. user.Id = id
  1209. return id, nil
  1210. }
  1211. // Insert user returning it's ID or any error
  1212. func (sub *Subscription) insertSub(db *sql.DB) (error) {
  1213. var query string
  1214. var row *sql.Row
  1215. var err error
  1216. query = `INSERT INTO subscription
  1217. (
  1218. stripe_id,
  1219. user_id,
  1220. customer_id,
  1221. current_period_end,
  1222. current_period_start,
  1223. client_secret,
  1224. payment_status
  1225. )
  1226. VALUES (?, ?, ?, ?, ?, ?, ?)
  1227. RETURNING id
  1228. `
  1229. row = db.QueryRow(query,
  1230. sub.StripeId,
  1231. sub.UserId,
  1232. sub.CustomerId,
  1233. sub.End,
  1234. sub.Start,
  1235. sub.ClientSecret,
  1236. sub.PaymentStatus,
  1237. )
  1238. err = row.Scan(&sub.Id)
  1239. return err
  1240. }
  1241. func (sub *Subscription) updateSub(db *sql.DB) error {
  1242. var query string
  1243. var err error
  1244. query = `UPDATE subscription
  1245. SET client_secret = ?, payment_status = ?
  1246. WHERE id = ?
  1247. `
  1248. s, err := subscription.Get(sub.StripeId, &stripe.SubscriptionParams{})
  1249. if err != nil { return err }
  1250. i, err := invoice.Get(s.LatestInvoice.ID, &stripe.InvoiceParams{})
  1251. if err != nil { return err }
  1252. p, err := paymentintent.Get(i.PaymentIntent.ID,
  1253. &stripe.PaymentIntentParams{})
  1254. if err != nil { return err }
  1255. _, err = db.Exec(query,
  1256. p.ClientSecret,
  1257. p.Status,
  1258. sub.Id,
  1259. )
  1260. if err != nil { return err }
  1261. sub.ClientSecret = p.ClientSecret
  1262. sub.PaymentStatus = string(p.Status)
  1263. return err
  1264. }
  1265. // Updates a user's stripe customer ID.
  1266. func (user *User) updateCustomerId(db *sql.DB, cid string) (error) {
  1267. var query string
  1268. var err error
  1269. query = `UPDATE user SET
  1270. customer_id = ?
  1271. WHERE id = ?
  1272. `
  1273. _, err = db.Exec(query,
  1274. cid,
  1275. user.Id,
  1276. )
  1277. if err != nil { return err }
  1278. user.CustomerId = cid
  1279. return nil
  1280. }
  1281. func updateAddress(address Address, db *sql.DB) error {
  1282. query := `
  1283. UPDATE address
  1284. SET
  1285. full_address = CASE @e := ? WHEN '' THEN full_address ELSE @e END,
  1286. street = CASE @fn := ? WHEN '' THEN street ELSE @fn END,
  1287. city = CASE @ln := ? WHEN '' THEN city ELSE @ln END,
  1288. region = CASE @r := ? WHEN '' THEN region ELSE @r END,
  1289. country = CASE @r := ? WHEN '' THEN country ELSE @r END,
  1290. zip = CASE @r := ? WHEN '' THEN zip ELSE @r END
  1291. WHERE id = ?
  1292. `
  1293. _, err := db.Exec(query,
  1294. address.Full,
  1295. address.Street,
  1296. address.City,
  1297. address.Region,
  1298. address.Country,
  1299. address.Zip,
  1300. address.Id,
  1301. )
  1302. return err
  1303. }
  1304. func updateUser(user User, db *sql.DB) error {
  1305. query := `
  1306. UPDATE user
  1307. SET
  1308. email = CASE @e := ? WHEN '' THEN email ELSE @e END,
  1309. first_name = CASE @fn := ? WHEN '' THEN first_name ELSE @fn END,
  1310. last_name = CASE @ln := ? WHEN '' THEN last_name ELSE @ln END,
  1311. role = CASE @r := ? WHEN '' THEN role ELSE @r END,
  1312. password = CASE @p := ? WHEN '' THEN password ELSE sha2(@p, 256) END
  1313. WHERE id = ?
  1314. `
  1315. _, err := db.Exec(query,
  1316. user.Email,
  1317. user.FirstName,
  1318. user.LastName,
  1319. user.Role,
  1320. user.Password,
  1321. user.Id,
  1322. )
  1323. return err
  1324. }
  1325. func getUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1326. claims, err := getClaims(r)
  1327. if err != nil {
  1328. w.WriteHeader(500)
  1329. return
  1330. }
  1331. user, err := queryUser(db, claims.Id)
  1332. if err != nil {
  1333. w.WriteHeader(422)
  1334. log.Println(err)
  1335. return
  1336. }
  1337. json.NewEncoder(w).Encode(user)
  1338. }
  1339. func getUsers(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1340. users, err := queryUsers(db, 0)
  1341. if err != nil {
  1342. w.WriteHeader(http.StatusInternalServerError)
  1343. return
  1344. }
  1345. json.NewEncoder(w).Encode(users)
  1346. }
  1347. // Updates a user using only specified values in the JSON body
  1348. func setUser(user User, db *sql.DB) error {
  1349. _, err := mail.ParseAddress(user.Email)
  1350. if err != nil {
  1351. return err
  1352. }
  1353. if roles[user.Role] == 0 {
  1354. return errors.New("Invalid role")
  1355. }
  1356. err = updateUser(user, db)
  1357. if err != nil {
  1358. return err
  1359. }
  1360. err = updateAddress(user.Address, db)
  1361. if err != nil {
  1362. return err
  1363. }
  1364. return nil
  1365. }
  1366. func patchUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1367. var user User
  1368. err := json.NewDecoder(r.Body).Decode(&user)
  1369. if err != nil {
  1370. http.Error(w, "Invalid fields", 422)
  1371. return
  1372. }
  1373. err = setUser(user, db)
  1374. if err != nil {
  1375. http.Error(w, err.Error(), 422)
  1376. return
  1377. }
  1378. }
  1379. // Update specified fields of the user specified in the claim
  1380. func patchSelf(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1381. claim, err := getClaims(r)
  1382. var user User
  1383. json.NewDecoder(r.Body).Decode(&user)
  1384. // First check that the target user to be updated is the same as the claim id, and
  1385. // their role is unchanged.
  1386. if err != nil || claim.Id != user.Id {
  1387. http.Error(w, "Target user's id does not match claim.", 401)
  1388. return
  1389. }
  1390. if claim.Role != user.Role && user.Role != "" {
  1391. http.Error(w, "Administrator required to escalate role.", 401)
  1392. return
  1393. }
  1394. err = setUser(user, db)
  1395. if err != nil {
  1396. http.Error(w, err.Error(), 422)
  1397. return
  1398. }
  1399. }
  1400. func deleteUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1401. var user User
  1402. err := json.NewDecoder(r.Body).Decode(&user)
  1403. if err != nil {
  1404. http.Error(w, "Invalid fields.", 422)
  1405. return
  1406. }
  1407. query := `DELETE FROM user WHERE id = ?`
  1408. _, err = db.Exec(query, user.Id)
  1409. if err != nil {
  1410. http.Error(w, "Could not delete.", 500)
  1411. }
  1412. }
  1413. // Checks if a user's entries are reasonable before database insertion.
  1414. // This function is very important because it is the only thing preventing
  1415. // anyone from creating an admin user. These error messages are displayed to
  1416. // the user.
  1417. func (user *User) validate() error {
  1418. _, err := mail.ParseAddress(user.Email)
  1419. if err != nil {
  1420. return errors.New("Invalid email.")
  1421. }
  1422. if roles[user.Role] == 0 {
  1423. return errors.New("Invalid role.")
  1424. }
  1425. if roles[user.Role] == roles["Admin"] {
  1426. return errors.New("New user cannot be an Admin.")
  1427. }
  1428. if user.FirstName == "" {
  1429. return errors.New("Given name cannot be empty.")
  1430. }
  1431. if user.LastName == "" {
  1432. return errors.New("Surname cannot be empty.")
  1433. }
  1434. if user.Password == "" {
  1435. return errors.New("Empty password")
  1436. }
  1437. return nil
  1438. }
  1439. func createUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1440. var user User
  1441. err := json.NewDecoder(r.Body).Decode(&user)
  1442. if err != nil {
  1443. http.Error(w, "Invalid fields.", 422)
  1444. return
  1445. }
  1446. user.Role = "User"
  1447. user.Status = "Trial"
  1448. err = user.validate()
  1449. if err != nil {
  1450. http.Error(w, err.Error(), 422)
  1451. return
  1452. }
  1453. user.Id, err = insertUser(db, user)
  1454. if err != nil {
  1455. http.Error(w, err.Error(), 422)
  1456. return
  1457. }
  1458. json.NewEncoder(w).Encode(user)
  1459. }
  1460. func checkPassword(db *sql.DB, id int, pass string) bool {
  1461. var p string
  1462. query := `SELECT
  1463. password
  1464. FROM user WHERE user.id = ? AND password = sha2(?, 256)
  1465. `
  1466. row := db.QueryRow(query, id, pass)
  1467. err := row.Scan(&p)
  1468. if err != nil {
  1469. return false
  1470. }
  1471. return true
  1472. }
  1473. func setPassword(db *sql.DB, id int, pass string) error {
  1474. query := `UPDATE user
  1475. SET password = sha2(?, 256)
  1476. WHERE user.id = ?
  1477. `
  1478. _, err := db.Exec(query, pass, id)
  1479. if err != nil {
  1480. return errors.New("Could not insert password.")
  1481. }
  1482. return nil
  1483. }
  1484. func changePassword(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1485. var pass Password
  1486. claim, err := getClaims(r)
  1487. err = json.NewDecoder(r.Body).Decode(&pass)
  1488. if err != nil {
  1489. http.Error(w, "Bad fields.", 422)
  1490. return
  1491. }
  1492. if checkPassword(db, claim.Id, pass.Old) {
  1493. err = setPassword(db, claim.Id, pass.New)
  1494. } else {
  1495. http.Error(w, "Incorrect old password.", 401)
  1496. return
  1497. }
  1498. if err != nil {
  1499. http.Error(w, err.Error(), 500)
  1500. return
  1501. }
  1502. }
  1503. func fetchAvatar(db *sql.DB, user int) ([]byte, error) {
  1504. var img []byte
  1505. var query string
  1506. var err error
  1507. query = `SELECT
  1508. avatar
  1509. FROM user WHERE user.id = ?
  1510. `
  1511. row := db.QueryRow(query, user)
  1512. err = row.Scan(&img)
  1513. if err != nil {
  1514. return img, err
  1515. }
  1516. return img, nil
  1517. }
  1518. func insertAvatar(db *sql.DB, user int, img []byte) error {
  1519. query := `UPDATE user
  1520. SET avatar = ?
  1521. WHERE id = ?
  1522. `
  1523. _, err := db.Exec(query, img, user)
  1524. if err != nil {
  1525. return err
  1526. }
  1527. return nil
  1528. }
  1529. func fetchLetterhead(db *sql.DB, user int) ([]byte, error) {
  1530. var img []byte
  1531. var query string
  1532. var err error
  1533. query = `SELECT
  1534. letterhead
  1535. FROM user WHERE user.id = ?
  1536. `
  1537. row := db.QueryRow(query, user)
  1538. err = row.Scan(&img)
  1539. if err != nil {
  1540. return img, err
  1541. }
  1542. return img, nil
  1543. }
  1544. func insertLetterhead(db *sql.DB, user int, img []byte) error {
  1545. query := `UPDATE user
  1546. SET letterhead = ?
  1547. WHERE id = ?
  1548. `
  1549. _, err := db.Exec(query, img, user)
  1550. if err != nil {
  1551. return err
  1552. }
  1553. return nil
  1554. }
  1555. func setAvatar(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1556. var validTypes []string = []string{"image/png", "image/jpeg"}
  1557. var isValidType bool
  1558. claims, err := getClaims(r)
  1559. if err != nil {
  1560. http.Error(w, "Invalid token.", 422)
  1561. return
  1562. }
  1563. img, err := io.ReadAll(r.Body)
  1564. if err != nil {
  1565. http.Error(w, "Invalid file.", 422)
  1566. return
  1567. }
  1568. for _, v := range validTypes {
  1569. if v == http.DetectContentType(img) {
  1570. isValidType = true
  1571. }
  1572. }
  1573. if !isValidType {
  1574. http.Error(w, "Invalid file type.", 422)
  1575. return
  1576. }
  1577. err = insertAvatar(db, claims.Id, img)
  1578. if err != nil {
  1579. http.Error(w, "Could not insert.", 500)
  1580. return
  1581. }
  1582. }
  1583. func getAvatar(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1584. claims, err := getClaims(r)
  1585. if err != nil {
  1586. http.Error(w, "Invalid token.", 422)
  1587. return
  1588. }
  1589. img, err := fetchAvatar(db, claims.Id)
  1590. if err != nil {
  1591. http.Error(w, "Could not retrieve.", 500)
  1592. return
  1593. }
  1594. w.Header().Set("Content-Type", http.DetectContentType(img))
  1595. w.Write(img)
  1596. }
  1597. func setLetterhead(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1598. var validTypes []string = []string{"image/png", "image/jpeg"}
  1599. var isValidType bool
  1600. claims, err := getClaims(r)
  1601. if err != nil {
  1602. http.Error(w, "Invalid token.", 422)
  1603. return
  1604. }
  1605. img, err := io.ReadAll(r.Body)
  1606. if err != nil {
  1607. http.Error(w, "Invalid file.", 422)
  1608. return
  1609. }
  1610. for _, v := range validTypes {
  1611. if v == http.DetectContentType(img) {
  1612. isValidType = true
  1613. }
  1614. }
  1615. if !isValidType {
  1616. http.Error(w, "Invalid file type.", 422)
  1617. return
  1618. }
  1619. err = insertLetterhead(db, claims.Id, img)
  1620. if err != nil {
  1621. http.Error(w, "Could not insert.", 500)
  1622. return
  1623. }
  1624. }
  1625. func getLetterhead(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1626. claims, err := getClaims(r)
  1627. if err != nil {
  1628. http.Error(w, "Invalid token.", 422)
  1629. return
  1630. }
  1631. img, err := fetchLetterhead(db, claims.Id)
  1632. if err != nil {
  1633. http.Error(w, "Could not retrieve.", 500)
  1634. return
  1635. }
  1636. w.Header().Set("Content-Type", http.DetectContentType(img))
  1637. w.Write(img)
  1638. }
  1639. func queryBorrower(db *sql.DB, id int) (Borrower, error) {
  1640. var borrower Borrower
  1641. var query string
  1642. var err error
  1643. query = `SELECT
  1644. l.id,
  1645. l.credit_score,
  1646. l.num,
  1647. l.monthly_income
  1648. FROM borrower l WHERE l.id = ?
  1649. `
  1650. row := db.QueryRow(query, id)
  1651. err = row.Scan(
  1652. borrower.Id,
  1653. borrower.Credit,
  1654. borrower.Num,
  1655. borrower.Income,
  1656. )
  1657. if err != nil {
  1658. return borrower, err
  1659. }
  1660. return borrower, nil
  1661. }
  1662. // Must have an estimate ID 'e', but not necessarily a loan id 'id'
  1663. func getResults(db *sql.DB, e int, id int) ([]Result, error) {
  1664. var results []Result
  1665. var query string
  1666. var rows *sql.Rows
  1667. var err error
  1668. query = `SELECT
  1669. r.id,
  1670. loan_id,
  1671. loan_payment,
  1672. total_monthly,
  1673. total_fees,
  1674. total_credits,
  1675. cash_to_close
  1676. FROM estimate_result r
  1677. INNER JOIN loan
  1678. ON r.loan_id = loan.id
  1679. WHERE r.id = CASE @e := ? WHEN 0 THEN r.id ELSE @e END
  1680. AND loan.estimate_id = ?
  1681. `
  1682. rows, err = db.Query(query, id, e)
  1683. if err != nil {
  1684. return results, err
  1685. }
  1686. defer rows.Close()
  1687. for rows.Next() {
  1688. var result Result
  1689. if err := rows.Scan(
  1690. &result.Id,
  1691. &result.LoanId,
  1692. &result.LoanPayment,
  1693. &result.TotalMonthly,
  1694. &result.TotalFees,
  1695. &result.TotalCredits,
  1696. &result.CashToClose,
  1697. ); err != nil {
  1698. return results, err
  1699. }
  1700. results = append(results, result)
  1701. }
  1702. // Prevents runtime panics
  1703. // if len(results) == 0 { return results, errors.New("Result not found.") }
  1704. return results, nil
  1705. }
  1706. // Retrieve an estimate result with a specified loan id
  1707. func getResult(db *sql.DB, loan int) (Result, error) {
  1708. var result Result
  1709. var query string
  1710. var err error
  1711. query = `SELECT
  1712. r.id,
  1713. loan_id,
  1714. loan_payment,
  1715. total_monthly,
  1716. total_fees,
  1717. total_credits,
  1718. cash_to_close
  1719. FROM estimate_result r
  1720. INNER JOIN loan
  1721. ON r.loan_id = loan.id
  1722. WHERE loan.Id = ?
  1723. `
  1724. row := db.QueryRow(query, loan)
  1725. err = row.Scan(
  1726. &result.Id,
  1727. &result.LoanId,
  1728. &result.LoanPayment,
  1729. &result.TotalMonthly,
  1730. &result.TotalFees,
  1731. &result.TotalCredits,
  1732. &result.CashToClose,
  1733. )
  1734. if err != nil {
  1735. return result, err
  1736. }
  1737. return result, nil
  1738. }
  1739. // Must have an estimate ID 'e', but not necessarily a loan id 'id'
  1740. func getLoans(db *sql.DB, e int, id int) ([]Loan, error) {
  1741. var loans []Loan
  1742. var query string
  1743. var rows *sql.Rows
  1744. var err error
  1745. query = `SELECT
  1746. l.id,
  1747. l.type_id,
  1748. l.estimate_id,
  1749. l.amount,
  1750. l.term,
  1751. l.interest,
  1752. l.ltv,
  1753. l.dti,
  1754. l.hoi,
  1755. l.tax,
  1756. l.name
  1757. FROM loan l WHERE l.id = CASE @e := ? WHEN 0 THEN l.id ELSE @e END AND
  1758. l.estimate_id = ?
  1759. `
  1760. rows, err = db.Query(query, id, e)
  1761. if err != nil {
  1762. return loans, err
  1763. }
  1764. defer rows.Close()
  1765. for rows.Next() {
  1766. var loan Loan
  1767. if err := rows.Scan(
  1768. &loan.Id,
  1769. &loan.Type.Id,
  1770. &loan.EstimateId,
  1771. &loan.Amount,
  1772. &loan.Term,
  1773. &loan.Interest,
  1774. &loan.Ltv,
  1775. &loan.Dti,
  1776. &loan.Hoi,
  1777. &loan.Tax,
  1778. &loan.Name,
  1779. ); err != nil {
  1780. return loans, err
  1781. }
  1782. mi, err := getMi(db, loan.Id)
  1783. if err != nil {
  1784. return loans, err
  1785. }
  1786. loan.Mi = mi
  1787. fees, err := getFees(db, loan.Id)
  1788. if err != nil {
  1789. return loans, err
  1790. }
  1791. loan.Fees = fees
  1792. loan.Result, err = getResult(db, loan.Id)
  1793. if err != nil {
  1794. return loans, err
  1795. }
  1796. loan.Type, err = getLoanType(db, loan.Type.Id)
  1797. if err != nil {
  1798. return loans, err
  1799. }
  1800. loans = append(loans, loan)
  1801. }
  1802. // Prevents runtime panics
  1803. if len(loans) == 0 {
  1804. return loans, errors.New("Loan not found.")
  1805. }
  1806. return loans, nil
  1807. }
  1808. func getEstimate(db *sql.DB, id int) (Estimate, error) {
  1809. estimates, err := getEstimates(db, id, 0)
  1810. if err != nil {
  1811. return Estimate{}, err
  1812. }
  1813. return estimates[0], nil
  1814. }
  1815. func getEstimates(db *sql.DB, id int, user int) ([]Estimate, error) {
  1816. var estimates []Estimate
  1817. var query string
  1818. var rows *sql.Rows
  1819. var err error
  1820. query = `SELECT
  1821. id,
  1822. user_id,
  1823. transaction,
  1824. price,
  1825. property,
  1826. occupancy,
  1827. zip,
  1828. pud
  1829. FROM estimate WHERE id = CASE @e := ? WHEN 0 THEN id ELSE @e END AND
  1830. user_id = CASE @e := ? WHEN 0 THEN user_id ELSE @e END
  1831. `
  1832. rows, err = db.Query(query, id, user)
  1833. if err != nil {
  1834. return estimates, err
  1835. }
  1836. defer rows.Close()
  1837. for rows.Next() {
  1838. var estimate Estimate
  1839. if err := rows.Scan(
  1840. &estimate.Id,
  1841. &estimate.User,
  1842. &estimate.Transaction,
  1843. &estimate.Price,
  1844. &estimate.Property,
  1845. &estimate.Occupancy,
  1846. &estimate.Zip,
  1847. &estimate.Pud,
  1848. ); err != nil {
  1849. return estimates, err
  1850. }
  1851. err := estimate.getBorrower(db)
  1852. if err != nil {
  1853. return estimates, err
  1854. }
  1855. estimates = append(estimates, estimate)
  1856. }
  1857. // Prevents runtime panics
  1858. if len(estimates) == 0 {
  1859. return estimates, errors.New("Estimate not found.")
  1860. }
  1861. for i := range estimates {
  1862. estimates[i].Loans, err = getLoans(db, estimates[i].Id, 0)
  1863. if err != nil {
  1864. return estimates, err
  1865. }
  1866. }
  1867. return estimates, nil
  1868. }
  1869. func queryETemplates(db *sql.DB, id int, user int) ([]ETemplate, error) {
  1870. var eTemplates []ETemplate
  1871. var query string
  1872. var rows *sql.Rows
  1873. var err error
  1874. query = `SELECT
  1875. id,
  1876. estimate_id,
  1877. user_id,
  1878. branch_id
  1879. FROM estimate_template WHERE id = CASE @e := ? WHEN 0 THEN id ELSE @e END AND
  1880. user_id = CASE @e := ? WHEN 0 THEN user_id ELSE @e END
  1881. `
  1882. rows, err = db.Query(query, id, user)
  1883. if err != nil {
  1884. return eTemplates, err
  1885. }
  1886. defer rows.Close()
  1887. for rows.Next() {
  1888. var e ETemplate
  1889. if err := rows.Scan(
  1890. &e.Id,
  1891. &e.Estimate.Id,
  1892. &e.UserId,
  1893. &e.BranchId,
  1894. ); err != nil {
  1895. return eTemplates, err
  1896. }
  1897. e.Estimate, err = getEstimate(db, e.Estimate.Id)
  1898. if err != nil {
  1899. return eTemplates, err
  1900. }
  1901. eTemplates = append(eTemplates, e)
  1902. }
  1903. return eTemplates, nil
  1904. }
  1905. // Accepts a borrower struct and returns the id of the inserted borrower and
  1906. // any related error.
  1907. func (estimate *Estimate) insertETemplate(db *sql.DB, user int, branch int) error {
  1908. var query string
  1909. var err error
  1910. query = `INSERT INTO estimate_template
  1911. (
  1912. estimate_id,
  1913. user_id,
  1914. branch_id
  1915. )
  1916. VALUES (?, ?, ?)
  1917. `
  1918. _, err = db.Exec(query,
  1919. estimate.Id,
  1920. user,
  1921. branch,
  1922. )
  1923. if err != nil {
  1924. return err
  1925. }
  1926. return nil
  1927. }
  1928. // Accepts a borrower struct and returns the id of the inserted borrower and
  1929. // any related error.
  1930. func (estimate *Estimate) insertBorrower(db *sql.DB) error {
  1931. var query string
  1932. var row *sql.Row
  1933. var err error
  1934. query = `INSERT INTO borrower
  1935. (
  1936. estimate_id,
  1937. credit_score,
  1938. monthly_income,
  1939. num
  1940. )
  1941. VALUES (?, ?, ?, ?)
  1942. RETURNING id
  1943. `
  1944. row = db.QueryRow(query,
  1945. estimate.Id,
  1946. estimate.Borrower.Credit,
  1947. estimate.Borrower.Income,
  1948. estimate.Borrower.Num,
  1949. )
  1950. err = row.Scan(&estimate.Borrower.Id)
  1951. if err != nil {
  1952. return err
  1953. }
  1954. return nil
  1955. }
  1956. func insertMi(db *sql.DB, mi MI) (int, error) {
  1957. var id int
  1958. query := `INSERT INTO mi
  1959. (
  1960. type,
  1961. label,
  1962. lender,
  1963. rate,
  1964. premium,
  1965. upfront,
  1966. five_year_total,
  1967. initial_premium,
  1968. initial_rate,
  1969. initial_amount
  1970. )
  1971. VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  1972. RETURNING id`
  1973. row := db.QueryRow(query,
  1974. &mi.Type,
  1975. &mi.Label,
  1976. &mi.Lender,
  1977. &mi.Rate,
  1978. &mi.Premium,
  1979. &mi.Upfront,
  1980. &mi.FiveYearTotal,
  1981. &mi.InitialAllInPremium,
  1982. &mi.InitialAllInRate,
  1983. &mi.InitialAmount,
  1984. )
  1985. err := row.Scan(&id)
  1986. if err != nil {
  1987. return 0, err
  1988. }
  1989. return id, nil
  1990. }
  1991. func insertFee(db *sql.DB, fee Fee) (int, error) {
  1992. var id int
  1993. query := `INSERT INTO fee
  1994. (loan_id, amount, perc, type, notes, name, category)
  1995. VALUES (?, ?, ?, ?, ?, ?, ?)
  1996. RETURNING id`
  1997. row := db.QueryRow(query,
  1998. fee.LoanId,
  1999. fee.Amount,
  2000. fee.Perc,
  2001. fee.Type,
  2002. fee.Notes,
  2003. fee.Name,
  2004. fee.Category,
  2005. )
  2006. err := row.Scan(&id)
  2007. if err != nil {
  2008. return 0, err
  2009. }
  2010. return id, nil
  2011. }
  2012. func insertLoanType(db *sql.DB, lt LoanType) (int, error) {
  2013. var id int
  2014. query := `INSERT INTO loan_type (branch_id, user_id, name)
  2015. VALUES (NULLIF(?, 0), NULLIF(?, 0), ?)
  2016. RETURNING id`
  2017. row := db.QueryRow(query,
  2018. lt.Branch,
  2019. lt.User,
  2020. lt.Name,
  2021. )
  2022. err := row.Scan(&id)
  2023. if err != nil {
  2024. return 0, err
  2025. }
  2026. return id, nil
  2027. }
  2028. func (loan *Loan) insertLoan(db *sql.DB) error {
  2029. var query string
  2030. var row *sql.Row
  2031. var err error
  2032. query = `INSERT INTO loan
  2033. (
  2034. estimate_id,
  2035. type_id,
  2036. amount,
  2037. term,
  2038. interest,
  2039. ltv,
  2040. dti,
  2041. hoi,
  2042. tax,
  2043. name
  2044. )
  2045. VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  2046. RETURNING id
  2047. `
  2048. row = db.QueryRow(query,
  2049. loan.EstimateId,
  2050. loan.Type.Id,
  2051. loan.Amount,
  2052. loan.Term,
  2053. loan.Interest,
  2054. loan.Ltv,
  2055. loan.Dti,
  2056. loan.Hoi,
  2057. loan.Tax,
  2058. loan.Name,
  2059. )
  2060. err = row.Scan(&loan.Id)
  2061. if err != nil {
  2062. return err
  2063. }
  2064. _, err = insertMi(db, loan.Mi)
  2065. if err != nil {
  2066. return err
  2067. }
  2068. for i := range loan.Fees {
  2069. loan.Fees[i].LoanId = loan.Id
  2070. _, err := insertFee(db, loan.Fees[i])
  2071. if err != nil {
  2072. return err
  2073. }
  2074. }
  2075. return nil
  2076. }
  2077. func (estimate *Estimate) insertEstimate(db *sql.DB) error {
  2078. var query string
  2079. var row *sql.Row
  2080. var err error
  2081. // var id int // Inserted estimate's id
  2082. query = `INSERT INTO estimate
  2083. (
  2084. user_id,
  2085. transaction,
  2086. price,
  2087. property,
  2088. occupancy,
  2089. zip,
  2090. pud
  2091. )
  2092. VALUES (?, ?, ?, ?, ?, ?, ?)
  2093. RETURNING id
  2094. `
  2095. row = db.QueryRow(query,
  2096. estimate.User,
  2097. estimate.Transaction,
  2098. estimate.Price,
  2099. estimate.Property,
  2100. estimate.Occupancy,
  2101. estimate.Zip,
  2102. estimate.Pud,
  2103. )
  2104. err = row.Scan(&estimate.Id)
  2105. if err != nil {
  2106. return err
  2107. }
  2108. err = estimate.insertBorrower(db)
  2109. if err != nil {
  2110. return err
  2111. }
  2112. for i := range estimate.Loans {
  2113. estimate.Loans[i].EstimateId = estimate.Id
  2114. err = estimate.Loans[i].insertLoan(db)
  2115. if err != nil {
  2116. return err
  2117. }
  2118. }
  2119. return nil
  2120. }
  2121. func (estimate *Estimate) del(db *sql.DB, user int) error {
  2122. var query string
  2123. var err error
  2124. query = `DELETE FROM estimate WHERE id = ? AND user_id = ?`
  2125. _, err = db.Exec(query, estimate.Id, user)
  2126. if err != nil {
  2127. return err
  2128. }
  2129. return nil
  2130. }
  2131. func (et *ETemplate) del(db *sql.DB, user int) error {
  2132. var query string
  2133. var err error
  2134. query = `DELETE FROM estimate_template WHERE id = ? AND user_id = ?`
  2135. _, err = db.Exec(query, et.Id, user)
  2136. if err != nil {
  2137. return err
  2138. }
  2139. return nil
  2140. }
  2141. func (eTemplate *ETemplate) insert(db *sql.DB) error {
  2142. var query string
  2143. var row *sql.Row
  2144. var err error
  2145. query = `INSERT INTO estimate_template
  2146. (
  2147. user_id,
  2148. branch_id,
  2149. estimate_id,
  2150. )
  2151. VALUES (?, ?, ?)
  2152. RETURNING id
  2153. `
  2154. row = db.QueryRow(query,
  2155. eTemplate.UserId,
  2156. eTemplate.BranchId,
  2157. eTemplate.Estimate.Id,
  2158. )
  2159. err = row.Scan(&eTemplate.Id)
  2160. if err != nil {
  2161. return err
  2162. }
  2163. return nil
  2164. }
  2165. func createEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2166. var estimate Estimate
  2167. err := json.NewDecoder(r.Body).Decode(&estimate)
  2168. if err != nil {
  2169. http.Error(w, "Invalid fields.", 422)
  2170. return
  2171. }
  2172. claims, err := getClaims(r)
  2173. estimate.User = claims.Id
  2174. err = estimate.insertEstimate(db)
  2175. if err != nil {
  2176. http.Error(w, err.Error(), 422)
  2177. return
  2178. }
  2179. estimate.makeResults()
  2180. err = estimate.insertResults(db)
  2181. if err != nil {
  2182. http.Error(w, err.Error(), 500)
  2183. return
  2184. }
  2185. e, err := getEstimates(db, estimate.Id, 0)
  2186. if err != nil {
  2187. http.Error(w, err.Error(), 500)
  2188. return
  2189. }
  2190. json.NewEncoder(w).Encode(e[0])
  2191. }
  2192. // Query all estimates that belong to the current user
  2193. func fetchEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2194. var estimates []Estimate
  2195. claims, err := getClaims(r)
  2196. estimates, err = getEstimates(db, 0, claims.Id)
  2197. if err != nil {
  2198. http.Error(w, err.Error(), 500)
  2199. return
  2200. }
  2201. json.NewEncoder(w).Encode(estimates)
  2202. }
  2203. func checkConventional(l Loan, b Borrower) error {
  2204. if b.Credit < 620 {
  2205. return errors.New("Credit score too low for conventional loan")
  2206. }
  2207. // Buyer needs to put down 20% to avoid mortgage insurance
  2208. if l.Ltv > 80 && l.Mi.Rate == 0 {
  2209. return errors.New(fmt.Sprintln(
  2210. l.Name,
  2211. "down payment must be 20% to avoid insurance",
  2212. ))
  2213. }
  2214. return nil
  2215. }
  2216. func checkFHA(l Loan, b Borrower) error {
  2217. if b.Credit < 500 {
  2218. return errors.New("Credit score too low for FHA loan")
  2219. }
  2220. if l.Ltv > 96.5 {
  2221. return errors.New("FHA down payment must be at least 3.5%")
  2222. }
  2223. if b.Credit < 580 && l.Ltv > 90 {
  2224. return errors.New("FHA down payment must be at least 10%")
  2225. }
  2226. // Debt-to-income must be below 45% if credit score is below 580.
  2227. if b.Credit < 580 && l.Dti > 45 {
  2228. return errors.New(fmt.Sprintln(
  2229. l.Name, "debt to income is too high for credit score.",
  2230. ))
  2231. }
  2232. return nil
  2233. }
  2234. // Loan option for veterans with no set rules. Mainly placeholder.
  2235. func checkVA(l Loan, b Borrower) error {
  2236. return nil
  2237. }
  2238. // Loan option for residents of rural areas with no set rules.
  2239. // Mainly placeholder.
  2240. func checkUSDA(l Loan, b Borrower) error {
  2241. return nil
  2242. }
  2243. // Should also check loan amount limit maybe with an API.
  2244. func checkEstimate(e Estimate) error {
  2245. if e.Property == "" {
  2246. return errors.New("Empty property type")
  2247. }
  2248. if e.Price == 0 {
  2249. return errors.New("Empty property price")
  2250. }
  2251. if e.Borrower.Num == 0 {
  2252. return errors.New("Missing number of borrowers")
  2253. }
  2254. if e.Borrower.Credit == 0 {
  2255. return errors.New("Missing borrower credit score")
  2256. }
  2257. if e.Borrower.Income == 0 {
  2258. return errors.New("Missing borrower credit income")
  2259. }
  2260. for _, l := range e.Loans {
  2261. if l.Amount == 0 {
  2262. return errors.New(fmt.Sprintln(l.Name, "loan amount cannot be zero"))
  2263. }
  2264. if l.Term == 0 {
  2265. return errors.New(fmt.Sprintln(l.Name, "loan term cannot be zero"))
  2266. }
  2267. if l.Interest == 0 {
  2268. return errors.New(fmt.Sprintln(l.Name, "loan interest cannot be zero"))
  2269. }
  2270. // Can be used to check rules for specific loan types
  2271. var err error
  2272. switch l.Type.Id {
  2273. case 1:
  2274. err = checkConventional(l, e.Borrower)
  2275. case 2:
  2276. err = checkFHA(l, e.Borrower)
  2277. case 3:
  2278. err = checkVA(l, e.Borrower)
  2279. case 4:
  2280. err = checkUSDA(l, e.Borrower)
  2281. default:
  2282. err = errors.New("Invalid loan type")
  2283. }
  2284. if err != nil {
  2285. return err
  2286. }
  2287. }
  2288. return nil
  2289. }
  2290. func validateEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2291. var estimate Estimate
  2292. err := json.NewDecoder(r.Body).Decode(&estimate)
  2293. if err != nil {
  2294. http.Error(w, err.Error(), 422)
  2295. return
  2296. }
  2297. err = checkEstimate(estimate)
  2298. if err != nil {
  2299. http.Error(w, err.Error(), 406)
  2300. return
  2301. }
  2302. }
  2303. func checkPdf(w http.ResponseWriter, r *http.Request) {
  2304. db, err := sql.Open("mysql",
  2305. fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/skouter_dev",
  2306. os.Getenv("DBUser"),
  2307. os.Getenv("DBPass")))
  2308. // w.Header().Set("Content-Type", "application/json; charset=UTF-8")
  2309. err = db.Ping()
  2310. if err != nil {
  2311. fmt.Println("Bad database configuration: %v\n", err)
  2312. panic(err)
  2313. // maybe os.Exit(1) instead
  2314. }
  2315. estimates, err := getEstimates(db, 1, 0)
  2316. if err != nil {
  2317. w.WriteHeader(500)
  2318. return
  2319. }
  2320. // claims, err := getClaims(r)
  2321. if err != nil {
  2322. w.WriteHeader(500)
  2323. return
  2324. }
  2325. user, err := queryUser(db, 1)
  2326. info := Report{
  2327. Title: "test PDF",
  2328. Name: "idk-random-name",
  2329. User: user,
  2330. Estimate: estimates[0],
  2331. }
  2332. avatar, err := fetchAvatar(db, info.User.Id)
  2333. letterhead, err := fetchLetterhead(db, info.User.Id)
  2334. info.Avatar =
  2335. base64.StdEncoding.EncodeToString(avatar)
  2336. info.Letterhead =
  2337. base64.StdEncoding.EncodeToString(letterhead)
  2338. for l := range info.Estimate.Loans {
  2339. loan := info.Estimate.Loans[l]
  2340. for f := range info.Estimate.Loans[l].Fees {
  2341. if info.Estimate.Loans[l].Fees[f].Amount < 0 {
  2342. loan.Credits = append(loan.Credits, loan.Fees[f])
  2343. }
  2344. }
  2345. }
  2346. err = pages["report"].tpl.ExecuteTemplate(w, "master.tpl", info)
  2347. if err != nil {
  2348. fmt.Println(err)
  2349. }
  2350. }
  2351. func getETemplates(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2352. claims, err := getClaims(r)
  2353. et, err := queryETemplates(db, 0, claims.Id)
  2354. if err != nil {
  2355. http.Error(w, err.Error(), 500)
  2356. return
  2357. }
  2358. json.NewEncoder(w).Encode(et)
  2359. }
  2360. func createETemplate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2361. var estimate Estimate
  2362. err := json.NewDecoder(r.Body).Decode(&estimate)
  2363. if err != nil {
  2364. http.Error(w, err.Error(), 422)
  2365. return
  2366. }
  2367. claims, err := getClaims(r)
  2368. if err != nil {
  2369. http.Error(w, err.Error(), 422)
  2370. return
  2371. }
  2372. err = estimate.insertETemplate(db, claims.Id, 0)
  2373. if err != nil {
  2374. http.Error(w, err.Error(), 500)
  2375. return
  2376. }
  2377. }
  2378. func getPdf(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2379. var estimate Estimate
  2380. err := json.NewDecoder(r.Body).Decode(&estimate)
  2381. cmd := exec.Command("wkhtmltopdf", "-", "-")
  2382. stdout, err := cmd.StdoutPipe()
  2383. if err != nil {
  2384. w.WriteHeader(500)
  2385. log.Println(err)
  2386. return
  2387. }
  2388. stdin, err := cmd.StdinPipe()
  2389. if err != nil {
  2390. w.WriteHeader(500)
  2391. log.Println(err)
  2392. return
  2393. }
  2394. if err := cmd.Start(); err != nil {
  2395. log.Fatal(err)
  2396. }
  2397. claims, err := getClaims(r)
  2398. if err != nil {
  2399. w.WriteHeader(500)
  2400. log.Println(err)
  2401. return
  2402. }
  2403. user, err := queryUser(db, claims.Id)
  2404. info := Report{
  2405. Title: "test PDF",
  2406. Name: "idk-random-name",
  2407. User: user,
  2408. Estimate: estimate,
  2409. }
  2410. avatar, err := fetchAvatar(db, info.User.Id)
  2411. letterhead, err := fetchLetterhead(db, info.User.Id)
  2412. if len(avatar) > 1 {
  2413. info.Avatar =
  2414. base64.StdEncoding.EncodeToString(avatar)
  2415. }
  2416. if len(letterhead) > 1 {
  2417. info.Letterhead =
  2418. base64.StdEncoding.EncodeToString(letterhead)
  2419. }
  2420. err = pages["report"].tpl.ExecuteTemplate(stdin, "master.tpl", info)
  2421. if err != nil {
  2422. w.WriteHeader(500)
  2423. log.Println(err)
  2424. return
  2425. }
  2426. stdin.Close()
  2427. buf, err := io.ReadAll(stdout)
  2428. if _, err := w.Write(buf); err != nil {
  2429. w.WriteHeader(500)
  2430. log.Println(err)
  2431. return
  2432. }
  2433. if err := cmd.Wait(); err != nil {
  2434. log.Println(err)
  2435. return
  2436. }
  2437. }
  2438. func clipLetterhead(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2439. var validTypes []string = []string{"image/png", "image/jpeg"}
  2440. var isValidType bool
  2441. var err error
  2442. // claims, err := getClaims(r)
  2443. if err != nil {
  2444. http.Error(w, "Invalid token.", 422)
  2445. return
  2446. }
  2447. img, t, err := image.Decode(r.Body)
  2448. if err != nil {
  2449. http.Error(w, "Invalid file, JPEG and PNG only.", 422)
  2450. return
  2451. }
  2452. for _, v := range validTypes {
  2453. if v == "image/"+t {
  2454. isValidType = true
  2455. }
  2456. }
  2457. if !isValidType {
  2458. http.Error(w, "Invalid file type.", 422)
  2459. return
  2460. }
  2461. g := gift.New(
  2462. gift.ResizeToFit(400, 200, gift.LanczosResampling),
  2463. )
  2464. dst := image.NewRGBA(g.Bounds(img.Bounds()))
  2465. g.Draw(dst, img)
  2466. w.Header().Set("Content-Type", "image/png")
  2467. err = png.Encode(w, dst)
  2468. if err != nil {
  2469. http.Error(w, "Error encoding.", 500)
  2470. return
  2471. }
  2472. }
  2473. func createCustomer(name string, email string, address Address) (
  2474. stripe.Customer, error) {
  2475. params := &stripe.CustomerParams{
  2476. Email: stripe.String(email),
  2477. Name: stripe.String(name),
  2478. Address: &stripe.AddressParams{
  2479. City: stripe.String(address.City),
  2480. Country: stripe.String(address.Country),
  2481. Line1: stripe.String(address.Street),
  2482. PostalCode: stripe.String(address.Zip),
  2483. State: stripe.String(address.Region),
  2484. },
  2485. };
  2486. result, err := customer.New(params)
  2487. return *result, err
  2488. }
  2489. // Initiates a new standard subscription using a given customer ID
  2490. func createSubscription(cid string) (*stripe.Subscription, error) {
  2491. // Automatically save the payment method to the subscription
  2492. // when the first payment is successful.
  2493. paymentSettings := &stripe.SubscriptionPaymentSettingsParams{
  2494. SaveDefaultPaymentMethod: stripe.String("on_subscription"),
  2495. }
  2496. // Create the subscription. Note we're expanding the Subscription's
  2497. // latest invoice and that invoice's payment_intent
  2498. // so we can pass it to the front end to confirm the payment
  2499. subscriptionParams := &stripe.SubscriptionParams{
  2500. Customer: stripe.String(cid),
  2501. Items: []*stripe.SubscriptionItemsParams{
  2502. {
  2503. Price: stripe.String("price_1OZLK9BPMoXn2pf9kuTAf8rs"),
  2504. },
  2505. },
  2506. PaymentSettings: paymentSettings,
  2507. PaymentBehavior: stripe.String("default_incomplete"),
  2508. }
  2509. subscriptionParams.AddExpand("latest_invoice.payment_intent")
  2510. s, err := subscription.New(subscriptionParams)
  2511. return s, err
  2512. }
  2513. func subscribe(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2514. claims, err := getClaims(r)
  2515. user, err := queryUser(db, claims.Id)
  2516. if err != nil {
  2517. w.WriteHeader(422)
  2518. return
  2519. }
  2520. user.querySub(db)
  2521. var name string = user.FirstName + " " + user.LastName
  2522. if user.CustomerId == "" {
  2523. c, err := createCustomer(name, user.Email, user.Address)
  2524. if err != nil {
  2525. http.Error(w, err.Error(), 500)
  2526. return
  2527. }
  2528. err = user.updateCustomerId(db, c.ID)
  2529. if err != nil {
  2530. http.Error(w, err.Error(), 500)
  2531. return
  2532. }
  2533. }
  2534. if user.Sub.Id == 0 {
  2535. s, err := createSubscription(user.CustomerId)
  2536. if err != nil {
  2537. http.Error(w, err.Error(), 500)
  2538. return
  2539. }
  2540. user.Sub.UserId = user.Id
  2541. user.Sub.StripeId = s.ID
  2542. user.Sub.CustomerId = user.CustomerId
  2543. user.Sub.End = int(s.CurrentPeriodEnd)
  2544. user.Sub.Start = int(s.CurrentPeriodStart)
  2545. user.Sub.ClientSecret = s.LatestInvoice.PaymentIntent.ClientSecret
  2546. user.Sub.PaymentStatus = string(s.LatestInvoice.PaymentIntent.Status)
  2547. err = user.Sub.insertSub(db)
  2548. if err != nil {
  2549. http.Error(w, err.Error(), 500)
  2550. return
  2551. }
  2552. json.NewEncoder(w).Encode(user.Sub)
  2553. } else {
  2554. err = user.Sub.updateSub(db)
  2555. if err != nil {
  2556. http.Error(w, err.Error(), 500)
  2557. return
  2558. }
  2559. json.NewEncoder(w).Encode(user.Sub)
  2560. }
  2561. // Should check that subscription is still valid and has payment intent
  2562. // here
  2563. }
  2564. func api(w http.ResponseWriter, r *http.Request) {
  2565. var args []string
  2566. p := r.URL.Path
  2567. db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s",
  2568. os.Getenv("DBUser"),
  2569. os.Getenv("DBPass"),
  2570. os.Getenv("DBName"),
  2571. ))
  2572. w.Header().Set("Content-Type", "application/json; charset=UTF-8")
  2573. err = db.Ping()
  2574. if err != nil {
  2575. fmt.Println("Bad database configuration: %v\n", err)
  2576. panic(err)
  2577. // maybe os.Exit(1) instead
  2578. }
  2579. switch {
  2580. case match(p, "/api/login", &args) &&
  2581. r.Method == http.MethodPost:
  2582. login(w, db, r)
  2583. case match(p, "/api/token", &args) &&
  2584. r.Method == http.MethodGet && guard(r, 1):
  2585. getToken(w, db, r)
  2586. case match(p, "/api/letterhead", &args) &&
  2587. r.Method == http.MethodPost && guard(r, 1):
  2588. clipLetterhead(w, db, r)
  2589. case match(p, "/api/users", &args) && // Array of all users
  2590. r.Method == http.MethodGet && guard(r, 3):
  2591. getUsers(w, db, r)
  2592. case match(p, "/api/user", &args) &&
  2593. r.Method == http.MethodGet && guard(r, 1):
  2594. getUser(w, db, r)
  2595. case match(p, "/api/user", &args) &&
  2596. r.Method == http.MethodPost:
  2597. createUser(w, db, r)
  2598. case match(p, "/api/user", &args) &&
  2599. r.Method == http.MethodPatch &&
  2600. guard(r, 3): // For admin to modify any user
  2601. patchUser(w, db, r)
  2602. case match(p, "/api/user", &args) &&
  2603. r.Method == http.MethodPatch &&
  2604. guard(r, 1): // For employees to modify own accounts
  2605. patchSelf(w, db, r)
  2606. case match(p, "/api/user", &args) &&
  2607. r.Method == http.MethodDelete &&
  2608. guard(r, 3):
  2609. deleteUser(w, db, r)
  2610. case match(p, "/api/user/avatar", &args) &&
  2611. r.Method == http.MethodGet &&
  2612. guard(r, 1):
  2613. getAvatar(w, db, r)
  2614. case match(p, "/api/user/avatar", &args) &&
  2615. r.Method == http.MethodPost &&
  2616. guard(r, 1):
  2617. setAvatar(w, db, r)
  2618. case match(p, "/api/user/letterhead", &args) &&
  2619. r.Method == http.MethodGet &&
  2620. guard(r, 1):
  2621. getLetterhead(w, db, r)
  2622. case match(p, "/api/user/letterhead", &args) &&
  2623. r.Method == http.MethodPost &&
  2624. guard(r, 1):
  2625. setLetterhead(w, db, r)
  2626. case match(p, "/api/user/password", &args) &&
  2627. r.Method == http.MethodPost &&
  2628. guard(r, 1):
  2629. changePassword(w, db, r)
  2630. case match(p, "/api/user/subscribe", &args) &&
  2631. r.Method == http.MethodPost &&
  2632. guard(r, 1):
  2633. subscribe(w, db, r)
  2634. case match(p, "/api/fees", &args) &&
  2635. r.Method == http.MethodGet &&
  2636. guard(r, 1):
  2637. getFeesTemp(w, db, r)
  2638. case match(p, "/api/fee", &args) &&
  2639. r.Method == http.MethodPost &&
  2640. guard(r, 1):
  2641. createFeesTemp(w, db, r)
  2642. case match(p, "/api/fee", &args) &&
  2643. r.Method == http.MethodDelete &&
  2644. guard(r, 1):
  2645. deleteFeeTemp(w, db, r)
  2646. case match(p, "/api/estimates", &args) &&
  2647. r.Method == http.MethodGet &&
  2648. guard(r, 1):
  2649. fetchEstimate(w, db, r)
  2650. case match(p, "/api/estimate", &args) &&
  2651. r.Method == http.MethodPost &&
  2652. guard(r, 1):
  2653. createEstimate(w, db, r)
  2654. case match(p, "/api/estimate", &args) &&
  2655. r.Method == http.MethodDelete &&
  2656. guard(r, 1):
  2657. deleteEstimate(w, db, r)
  2658. case match(p, "/api/estimate/validate", &args) &&
  2659. r.Method == http.MethodPost &&
  2660. guard(r, 1):
  2661. validateEstimate(w, db, r)
  2662. case match(p, "/api/estimate/summarize", &args) &&
  2663. r.Method == http.MethodPost &&
  2664. guard(r, 1):
  2665. summarize(w, db, r)
  2666. case match(p, "/api/templates", &args) &&
  2667. r.Method == http.MethodGet &&
  2668. guard(r, 1):
  2669. getETemplates(w, db, r)
  2670. case match(p, "/api/templates", &args) &&
  2671. r.Method == http.MethodPost &&
  2672. guard(r, 1):
  2673. createETemplate(w, db, r)
  2674. case match(p, "/api/templates", &args) &&
  2675. r.Method == http.MethodDelete &&
  2676. guard(r, 1):
  2677. deleteET(w, db, r)
  2678. case match(p, "/api/pdf", &args) &&
  2679. r.Method == http.MethodPost &&
  2680. guard(r, 1):
  2681. getPdf(w, db, r)
  2682. default:
  2683. http.Error(w, "Invalid route or token", 404)
  2684. }
  2685. db.Close()
  2686. }
  2687. func route(w http.ResponseWriter, r *http.Request) {
  2688. var page Page
  2689. var args []string
  2690. p := r.URL.Path
  2691. switch {
  2692. case r.Method == "GET" && match(p, "/", &args):
  2693. page = pages["home"]
  2694. case match(p, "/terms", &args):
  2695. page = pages["terms"]
  2696. case match(p, "/app", &args):
  2697. page = pages["app"]
  2698. default:
  2699. http.NotFound(w, r)
  2700. return
  2701. }
  2702. page.Render(w)
  2703. }
  2704. func serve() {
  2705. files := http.FileServer(http.Dir(""))
  2706. proxy, err := url.Parse("http://localhost:8002")
  2707. if err != nil {
  2708. log.Fatal("invalid origin server URL")
  2709. }
  2710. http.Handle("/assets/", files)
  2711. http.HandleFunc("/api/", api)
  2712. http.HandleFunc("/app", route)
  2713. http.Handle("/", httputil.NewSingleHostReverseProxy(proxy))
  2714. log.Fatal(http.ListenAndServe(address, nil))
  2715. }
  2716. func dbReset(db *sql.DB) {
  2717. b, err := os.ReadFile("migrations/reset.sql")
  2718. if err != nil {
  2719. log.Fatal(err)
  2720. }
  2721. _, err = db.Exec(string(b))
  2722. if err != nil {
  2723. log.Fatal(err)
  2724. }
  2725. b, err = os.ReadFile("migrations/0_29092022_setup_tables.sql")
  2726. if err != nil {
  2727. log.Fatal(err)
  2728. }
  2729. _, err = db.Exec(string(b))
  2730. if err != nil {
  2731. log.Fatal(err)
  2732. }
  2733. }
  2734. func generateFees(loan Loan) []Fee {
  2735. var fees []Fee
  2736. var fee Fee
  2737. p := gofakeit.Float32Range(0.5, 10)
  2738. size := gofakeit.Number(1, 10)
  2739. for f := 0; f < size; f++ {
  2740. fee = Fee{
  2741. Amount: int(float32(loan.Amount) * p / 100),
  2742. Perc: p,
  2743. Name: gofakeit.BuzzWord(),
  2744. Type: feeTypes[gofakeit.Number(0, len(feeTypes)-1)],
  2745. }
  2746. fees = append(fees, fee)
  2747. }
  2748. return fees
  2749. }
  2750. func generateCredits(loan Loan) []Fee {
  2751. var fees []Fee
  2752. var fee Fee
  2753. p := gofakeit.Float32Range(-10, -0.5)
  2754. size := gofakeit.Number(1, 10)
  2755. for f := 0; f < size; f++ {
  2756. fee = Fee{
  2757. Amount: int(float32(loan.Amount) * p / 100),
  2758. Perc: p,
  2759. Name: gofakeit.BuzzWord(),
  2760. Type: feeTypes[gofakeit.Number(0, len(feeTypes)-1)],
  2761. }
  2762. fees = append(fees, fee)
  2763. }
  2764. return fees
  2765. }
  2766. func seedAddresses(db *sql.DB) []Address {
  2767. addresses := make([]Address, 10)
  2768. for i, a := range addresses {
  2769. a.Street = gofakeit.Street()
  2770. a.City = gofakeit.City()
  2771. a.Region = gofakeit.State()
  2772. a.Country = "Canada"
  2773. a.Full = fmt.Sprintf("%s, %s %s", a.Street, a.City, a.Region)
  2774. id, err := insertAddress(db, a)
  2775. if err != nil {
  2776. log.Println(err)
  2777. break
  2778. }
  2779. addresses[i].Id = id
  2780. }
  2781. return addresses
  2782. }
  2783. func seedBranches(db *sql.DB, addresses []Address) []Branch {
  2784. branches := make([]Branch, 4)
  2785. for i := range branches {
  2786. branches[i].Name = gofakeit.Company()
  2787. branches[i].Type = "NMLS"
  2788. branches[i].Letterhead = gofakeit.ImagePng(400, 200)
  2789. branches[i].Num = gofakeit.HexUint8()
  2790. branches[i].Phone = gofakeit.Phone()
  2791. branches[i].Address.Id = gofakeit.Number(1, 5)
  2792. id, err := insertBranch(db, branches[i])
  2793. if err != nil {
  2794. log.Println(err)
  2795. break
  2796. }
  2797. branches[i].Id = id
  2798. }
  2799. return branches
  2800. }
  2801. func seedUsers(db *sql.DB, addresses []Address, branches []Branch) []User {
  2802. users := make([]User, 10)
  2803. for i := range users {
  2804. p := gofakeit.Person()
  2805. users[i].FirstName = p.FirstName
  2806. users[i].LastName = p.LastName
  2807. users[i].Email = p.Contact.Email
  2808. users[i].Phone = p.Contact.Phone
  2809. users[i].Branch = branches[gofakeit.Number(0, 3)]
  2810. users[i].Address = addresses[gofakeit.Number(1, 9)]
  2811. // users[i].Letterhead = gofakeit.ImagePng(400, 200)
  2812. // users[i].Avatar = gofakeit.ImagePng(200, 200)
  2813. users[i].Country = []string{"Canada", "USA"}[gofakeit.Number(0, 1)]
  2814. users[i].Password = "test123"
  2815. users[i].Verified = true
  2816. users[i].Title = "Loan Officer"
  2817. users[i].Status = "Subscribed"
  2818. users[i].Role = "User"
  2819. }
  2820. users[0].Email = "test@example.com"
  2821. users[0].Email = "test@example.com"
  2822. users[1].Email = "test2@example.com"
  2823. users[1].Status = "Branch"
  2824. users[1].Role = "Manager"
  2825. users[2].Email = "test3@example.com"
  2826. users[2].Status = "Free"
  2827. users[2].Role = "Admin"
  2828. for i := range users {
  2829. var err error
  2830. users[i].Id, err = insertUser(db, users[i])
  2831. if err != nil {
  2832. log.Println(err)
  2833. break
  2834. }
  2835. }
  2836. return users
  2837. }
  2838. func seedLicenses(db *sql.DB, users []User) []License {
  2839. licenses := make([]License, len(users))
  2840. for i := range licenses {
  2841. licenses[i].UserId = users[i].Id
  2842. licenses[i].Type = []string{"NMLS", "FSRA"}[gofakeit.Number(0, 1)]
  2843. licenses[i].Num = gofakeit.UUID()
  2844. id, err := insertLicense(db, licenses[i])
  2845. if err != nil {
  2846. log.Println(err)
  2847. break
  2848. }
  2849. licenses[i].Id = id
  2850. }
  2851. return licenses
  2852. }
  2853. func seedLoanTypes(db *sql.DB) []LoanType {
  2854. var loantypes []LoanType
  2855. var loantype LoanType
  2856. var err error
  2857. loantype = LoanType{Branch: 0, User: 0, Name: "Conventional"}
  2858. loantype.Id, err = insertLoanType(db, loantype)
  2859. if err != nil {
  2860. panic(err)
  2861. }
  2862. loantypes = append(loantypes, loantype)
  2863. loantype = LoanType{Branch: 0, User: 0, Name: "FHA"}
  2864. loantype.Id, err = insertLoanType(db, loantype)
  2865. if err != nil {
  2866. panic(err)
  2867. }
  2868. loantypes = append(loantypes, loantype)
  2869. loantype = LoanType{Branch: 0, User: 0, Name: "USDA"}
  2870. loantype.Id, err = insertLoanType(db, loantype)
  2871. if err != nil {
  2872. panic(err)
  2873. }
  2874. loantypes = append(loantypes, loantype)
  2875. loantype = LoanType{Branch: 0, User: 0, Name: "VA"}
  2876. loantype.Id, err = insertLoanType(db, loantype)
  2877. if err != nil {
  2878. panic(err)
  2879. }
  2880. loantypes = append(loantypes, loantype)
  2881. return loantypes
  2882. }
  2883. func seedEstimates(db *sql.DB, users []User, ltypes []LoanType) []Estimate {
  2884. var estimates []Estimate
  2885. var estimate Estimate
  2886. var l Loan
  2887. var err error
  2888. for i := 0; i < 15; i++ {
  2889. estimate = Estimate{}
  2890. estimate.User = users[gofakeit.Number(0, len(users)-1)].Id
  2891. estimate.Borrower = Borrower{
  2892. Credit: gofakeit.Number(600, 800),
  2893. Income: gofakeit.Number(1000000, 15000000),
  2894. Num: gofakeit.Number(1, 20),
  2895. }
  2896. estimate.Transaction = []string{"Purchase", "Refinance"}[gofakeit.Number(0, 1)]
  2897. estimate.Price = gofakeit.Number(50000, 200000000)
  2898. estimate.Property =
  2899. propertyTypes[gofakeit.Number(0, len(propertyTypes)-1)]
  2900. estimate.Occupancy =
  2901. []string{"Primary", "Secondary", "Investment"}[gofakeit.Number(0, 2)]
  2902. estimate.Zip = gofakeit.Zip()
  2903. lsize := gofakeit.Number(1, 6)
  2904. for j := 0; j < lsize; j++ {
  2905. l.Type = ltypes[gofakeit.Number(0, len(ltypes)-1)]
  2906. l.Amount = gofakeit.Number(
  2907. int(float32(estimate.Price)*0.5),
  2908. int(float32(estimate.Price)*0.93))
  2909. l.Term = gofakeit.Number(4, 30)
  2910. l.Hoi = gofakeit.Number(50000, 700000)
  2911. l.Hazard = gofakeit.Number(5000, 200000)
  2912. l.Tax = gofakeit.Number(5000, 200000)
  2913. l.Interest = gofakeit.Float32Range(0.5, 8)
  2914. l.Fees = generateFees(l)
  2915. l.Credits = generateCredits(l)
  2916. l.Name = gofakeit.AdjectiveDescriptive()
  2917. estimate.Loans = append(estimate.Loans, l)
  2918. }
  2919. estimates = append(estimates, estimate)
  2920. }
  2921. estimates[0].User = users[0].Id
  2922. estimates[1].User = users[0].Id
  2923. for i := range estimates {
  2924. err = estimates[i].insertEstimate(db)
  2925. if err != nil {
  2926. log.Println(err)
  2927. return estimates
  2928. }
  2929. }
  2930. return estimates
  2931. }
  2932. func seedResults(db *sql.DB, estimates []Estimate) error {
  2933. var err error
  2934. for i := range estimates {
  2935. estimates[i].makeResults()
  2936. err = estimates[i].insertResults(db)
  2937. if err != nil {
  2938. log.Println(err)
  2939. return err
  2940. }
  2941. }
  2942. return nil
  2943. }
  2944. func dbSeed(db *sql.DB) {
  2945. addresses := seedAddresses(db)
  2946. branches := seedBranches(db, addresses)
  2947. users := seedUsers(db, addresses, branches)
  2948. _ = seedLicenses(db, users)
  2949. loantypes := seedLoanTypes(db)
  2950. estimates := seedEstimates(db, users, loantypes)
  2951. _ = seedResults(db, estimates)
  2952. }
  2953. func dev(args []string) {
  2954. os.Setenv("DBName", "skouter_dev")
  2955. os.Setenv("DBUser", "tester")
  2956. os.Setenv("DBPass", "test123")
  2957. stripe.Key = os.Getenv("STRIPE_SECRET_KEY")
  2958. db, err := sql.Open("mysql",
  2959. fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s?multiStatements=true",
  2960. os.Getenv("DBUser"),
  2961. os.Getenv("DBPass"),
  2962. os.Getenv("DBName"),
  2963. ))
  2964. err = db.Ping()
  2965. if err != nil {
  2966. log.Println("Bad database configuration: %v", err)
  2967. panic(err)
  2968. // maybe os.Exit(1) instead
  2969. }
  2970. if len(args) == 0 {
  2971. serve()
  2972. return
  2973. }
  2974. switch args[0] {
  2975. case "seed":
  2976. dbSeed(db)
  2977. case "reset":
  2978. dbReset(db)
  2979. default:
  2980. return
  2981. }
  2982. db.Close()
  2983. }
  2984. func check(args []string) {
  2985. os.Setenv("DBName", "skouter_dev")
  2986. os.Setenv("DBUser", "tester")
  2987. os.Setenv("DBPass", "test123")
  2988. files := http.FileServer(http.Dir(""))
  2989. http.Handle("/assets/", files)
  2990. http.HandleFunc("/", checkPdf)
  2991. log.Fatal(http.ListenAndServe(address, nil))
  2992. }
  2993. func main() {
  2994. if len(os.Args) <= 1 {
  2995. serve()
  2996. return
  2997. }
  2998. switch os.Args[1] {
  2999. case "dev":
  3000. dev(os.Args[2:])
  3001. case "checkpdf":
  3002. check(os.Args[2:])
  3003. default:
  3004. return
  3005. }
  3006. }