Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

3676 řádky
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. }