Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

2975 linhas
63 KiB

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