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

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