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

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