Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3676 line
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 * 1)}
  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. if user.Branch.Id > 0 {
  994. user.Branch, err = queryBranch(db, user.Branch.Id)
  995. if err != nil {
  996. return user, err
  997. }
  998. }
  999. return user, nil
  1000. }
  1001. func queryCustomer(db *sql.DB, id string) (User, error) {
  1002. var user User
  1003. var query string
  1004. var err error
  1005. query = `SELECT
  1006. u.id,
  1007. u.email,
  1008. u.first_name,
  1009. u.last_name,
  1010. coalesce(u.branch_id, 0),
  1011. u.country,
  1012. u.title,
  1013. coalesce(u.status, ''),
  1014. coalesce(u.customer_id, ''),
  1015. u.verified,
  1016. u.role,
  1017. u.address,
  1018. u.phone
  1019. FROM user u WHERE u.customer_id = ?
  1020. `
  1021. row := db.QueryRow(query, id)
  1022. if err != nil {
  1023. return user, err
  1024. }
  1025. err = row.Scan(
  1026. &user.Id,
  1027. &user.Email,
  1028. &user.FirstName,
  1029. &user.LastName,
  1030. &user.Branch.Id,
  1031. &user.Country,
  1032. &user.Title,
  1033. &user.Status,
  1034. &user.CustomerId,
  1035. &user.Verified,
  1036. &user.Role,
  1037. &user.Address.Id,
  1038. &user.Phone,
  1039. )
  1040. if err != nil {
  1041. return user, err
  1042. }
  1043. user.Address, err = queryAddress(db, user.Address.Id)
  1044. if err != nil {
  1045. return user, err
  1046. }
  1047. user.Branch, err = queryBranch(db, user.Branch.Id)
  1048. if err != nil {
  1049. return user, err
  1050. }
  1051. return user, nil
  1052. }
  1053. // Can probably be deleted.
  1054. func queryUsers(db *sql.DB, id int) ([]User, error) {
  1055. var users []User
  1056. var query string
  1057. var rows *sql.Rows
  1058. var err error
  1059. query = `SELECT
  1060. u.id,
  1061. u.email,
  1062. u.first_name,
  1063. u.last_name,
  1064. coalesce(u.branch_id, 0),
  1065. u.country,
  1066. u.title,
  1067. coalesce(u.status, ''),
  1068. u.verified,
  1069. u.role,
  1070. u.address,
  1071. u.phone
  1072. FROM user u WHERE u.id = CASE @e := ? WHEN 0 THEN u.id ELSE @e END
  1073. `
  1074. rows, err = db.Query(query, id)
  1075. if err != nil {
  1076. return users, err
  1077. }
  1078. defer rows.Close()
  1079. for rows.Next() {
  1080. var user User
  1081. if err := rows.Scan(
  1082. &user.Id,
  1083. &user.Email,
  1084. &user.FirstName,
  1085. &user.LastName,
  1086. &user.Branch.Id,
  1087. &user.Country,
  1088. &user.Title,
  1089. &user.Status,
  1090. &user.Verified,
  1091. &user.Role,
  1092. &user.Address.Id,
  1093. &user.Phone,
  1094. ); err != nil {
  1095. return users, err
  1096. }
  1097. user.Address, err = queryAddress(db, user.Address.Id)
  1098. if err != nil {
  1099. return users, err
  1100. }
  1101. user.Branch, err = queryBranch(db, user.Branch.Id)
  1102. if err != nil {
  1103. return users, err
  1104. }
  1105. users = append(users, user)
  1106. }
  1107. // Prevents runtime panics
  1108. if len(users) == 0 {
  1109. return users, errors.New("User not found.")
  1110. }
  1111. return users, nil
  1112. }
  1113. func querySub(db *sql.DB, id int) (Subscription, error) {
  1114. var query string
  1115. var err error
  1116. var s Subscription
  1117. query = `SELECT
  1118. id,
  1119. stripe_id,
  1120. user_id,
  1121. customer_id,
  1122. current_period_end,
  1123. current_period_start,
  1124. client_secret,
  1125. payment_status
  1126. FROM subscription WHERE id = ?
  1127. `
  1128. row := db.QueryRow(query, id)
  1129. err = row.Scan(
  1130. &s.Id,
  1131. &s.StripeId,
  1132. &s.CustomerId,
  1133. &s.End,
  1134. &s.Start,
  1135. &s.ClientSecret,
  1136. &s.PaymentStatus,
  1137. )
  1138. return s, err
  1139. }
  1140. func (user *User) querySub(db *sql.DB) error {
  1141. var query string
  1142. var err error
  1143. query = `SELECT
  1144. id,
  1145. stripe_id,
  1146. user_id,
  1147. customer_id,
  1148. price_id,
  1149. current_period_end,
  1150. current_period_start,
  1151. client_secret,
  1152. payment_status
  1153. FROM subscription WHERE user_id = ?
  1154. `
  1155. row := db.QueryRow(query, user.Id)
  1156. err = row.Scan(
  1157. &user.Sub.Id,
  1158. &user.Sub.StripeId,
  1159. &user.Sub.UserId,
  1160. &user.Sub.CustomerId,
  1161. &user.Sub.PriceId,
  1162. &user.Sub.End,
  1163. &user.Sub.Start,
  1164. &user.Sub.ClientSecret,
  1165. &user.Sub.PaymentStatus,
  1166. )
  1167. return err
  1168. }
  1169. func (estimate *Estimate) insertResults(db *sql.DB) error {
  1170. var query string
  1171. var row *sql.Row
  1172. var err error
  1173. var id int
  1174. query = `INSERT INTO estimate_result
  1175. (
  1176. loan_id,
  1177. loan_payment,
  1178. total_monthly,
  1179. total_fees,
  1180. total_credits,
  1181. cash_to_close
  1182. )
  1183. VALUES (?, ?, ?, ?, ?, ?)
  1184. RETURNING id
  1185. `
  1186. for i := range estimate.Loans {
  1187. r := estimate.Loans[i].Result
  1188. r.LoanId = estimate.Loans[i].Id
  1189. row = db.QueryRow(query,
  1190. r.LoanId,
  1191. r.LoanPayment,
  1192. r.TotalMonthly,
  1193. r.TotalFees,
  1194. r.TotalCredits,
  1195. r.CashToClose,
  1196. )
  1197. err = row.Scan(&id)
  1198. if err != nil {
  1199. return err
  1200. }
  1201. r.Id = id
  1202. }
  1203. return nil
  1204. }
  1205. // Insert user returning it's ID or any error
  1206. func insertUser(db *sql.DB, user User) (int, error) {
  1207. var query string
  1208. var row *sql.Row
  1209. var err error
  1210. var id int // Inserted user's id
  1211. user.Address.Id, err = insertAddress(db, user.Address)
  1212. if err != nil {
  1213. return 0, err
  1214. }
  1215. query = `INSERT INTO user
  1216. (
  1217. email,
  1218. first_name,
  1219. last_name,
  1220. password,
  1221. role,
  1222. title,
  1223. status,
  1224. verified,
  1225. address,
  1226. country,
  1227. branch_id,
  1228. phone,
  1229. created,
  1230. last_login
  1231. )
  1232. VALUES (?, ?, ?, sha2(?, 256), ?, ?, ?, ?, ?, ?,
  1233. CASE @b := ? WHEN 0 THEN NULL ELSE @b END,
  1234. ?, NOW(), NOW())
  1235. RETURNING id
  1236. `
  1237. row = db.QueryRow(query,
  1238. user.Email,
  1239. user.FirstName,
  1240. user.LastName,
  1241. user.Password,
  1242. user.Role,
  1243. user.Title,
  1244. user.Status,
  1245. user.Verified,
  1246. user.Address.Id,
  1247. user.Country,
  1248. user.Branch.Id,
  1249. user.Phone,
  1250. )
  1251. err = row.Scan(&id)
  1252. if err != nil {
  1253. return 0, err
  1254. }
  1255. user.Id = id
  1256. return id, nil
  1257. }
  1258. // Insert user returning it's ID or any error
  1259. func (sub *Subscription) insertSub(db *sql.DB) (error) {
  1260. var query string
  1261. var row *sql.Row
  1262. var err error
  1263. query = `INSERT INTO subscription
  1264. (
  1265. stripe_id,
  1266. user_id,
  1267. customer_id,
  1268. price_id,
  1269. current_period_end,
  1270. current_period_start,
  1271. client_secret,
  1272. payment_status
  1273. )
  1274. VALUES (?, ?, ?, ?, ?, ?, ?, ?)
  1275. RETURNING id
  1276. `
  1277. row = db.QueryRow(query,
  1278. sub.StripeId,
  1279. sub.UserId,
  1280. sub.CustomerId,
  1281. sub.PriceId,
  1282. sub.End,
  1283. sub.Start,
  1284. sub.ClientSecret,
  1285. sub.PaymentStatus,
  1286. )
  1287. err = row.Scan(&sub.Id)
  1288. return err
  1289. }
  1290. func (sub *Subscription) updateSub(db *sql.DB) error {
  1291. var query string
  1292. var err error
  1293. query = `UPDATE subscription
  1294. SET client_secret = ?, payment_status = ?
  1295. WHERE id = ?
  1296. `
  1297. s, err := subscription.Get(sub.StripeId, &stripe.SubscriptionParams{})
  1298. if err != nil { return err }
  1299. i, err := invoice.Get(s.LatestInvoice.ID, &stripe.InvoiceParams{})
  1300. if err != nil { return err }
  1301. p, err := paymentintent.Get(i.PaymentIntent.ID,
  1302. &stripe.PaymentIntentParams{})
  1303. if err != nil { return err }
  1304. _, err = db.Exec(query,
  1305. p.ClientSecret,
  1306. p.Status,
  1307. sub.Id,
  1308. )
  1309. if err != nil { return err }
  1310. sub.ClientSecret = p.ClientSecret
  1311. sub.PaymentStatus = string(p.Status)
  1312. return err
  1313. }
  1314. // Updates a user's stripe customer ID.
  1315. func (user *User) updateCustomerId(db *sql.DB, cid string) (error) {
  1316. var query string
  1317. var err error
  1318. query = `UPDATE user SET
  1319. customer_id = ?
  1320. WHERE id = ?
  1321. `
  1322. _, err = db.Exec(query,
  1323. cid,
  1324. user.Id,
  1325. )
  1326. if err != nil { return err }
  1327. user.CustomerId = cid
  1328. return nil
  1329. }
  1330. func updateAddress(address Address, db *sql.DB) error {
  1331. query := `
  1332. UPDATE address
  1333. SET
  1334. full_address = CASE @e := ? WHEN '' THEN full_address ELSE @e END,
  1335. street = CASE @fn := ? WHEN '' THEN street ELSE @fn END,
  1336. city = CASE @ln := ? WHEN '' THEN city ELSE @ln END,
  1337. region = CASE @r := ? WHEN '' THEN region ELSE @r END,
  1338. country = CASE @r := ? WHEN '' THEN country ELSE @r END,
  1339. zip = CASE @r := ? WHEN '' THEN zip ELSE @r END
  1340. WHERE id = ?
  1341. `
  1342. _, err := db.Exec(query,
  1343. address.Full,
  1344. address.Street,
  1345. address.City,
  1346. address.Region,
  1347. address.Country,
  1348. address.Zip,
  1349. address.Id,
  1350. )
  1351. return err
  1352. }
  1353. func updateUser(user User, db *sql.DB) error {
  1354. query := `
  1355. UPDATE user
  1356. SET
  1357. email = CASE @e := ? WHEN '' THEN email ELSE @e END,
  1358. first_name = CASE @fn := ? WHEN '' THEN first_name ELSE @fn END,
  1359. last_name = CASE @ln := ? WHEN '' THEN last_name ELSE @ln END,
  1360. role = CASE @r := ? WHEN '' THEN role ELSE @r END,
  1361. password = CASE @p := ? WHEN '' THEN password ELSE sha2(@p, 256) END
  1362. WHERE id = ?
  1363. `
  1364. _, err := db.Exec(query,
  1365. user.Email,
  1366. user.FirstName,
  1367. user.LastName,
  1368. user.Role,
  1369. user.Password,
  1370. user.Id,
  1371. )
  1372. return err
  1373. }
  1374. func getUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1375. claims, err := getClaims(r)
  1376. if err != nil {
  1377. w.WriteHeader(500)
  1378. return
  1379. }
  1380. user, err := queryUser(db, claims.Id)
  1381. if err != nil {
  1382. w.WriteHeader(422)
  1383. log.Println(err)
  1384. return
  1385. }
  1386. json.NewEncoder(w).Encode(user)
  1387. }
  1388. func getUsers(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1389. users, err := queryUsers(db, 0)
  1390. if err != nil {
  1391. w.WriteHeader(http.StatusInternalServerError)
  1392. return
  1393. }
  1394. json.NewEncoder(w).Encode(users)
  1395. }
  1396. // Updates a user using only specified values in the JSON body
  1397. func setUser(user User, db *sql.DB) error {
  1398. _, err := mail.ParseAddress(user.Email)
  1399. if err != nil {
  1400. return err
  1401. }
  1402. if roles[user.Role] == 0 {
  1403. return errors.New("Invalid role")
  1404. }
  1405. err = updateUser(user, db)
  1406. if err != nil {
  1407. return err
  1408. }
  1409. err = updateAddress(user.Address, db)
  1410. if err != nil {
  1411. return err
  1412. }
  1413. return nil
  1414. }
  1415. func patchUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1416. var user User
  1417. err := json.NewDecoder(r.Body).Decode(&user)
  1418. if err != nil {
  1419. http.Error(w, "Invalid fields", 422)
  1420. return
  1421. }
  1422. err = setUser(user, db)
  1423. if err != nil {
  1424. http.Error(w, err.Error(), 422)
  1425. return
  1426. }
  1427. }
  1428. // Update specified fields of the user specified in the claim
  1429. func patchSelf(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1430. claim, err := getClaims(r)
  1431. var user User
  1432. json.NewDecoder(r.Body).Decode(&user)
  1433. // First check that the target user to be updated is the same as the claim id, and
  1434. // their role is unchanged.
  1435. if err != nil || claim.Id != user.Id {
  1436. http.Error(w, "Target user's id does not match claim.", 401)
  1437. return
  1438. }
  1439. if claim.Role != user.Role && user.Role != "" {
  1440. http.Error(w, "Administrator required to escalate role.", 401)
  1441. return
  1442. }
  1443. err = setUser(user, db)
  1444. if err != nil {
  1445. http.Error(w, err.Error(), 422)
  1446. return
  1447. }
  1448. }
  1449. func deleteUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1450. var user User
  1451. err := json.NewDecoder(r.Body).Decode(&user)
  1452. if err != nil {
  1453. http.Error(w, "Invalid fields.", 422)
  1454. return
  1455. }
  1456. query := `DELETE FROM user WHERE id = ?`
  1457. _, err = db.Exec(query, user.Id)
  1458. if err != nil {
  1459. http.Error(w, "Could not delete.", 500)
  1460. }
  1461. }
  1462. // Checks if a user's entries are reasonable before database insertion.
  1463. // This function is very important because it is the only thing preventing
  1464. // anyone from creating an admin user. These error messages are displayed to
  1465. // the user.
  1466. func (user *User) validate() error {
  1467. _, err := mail.ParseAddress(user.Email)
  1468. if err != nil {
  1469. return errors.New("Invalid email.")
  1470. }
  1471. if roles[user.Role] == 0 {
  1472. return errors.New("Invalid role.")
  1473. }
  1474. if roles[user.Role] == roles["Admin"] {
  1475. return errors.New("New user cannot be an Admin.")
  1476. }
  1477. if user.FirstName == "" {
  1478. return errors.New("Given name cannot be empty.")
  1479. }
  1480. if user.LastName == "" {
  1481. return errors.New("Surname cannot be empty.")
  1482. }
  1483. if user.Password == "" {
  1484. return errors.New("Empty password")
  1485. }
  1486. return nil
  1487. }
  1488. func createUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1489. var user User
  1490. err := json.NewDecoder(r.Body).Decode(&user)
  1491. if err != nil {
  1492. http.Error(w, "Invalid fields.", 422)
  1493. return
  1494. }
  1495. user.Role = "User"
  1496. user.Status = "Trial"
  1497. err = user.validate()
  1498. if err != nil {
  1499. http.Error(w, err.Error(), 422)
  1500. return
  1501. }
  1502. user.Id, err = insertUser(db, user)
  1503. if err != nil {
  1504. http.Error(w, err.Error(), 422)
  1505. return
  1506. }
  1507. err = setTokenCookie(user.Id, user.Role, w)
  1508. if err != nil {
  1509. http.Error(w, err.Error(), 500)
  1510. return
  1511. }
  1512. json.NewEncoder(w).Encode(user)
  1513. }
  1514. func checkPassword(db *sql.DB, id int, pass string) bool {
  1515. var p string
  1516. query := `SELECT
  1517. password
  1518. FROM user WHERE user.id = ? AND password = sha2(?, 256)
  1519. `
  1520. row := db.QueryRow(query, id, pass)
  1521. err := row.Scan(&p)
  1522. if err != nil {
  1523. return false
  1524. }
  1525. return true
  1526. }
  1527. func setPassword(db *sql.DB, id int, pass string) error {
  1528. query := `UPDATE user
  1529. SET password = sha2(?, 256)
  1530. WHERE user.id = ?
  1531. `
  1532. _, err := db.Exec(query, pass, id)
  1533. if err != nil {
  1534. return errors.New("Could not insert password.")
  1535. }
  1536. return nil
  1537. }
  1538. func changePassword(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1539. var pass Password
  1540. claim, err := getClaims(r)
  1541. err = json.NewDecoder(r.Body).Decode(&pass)
  1542. if err != nil {
  1543. http.Error(w, "Bad fields.", 422)
  1544. return
  1545. }
  1546. if checkPassword(db, claim.Id, pass.Old) {
  1547. err = setPassword(db, claim.Id, pass.New)
  1548. } else {
  1549. http.Error(w, "Incorrect old password.", 401)
  1550. return
  1551. }
  1552. if err != nil {
  1553. http.Error(w, err.Error(), 500)
  1554. return
  1555. }
  1556. }
  1557. func fetchAvatar(db *sql.DB, user int) ([]byte, error) {
  1558. var img []byte
  1559. var query string
  1560. var err error
  1561. query = `SELECT
  1562. avatar
  1563. FROM user WHERE user.id = ?
  1564. `
  1565. row := db.QueryRow(query, user)
  1566. err = row.Scan(&img)
  1567. if err != nil {
  1568. return img, err
  1569. }
  1570. return img, nil
  1571. }
  1572. func insertAvatar(db *sql.DB, user int, img []byte) error {
  1573. query := `UPDATE user
  1574. SET avatar = ?
  1575. WHERE id = ?
  1576. `
  1577. _, err := db.Exec(query, img, user)
  1578. if err != nil {
  1579. return err
  1580. }
  1581. return nil
  1582. }
  1583. func fetchLetterhead(db *sql.DB, user int) ([]byte, error) {
  1584. var img []byte
  1585. var query string
  1586. var err error
  1587. query = `SELECT
  1588. letterhead
  1589. FROM user WHERE user.id = ?
  1590. `
  1591. row := db.QueryRow(query, user)
  1592. err = row.Scan(&img)
  1593. if err != nil {
  1594. return img, err
  1595. }
  1596. return img, nil
  1597. }
  1598. func insertLetterhead(db *sql.DB, user int, img []byte) error {
  1599. query := `UPDATE user
  1600. SET letterhead = ?
  1601. WHERE id = ?
  1602. `
  1603. _, err := db.Exec(query, img, user)
  1604. if err != nil {
  1605. return err
  1606. }
  1607. return nil
  1608. }
  1609. func setAvatar(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1610. var validTypes []string = []string{"image/png", "image/jpeg"}
  1611. var isValidType bool
  1612. claims, err := getClaims(r)
  1613. if err != nil {
  1614. http.Error(w, "Invalid token.", 422)
  1615. return
  1616. }
  1617. img, err := io.ReadAll(r.Body)
  1618. if err != nil {
  1619. http.Error(w, "Invalid file.", 422)
  1620. return
  1621. }
  1622. for _, v := range validTypes {
  1623. if v == http.DetectContentType(img) {
  1624. isValidType = true
  1625. }
  1626. }
  1627. if !isValidType {
  1628. http.Error(w, "Invalid file type.", 422)
  1629. return
  1630. }
  1631. err = insertAvatar(db, claims.Id, img)
  1632. if err != nil {
  1633. http.Error(w, "Could not insert.", 500)
  1634. return
  1635. }
  1636. }
  1637. func getAvatar(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1638. claims, err := getClaims(r)
  1639. if err != nil {
  1640. http.Error(w, "Invalid token.", 422)
  1641. return
  1642. }
  1643. img, err := fetchAvatar(db, claims.Id)
  1644. if err != nil {
  1645. http.Error(w, "Could not retrieve.", 500)
  1646. return
  1647. }
  1648. w.Header().Set("Content-Type", http.DetectContentType(img))
  1649. w.Write(img)
  1650. }
  1651. func setLetterhead(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1652. var validTypes []string = []string{"image/png", "image/jpeg"}
  1653. var isValidType bool
  1654. claims, err := getClaims(r)
  1655. if err != nil {
  1656. http.Error(w, "Invalid token.", 422)
  1657. return
  1658. }
  1659. img, err := io.ReadAll(r.Body)
  1660. if err != nil {
  1661. http.Error(w, "Invalid file.", 422)
  1662. return
  1663. }
  1664. for _, v := range validTypes {
  1665. if v == http.DetectContentType(img) {
  1666. isValidType = true
  1667. }
  1668. }
  1669. if !isValidType {
  1670. http.Error(w, "Invalid file type.", 422)
  1671. return
  1672. }
  1673. err = insertLetterhead(db, claims.Id, img)
  1674. if err != nil {
  1675. http.Error(w, "Could not insert.", 500)
  1676. return
  1677. }
  1678. }
  1679. func getLetterhead(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1680. claims, err := getClaims(r)
  1681. if err != nil {
  1682. http.Error(w, "Invalid token.", 422)
  1683. return
  1684. }
  1685. img, err := fetchLetterhead(db, claims.Id)
  1686. if err != nil {
  1687. http.Error(w, "Could not retrieve.", 500)
  1688. return
  1689. }
  1690. w.Header().Set("Content-Type", http.DetectContentType(img))
  1691. w.Write(img)
  1692. }
  1693. func queryBorrower(db *sql.DB, id int) (Borrower, error) {
  1694. var borrower Borrower
  1695. var query string
  1696. var err error
  1697. query = `SELECT
  1698. l.id,
  1699. l.credit_score,
  1700. l.num,
  1701. l.monthly_income
  1702. FROM borrower l WHERE l.id = ?
  1703. `
  1704. row := db.QueryRow(query, id)
  1705. err = row.Scan(
  1706. borrower.Id,
  1707. borrower.Credit,
  1708. borrower.Num,
  1709. borrower.Income,
  1710. )
  1711. if err != nil {
  1712. return borrower, err
  1713. }
  1714. return borrower, nil
  1715. }
  1716. // Must have an estimate ID 'e', but not necessarily a loan id 'id'
  1717. func getResults(db *sql.DB, e int, id int) ([]Result, error) {
  1718. var results []Result
  1719. var query string
  1720. var rows *sql.Rows
  1721. var err error
  1722. query = `SELECT
  1723. r.id,
  1724. loan_id,
  1725. loan_payment,
  1726. total_monthly,
  1727. total_fees,
  1728. total_credits,
  1729. cash_to_close
  1730. FROM estimate_result r
  1731. INNER JOIN loan
  1732. ON r.loan_id = loan.id
  1733. WHERE r.id = CASE @e := ? WHEN 0 THEN r.id ELSE @e END
  1734. AND loan.estimate_id = ?
  1735. `
  1736. rows, err = db.Query(query, id, e)
  1737. if err != nil {
  1738. return results, err
  1739. }
  1740. defer rows.Close()
  1741. for rows.Next() {
  1742. var result Result
  1743. if err := rows.Scan(
  1744. &result.Id,
  1745. &result.LoanId,
  1746. &result.LoanPayment,
  1747. &result.TotalMonthly,
  1748. &result.TotalFees,
  1749. &result.TotalCredits,
  1750. &result.CashToClose,
  1751. ); err != nil {
  1752. return results, err
  1753. }
  1754. results = append(results, result)
  1755. }
  1756. // Prevents runtime panics
  1757. // if len(results) == 0 { return results, errors.New("Result not found.") }
  1758. return results, nil
  1759. }
  1760. // Retrieve an estimate result with a specified loan id
  1761. func getResult(db *sql.DB, loan int) (Result, error) {
  1762. var result Result
  1763. var query string
  1764. var err error
  1765. query = `SELECT
  1766. r.id,
  1767. loan_id,
  1768. loan_payment,
  1769. total_monthly,
  1770. total_fees,
  1771. total_credits,
  1772. cash_to_close
  1773. FROM estimate_result r
  1774. INNER JOIN loan
  1775. ON r.loan_id = loan.id
  1776. WHERE loan.Id = ?
  1777. `
  1778. row := db.QueryRow(query, loan)
  1779. err = row.Scan(
  1780. &result.Id,
  1781. &result.LoanId,
  1782. &result.LoanPayment,
  1783. &result.TotalMonthly,
  1784. &result.TotalFees,
  1785. &result.TotalCredits,
  1786. &result.CashToClose,
  1787. )
  1788. if err != nil {
  1789. return result, err
  1790. }
  1791. return result, nil
  1792. }
  1793. // Must have an estimate ID 'e', but not necessarily a loan id 'id'
  1794. func getLoans(db *sql.DB, e int, id int) ([]Loan, error) {
  1795. var loans []Loan
  1796. var query string
  1797. var rows *sql.Rows
  1798. var err error
  1799. query = `SELECT
  1800. l.id,
  1801. l.type_id,
  1802. l.estimate_id,
  1803. l.amount,
  1804. l.term,
  1805. l.interest,
  1806. l.ltv,
  1807. l.dti,
  1808. l.hoi,
  1809. l.tax,
  1810. l.name
  1811. FROM loan l WHERE l.id = CASE @e := ? WHEN 0 THEN l.id ELSE @e END AND
  1812. l.estimate_id = ?
  1813. `
  1814. rows, err = db.Query(query, id, e)
  1815. if err != nil {
  1816. return loans, err
  1817. }
  1818. defer rows.Close()
  1819. for rows.Next() {
  1820. var loan Loan
  1821. if err := rows.Scan(
  1822. &loan.Id,
  1823. &loan.Type.Id,
  1824. &loan.EstimateId,
  1825. &loan.Amount,
  1826. &loan.Term,
  1827. &loan.Interest,
  1828. &loan.Ltv,
  1829. &loan.Dti,
  1830. &loan.Hoi,
  1831. &loan.Tax,
  1832. &loan.Name,
  1833. ); err != nil {
  1834. return loans, err
  1835. }
  1836. mi, err := getMi(db, loan.Id)
  1837. if err != nil {
  1838. return loans, err
  1839. }
  1840. loan.Mi = mi
  1841. fees, err := getFees(db, loan.Id)
  1842. if err != nil {
  1843. return loans, err
  1844. }
  1845. loan.Fees = fees
  1846. loan.Result, err = getResult(db, loan.Id)
  1847. if err != nil {
  1848. return loans, err
  1849. }
  1850. loan.Type, err = getLoanType(db, loan.Type.Id)
  1851. if err != nil {
  1852. return loans, err
  1853. }
  1854. loans = append(loans, loan)
  1855. }
  1856. // Prevents runtime panics
  1857. if len(loans) == 0 {
  1858. return loans, errors.New("Loan not found.")
  1859. }
  1860. return loans, nil
  1861. }
  1862. func getEstimate(db *sql.DB, id int) (Estimate, error) {
  1863. estimates, err := getEstimates(db, id, 0)
  1864. if err != nil {
  1865. return Estimate{}, err
  1866. }
  1867. return estimates[0], nil
  1868. }
  1869. func getEstimates(db *sql.DB, id int, user int) ([]Estimate, error) {
  1870. var estimates []Estimate
  1871. var query string
  1872. var rows *sql.Rows
  1873. var err error
  1874. query = `SELECT
  1875. id,
  1876. user_id,
  1877. transaction,
  1878. price,
  1879. property,
  1880. occupancy,
  1881. zip,
  1882. pud
  1883. FROM estimate WHERE id = CASE @e := ? WHEN 0 THEN id ELSE @e END AND
  1884. user_id = CASE @e := ? WHEN 0 THEN user_id ELSE @e END
  1885. `
  1886. rows, err = db.Query(query, id, user)
  1887. if err != nil {
  1888. return estimates, err
  1889. }
  1890. defer rows.Close()
  1891. for rows.Next() {
  1892. var estimate Estimate
  1893. if err := rows.Scan(
  1894. &estimate.Id,
  1895. &estimate.User,
  1896. &estimate.Transaction,
  1897. &estimate.Price,
  1898. &estimate.Property,
  1899. &estimate.Occupancy,
  1900. &estimate.Zip,
  1901. &estimate.Pud,
  1902. ); err != nil {
  1903. return estimates, err
  1904. }
  1905. err := estimate.getBorrower(db)
  1906. if err != nil {
  1907. return estimates, err
  1908. }
  1909. estimates = append(estimates, estimate)
  1910. }
  1911. // Prevents runtime panics
  1912. if len(estimates) == 0 {
  1913. return estimates, errors.New("Estimate not found.")
  1914. }
  1915. for i := range estimates {
  1916. estimates[i].Loans, err = getLoans(db, estimates[i].Id, 0)
  1917. if err != nil {
  1918. return estimates, err
  1919. }
  1920. }
  1921. return estimates, nil
  1922. }
  1923. func queryETemplates(db *sql.DB, id int, user int) ([]ETemplate, error) {
  1924. var eTemplates []ETemplate
  1925. var query string
  1926. var rows *sql.Rows
  1927. var err error
  1928. query = `SELECT
  1929. id,
  1930. estimate_id,
  1931. user_id,
  1932. branch_id
  1933. FROM estimate_template WHERE id = CASE @e := ? WHEN 0 THEN id ELSE @e END AND
  1934. user_id = CASE @e := ? WHEN 0 THEN user_id ELSE @e END
  1935. `
  1936. rows, err = db.Query(query, id, user)
  1937. if err != nil {
  1938. return eTemplates, err
  1939. }
  1940. defer rows.Close()
  1941. for rows.Next() {
  1942. var e ETemplate
  1943. if err := rows.Scan(
  1944. &e.Id,
  1945. &e.Estimate.Id,
  1946. &e.UserId,
  1947. &e.BranchId,
  1948. ); err != nil {
  1949. return eTemplates, err
  1950. }
  1951. e.Estimate, err = getEstimate(db, e.Estimate.Id)
  1952. if err != nil {
  1953. return eTemplates, err
  1954. }
  1955. eTemplates = append(eTemplates, e)
  1956. }
  1957. return eTemplates, nil
  1958. }
  1959. // Accepts a borrower struct and returns the id of the inserted borrower and
  1960. // any related error.
  1961. func (estimate *Estimate) insertETemplate(db *sql.DB, user int, branch int) error {
  1962. var query string
  1963. var err error
  1964. query = `INSERT INTO estimate_template
  1965. (
  1966. estimate_id,
  1967. user_id,
  1968. branch_id
  1969. )
  1970. VALUES (?, ?, ?)
  1971. `
  1972. _, err = db.Exec(query,
  1973. estimate.Id,
  1974. user,
  1975. branch,
  1976. )
  1977. if err != nil {
  1978. return err
  1979. }
  1980. return nil
  1981. }
  1982. // Accepts a borrower struct and returns the id of the inserted borrower and
  1983. // any related error.
  1984. func (estimate *Estimate) insertBorrower(db *sql.DB) error {
  1985. var query string
  1986. var row *sql.Row
  1987. var err error
  1988. query = `INSERT INTO borrower
  1989. (
  1990. estimate_id,
  1991. credit_score,
  1992. monthly_income,
  1993. num
  1994. )
  1995. VALUES (?, ?, ?, ?)
  1996. RETURNING id
  1997. `
  1998. row = db.QueryRow(query,
  1999. estimate.Id,
  2000. estimate.Borrower.Credit,
  2001. estimate.Borrower.Income,
  2002. estimate.Borrower.Num,
  2003. )
  2004. err = row.Scan(&estimate.Borrower.Id)
  2005. if err != nil {
  2006. return err
  2007. }
  2008. return nil
  2009. }
  2010. func insertMi(db *sql.DB, mi MI) (int, error) {
  2011. var id int
  2012. query := `INSERT INTO mi
  2013. (
  2014. type,
  2015. label,
  2016. lender,
  2017. rate,
  2018. premium,
  2019. upfront,
  2020. five_year_total,
  2021. initial_premium,
  2022. initial_rate,
  2023. initial_amount
  2024. )
  2025. VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  2026. RETURNING id`
  2027. row := db.QueryRow(query,
  2028. &mi.Type,
  2029. &mi.Label,
  2030. &mi.Lender,
  2031. &mi.Rate,
  2032. &mi.Premium,
  2033. &mi.Upfront,
  2034. &mi.FiveYearTotal,
  2035. &mi.InitialAllInPremium,
  2036. &mi.InitialAllInRate,
  2037. &mi.InitialAmount,
  2038. )
  2039. err := row.Scan(&id)
  2040. if err != nil {
  2041. return 0, err
  2042. }
  2043. return id, nil
  2044. }
  2045. func insertFee(db *sql.DB, fee Fee) (int, error) {
  2046. var id int
  2047. query := `INSERT INTO fee
  2048. (loan_id, amount, perc, type, notes, name, category)
  2049. VALUES (?, ?, ?, ?, ?, ?, ?)
  2050. RETURNING id`
  2051. row := db.QueryRow(query,
  2052. fee.LoanId,
  2053. fee.Amount,
  2054. fee.Perc,
  2055. fee.Type,
  2056. fee.Notes,
  2057. fee.Name,
  2058. fee.Category,
  2059. )
  2060. err := row.Scan(&id)
  2061. if err != nil {
  2062. return 0, err
  2063. }
  2064. return id, nil
  2065. }
  2066. func insertLoanType(db *sql.DB, lt LoanType) (int, error) {
  2067. var id int
  2068. query := `INSERT INTO loan_type (branch_id, user_id, name)
  2069. VALUES (NULLIF(?, 0), NULLIF(?, 0), ?)
  2070. RETURNING id`
  2071. row := db.QueryRow(query,
  2072. lt.Branch,
  2073. lt.User,
  2074. lt.Name,
  2075. )
  2076. err := row.Scan(&id)
  2077. if err != nil {
  2078. return 0, err
  2079. }
  2080. return id, nil
  2081. }
  2082. func (loan *Loan) insertLoan(db *sql.DB) error {
  2083. var query string
  2084. var row *sql.Row
  2085. var err error
  2086. query = `INSERT INTO loan
  2087. (
  2088. estimate_id,
  2089. type_id,
  2090. amount,
  2091. term,
  2092. interest,
  2093. ltv,
  2094. dti,
  2095. hoi,
  2096. tax,
  2097. name
  2098. )
  2099. VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  2100. RETURNING id
  2101. `
  2102. row = db.QueryRow(query,
  2103. loan.EstimateId,
  2104. loan.Type.Id,
  2105. loan.Amount,
  2106. loan.Term,
  2107. loan.Interest,
  2108. loan.Ltv,
  2109. loan.Dti,
  2110. loan.Hoi,
  2111. loan.Tax,
  2112. loan.Name,
  2113. )
  2114. err = row.Scan(&loan.Id)
  2115. if err != nil {
  2116. return err
  2117. }
  2118. _, err = insertMi(db, loan.Mi)
  2119. if err != nil {
  2120. return err
  2121. }
  2122. for i := range loan.Fees {
  2123. loan.Fees[i].LoanId = loan.Id
  2124. _, err := insertFee(db, loan.Fees[i])
  2125. if err != nil {
  2126. return err
  2127. }
  2128. }
  2129. return nil
  2130. }
  2131. func (estimate *Estimate) insertEstimate(db *sql.DB) error {
  2132. var query string
  2133. var row *sql.Row
  2134. var err error
  2135. // var id int // Inserted estimate's id
  2136. query = `INSERT INTO estimate
  2137. (
  2138. user_id,
  2139. transaction,
  2140. price,
  2141. property,
  2142. occupancy,
  2143. zip,
  2144. pud
  2145. )
  2146. VALUES (?, ?, ?, ?, ?, ?, ?)
  2147. RETURNING id
  2148. `
  2149. row = db.QueryRow(query,
  2150. estimate.User,
  2151. estimate.Transaction,
  2152. estimate.Price,
  2153. estimate.Property,
  2154. estimate.Occupancy,
  2155. estimate.Zip,
  2156. estimate.Pud,
  2157. )
  2158. err = row.Scan(&estimate.Id)
  2159. if err != nil {
  2160. return err
  2161. }
  2162. err = estimate.insertBorrower(db)
  2163. if err != nil {
  2164. return err
  2165. }
  2166. for i := range estimate.Loans {
  2167. estimate.Loans[i].EstimateId = estimate.Id
  2168. err = estimate.Loans[i].insertLoan(db)
  2169. if err != nil {
  2170. return err
  2171. }
  2172. }
  2173. return nil
  2174. }
  2175. func (estimate *Estimate) del(db *sql.DB, user int) error {
  2176. var query string
  2177. var err error
  2178. query = `DELETE FROM estimate WHERE id = ? AND user_id = ?`
  2179. _, err = db.Exec(query, estimate.Id, user)
  2180. if err != nil {
  2181. return err
  2182. }
  2183. return nil
  2184. }
  2185. func (et *ETemplate) del(db *sql.DB, user int) error {
  2186. var query string
  2187. var err error
  2188. query = `DELETE FROM estimate_template WHERE id = ? AND user_id = ?`
  2189. _, err = db.Exec(query, et.Id, user)
  2190. if err != nil {
  2191. return err
  2192. }
  2193. return nil
  2194. }
  2195. func (eTemplate *ETemplate) insert(db *sql.DB) error {
  2196. var query string
  2197. var row *sql.Row
  2198. var err error
  2199. query = `INSERT INTO estimate_template
  2200. (
  2201. user_id,
  2202. branch_id,
  2203. estimate_id,
  2204. )
  2205. VALUES (?, ?, ?)
  2206. RETURNING id
  2207. `
  2208. row = db.QueryRow(query,
  2209. eTemplate.UserId,
  2210. eTemplate.BranchId,
  2211. eTemplate.Estimate.Id,
  2212. )
  2213. err = row.Scan(&eTemplate.Id)
  2214. if err != nil {
  2215. return err
  2216. }
  2217. return nil
  2218. }
  2219. func createEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2220. var estimate Estimate
  2221. err := json.NewDecoder(r.Body).Decode(&estimate)
  2222. if err != nil {
  2223. http.Error(w, "Invalid fields.", 422)
  2224. return
  2225. }
  2226. claims, err := getClaims(r)
  2227. estimate.User = claims.Id
  2228. err = estimate.insertEstimate(db)
  2229. if err != nil {
  2230. http.Error(w, err.Error(), 422)
  2231. return
  2232. }
  2233. estimate.makeResults()
  2234. err = estimate.insertResults(db)
  2235. if err != nil {
  2236. http.Error(w, err.Error(), 500)
  2237. return
  2238. }
  2239. e, err := getEstimates(db, estimate.Id, 0)
  2240. if err != nil {
  2241. http.Error(w, err.Error(), 500)
  2242. return
  2243. }
  2244. json.NewEncoder(w).Encode(e[0])
  2245. }
  2246. // Query all estimates that belong to the current user
  2247. func fetchEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2248. var estimates []Estimate
  2249. claims, err := getClaims(r)
  2250. estimates, err = getEstimates(db, 0, claims.Id)
  2251. if err != nil {
  2252. http.Error(w, err.Error(), 500)
  2253. return
  2254. }
  2255. json.NewEncoder(w).Encode(estimates)
  2256. }
  2257. func checkConventional(l Loan, b Borrower) error {
  2258. if b.Credit < 620 {
  2259. return errors.New("Credit score too low for conventional loan")
  2260. }
  2261. // Buyer needs to put down 20% to avoid mortgage insurance
  2262. if l.Ltv > 80 && l.Mi.Rate == 0 {
  2263. return errors.New(fmt.Sprintln(
  2264. l.Name,
  2265. "down payment must be 20% to avoid insurance",
  2266. ))
  2267. }
  2268. return nil
  2269. }
  2270. func checkFHA(l Loan, b Borrower) error {
  2271. if b.Credit < 500 {
  2272. return errors.New("Credit score too low for FHA loan")
  2273. }
  2274. if l.Ltv > 96.5 {
  2275. return errors.New("FHA down payment must be at least 3.5%")
  2276. }
  2277. if b.Credit < 580 && l.Ltv > 90 {
  2278. return errors.New("FHA down payment must be at least 10%")
  2279. }
  2280. // Debt-to-income must be below 45% if credit score is below 580.
  2281. if b.Credit < 580 && l.Dti > 45 {
  2282. return errors.New(fmt.Sprintln(
  2283. l.Name, "debt to income is too high for credit score.",
  2284. ))
  2285. }
  2286. return nil
  2287. }
  2288. // Loan option for veterans with no set rules. Mainly placeholder.
  2289. func checkVA(l Loan, b Borrower) error {
  2290. return nil
  2291. }
  2292. // Loan option for residents of rural areas with no set rules.
  2293. // Mainly placeholder.
  2294. func checkUSDA(l Loan, b Borrower) error {
  2295. return nil
  2296. }
  2297. // Should also check loan amount limit maybe with an API.
  2298. func checkEstimate(e Estimate) error {
  2299. if e.Property == "" {
  2300. return errors.New("Empty property type")
  2301. }
  2302. if e.Price == 0 {
  2303. return errors.New("Empty property price")
  2304. }
  2305. if e.Borrower.Num == 0 {
  2306. return errors.New("Missing number of borrowers")
  2307. }
  2308. if e.Borrower.Credit == 0 {
  2309. return errors.New("Missing borrower credit score")
  2310. }
  2311. if e.Borrower.Income == 0 {
  2312. return errors.New("Missing borrower credit income")
  2313. }
  2314. for _, l := range e.Loans {
  2315. if l.Amount == 0 {
  2316. return errors.New(fmt.Sprintln(l.Name, "loan amount cannot be zero"))
  2317. }
  2318. if l.Term == 0 {
  2319. return errors.New(fmt.Sprintln(l.Name, "loan term cannot be zero"))
  2320. }
  2321. if l.Interest == 0 {
  2322. return errors.New(fmt.Sprintln(l.Name, "loan interest cannot be zero"))
  2323. }
  2324. // Can be used to check rules for specific loan types
  2325. var err error
  2326. switch l.Type.Id {
  2327. case 1:
  2328. err = checkConventional(l, e.Borrower)
  2329. case 2:
  2330. err = checkFHA(l, e.Borrower)
  2331. case 3:
  2332. err = checkVA(l, e.Borrower)
  2333. case 4:
  2334. err = checkUSDA(l, e.Borrower)
  2335. default:
  2336. err = errors.New("Invalid loan type")
  2337. }
  2338. if err != nil {
  2339. return err
  2340. }
  2341. }
  2342. return nil
  2343. }
  2344. func validateEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2345. var estimate Estimate
  2346. err := json.NewDecoder(r.Body).Decode(&estimate)
  2347. if err != nil {
  2348. http.Error(w, err.Error(), 422)
  2349. return
  2350. }
  2351. err = checkEstimate(estimate)
  2352. if err != nil {
  2353. http.Error(w, err.Error(), 406)
  2354. return
  2355. }
  2356. }
  2357. func checkPdf(w http.ResponseWriter, r *http.Request) {
  2358. db, err := sql.Open("mysql",
  2359. fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/skouter_dev",
  2360. os.Getenv("DBUser"),
  2361. os.Getenv("DBPass")))
  2362. // w.Header().Set("Content-Type", "application/json; charset=UTF-8")
  2363. err = db.Ping()
  2364. if err != nil {
  2365. fmt.Println("Bad database configuration: %v\n", err)
  2366. panic(err)
  2367. // maybe os.Exit(1) instead
  2368. }
  2369. estimates, err := getEstimates(db, 1, 0)
  2370. if err != nil {
  2371. w.WriteHeader(500)
  2372. return
  2373. }
  2374. // claims, err := getClaims(r)
  2375. if err != nil {
  2376. w.WriteHeader(500)
  2377. return
  2378. }
  2379. user, err := queryUser(db, 1)
  2380. info := Report{
  2381. Title: "test PDF",
  2382. Name: "idk-random-name",
  2383. User: user,
  2384. Estimate: estimates[0],
  2385. }
  2386. avatar, err := fetchAvatar(db, info.User.Id)
  2387. letterhead, err := fetchLetterhead(db, info.User.Id)
  2388. info.Avatar =
  2389. base64.StdEncoding.EncodeToString(avatar)
  2390. info.Letterhead =
  2391. base64.StdEncoding.EncodeToString(letterhead)
  2392. for l := range info.Estimate.Loans {
  2393. loan := info.Estimate.Loans[l]
  2394. for f := range info.Estimate.Loans[l].Fees {
  2395. if info.Estimate.Loans[l].Fees[f].Amount < 0 {
  2396. loan.Credits = append(loan.Credits, loan.Fees[f])
  2397. }
  2398. }
  2399. }
  2400. err = pages["report"].tpl.ExecuteTemplate(w, "master.tpl", info)
  2401. if err != nil {
  2402. fmt.Println(err)
  2403. }
  2404. }
  2405. func getETemplates(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2406. claims, err := getClaims(r)
  2407. et, err := queryETemplates(db, 0, claims.Id)
  2408. if err != nil {
  2409. http.Error(w, err.Error(), 500)
  2410. return
  2411. }
  2412. json.NewEncoder(w).Encode(et)
  2413. }
  2414. func createETemplate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2415. var estimate Estimate
  2416. err := json.NewDecoder(r.Body).Decode(&estimate)
  2417. if err != nil {
  2418. http.Error(w, err.Error(), 422)
  2419. return
  2420. }
  2421. claims, err := getClaims(r)
  2422. if err != nil {
  2423. http.Error(w, err.Error(), 422)
  2424. return
  2425. }
  2426. err = estimate.insertETemplate(db, claims.Id, 0)
  2427. if err != nil {
  2428. http.Error(w, err.Error(), 500)
  2429. return
  2430. }
  2431. }
  2432. func getPdf(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2433. var estimate Estimate
  2434. err := json.NewDecoder(r.Body).Decode(&estimate)
  2435. cmd := exec.Command("wkhtmltopdf", "-", "-")
  2436. stdout, err := cmd.StdoutPipe()
  2437. if err != nil {
  2438. w.WriteHeader(500)
  2439. log.Println(err)
  2440. return
  2441. }
  2442. stdin, err := cmd.StdinPipe()
  2443. if err != nil {
  2444. w.WriteHeader(500)
  2445. log.Println(err)
  2446. return
  2447. }
  2448. if err := cmd.Start(); err != nil {
  2449. log.Fatal(err)
  2450. }
  2451. claims, err := getClaims(r)
  2452. if err != nil {
  2453. w.WriteHeader(500)
  2454. log.Println(err)
  2455. return
  2456. }
  2457. user, err := queryUser(db, claims.Id)
  2458. info := Report{
  2459. Title: "test PDF",
  2460. Name: "idk-random-name",
  2461. User: user,
  2462. Estimate: estimate,
  2463. }
  2464. avatar, err := fetchAvatar(db, info.User.Id)
  2465. letterhead, err := fetchLetterhead(db, info.User.Id)
  2466. if len(avatar) > 1 {
  2467. info.Avatar =
  2468. base64.StdEncoding.EncodeToString(avatar)
  2469. }
  2470. if len(letterhead) > 1 {
  2471. info.Letterhead =
  2472. base64.StdEncoding.EncodeToString(letterhead)
  2473. }
  2474. err = pages["report"].tpl.ExecuteTemplate(stdin, "master.tpl", info)
  2475. if err != nil {
  2476. w.WriteHeader(500)
  2477. log.Println(err)
  2478. return
  2479. }
  2480. stdin.Close()
  2481. buf, err := io.ReadAll(stdout)
  2482. if _, err := w.Write(buf); err != nil {
  2483. w.WriteHeader(500)
  2484. log.Println(err)
  2485. return
  2486. }
  2487. if err := cmd.Wait(); err != nil {
  2488. log.Println(err)
  2489. return
  2490. }
  2491. }
  2492. func clipLetterhead(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2493. var validTypes []string = []string{"image/png", "image/jpeg"}
  2494. var isValidType bool
  2495. var err error
  2496. // claims, err := getClaims(r)
  2497. if err != nil {
  2498. http.Error(w, "Invalid token.", 422)
  2499. return
  2500. }
  2501. img, t, err := image.Decode(r.Body)
  2502. if err != nil {
  2503. http.Error(w, "Invalid file, JPEG and PNG only.", 422)
  2504. return
  2505. }
  2506. for _, v := range validTypes {
  2507. if v == "image/"+t {
  2508. isValidType = true
  2509. }
  2510. }
  2511. if !isValidType {
  2512. http.Error(w, "Invalid file type.", 422)
  2513. return
  2514. }
  2515. g := gift.New(
  2516. gift.ResizeToFit(400, 200, gift.LanczosResampling),
  2517. )
  2518. dst := image.NewRGBA(g.Bounds(img.Bounds()))
  2519. g.Draw(dst, img)
  2520. w.Header().Set("Content-Type", "image/png")
  2521. err = png.Encode(w, dst)
  2522. if err != nil {
  2523. http.Error(w, "Error encoding.", 500)
  2524. return
  2525. }
  2526. }
  2527. func createCustomer(name string, email string, address Address) (
  2528. stripe.Customer, error) {
  2529. params := &stripe.CustomerParams{
  2530. Email: stripe.String(email),
  2531. Name: stripe.String(name),
  2532. Address: &stripe.AddressParams{
  2533. City: stripe.String(address.City),
  2534. Country: stripe.String(address.Country),
  2535. Line1: stripe.String(address.Street),
  2536. PostalCode: stripe.String(address.Zip),
  2537. State: stripe.String(address.Region),
  2538. },
  2539. };
  2540. result, err := customer.New(params)
  2541. return *result, err
  2542. }
  2543. // Initiates a new standard subscription using a given customer ID
  2544. func createSubscription(cid string) (*stripe.Subscription, error) {
  2545. // Automatically save the payment method to the subscription
  2546. // when the first payment is successful.
  2547. paymentSettings := &stripe.SubscriptionPaymentSettingsParams{
  2548. SaveDefaultPaymentMethod: stripe.String("on_subscription"),
  2549. }
  2550. // Create the subscription. Note we're expanding the Subscription's
  2551. // latest invoice and that invoice's payment_intent
  2552. // so we can pass it to the front end to confirm the payment
  2553. subscriptionParams := &stripe.SubscriptionParams{
  2554. Customer: stripe.String(cid),
  2555. Items: []*stripe.SubscriptionItemsParams{
  2556. {
  2557. Price: stripe.String(standardPriceId),
  2558. },
  2559. },
  2560. PaymentSettings: paymentSettings,
  2561. PaymentBehavior: stripe.String("default_incomplete"),
  2562. }
  2563. subscriptionParams.AddExpand("latest_invoice.payment_intent")
  2564. s, err := subscription.New(subscriptionParams)
  2565. return s, err
  2566. }
  2567. // Creates a new subscription instance for a new user or retrieves the
  2568. // existing instance if possible. It's main purpose is to supply a
  2569. // client secret used for sending billing information to stripe.
  2570. func subscribe(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2571. claims, err := getClaims(r)
  2572. user, err := queryUser(db, claims.Id)
  2573. if err != nil {
  2574. w.WriteHeader(422)
  2575. return
  2576. }
  2577. user.querySub(db)
  2578. var name string = user.FirstName + " " + user.LastName
  2579. if user.CustomerId == "" {
  2580. c, err := createCustomer(name, user.Email, user.Address)
  2581. if err != nil {
  2582. http.Error(w, err.Error(), 500)
  2583. return
  2584. }
  2585. err = user.updateCustomerId(db, c.ID)
  2586. if err != nil {
  2587. http.Error(w, err.Error(), 500)
  2588. return
  2589. }
  2590. }
  2591. if user.Sub.Id == 0 {
  2592. s, err := createSubscription(user.CustomerId)
  2593. if err != nil {
  2594. http.Error(w, err.Error(), 500)
  2595. return
  2596. }
  2597. user.Sub.UserId = user.Id
  2598. user.Sub.StripeId = s.ID
  2599. user.Sub.CustomerId = user.CustomerId
  2600. user.Sub.PriceId = standardPriceId
  2601. user.Sub.End = int(s.CurrentPeriodEnd)
  2602. user.Sub.Start = int(s.CurrentPeriodStart)
  2603. user.Sub.ClientSecret = s.LatestInvoice.PaymentIntent.ClientSecret
  2604. user.Sub.PaymentStatus = string(s.LatestInvoice.PaymentIntent.Status)
  2605. err = user.Sub.insertSub(db)
  2606. if err != nil {
  2607. http.Error(w, err.Error(), 500)
  2608. return
  2609. }
  2610. json.NewEncoder(w).Encode(user.Sub)
  2611. } else {
  2612. err = user.Sub.updateSub(db)
  2613. if err != nil {
  2614. http.Error(w, err.Error(), 500)
  2615. return
  2616. }
  2617. json.NewEncoder(w).Encode(user.Sub)
  2618. }
  2619. // Should check that subscription is still valid and has payment intent
  2620. // here
  2621. }
  2622. // A successful subscription payment should be confirmed by Stripe and
  2623. // Updated through this hook.
  2624. func invoicePaid(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2625. var invoice stripe.Invoice
  2626. b, err := io.ReadAll(r.Body)
  2627. if err != nil {
  2628. http.Error(w, err.Error(), http.StatusBadRequest)
  2629. log.Printf("io.ReadAll: %v", err)
  2630. return
  2631. }
  2632. event, err := webhook.ConstructEvent(b,
  2633. r.Header.Get("Stripe-Signature"),
  2634. hookKeys.InvoicePaid)
  2635. if err != nil {
  2636. http.Error(w, err.Error(), http.StatusBadRequest)
  2637. log.Printf("webhook.ConstructEvent: %v", err)
  2638. return
  2639. }
  2640. // OK should be sent before any processing to confirm with Stripe that
  2641. // the hook was received
  2642. w.WriteHeader(http.StatusOK)
  2643. if event.Type != "invoice.paid" {
  2644. log.Println("Invalid event type sent to invoice-paid.")
  2645. return
  2646. }
  2647. json.Unmarshal(event.Data.Raw, &invoice)
  2648. log.Println(event.Type, invoice.ID, invoice.Customer.ID)
  2649. user, err := queryCustomer(db, invoice.Customer.ID)
  2650. if err != nil {
  2651. log.Printf("Could not query customer: %v", err)
  2652. return
  2653. }
  2654. s, err := subscription.Get(invoice.Subscription.ID, nil)
  2655. if err != nil {
  2656. log.Printf("Could not fetch subscription: %v", err)
  2657. return
  2658. }
  2659. log.Println(user.Id, s.ID)
  2660. }
  2661. func invoiceFailed(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2662. b, err := io.ReadAll(r.Body)
  2663. if err != nil {
  2664. http.Error(w, err.Error(), http.StatusBadRequest)
  2665. log.Printf("io.ReadAll: %v", err)
  2666. return
  2667. }
  2668. event, err := webhook.ConstructEvent(b, r.Header.Get("Stripe-Signature"),
  2669. os.Getenv("STRIPE_SECRET_KEY"))
  2670. if err != nil {
  2671. http.Error(w, err.Error(), http.StatusBadRequest)
  2672. log.Printf("webhook.ConstructEvent: %v", err)
  2673. return
  2674. }
  2675. log.Println(event.Data)
  2676. }
  2677. func api(w http.ResponseWriter, r *http.Request) {
  2678. var args []string
  2679. p := r.URL.Path
  2680. db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s",
  2681. os.Getenv("DBUser"),
  2682. os.Getenv("DBPass"),
  2683. os.Getenv("DBName"),
  2684. ))
  2685. w.Header().Set("Content-Type", "application/json; charset=UTF-8")
  2686. err = db.Ping()
  2687. if err != nil {
  2688. fmt.Println("Bad database configuration: %v\n", err)
  2689. panic(err)
  2690. // maybe os.Exit(1) instead
  2691. }
  2692. switch {
  2693. case match(p, "/api/login", &args) &&
  2694. r.Method == http.MethodPost:
  2695. login(w, db, r)
  2696. case match(p, "/api/token", &args) &&
  2697. r.Method == http.MethodGet && guard(r, 1):
  2698. getToken(w, db, r)
  2699. case match(p, "/api/letterhead", &args) &&
  2700. r.Method == http.MethodPost && guard(r, 1):
  2701. clipLetterhead(w, db, r)
  2702. case match(p, "/api/users", &args) && // Array of all users
  2703. r.Method == http.MethodGet && guard(r, 3):
  2704. getUsers(w, db, r)
  2705. case match(p, "/api/user", &args) &&
  2706. r.Method == http.MethodGet && guard(r, 1):
  2707. getUser(w, db, r)
  2708. case match(p, "/api/user", &args) &&
  2709. r.Method == http.MethodPost:
  2710. createUser(w, db, r)
  2711. case match(p, "/api/user", &args) &&
  2712. r.Method == http.MethodPatch &&
  2713. guard(r, 3): // For admin to modify any user
  2714. patchUser(w, db, r)
  2715. case match(p, "/api/user", &args) &&
  2716. r.Method == http.MethodPatch &&
  2717. guard(r, 1): // For employees to modify own accounts
  2718. patchSelf(w, db, r)
  2719. case match(p, "/api/user", &args) &&
  2720. r.Method == http.MethodDelete &&
  2721. guard(r, 3):
  2722. deleteUser(w, db, r)
  2723. case match(p, "/api/user/avatar", &args) &&
  2724. r.Method == http.MethodGet &&
  2725. guard(r, 1):
  2726. getAvatar(w, db, r)
  2727. case match(p, "/api/user/avatar", &args) &&
  2728. r.Method == http.MethodPost &&
  2729. guard(r, 1):
  2730. setAvatar(w, db, r)
  2731. case match(p, "/api/user/letterhead", &args) &&
  2732. r.Method == http.MethodGet &&
  2733. guard(r, 1):
  2734. getLetterhead(w, db, r)
  2735. case match(p, "/api/user/letterhead", &args) &&
  2736. r.Method == http.MethodPost &&
  2737. guard(r, 1):
  2738. setLetterhead(w, db, r)
  2739. case match(p, "/api/user/password", &args) &&
  2740. r.Method == http.MethodPost &&
  2741. guard(r, 1):
  2742. changePassword(w, db, r)
  2743. case match(p, "/api/user/subscribe", &args) &&
  2744. r.Method == http.MethodPost &&
  2745. guard(r, 1):
  2746. subscribe(w, db, r)
  2747. case match(p, "/api/fees", &args) &&
  2748. r.Method == http.MethodGet &&
  2749. guard(r, 1):
  2750. getFeesTemp(w, db, r)
  2751. case match(p, "/api/fee", &args) &&
  2752. r.Method == http.MethodPost &&
  2753. guard(r, 1):
  2754. createFeesTemp(w, db, r)
  2755. case match(p, "/api/fee", &args) &&
  2756. r.Method == http.MethodDelete &&
  2757. guard(r, 1):
  2758. deleteFeeTemp(w, db, r)
  2759. case match(p, "/api/estimates", &args) &&
  2760. r.Method == http.MethodGet &&
  2761. guard(r, 1):
  2762. fetchEstimate(w, db, r)
  2763. case match(p, "/api/estimate", &args) &&
  2764. r.Method == http.MethodPost &&
  2765. guard(r, 1):
  2766. createEstimate(w, db, r)
  2767. case match(p, "/api/estimate", &args) &&
  2768. r.Method == http.MethodDelete &&
  2769. guard(r, 1):
  2770. deleteEstimate(w, db, r)
  2771. case match(p, "/api/estimate/validate", &args) &&
  2772. r.Method == http.MethodPost &&
  2773. guard(r, 1):
  2774. validateEstimate(w, db, r)
  2775. case match(p, "/api/estimate/summarize", &args) &&
  2776. r.Method == http.MethodPost &&
  2777. guard(r, 1):
  2778. summarize(w, db, r)
  2779. case match(p, "/api/templates", &args) &&
  2780. r.Method == http.MethodGet &&
  2781. guard(r, 1):
  2782. getETemplates(w, db, r)
  2783. case match(p, "/api/templates", &args) &&
  2784. r.Method == http.MethodPost &&
  2785. guard(r, 1):
  2786. createETemplate(w, db, r)
  2787. case match(p, "/api/templates", &args) &&
  2788. r.Method == http.MethodDelete &&
  2789. guard(r, 1):
  2790. deleteET(w, db, r)
  2791. case match(p, "/api/pdf", &args) &&
  2792. r.Method == http.MethodPost &&
  2793. guard(r, 1):
  2794. getPdf(w, db, r)
  2795. case match(p, "/api/stripe/invoice-paid", &args) &&
  2796. r.Method == http.MethodPost:
  2797. invoicePaid(w, db, r)
  2798. case match(p, "/api/stripe/invoice-payment-failed", &args) &&
  2799. r.Method == http.MethodPost:
  2800. invoiceFailed(w, db, r)
  2801. default:
  2802. http.Error(w, "Invalid route or token", 404)
  2803. }
  2804. db.Close()
  2805. }
  2806. func route(w http.ResponseWriter, r *http.Request) {
  2807. var page Page
  2808. var args []string
  2809. p := r.URL.Path
  2810. switch {
  2811. case r.Method == "GET" && match(p, "/", &args):
  2812. page = pages["home"]
  2813. case match(p, "/terms", &args):
  2814. page = pages["terms"]
  2815. case match(p, "/app", &args):
  2816. page = pages["app"]
  2817. default:
  2818. http.NotFound(w, r)
  2819. return
  2820. }
  2821. page.Render(w)
  2822. }
  2823. func serve() {
  2824. files := http.FileServer(http.Dir(""))
  2825. proxy, err := url.Parse("http://localhost:8002")
  2826. if err != nil {
  2827. log.Fatal("invalid origin server URL")
  2828. }
  2829. http.Handle("/assets/", files)
  2830. http.HandleFunc("/api/", api)
  2831. http.HandleFunc("/app", route)
  2832. http.Handle("/", httputil.NewSingleHostReverseProxy(proxy))
  2833. log.Fatal(http.ListenAndServe(address, nil))
  2834. }
  2835. func dbReset(db *sql.DB) {
  2836. b, err := os.ReadFile("migrations/reset.sql")
  2837. if err != nil {
  2838. log.Fatal(err)
  2839. }
  2840. _, err = db.Exec(string(b))
  2841. if err != nil {
  2842. log.Fatal(err)
  2843. }
  2844. b, err = os.ReadFile("migrations/0_29092022_setup_tables.sql")
  2845. if err != nil {
  2846. log.Fatal(err)
  2847. }
  2848. _, err = db.Exec(string(b))
  2849. if err != nil {
  2850. log.Fatal(err)
  2851. }
  2852. }
  2853. func generateFees(loan Loan) []Fee {
  2854. var fees []Fee
  2855. var fee Fee
  2856. p := gofakeit.Float32Range(0.5, 10)
  2857. size := gofakeit.Number(1, 10)
  2858. for f := 0; f < size; f++ {
  2859. fee = Fee{
  2860. Amount: int(float32(loan.Amount) * p / 100),
  2861. Perc: p,
  2862. Name: gofakeit.BuzzWord(),
  2863. Type: feeTypes[gofakeit.Number(0, len(feeTypes)-1)],
  2864. }
  2865. fees = append(fees, fee)
  2866. }
  2867. return fees
  2868. }
  2869. func generateCredits(loan Loan) []Fee {
  2870. var fees []Fee
  2871. var fee Fee
  2872. p := gofakeit.Float32Range(-10, -0.5)
  2873. size := gofakeit.Number(1, 10)
  2874. for f := 0; f < size; f++ {
  2875. fee = Fee{
  2876. Amount: int(float32(loan.Amount) * p / 100),
  2877. Perc: p,
  2878. Name: gofakeit.BuzzWord(),
  2879. Type: feeTypes[gofakeit.Number(0, len(feeTypes)-1)],
  2880. }
  2881. fees = append(fees, fee)
  2882. }
  2883. return fees
  2884. }
  2885. func seedAddresses(db *sql.DB) []Address {
  2886. addresses := make([]Address, 10)
  2887. for i, a := range addresses {
  2888. a.Street = gofakeit.Street()
  2889. a.City = gofakeit.City()
  2890. a.Region = gofakeit.State()
  2891. a.Country = "Canada"
  2892. a.Full = fmt.Sprintf("%s, %s %s", a.Street, a.City, a.Region)
  2893. id, err := insertAddress(db, a)
  2894. if err != nil {
  2895. log.Println(err)
  2896. break
  2897. }
  2898. addresses[i].Id = id
  2899. }
  2900. return addresses
  2901. }
  2902. func seedBranches(db *sql.DB, addresses []Address) []Branch {
  2903. branches := make([]Branch, 4)
  2904. for i := range branches {
  2905. branches[i].Name = gofakeit.Company()
  2906. branches[i].Type = "NMLS"
  2907. branches[i].Letterhead = gofakeit.ImagePng(400, 200)
  2908. branches[i].Num = gofakeit.HexUint8()
  2909. branches[i].Phone = gofakeit.Phone()
  2910. branches[i].Address.Id = gofakeit.Number(1, 5)
  2911. id, err := insertBranch(db, branches[i])
  2912. if err != nil {
  2913. log.Println(err)
  2914. break
  2915. }
  2916. branches[i].Id = id
  2917. }
  2918. return branches
  2919. }
  2920. func seedUsers(db *sql.DB, addresses []Address, branches []Branch) []User {
  2921. users := make([]User, 10)
  2922. for i := range users {
  2923. p := gofakeit.Person()
  2924. users[i].FirstName = p.FirstName
  2925. users[i].LastName = p.LastName
  2926. users[i].Email = p.Contact.Email
  2927. users[i].Phone = p.Contact.Phone
  2928. users[i].Branch = branches[gofakeit.Number(0, 3)]
  2929. users[i].Address = addresses[gofakeit.Number(1, 9)]
  2930. // users[i].Letterhead = gofakeit.ImagePng(400, 200)
  2931. // users[i].Avatar = gofakeit.ImagePng(200, 200)
  2932. users[i].Country = []string{"Canada", "USA"}[gofakeit.Number(0, 1)]
  2933. users[i].Password = "test123"
  2934. users[i].Verified = true
  2935. users[i].Title = "Loan Officer"
  2936. users[i].Status = "Subscribed"
  2937. users[i].Role = "User"
  2938. }
  2939. users[0].Email = "test@example.com"
  2940. users[0].Email = "test@example.com"
  2941. users[1].Email = "test2@example.com"
  2942. users[1].Status = "Branch"
  2943. users[1].Role = "Manager"
  2944. users[2].Email = "test3@example.com"
  2945. users[2].Status = "Free"
  2946. users[2].Role = "Admin"
  2947. for i := range users {
  2948. var err error
  2949. users[i].Id, err = insertUser(db, users[i])
  2950. if err != nil {
  2951. log.Println(err)
  2952. break
  2953. }
  2954. }
  2955. return users
  2956. }
  2957. func seedLicenses(db *sql.DB, users []User) []License {
  2958. licenses := make([]License, len(users))
  2959. for i := range licenses {
  2960. licenses[i].UserId = users[i].Id
  2961. licenses[i].Type = []string{"NMLS", "FSRA"}[gofakeit.Number(0, 1)]
  2962. licenses[i].Num = gofakeit.UUID()
  2963. id, err := insertLicense(db, licenses[i])
  2964. if err != nil {
  2965. log.Println(err)
  2966. break
  2967. }
  2968. licenses[i].Id = id
  2969. }
  2970. return licenses
  2971. }
  2972. func seedLoanTypes(db *sql.DB) []LoanType {
  2973. var loantypes []LoanType
  2974. var loantype LoanType
  2975. var err error
  2976. loantype = LoanType{Branch: 0, User: 0, Name: "Conventional"}
  2977. loantype.Id, err = insertLoanType(db, loantype)
  2978. if err != nil {
  2979. panic(err)
  2980. }
  2981. loantypes = append(loantypes, loantype)
  2982. loantype = LoanType{Branch: 0, User: 0, Name: "FHA"}
  2983. loantype.Id, err = insertLoanType(db, loantype)
  2984. if err != nil {
  2985. panic(err)
  2986. }
  2987. loantypes = append(loantypes, loantype)
  2988. loantype = LoanType{Branch: 0, User: 0, Name: "USDA"}
  2989. loantype.Id, err = insertLoanType(db, loantype)
  2990. if err != nil {
  2991. panic(err)
  2992. }
  2993. loantypes = append(loantypes, loantype)
  2994. loantype = LoanType{Branch: 0, User: 0, Name: "VA"}
  2995. loantype.Id, err = insertLoanType(db, loantype)
  2996. if err != nil {
  2997. panic(err)
  2998. }
  2999. loantypes = append(loantypes, loantype)
  3000. return loantypes
  3001. }
  3002. func seedEstimates(db *sql.DB, users []User, ltypes []LoanType) []Estimate {
  3003. var estimates []Estimate
  3004. var estimate Estimate
  3005. var l Loan
  3006. var err error
  3007. for i := 0; i < 15; i++ {
  3008. estimate = Estimate{}
  3009. estimate.User = users[gofakeit.Number(0, len(users)-1)].Id
  3010. estimate.Borrower = Borrower{
  3011. Credit: gofakeit.Number(600, 800),
  3012. Income: gofakeit.Number(1000000, 15000000),
  3013. Num: gofakeit.Number(1, 20),
  3014. }
  3015. estimate.Transaction = []string{"Purchase", "Refinance"}[gofakeit.Number(0, 1)]
  3016. estimate.Price = gofakeit.Number(50000, 200000000)
  3017. estimate.Property =
  3018. propertyTypes[gofakeit.Number(0, len(propertyTypes)-1)]
  3019. estimate.Occupancy =
  3020. []string{"Primary", "Secondary", "Investment"}[gofakeit.Number(0, 2)]
  3021. estimate.Zip = gofakeit.Zip()
  3022. lsize := gofakeit.Number(1, 6)
  3023. for j := 0; j < lsize; j++ {
  3024. l.Type = ltypes[gofakeit.Number(0, len(ltypes)-1)]
  3025. l.Amount = gofakeit.Number(
  3026. int(float32(estimate.Price)*0.5),
  3027. int(float32(estimate.Price)*0.93))
  3028. l.Term = gofakeit.Number(4, 30)
  3029. l.Hoi = gofakeit.Number(50000, 700000)
  3030. l.Hazard = gofakeit.Number(5000, 200000)
  3031. l.Tax = gofakeit.Number(5000, 200000)
  3032. l.Interest = gofakeit.Float32Range(0.5, 8)
  3033. l.Fees = generateFees(l)
  3034. l.Credits = generateCredits(l)
  3035. l.Name = gofakeit.AdjectiveDescriptive()
  3036. estimate.Loans = append(estimate.Loans, l)
  3037. }
  3038. estimates = append(estimates, estimate)
  3039. }
  3040. estimates[0].User = users[0].Id
  3041. estimates[1].User = users[0].Id
  3042. for i := range estimates {
  3043. err = estimates[i].insertEstimate(db)
  3044. if err != nil {
  3045. log.Println(err)
  3046. return estimates
  3047. }
  3048. }
  3049. return estimates
  3050. }
  3051. func seedResults(db *sql.DB, estimates []Estimate) error {
  3052. var err error
  3053. for i := range estimates {
  3054. estimates[i].makeResults()
  3055. err = estimates[i].insertResults(db)
  3056. if err != nil {
  3057. log.Println(err)
  3058. return err
  3059. }
  3060. }
  3061. return nil
  3062. }
  3063. func dbSeed(db *sql.DB) {
  3064. addresses := seedAddresses(db)
  3065. branches := seedBranches(db, addresses)
  3066. users := seedUsers(db, addresses, branches)
  3067. _ = seedLicenses(db, users)
  3068. loantypes := seedLoanTypes(db)
  3069. estimates := seedEstimates(db, users, loantypes)
  3070. _ = seedResults(db, estimates)
  3071. }
  3072. func dev(args []string) {
  3073. os.Setenv("DBName", "skouter_dev")
  3074. os.Setenv("DBUser", "tester")
  3075. os.Setenv("DBPass", "test123")
  3076. stripe.Key = os.Getenv("STRIPE_SECRET_KEY")
  3077. standardPriceId = "price_1OZLK9BPMoXn2pf9kuTAf8rs"
  3078. hookKeys = HookKeys{
  3079. InvoicePaid: os.Getenv("DEV_WEBHOOK_KEY"),
  3080. InvoiceFailed: os.Getenv("DEV_WEBHOOK_KEY"),
  3081. }
  3082. db, err := sql.Open("mysql",
  3083. fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s?multiStatements=true",
  3084. os.Getenv("DBUser"),
  3085. os.Getenv("DBPass"),
  3086. os.Getenv("DBName"),
  3087. ))
  3088. err = db.Ping()
  3089. if err != nil {
  3090. log.Println("Bad database configuration: %v", err)
  3091. panic(err)
  3092. // maybe os.Exit(1) instead
  3093. }
  3094. if len(args) == 0 {
  3095. serve()
  3096. return
  3097. }
  3098. switch args[0] {
  3099. case "seed":
  3100. dbSeed(db)
  3101. case "reset":
  3102. dbReset(db)
  3103. default:
  3104. return
  3105. }
  3106. db.Close()
  3107. }
  3108. func check(args []string) {
  3109. os.Setenv("DBName", "skouter_dev")
  3110. os.Setenv("DBUser", "tester")
  3111. os.Setenv("DBPass", "test123")
  3112. files := http.FileServer(http.Dir(""))
  3113. http.Handle("/assets/", files)
  3114. http.HandleFunc("/", checkPdf)
  3115. log.Fatal(http.ListenAndServe(address, nil))
  3116. }
  3117. func main() {
  3118. if len(os.Args) <= 1 {
  3119. serve()
  3120. return
  3121. }
  3122. switch os.Args[1] {
  3123. case "dev":
  3124. dev(os.Args[2:])
  3125. case "checkpdf":
  3126. check(os.Args[2:])
  3127. default:
  3128. return
  3129. }
  3130. }