Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 
 
 

4237 lines
88 KiB

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