Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 
 
 

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