Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

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