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

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