Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

skouter.go 62 KiB

il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
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. }