Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

258 lines
4.6 KiB

  1. package main
  2. import (
  3. "net/http"
  4. "log"
  5. "sync"
  6. "regexp"
  7. "html/template"
  8. "database/sql"
  9. _ "github.com/go-sql-driver/mysql"
  10. "fmt"
  11. "encoding/json"
  12. )
  13. type Page struct {
  14. tpl *template.Template
  15. Title string
  16. Name string
  17. }
  18. type LoanType struct {
  19. Id int `json:"id"`
  20. User int `json:"user"`
  21. Branch int `json:"branch"`
  22. Name string `json:"name"`
  23. }
  24. type FeeTemplate struct {
  25. Id int
  26. User int
  27. Branch int
  28. Amount int
  29. Perc int
  30. Ftype string
  31. Notes string
  32. Name string
  33. Category string
  34. Auto bool
  35. }
  36. type Estimate struct {
  37. user int
  38. borrower int
  39. comparison int
  40. transaction int
  41. loanType LoanType
  42. loanAmount int
  43. price int
  44. property string
  45. pud bool
  46. term int
  47. interest int
  48. hoi int
  49. miName string
  50. miAmount int
  51. }
  52. var (
  53. regexen = make(map[string]*regexp.Regexp)
  54. relock sync.Mutex
  55. address = "127.0.0.1:8001"
  56. )
  57. var paths = map[string]string {
  58. "home": "home.tpl",
  59. "terms": "terms.tpl",
  60. "app": "app.tpl",
  61. }
  62. var pages = map[string]Page {
  63. "home": cache("home", "Home"),
  64. "terms": cache("terms", "Terms and Conditions"),
  65. "app": cache("app", "App"),
  66. }
  67. func cache(name string, title string) Page {
  68. var p = []string{"master.tpl", paths[name]}
  69. tpl := template.Must(template.ParseFiles(p...))
  70. return Page{tpl: tpl,
  71. Title: title,
  72. Name: name,
  73. }
  74. }
  75. func (page Page) Render(w http.ResponseWriter) {
  76. err := page.tpl.Execute(w, page)
  77. if err != nil {
  78. log.Print(err)
  79. }
  80. }
  81. func match(path, pattern string, args *[]string) bool {
  82. relock.Lock()
  83. defer relock.Unlock()
  84. regex := regexen[pattern]
  85. if regex == nil {
  86. regex = regexp.MustCompile("^" + pattern + "$")
  87. regexen[pattern] = regex
  88. }
  89. matches := regex.FindStringSubmatch(path)
  90. if len(matches) <= 0 {
  91. return false
  92. }
  93. *args = matches[1:]
  94. return true
  95. }
  96. func getLoanType(
  97. db *sql.DB,
  98. user int,
  99. branch int,
  100. isUser bool) ([]LoanType, error) {
  101. var loans []LoanType
  102. // Should be changed to specify user
  103. rows, err :=
  104. db.Query(`SELECT * FROM loan_type WHERE user_id = ? AND branch_id = ? ` +
  105. "OR (user_id = 0 AND branch_id = 0)", user, branch)
  106. if err != nil {
  107. return nil, fmt.Errorf("loan_type error: %v", err)
  108. }
  109. defer rows.Close()
  110. for rows.Next() {
  111. var loan LoanType
  112. if err := rows.Scan(
  113. &loan.Id,
  114. &loan.User,
  115. &loan.Branch,
  116. &loan.Name)
  117. err != nil {
  118. log.Printf("Error occured fetching loan: %v", err)
  119. return nil, fmt.Errorf("Error occured fetching loan: %v", err)
  120. }
  121. loans = append(loans, loan)
  122. }
  123. log.Printf("The loans: %v", loans)
  124. return loans, nil
  125. }
  126. // Fetch fees from the database
  127. func getFees(db *sql.DB, user int) ([]FeeTemplate, error) {
  128. var fees []FeeTemplate
  129. // Should be changed to specify user
  130. rows, err := db.Query(
  131. "SELECT * FROM fee_template " +
  132. "WHERE user_id = ? OR user_id = 0",
  133. user)
  134. if err != nil {
  135. return nil, fmt.Errorf("Fee error %v", err)
  136. }
  137. defer rows.Close()
  138. for rows.Next() {
  139. var fee FeeTemplate
  140. if err := rows.Scan(
  141. &fee.Id,
  142. &fee.User,
  143. &fee.Branch,
  144. &fee.Amount,
  145. &fee.Perc,
  146. &fee.Ftype,
  147. &fee.Notes,
  148. &fee.Name,
  149. &fee.Category,
  150. &fee.Auto)
  151. err != nil {
  152. return nil, fmt.Errorf("The fees %q: %v", user, err)
  153. }
  154. fees = append(fees, fee)
  155. }
  156. return fees, nil
  157. }
  158. func route(w http.ResponseWriter, r *http.Request) {
  159. var page Page
  160. var args []string
  161. p := r.URL.Path
  162. switch {
  163. case r.Method == "GET" && match(p, "/", &args):
  164. page = pages[ "home" ]
  165. case match(p, "/terms", &args):
  166. page = pages[ "terms" ]
  167. case match(p, "/app", &args):
  168. page = pages[ "app" ]
  169. case match(p, "/assets", &args):
  170. page = pages[ "app" ]
  171. default:
  172. http.NotFound(w, r)
  173. return
  174. }
  175. page.Render(w)
  176. }
  177. func api(w http.ResponseWriter, r *http.Request) {
  178. var args []string
  179. // var response string
  180. p := r.URL.Path
  181. db, err := sql.Open("mysql",
  182. fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/skouter",
  183. config["DBUser"],
  184. config["DBPass"]))
  185. w.Header().Set("Content-Type", "application/json; charset=UTF-8")
  186. err = db.Ping()
  187. if err != nil {
  188. print("Bad database configuration: %v", err)
  189. panic(err)
  190. // maybe os.Exit(1) instead
  191. }
  192. switch {
  193. case match(p, "/api/loans", &args):
  194. resp, err := getLoanType(db, 0, 0, true)
  195. if resp != nil {
  196. json.NewEncoder(w).Encode(resp)
  197. } else {
  198. json.NewEncoder(w).Encode(err)
  199. }
  200. case match(p, "/api/fees", &args):
  201. resp, err := getFees(db, 0)
  202. if resp != nil {
  203. json.NewEncoder(w).Encode(resp)
  204. } else {
  205. json.NewEncoder(w).Encode(err)
  206. }
  207. }
  208. }
  209. func main() {
  210. files := http.FileServer(http.Dir(""))
  211. http.Handle("/assets/", files)
  212. http.HandleFunc("/api/", api)
  213. http.HandleFunc("/", route)
  214. log.Fatal(http.ListenAndServe(address, nil))
  215. }