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

2420 lines
52 KiB

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