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

981 líneas
21 KiB

  1. package main
  2. import (
  3. "net/http"
  4. "net/mail"
  5. "log"
  6. "sync"
  7. "regexp"
  8. "html/template"
  9. "database/sql"
  10. _ "github.com/go-sql-driver/mysql"
  11. "fmt"
  12. "encoding/json"
  13. "strconv"
  14. "bytes"
  15. "time"
  16. "errors"
  17. "strings"
  18. // pdf "github.com/SebastiaanKlippert/go-wkhtmltopdf"
  19. "github.com/golang-jwt/jwt/v4"
  20. )
  21. type User struct {
  22. Id int `json:"id"`
  23. Email string `json:"email"`
  24. FirstName string `json:"firstName"`
  25. LastName string `json:"lastName"`
  26. BranchId int `json:"branchId"`
  27. Status string `json:"status"`
  28. Country string `json:"country"`
  29. Title string `json:"title"`
  30. Verified bool `json:"verified"`
  31. Role string `json:"role"`
  32. Password string `json:"password,omitempty"`
  33. }
  34. type UserClaims struct {
  35. Id int `json:"id"`
  36. Role string `json:"role"`
  37. Exp string `json:"exp"`
  38. }
  39. type Page struct {
  40. tpl *template.Template
  41. Title string
  42. Name string
  43. }
  44. type Borrower struct {
  45. Id int `json:"id"`
  46. Credit int `json:"credit"`
  47. Income int `json:"income"`
  48. Num int `json:"num"`
  49. }
  50. type FeeTemplate struct {
  51. Id int `json:"id"`
  52. User int `json:"user"`
  53. Branch int `json:"branch"`
  54. Amount int `json:"amount"`
  55. Perc int `json:"perc"`
  56. Type string `json:"type"`
  57. Notes string `json:"notes"`
  58. Name string `json:"name"`
  59. Category string `json:"category"`
  60. Auto bool `json:"auto"`
  61. }
  62. type Fee struct {
  63. Id int `json:"id"`
  64. LoanId int `json:"loan_id"`
  65. Amount int `json:"amount"`
  66. Perc int `json:"perc"`
  67. Type string `json:"type"`
  68. Notes string `json:"notes"`
  69. Name string `json:"name"`
  70. Category string `json:"category"`
  71. }
  72. type LoanType struct {
  73. Id int `json:"id"`
  74. User int `json:"user"`
  75. Branch int `json:"branch"`
  76. Name string `json:"name"`
  77. }
  78. type Loan struct {
  79. Id int `json:id`
  80. EstimateId int `json:estimate_id`
  81. Type LoanType `json:"loanType"`
  82. Amount int `json:"loanAmount"`
  83. Amortization string `json:"loanAmount"`
  84. Term int `json:"term"`
  85. Ltv float32 `json:"ltv"`
  86. Dti float32 `json:"dti"`
  87. Hoi int `json:"hoi"`
  88. Interest int `json:"interest"`
  89. Mi MI `json:"mi"`
  90. Fees []Fee `json:"fees"`
  91. Name string `json:"name"`
  92. }
  93. type MI struct {
  94. Type string
  95. Label string
  96. Lender string
  97. Rate float32
  98. Premium float32
  99. Upfront float32
  100. FiveYearTotal float32
  101. InitialAllInPremium float32
  102. InitialAllInRate float32
  103. InitialAmount float32
  104. }
  105. type Result struct {
  106. User int `json:"user"`
  107. Borrower Borrower `json:"borrower"`
  108. Transaction string `json:"transaction"`
  109. Price int `json:"price"`
  110. Property string `json:"property"`
  111. Occupancy string `json:"occupancy"`
  112. Zip string `json:"zip"`
  113. Pud bool `json:"pud"`
  114. Loans []Loan `json:"loans"`
  115. }
  116. type Estimate struct {
  117. Id int `json:"id"`
  118. User int `json:"user"`
  119. Borrower Borrower `json:"borrower"`
  120. Transaction string `json:"transaction"`
  121. Price int `json:"price"`
  122. Property string `json:"property"`
  123. Occupancy string `json:"occupancy"`
  124. Zip string `json:"zip"`
  125. Pud bool `json:"pud"`
  126. Loans []Loan `json:"loans"`
  127. }
  128. var (
  129. regexen = make(map[string]*regexp.Regexp)
  130. relock sync.Mutex
  131. address = "127.0.0.1:8001"
  132. )
  133. var paths = map[string]string {
  134. "home": "home.tpl",
  135. "terms": "terms.tpl",
  136. "app": "app.tpl",
  137. }
  138. var pages = map[string]Page {
  139. "home": cache("home", "Home"),
  140. "terms": cache("terms", "Terms and Conditions"),
  141. "app": cache("app", "App"),
  142. }
  143. var roles = map[string]int{
  144. "User": 1,
  145. "Manager": 2,
  146. "Admin": 3,
  147. }
  148. // Used to validate claim in JWT token body. Checks if user id is greater than
  149. // zero and time format is valid
  150. func (c UserClaims) Valid() error {
  151. if c.Id < 1 { return errors.New("Invalid id") }
  152. t, err := time.Parse(time.UnixDate, c.Exp)
  153. if err != nil { return err }
  154. if t.Before(time.Now()) { return errors.New("Token expired.") }
  155. return err
  156. }
  157. func cache(name string, title string) Page {
  158. var p = []string{"master.tpl", paths[name]}
  159. tpl := template.Must(template.ParseFiles(p...))
  160. return Page{tpl: tpl,
  161. Title: title,
  162. Name: name,
  163. }
  164. }
  165. func (page Page) Render(w http.ResponseWriter) {
  166. err := page.tpl.Execute(w, page)
  167. if err != nil {
  168. log.Print(err)
  169. }
  170. }
  171. func match(path, pattern string, args *[]string) bool {
  172. relock.Lock()
  173. defer relock.Unlock()
  174. regex := regexen[pattern]
  175. if regex == nil {
  176. regex = regexp.MustCompile("^" + pattern + "$")
  177. regexen[pattern] = regex
  178. }
  179. matches := regex.FindStringSubmatch(path)
  180. if len(matches) <= 0 {
  181. return false
  182. }
  183. *args = matches[1:]
  184. return true
  185. }
  186. func getLoanType(
  187. db *sql.DB,
  188. user int,
  189. branch int,
  190. isUser bool) ([]LoanType, error) {
  191. var loans []LoanType
  192. // Should be changed to specify user
  193. rows, err :=
  194. db.Query(`SELECT * FROM loan_type WHERE user_id = ? AND branch_id = ? ` +
  195. "OR (user_id = 0 AND branch_id = 0)", user, branch)
  196. if err != nil {
  197. return nil, fmt.Errorf("loan_type error: %v", err)
  198. }
  199. defer rows.Close()
  200. for rows.Next() {
  201. var loan LoanType
  202. if err := rows.Scan(
  203. &loan.Id,
  204. &loan.User,
  205. &loan.Branch,
  206. &loan.Name)
  207. err != nil {
  208. log.Printf("Error occured fetching loan: %v", err)
  209. return nil, fmt.Errorf("Error occured fetching loan: %v", err)
  210. }
  211. loans = append(loans, loan)
  212. }
  213. log.Printf("The loans: %v", loans)
  214. return loans, nil
  215. }
  216. func getEstimate(db *sql.DB, id int) (Estimate, error) {
  217. var estimate Estimate
  218. var err error
  219. query := `SELECT e.id, e.user_id, e.transaction,
  220. e.price, e.property, e.occupancy, e.zip, e.pud,
  221. b.id, b.credit_score, b.monthly_income, b.num
  222. FROM estimate e
  223. INNER JOIN borrower b ON e.borrower_id = b.id
  224. WHERE e.id = ?
  225. `
  226. // Inner join should always be valid because a borrower is a required
  227. // foreign key.
  228. row := db.QueryRow(query, id)
  229. if err = row.Scan(
  230. &estimate.Id,
  231. &estimate.User,
  232. &estimate.Transaction,
  233. &estimate.Price,
  234. &estimate.Property,
  235. &estimate.Occupancy,
  236. &estimate.Zip,
  237. &estimate.Pud,
  238. &estimate.Borrower.Id,
  239. &estimate.Borrower.Credit,
  240. &estimate.Borrower.Income,
  241. &estimate.Borrower.Num,
  242. )
  243. err != nil {
  244. return estimate, fmt.Errorf("Estimate scanning error: %v", err)
  245. }
  246. estimate.Loans, err = getLoans(db, estimate.Id)
  247. return estimate, err
  248. }
  249. func getFees(db *sql.DB, loan int) ([]Fee, error) {
  250. var fees []Fee
  251. rows, err := db.Query(
  252. "SELECT * FROM fees " +
  253. "WHERE loan_id = ?",
  254. loan)
  255. if err != nil {
  256. return nil, fmt.Errorf("Fee query error %v", err)
  257. }
  258. defer rows.Close()
  259. for rows.Next() {
  260. var fee Fee
  261. if err := rows.Scan(
  262. &fee.Id,
  263. &fee.LoanId,
  264. &fee.Amount,
  265. &fee.Perc,
  266. &fee.Type,
  267. &fee.Notes,
  268. &fee.Name,
  269. &fee.Category,
  270. )
  271. err != nil {
  272. return nil, fmt.Errorf("Fees scanning error: %v", err)
  273. }
  274. fees = append(fees, fee)
  275. }
  276. return fees, nil
  277. }
  278. func fetchFeesTemp(db *sql.DB, user int, branch int) ([]FeeTemplate, error) {
  279. var fees []FeeTemplate
  280. rows, err := db.Query(
  281. "SELECT * FROM fee_template " +
  282. "WHERE user_id = ? OR branch_id = ?",
  283. user, branch)
  284. if err != nil {
  285. return nil, fmt.Errorf("Fee template query error %v", err)
  286. }
  287. defer rows.Close()
  288. for rows.Next() {
  289. var fee FeeTemplate
  290. if err := rows.Scan(
  291. &fee.Id,
  292. &fee.User,
  293. &fee.Branch,
  294. &fee.Amount,
  295. &fee.Perc,
  296. &fee.Type,
  297. &fee.Notes,
  298. &fee.Name,
  299. &fee.Category,
  300. &fee.Auto)
  301. err != nil {
  302. return nil, fmt.Errorf("FeesTemplate scanning error: %v", err)
  303. }
  304. fees = append(fees, fee)
  305. }
  306. return fees, nil
  307. }
  308. // Fetch fees from the database
  309. func getFeesTemp(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  310. var fees []FeeTemplate
  311. claims, err := getClaims(r)
  312. if err != nil { w.WriteHeader(500); return }
  313. users, err := queryUsers(db, claims.Id)
  314. if err != nil { w.WriteHeader(422); return }
  315. fees, err = fetchFeesTemp(db, claims.Id, users[0].BranchId)
  316. json.NewEncoder(w).Encode(fees)
  317. }
  318. func getMi(db *sql.DB, loan int) (MI, error) {
  319. var mi MI
  320. query := `SELECT
  321. type, label, lender, rate, premium, upfront, five_year_total,
  322. initial_premium, initial_rate, initial_amount
  323. FROM mi WHERE loan_id = ?`
  324. row := db.QueryRow(query, loan)
  325. if err := row.Scan(
  326. &mi.Type,
  327. &mi.Label,
  328. &mi.Lender,
  329. &mi.Rate,
  330. &mi.Premium,
  331. &mi.Upfront,
  332. &mi.FiveYearTotal,
  333. &mi.InitialAllInPremium,
  334. &mi.InitialAllInRate,
  335. &mi.InitialAmount,
  336. )
  337. err != nil {
  338. return mi, err
  339. }
  340. return mi, nil
  341. }
  342. func getLoans(db *sql.DB, estimate int) ([]Loan, error) {
  343. var loans []Loan
  344. query := `SELECT
  345. l.id, l.amount, l.term, l.interest, l.ltv, l.dti, l.hoi,
  346. lt.id, lt.user_id, lt.branch_id, lt.name
  347. FROM loan l INNER JOIN loan_type lt ON l.type_id = lt.id
  348. WHERE l.estimate_id = ?
  349. `
  350. rows, err := db.Query(query, estimate)
  351. if err != nil {
  352. return nil, fmt.Errorf("Loan query error %v", err)
  353. }
  354. defer rows.Close()
  355. for rows.Next() {
  356. var loan Loan
  357. if err := rows.Scan(
  358. &loan.Id,
  359. &loan.Amount,
  360. &loan.Term,
  361. &loan.Interest,
  362. &loan.Ltv,
  363. &loan.Dti,
  364. &loan.Hoi,
  365. &loan.Type.Id,
  366. &loan.Type.User,
  367. &loan.Type.Branch,
  368. &loan.Type.Name,
  369. )
  370. err != nil {
  371. return loans, fmt.Errorf("Loans scanning error: %v", err)
  372. }
  373. mi, err := getMi(db, loan.Id)
  374. if err != nil {
  375. return loans, err
  376. }
  377. loan.Mi = mi
  378. loans = append(loans, loan)
  379. }
  380. return loans, nil
  381. }
  382. func getBorrower(db *sql.DB, id int) (Borrower, error) {
  383. var borrower Borrower
  384. row := db.QueryRow(
  385. "SELECT * FROM borrower " +
  386. "WHERE id = ? LIMIT 1",
  387. id)
  388. if err := row.Scan(
  389. &borrower.Id,
  390. &borrower.Credit,
  391. &borrower.Income,
  392. &borrower.Num,
  393. )
  394. err != nil {
  395. return borrower, fmt.Errorf("Borrower scanning error: %v", err)
  396. }
  397. return borrower, nil
  398. }
  399. // Query Lender APIs and parse responses into MI structs
  400. func fetchMi(db *sql.DB, estimate *Estimate, pos int) []MI {
  401. var err error
  402. var loan Loan = estimate.Loans[pos]
  403. var ltv = func(l float32) string {
  404. switch {
  405. case l > 95: return "LTV97"
  406. case l > 90: return "LTV95"
  407. case l > 85: return "LTV90"
  408. default: return "LTV85"
  409. }
  410. }
  411. var term = func(t int) string {
  412. switch {
  413. case t <= 10: return "A10"
  414. case t <= 15: return "A15"
  415. case t <= 20: return "A20"
  416. case t <= 25: return "A25"
  417. case t <= 30: return "A30"
  418. default: return "A40"
  419. }
  420. }
  421. var propertyCodes = map[string]string {
  422. "Single Attached": "SFO",
  423. "Single Detached": "SFO",
  424. "Condo Lo-rise": "CON",
  425. "Condo Hi-rise": "CON",
  426. }
  427. var purposeCodes = map[string]string {
  428. "Purchase": "PUR",
  429. "Refinance": "RRT",
  430. }
  431. body, err := json.Marshal(map[string]any{
  432. "zipCode": estimate.Zip,
  433. "stateCode": "CA",
  434. "address": "",
  435. "propertyTypeCode": propertyCodes[estimate.Property],
  436. "occupancyTypeCode": "PRS",
  437. "loanPurposeCode": purposeCodes[estimate.Transaction],
  438. "loanAmount": loan.Amount,
  439. "loanToValue": ltv(loan.Ltv),
  440. "amortizationTerm": term(loan.Term),
  441. "loanTypeCode": "FXD",
  442. "duLpDecisionCode": "DAE",
  443. "loanProgramCodes": []any{},
  444. "debtToIncome": loan.Dti,
  445. "wholesaleLoan": 0,
  446. "coveragePercentageCode": "L30",
  447. "productCode": "BPM",
  448. "renewalTypeCode": "CON",
  449. "numberOfBorrowers": 1,
  450. "coBorrowerCreditScores": []any{},
  451. "borrowerCreditScore": strconv.Itoa(estimate.Borrower.Credit),
  452. "masterPolicy": nil,
  453. "selfEmployedIndicator": false,
  454. "armType": "",
  455. "userId": 44504,
  456. })
  457. if err != nil {
  458. log.Printf("Could not marshal NationalMI body: \n%v\n%v\n",
  459. bytes.NewBuffer(body), err)
  460. }
  461. req, err := http.NewRequest("POST",
  462. "https://rate-gps.nationalmi.com/rates/productRateQuote",
  463. bytes.NewBuffer(body))
  464. req.Header.Add("Content-Type", "application/json")
  465. req.AddCookie(&http.Cookie{
  466. Name: "nmirategps_email",
  467. Value: config["NationalMIEmail"]})
  468. resp, err := http.DefaultClient.Do(req)
  469. var res map[string]interface{}
  470. var result []MI
  471. if resp.StatusCode != 200 {
  472. log.Printf("the status: %v\nthe resp: %v\n the req: %v\n the body: %v\n",
  473. resp.Status, resp, req.Body, bytes.NewBuffer(body))
  474. } else {
  475. json.NewDecoder(resp.Body).Decode(&res)
  476. // estimate.Loans[pos].Mi = res
  477. // Parse res into result here
  478. }
  479. return result
  480. }
  481. // Make comparison PDF
  482. func generatePDF(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  483. }
  484. func login(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  485. var id int
  486. var role string
  487. var err error
  488. var user User
  489. json.NewDecoder(r.Body).Decode(&user)
  490. row := db.QueryRow(
  491. `SELECT id, role FROM user WHERE email = ? AND password = sha2(?, 256)`,
  492. user.Email, user.Password,
  493. )
  494. err = row.Scan(&id, &role)
  495. if err != nil {
  496. http.Error(w, "Invalid Credentials.", http.StatusUnauthorized)
  497. return
  498. }
  499. token := jwt.NewWithClaims(jwt.SigningMethodHS256,
  500. UserClaims{ Id: id, Role: role,
  501. Exp: time.Now().Add(time.Minute * 30).Format(time.UnixDate)})
  502. tokenStr, err := token.SignedString([]byte(config["JWT_SECRET"]))
  503. if err != nil {
  504. log.Println("Token could not be signed: ", err, tokenStr)
  505. http.Error(w, "Token generation error.", http.StatusInternalServerError)
  506. return
  507. }
  508. cookie := http.Cookie{Name: "skouter",
  509. Value: tokenStr,
  510. Path: "/",
  511. Expires: time.Now().Add(time.Hour * 24)}
  512. http.SetCookie(w, &cookie)
  513. _, err = w.Write([]byte(tokenStr))
  514. if err != nil {
  515. http.Error(w,
  516. "Could not complete token write.",
  517. http.StatusInternalServerError)}
  518. }
  519. func getToken(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  520. claims, err := getClaims(r)
  521. // Will verify existing signature and expiry time
  522. token := jwt.NewWithClaims(jwt.SigningMethodHS256,
  523. UserClaims{ Id: claims.Id, Role: claims.Role,
  524. Exp: time.Now().Add(time.Minute * 30).Format(time.UnixDate)})
  525. tokenStr, err := token.SignedString([]byte(config["JWT_SECRET"]))
  526. if err != nil {
  527. log.Println("Token could not be signed: ", err, tokenStr)
  528. http.Error(w, "Token generation error.", http.StatusInternalServerError)
  529. return
  530. }
  531. cookie := http.Cookie{Name: "skouter",
  532. Value: tokenStr,
  533. Path: "/",
  534. Expires: time.Now().Add(time.Hour * 24)}
  535. http.SetCookie(w, &cookie)
  536. _, err = w.Write([]byte(tokenStr))
  537. if err != nil {
  538. http.Error(w,
  539. "Could not complete token write.",
  540. http.StatusInternalServerError)}
  541. }
  542. func validateEstimate() {
  543. return
  544. }
  545. func getClaims(r *http.Request) (UserClaims, error) {
  546. claims := new(UserClaims)
  547. _, tokenStr, found := strings.Cut(r.Header.Get("Authorization"), " ")
  548. if !found {
  549. return *claims, errors.New("Token not found")
  550. }
  551. // Pull token payload into UserClaims
  552. _, err := jwt.ParseWithClaims(tokenStr, claims,
  553. func(token *jwt.Token) (any, error) {
  554. return []byte(config["JWT_SECRET"]), nil
  555. })
  556. if err != nil {
  557. return *claims, err
  558. }
  559. if err = claims.Valid(); err != nil {
  560. return *claims, err
  561. }
  562. return *claims, nil
  563. }
  564. func guard(r *http.Request, required int) bool {
  565. claims, err := getClaims(r)
  566. if err != nil { return false }
  567. if roles[claims.Role] < required { return false }
  568. return true
  569. }
  570. func queryUsers(db *sql.DB, id int) ( []User, error ) {
  571. var users []User
  572. var query string
  573. var rows *sql.Rows
  574. var err error
  575. query = `SELECT
  576. u.id,
  577. u.email,
  578. u.first_name,
  579. u.last_name,
  580. u.branch_id,
  581. u.country,
  582. u.title,
  583. u.status,
  584. u.verified,
  585. u.role
  586. FROM user u WHERE u.id = CASE @e := ? WHEN 0 THEN u.id ELSE @e END
  587. `
  588. rows, err = db.Query(query, id)
  589. if err != nil {
  590. return users, err
  591. }
  592. defer rows.Close()
  593. for rows.Next() {
  594. var user User
  595. if err := rows.Scan(
  596. &user.Id,
  597. &user.Email,
  598. &user.FirstName,
  599. &user.LastName,
  600. &user.BranchId,
  601. &user.Country,
  602. &user.Title,
  603. &user.Status,
  604. &user.Verified,
  605. &user.Role,
  606. )
  607. err != nil {
  608. return users, err
  609. }
  610. users = append(users, user)
  611. }
  612. // Prevents runtime panics
  613. if len(users) == 0 { return users, errors.New("User not found.") }
  614. return users, nil
  615. }
  616. func insertUser(db *sql.DB, user User) (User, error){
  617. var query string
  618. var row *sql.Row
  619. var err error
  620. var id int // Inserted user's id
  621. query = `INSERT INTO user
  622. (
  623. email,
  624. first_name,
  625. last_name,
  626. password,
  627. created,
  628. role,
  629. verified,
  630. last_login
  631. )
  632. VALUES (?, ?, ?, sha2(?, 256), NOW(), ?, ?, NOW())
  633. RETURNING id
  634. `
  635. row = db.QueryRow(query,
  636. user.Email,
  637. user.FirstName,
  638. user.LastName,
  639. user.Password,
  640. user.Role,
  641. user.Verified,
  642. )
  643. err = row.Scan(&id)
  644. if err != nil { return User{}, err }
  645. users, err := queryUsers(db, id)
  646. if err != nil { return User{}, err }
  647. return users[0], nil
  648. }
  649. func updateUser(user User, db *sql.DB) error {
  650. query := `
  651. UPDATE user
  652. SET
  653. email = CASE @e := ? WHEN '' THEN email ELSE @e END,
  654. first_name = CASE @fn := ? WHEN '' THEN first_name ELSE @fn END,
  655. last_name = CASE @ln := ? WHEN '' THEN last_name ELSE @ln END,
  656. role = CASE @r := ? WHEN '' THEN role ELSE @r END,
  657. password = CASE @p := ? WHEN '' THEN password ELSE sha2(@p, 256) END
  658. WHERE id = ?
  659. `
  660. _, err := db.Exec(query,
  661. user.Email,
  662. user.FirstName,
  663. user.LastName,
  664. user.Role,
  665. user.Password,
  666. user.Id,
  667. )
  668. return err
  669. }
  670. func getUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  671. claims, err := getClaims(r)
  672. if err != nil { w.WriteHeader(500); return }
  673. users, err := queryUsers(db, claims.Id)
  674. if err != nil { w.WriteHeader(422); log.Println(err); return }
  675. json.NewEncoder(w).Encode(users)
  676. }
  677. func getUsers(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  678. users, err := queryUsers(db, 0)
  679. if err != nil {
  680. w.WriteHeader(http.StatusInternalServerError)
  681. return
  682. }
  683. json.NewEncoder(w).Encode(users)
  684. }
  685. // Updates a user using only specified values in the JSON body
  686. func patchUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  687. var user User
  688. err := json.NewDecoder(r.Body).Decode(&user)
  689. _, err = mail.ParseAddress(user.Email)
  690. if err != nil { http.Error(w, "Invalid email.", 422); return }
  691. if roles[user.Role] == 0 {
  692. http.Error(w, "Invalid role.", 422)
  693. return
  694. }
  695. err = updateUser(user, db)
  696. if err != nil { http.Error(w, "Bad form values.", 422); return }
  697. users, err := queryUsers(db, user.Id)
  698. if err != nil { http.Error(w, "Bad form values.", 422); return }
  699. json.NewEncoder(w).Encode(users[0])
  700. }
  701. // Update specified fields of the user specified in the claim
  702. func patchSelf(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  703. claim, err := getClaims(r)
  704. var user User
  705. json.NewDecoder(r.Body).Decode(&user)
  706. // First check that the target user to be updated is the same as the claim id, and
  707. // their role is unchanged.
  708. if err != nil || claim.Id != user.Id {
  709. http.Error(w, "Target user's id does not match claim.", 401)
  710. return
  711. }
  712. if claim.Role != user.Role && user.Role != "" {
  713. http.Error(w, "Administrator required to escalate role.", 401)
  714. return
  715. }
  716. patchUser(w, db, r)
  717. }
  718. func deleteUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  719. var user User
  720. err := json.NewDecoder(r.Body).Decode(&user)
  721. if err != nil {
  722. http.Error(w, "Invalid fields.", 422)
  723. return
  724. }
  725. query := `DELETE FROM user WHERE id = ?`
  726. _, err = db.Exec(query, user.Id)
  727. if err != nil {
  728. http.Error(w, "Could not delete.", 500)
  729. }
  730. }
  731. func createUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  732. var user User
  733. err := json.NewDecoder(r.Body).Decode(&user)
  734. if err != nil { http.Error(w, "Invalid fields.", 422); return }
  735. _, err = mail.ParseAddress(user.Email)
  736. if err != nil { http.Error(w, "Invalid email.", 422); return }
  737. if roles[user.Role] == 0 {
  738. http.Error(w, "Invalid role.", 422)
  739. }
  740. user, err = insertUser(db, user)
  741. if err != nil { http.Error(w, "Error creating user.", 422); return }
  742. json.NewEncoder(w).Encode(user)
  743. }
  744. func createEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  745. var estimate Estimate
  746. err := json.NewDecoder(r.Body).Decode(&estimate)
  747. if err != nil { http.Error(w, "Invalid fields.", 422); return }
  748. if err != nil { http.Error(w, "Error creating user.", 422); return }
  749. json.NewEncoder(w).Encode(estimate)
  750. }
  751. func api(w http.ResponseWriter, r *http.Request) {
  752. var args []string
  753. p := r.URL.Path
  754. db, err := sql.Open("mysql",
  755. fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/skouter",
  756. config["DBUser"],
  757. config["DBPass"]))
  758. w.Header().Set("Content-Type", "application/json; charset=UTF-8")
  759. err = db.Ping()
  760. if err != nil {
  761. fmt.Println("Bad database configuration: %v\n", err)
  762. panic(err)
  763. // maybe os.Exit(1) instead
  764. }
  765. switch {
  766. case match(p, "/api/login", &args) &&
  767. r.Method == http.MethodPost:
  768. login(w, db, r)
  769. case match(p, "/api/token", &args) &&
  770. r.Method == http.MethodGet && guard(r, 1):
  771. getToken(w, db, r)
  772. case match(p, "/api/users", &args) && // Array of all users
  773. r.Method == http.MethodGet && guard(r, 3):
  774. getUsers(w, db, r)
  775. case match(p, "/api/user", &args) &&
  776. r.Method == http.MethodGet && guard(r, 1):
  777. getUser(w, db, r)
  778. case match(p, "/api/user", &args) &&
  779. r.Method == http.MethodPost &&
  780. guard(r, 3):
  781. createUser(w, db, r)
  782. case match(p, "/api/user", &args) &&
  783. r.Method == http.MethodPatch &&
  784. guard(r, 3): // For admin to modify any user
  785. patchUser(w, db, r)
  786. case match(p, "/api/user", &args) &&
  787. r.Method == http.MethodPatch &&
  788. guard(r, 2): // For employees to modify own accounts
  789. patchSelf(w, db, r)
  790. case match(p, "/api/user", &args) &&
  791. r.Method == http.MethodDelete &&
  792. guard(r, 3):
  793. deleteUser(w, db, r)
  794. case match(p, "/api/fees", &args) &&
  795. r.Method == http.MethodGet &&
  796. guard(r, 1):
  797. getFeesTemp(w, db, r)
  798. case match(p, "/api/estimate", &args) &&
  799. r.Method == http.MethodPost &&
  800. guard(r, 1):
  801. createEstimate(w, db, r)
  802. }
  803. db.Close()
  804. }
  805. func route(w http.ResponseWriter, r *http.Request) {
  806. var page Page
  807. var args []string
  808. p := r.URL.Path
  809. switch {
  810. case r.Method == "GET" && match(p, "/", &args):
  811. page = pages[ "home" ]
  812. case match(p, "/terms", &args):
  813. page = pages[ "terms" ]
  814. case match(p, "/app", &args):
  815. page = pages[ "app" ]
  816. default:
  817. http.NotFound(w, r)
  818. return
  819. }
  820. page.Render(w)
  821. }
  822. func main() {
  823. files := http.FileServer(http.Dir(""))
  824. http.Handle("/assets/", files)
  825. http.HandleFunc("/api/", api)
  826. http.HandleFunc("/", route)
  827. log.Fatal(http.ListenAndServe(address, nil))
  828. }