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

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