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.

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