Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 
 
 

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