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

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