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.

skouter.go 73 KiB

2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
1 jaar geleden
1 jaar geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
2 jaren geleden
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675
  1. package main
  2. import (
  3. "bytes"
  4. "database/sql"
  5. "encoding/base64"
  6. "encoding/json"
  7. "errors"
  8. "fmt"
  9. _ "github.com/go-sql-driver/mysql"
  10. "html/template"
  11. "io"
  12. "log"
  13. "math"
  14. "net/http"
  15. "net/http/httputil"
  16. "net/mail"
  17. "net/url"
  18. "os"
  19. "os/exec"
  20. "regexp"
  21. "strconv"
  22. "strings"
  23. "sync"
  24. "time"
  25. // pdf "github.com/SebastiaanKlippert/go-wkhtmltopdf"
  26. "github.com/brianvoe/gofakeit/v6"
  27. "github.com/disintegration/gift"
  28. "github.com/dustin/go-humanize"
  29. "github.com/golang-jwt/jwt/v4"
  30. "github.com/stripe/stripe-go/v76"
  31. "github.com/stripe/stripe-go/v76/customer"
  32. "github.com/stripe/stripe-go/v76/subscription"
  33. "github.com/stripe/stripe-go/v76/invoice"
  34. "github.com/stripe/stripe-go/v76/paymentintent"
  35. "github.com/stripe/stripe-go/v76/webhook"
  36. "image"
  37. _ "image/jpeg"
  38. "image/png"
  39. )
  40. type Config struct {
  41. DBName string
  42. DBUsername string
  43. DBPassword string
  44. }
  45. type Address struct {
  46. Id int `json:"id"`
  47. Full string `json:"full"`
  48. Street string `json:"street"`
  49. City string `json:"city"`
  50. Region string `json:"region"`
  51. Country string `json:"country"`
  52. Zip string `json:"zip"`
  53. }
  54. type Branch struct {
  55. Id int `json:"id"`
  56. Name string `json:"name"`
  57. Type string `json:"type"`
  58. Letterhead []byte `json:"letterhead"`
  59. Num string `json:"num"`
  60. Phone string `json:"phone"`
  61. Address Address `json:"address"`
  62. }
  63. type Subscription struct {
  64. Id int `json:"id"`
  65. UserId int `json:"userId"`
  66. StripeId string `json:"stripeId"`
  67. CustomerId string `json:"customerId"`
  68. PriceId string `json:"priceId"`
  69. Start int `json:"start"`
  70. End int `json:"end"`
  71. ClientSecret string `json:"clientSecret,omitempty"`
  72. PaymentStatus string `json:"paymentStatus"`
  73. }
  74. type User struct {
  75. Id int `json:"id"`
  76. Email string `json:"email"`
  77. FirstName string `json:"firstName"`
  78. LastName string `json:"lastName"`
  79. Phone string `json:"phone"`
  80. Address Address `json:"address"`
  81. Branch Branch `json:"branch"`
  82. License License `json:"license"`
  83. Sub Subscription `json:"sub"`
  84. Status string `json:"status"`
  85. Country string `json:"country"`
  86. Title string `json:"title"`
  87. Verified bool `json:"verified"`
  88. Role string `json:"role"`
  89. Password string `json:"password,omitempty"`
  90. CustomerId string `json:"customerId"`
  91. }
  92. type License struct {
  93. Id int `json:"id"`
  94. UserId int `json:"userId"`
  95. Type string `json:"type"`
  96. Num string `json:"num"`
  97. }
  98. type UserClaims struct {
  99. Id int `json:"id"`
  100. Role string `json:"role"`
  101. Exp string `json:"exp"`
  102. }
  103. type Page struct {
  104. tpl *template.Template
  105. Title string
  106. Name string
  107. }
  108. type Borrower struct {
  109. Id int `json:"id"`
  110. Credit int `json:"credit"`
  111. Income int `json:"income"`
  112. Num int `json:"num"`
  113. }
  114. type FeeTemplate struct {
  115. Id int `json:"id"`
  116. User int `json:"user"`
  117. Branch int `json:"branch"`
  118. Amount int `json:"amount"`
  119. Perc float32 `json:"perc"`
  120. Type string `json:"type"`
  121. Notes string `json:"notes"`
  122. Name string `json:"name"`
  123. Category string `json:"category"`
  124. Auto bool `json:"auto"`
  125. }
  126. type Fee struct {
  127. Id int `json:"id"`
  128. LoanId int `json:"loan_id"`
  129. Amount int `json:"amount"`
  130. Perc float32 `json:"perc"`
  131. Type string `json:"type"`
  132. Notes string `json:"notes"`
  133. Name string `json:"name"`
  134. Category string `json:"category"`
  135. }
  136. type LoanType struct {
  137. Id int `json:"id"`
  138. User int `json:"user"`
  139. Branch int `json:"branch"`
  140. Name string `json:"name"`
  141. }
  142. type Loan struct {
  143. Id int `json:"id"`
  144. EstimateId int `json:"estimateId"`
  145. Type LoanType `json:"type"`
  146. Amount int `json:"amount"`
  147. Amortization string `json:"amortization"`
  148. Term int `json:"term"`
  149. Ltv float32 `json:"ltv"`
  150. Dti float32 `json:"dti"`
  151. Hoi int `json:"hoi"`
  152. Hazard int `json:"hazard"`
  153. Tax int `json:"tax"`
  154. Interest float32 `json:"interest"`
  155. Mi MI `json:"mi"`
  156. Fees []Fee `json:"fees"`
  157. Credits []Fee // Fees with negative amounts for internal use
  158. Name string `json:"title"`
  159. Result Result `json:"result"`
  160. }
  161. type MI struct {
  162. Type string `json:"user"`
  163. Label string `json:"label"`
  164. Lender string `json:"lender"`
  165. Rate float32 `json:"rate"`
  166. Premium int `json:"premium"`
  167. Upfront int `json:"upfront"`
  168. Monthly bool `json:"monthly"`
  169. FiveYearTotal float32 `json:"fiveYearTotal"`
  170. InitialAllInPremium float32 `json:"initialAllInPremium"`
  171. InitialAllInRate float32 `json:"initialAllInRate"`
  172. InitialAmount float32 `json:"initialAmount"`
  173. }
  174. type Result struct {
  175. Id int `json:"id"`
  176. LoanId int `json:"loanId"`
  177. LoanPayment int `json:"loanPayment"`
  178. TotalMonthly int `json:"totalMonthly"`
  179. TotalFees int `json:"totalFees"`
  180. TotalCredits int `json:"totalCredits"`
  181. CashToClose int `json:"cashToClose"`
  182. }
  183. type Estimate struct {
  184. Id int `json:"id"`
  185. User int `json:"user"`
  186. Borrower Borrower `json:"borrower"`
  187. Transaction string `json:"transaction"`
  188. Price int `json:"price"`
  189. Property string `json:"property"`
  190. Occupancy string `json:"occupancy"`
  191. Zip string `json:"zip"`
  192. Pud bool `json:"pud"`
  193. Loans []Loan `json:"loans"`
  194. }
  195. type ETemplate struct {
  196. Id int `json:"id"`
  197. Estimate Estimate `json:"estimate"`
  198. UserId int `json:"userId"`
  199. BranchId int `json:"branchId"`
  200. }
  201. type Report struct {
  202. Title string
  203. Name string
  204. Avatar string
  205. Letterhead string
  206. User User
  207. Estimate Estimate
  208. }
  209. type Password struct {
  210. Old string `json:"old"`
  211. New string `json:"new"`
  212. }
  213. type Endpoint func(http.ResponseWriter, *sql.DB, *http.Request)
  214. type HookKeys struct {
  215. InvoicePaid string
  216. InvoiceFailed string
  217. }
  218. var (
  219. regexen = make(map[string]*regexp.Regexp)
  220. relock sync.Mutex
  221. address = "localhost:8001"
  222. mainAddress = "localhost:8002"
  223. )
  224. var paths = map[string]string{
  225. "home": "views/home.tpl",
  226. "terms": "views/terms.tpl",
  227. "app": "views/app.tpl",
  228. "comparison": "views/report/comparison.tpl",
  229. }
  230. var pages = map[string]Page{
  231. "home": cache("home", "Home"),
  232. "terms": cache("terms", "Terms and Conditions"),
  233. "report": cachePdf("comparison"),
  234. "app": cache("app", "App"),
  235. }
  236. var roles = map[string]int{
  237. "User": 1,
  238. "Manager": 2,
  239. "Admin": 3,
  240. }
  241. var propertyTypes = []string{
  242. "Single Detached",
  243. "Single Attached",
  244. "Condo Lo-rise",
  245. "Condo Hi-rise",
  246. }
  247. var feeTypes = []string{
  248. "Government",
  249. "Title",
  250. "Required",
  251. "Lender",
  252. "Other",
  253. }
  254. var hookKeys = HookKeys{
  255. InvoicePaid: "",
  256. InvoiceFailed: "",
  257. }
  258. var standardPriceId = "price_1OZLK9BPMoXn2pf9kuTAf8rs"
  259. // Used to validate claim in JWT token body. Checks if user id is greater than
  260. // zero and time format is valid
  261. func (c UserClaims) Valid() error {
  262. if c.Id < 1 {
  263. return errors.New("Invalid id")
  264. }
  265. t, err := time.Parse(time.UnixDate, c.Exp)
  266. if err != nil {
  267. return err
  268. }
  269. if t.Before(time.Now()) {
  270. return errors.New("Token expired.")
  271. }
  272. return err
  273. }
  274. func cache(name string, title string) Page {
  275. var p = []string{"views/master.tpl", paths[name]}
  276. tpl := template.Must(template.ParseFiles(p...))
  277. return Page{tpl: tpl, Title: title, Name: name}
  278. }
  279. func cachePdf(name string) Page {
  280. // Money is stored in cents, so it must be converted to dollars in reports
  281. dollars := func(cents int) string {
  282. return humanize.Commaf(float64(cents) / 100)
  283. }
  284. // For calculating down payments
  285. diff := func(a, b int) string {
  286. return humanize.Commaf(float64(b-a) / 100)
  287. }
  288. sortFees := func(ftype string, fees []Fee) []Fee {
  289. result := make([]Fee, 0)
  290. for i := range fees {
  291. if fees[i].Type != ftype {
  292. continue
  293. }
  294. result = append(result, fees[i])
  295. }
  296. return result
  297. }
  298. fm := template.FuncMap{
  299. "dollars": dollars,
  300. "diff": diff,
  301. "sortFees": sortFees}
  302. var p = []string{"views/report/master.tpl",
  303. "views/report/header.tpl",
  304. "views/report/summary.tpl",
  305. "views/report/comparison.tpl"}
  306. tpl := template.Must(template.New("master.tpl").Funcs(fm).ParseFiles(p...))
  307. return Page{tpl: tpl, Title: "", Name: name}
  308. }
  309. func (page Page) Render(w http.ResponseWriter) {
  310. err := page.tpl.Execute(w, page)
  311. if err != nil {
  312. log.Print(err)
  313. }
  314. }
  315. func match(path, pattern string, args *[]string) bool {
  316. relock.Lock()
  317. defer relock.Unlock()
  318. regex := regexen[pattern]
  319. if regex == nil {
  320. regex = regexp.MustCompile("^" + pattern + "$")
  321. regexen[pattern] = regex
  322. }
  323. matches := regex.FindStringSubmatch(path)
  324. if len(matches) <= 0 {
  325. return false
  326. }
  327. *args = matches[1:]
  328. return true
  329. }
  330. func (estimate *Estimate) makeResults() []Result {
  331. var results []Result
  332. amortize := func(principle float64, rate float64, periods float64) int {
  333. exp := math.Pow(1+rate, periods)
  334. return int(principle * rate * exp / (exp - 1))
  335. }
  336. for i := range estimate.Loans {
  337. var loan = &estimate.Loans[i]
  338. var result Result = Result{}
  339. // Monthly payments use amortized loan payment formula plus monthly MI,
  340. // plus all other recurring fees
  341. result.LoanPayment = amortize(float64(loan.Amount),
  342. float64(loan.Interest/100/12),
  343. float64(loan.Term*12))
  344. result.TotalMonthly = result.LoanPayment + loan.Hoi + loan.Tax + loan.Hazard
  345. if loan.Mi.Monthly {
  346. result.TotalMonthly = result.TotalMonthly +
  347. int(loan.Mi.Rate/100/12*float32(loan.Amount))
  348. } else {
  349. loan.Mi.Upfront = int(loan.Mi.Rate / 100 * float32(loan.Amount))
  350. }
  351. for i := range loan.Fees {
  352. if loan.Fees[i].Amount > 0 {
  353. result.TotalFees = result.TotalFees + loan.Fees[i].Amount
  354. } else {
  355. result.TotalCredits = result.TotalCredits + loan.Fees[i].Amount
  356. }
  357. }
  358. result.CashToClose =
  359. result.TotalFees + result.TotalCredits + (estimate.Price - loan.Amount)
  360. result.LoanId = loan.Id
  361. loan.Result = result
  362. results = append(results, result)
  363. }
  364. return results
  365. }
  366. func summarize(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  367. var estimate Estimate
  368. err := json.NewDecoder(r.Body).Decode(&estimate)
  369. if err != nil {
  370. http.Error(w, "Invalid estimate.", 422)
  371. return
  372. }
  373. estimate.makeResults()
  374. json.NewEncoder(w).Encode(estimate)
  375. }
  376. func getLoanType(db *sql.DB, id int) (LoanType, error) {
  377. types, err := getLoanTypes(db, id, 0, 0)
  378. if err != nil {
  379. return LoanType{Id: id}, err
  380. }
  381. if len(types) == 0 {
  382. return LoanType{Id: id}, errors.New("No type with that id")
  383. }
  384. return types[0], nil
  385. }
  386. func getLoanTypes(db *sql.DB, id int, user int, branch int) (
  387. []LoanType, error) {
  388. var loans []LoanType
  389. var query = `SELECT
  390. id,
  391. coalesce(user_id, 0),
  392. coalesce(branch_id, 0),
  393. name
  394. FROM loan_type WHERE loan_type.id = CASE @e := ? WHEN 0 THEN id ELSE @e END
  395. OR
  396. loan_type.user_id = CASE @e := ? WHEN 0 THEN id ELSE @e END
  397. OR
  398. loan_type.branch_id = CASE @e := ? WHEN 0 THEN id ELSE @e END`
  399. // Should be changed to specify user
  400. rows, err := db.Query(query, id, user, branch)
  401. if err != nil {
  402. return nil, fmt.Errorf("loan_type error: %v", err)
  403. }
  404. defer rows.Close()
  405. for rows.Next() {
  406. var loan LoanType
  407. if err := rows.Scan(
  408. &loan.Id,
  409. &loan.User,
  410. &loan.Branch,
  411. &loan.Name); err != nil {
  412. log.Printf("Error occured fetching loan: %v", err)
  413. return nil, fmt.Errorf("Error occured fetching loan: %v", err)
  414. }
  415. loans = append(loans, loan)
  416. }
  417. return loans, nil
  418. }
  419. func getFees(db *sql.DB, loan int) ([]Fee, error) {
  420. var fees []Fee
  421. query := `SELECT id, loan_id, amount, perc, type, notes, name, category
  422. FROM fee
  423. WHERE loan_id = ?`
  424. rows, err := db.Query(query, loan)
  425. if err != nil {
  426. return nil, fmt.Errorf("Fee query error %v", err)
  427. }
  428. defer rows.Close()
  429. for rows.Next() {
  430. var fee Fee
  431. if err := rows.Scan(
  432. &fee.Id,
  433. &fee.LoanId,
  434. &fee.Amount,
  435. &fee.Perc,
  436. &fee.Type,
  437. &fee.Notes,
  438. &fee.Name,
  439. &fee.Category,
  440. ); err != nil {
  441. return nil, fmt.Errorf("Fees scanning error: %v", err)
  442. }
  443. fees = append(fees, fee)
  444. }
  445. return fees, nil
  446. }
  447. func fetchFeesTemp(db *sql.DB, user int, branch int) ([]FeeTemplate, error) {
  448. var fees []FeeTemplate
  449. query := `SELECT
  450. id, user_id, COALESCE(branch_id, 0), amount, perc, type, notes, name,
  451. category, auto
  452. FROM fee_template
  453. WHERE user_id = ? OR branch_id = ?
  454. `
  455. rows, err := db.Query(query, user, branch)
  456. if err != nil {
  457. return nil, fmt.Errorf("Fee template query error %v", err)
  458. }
  459. defer rows.Close()
  460. for rows.Next() {
  461. var fee FeeTemplate
  462. if err := rows.Scan(
  463. &fee.Id,
  464. &fee.User,
  465. &fee.Branch,
  466. &fee.Amount,
  467. &fee.Perc,
  468. &fee.Type,
  469. &fee.Notes,
  470. &fee.Name,
  471. &fee.Category,
  472. &fee.Auto); err != nil {
  473. return nil, fmt.Errorf("FeesTemplate scanning error: %v", err)
  474. }
  475. fees = append(fees, fee)
  476. }
  477. return fees, nil
  478. }
  479. // Fetch fees from the database
  480. func getFeesTemp(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  481. var fees []FeeTemplate
  482. claims, err := getClaims(r)
  483. if err != nil {
  484. w.WriteHeader(500)
  485. return
  486. }
  487. user, err := queryUser(db, claims.Id)
  488. if err != nil {
  489. w.WriteHeader(422)
  490. return
  491. }
  492. fees, err = fetchFeesTemp(db, claims.Id, user.Branch.Id)
  493. json.NewEncoder(w).Encode(fees)
  494. }
  495. // Fetch fees from the database
  496. func createFeesTemp(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  497. var fee FeeTemplate
  498. var query string
  499. var row *sql.Row
  500. var err error
  501. claims, err := getClaims(r)
  502. // var id int // Inserted estimate's id
  503. err = json.NewDecoder(r.Body).Decode(&fee)
  504. if err != nil {
  505. w.WriteHeader(422)
  506. return
  507. }
  508. query = `INSERT INTO fee_template
  509. (
  510. user_id,
  511. branch_id,
  512. amount,
  513. perc,
  514. type,
  515. notes,
  516. name,
  517. auto
  518. )
  519. VALUES (?, NULL, ?, ?, ?, ?, ?, ?)
  520. RETURNING id
  521. `
  522. row = db.QueryRow(query,
  523. claims.Id,
  524. fee.Amount,
  525. fee.Perc,
  526. fee.Type,
  527. fee.Notes,
  528. fee.Name,
  529. fee.Auto,
  530. )
  531. err = row.Scan(&fee.Id)
  532. if err != nil {
  533. w.WriteHeader(500)
  534. return
  535. }
  536. json.NewEncoder(w).Encode(fee)
  537. }
  538. // Fetch fees from the database
  539. func deleteFeeTemp(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  540. var fee FeeTemplate
  541. var query string
  542. var err error
  543. // claims, err := getClaims(r)
  544. // var id int // Inserted estimate's id
  545. err = json.NewDecoder(r.Body).Decode(&fee)
  546. if err != nil {
  547. w.WriteHeader(422)
  548. return
  549. }
  550. query = `DELETE FROM fee_template WHERE id = ?`
  551. _, err = db.Exec(query, fee.Id)
  552. if err != nil {
  553. w.WriteHeader(500)
  554. return
  555. }
  556. }
  557. func deleteEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  558. var estimate Estimate
  559. var err error
  560. err = json.NewDecoder(r.Body).Decode(&estimate)
  561. if err != nil {
  562. w.WriteHeader(422)
  563. return
  564. }
  565. claims, err := getClaims(r)
  566. err = estimate.del(db, claims.Id)
  567. if err != nil {
  568. http.Error(w, err.Error(), 500)
  569. return
  570. }
  571. }
  572. func deleteET(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  573. var et ETemplate
  574. var err error
  575. err = json.NewDecoder(r.Body).Decode(&et)
  576. if err != nil {
  577. w.WriteHeader(422)
  578. return
  579. }
  580. claims, err := getClaims(r)
  581. err = et.del(db, claims.Id)
  582. if err != nil {
  583. http.Error(w, err.Error(), 500)
  584. return
  585. }
  586. }
  587. func getMi(db *sql.DB, loan int) (MI, error) {
  588. var mi MI
  589. query := `SELECT
  590. type, label, lender, rate, premium, upfront, five_year_total,
  591. initial_premium, initial_rate, initial_amount
  592. FROM mi WHERE loan_id = ?`
  593. rows, err := db.Query(query, loan)
  594. if err != nil {
  595. return mi, err
  596. }
  597. defer rows.Close()
  598. if !rows.Next() {
  599. return mi, nil
  600. }
  601. if err := rows.Scan(
  602. &mi.Type,
  603. &mi.Label,
  604. &mi.Lender,
  605. &mi.Rate,
  606. &mi.Premium,
  607. &mi.Upfront,
  608. &mi.FiveYearTotal,
  609. &mi.InitialAllInPremium,
  610. &mi.InitialAllInRate,
  611. &mi.InitialAmount,
  612. ); err != nil {
  613. return mi, err
  614. }
  615. return mi, nil
  616. }
  617. func (estimate *Estimate) getBorrower(db *sql.DB) error {
  618. query := `SELECT id, credit_score, monthly_income, num FROM borrower
  619. WHERE estimate_id = ? LIMIT 1`
  620. row := db.QueryRow(query, estimate.Id)
  621. if err := row.Scan(
  622. &estimate.Borrower.Id,
  623. &estimate.Borrower.Credit,
  624. &estimate.Borrower.Income,
  625. &estimate.Borrower.Num,
  626. ); err != nil {
  627. return fmt.Errorf("Borrower scanning error: %v", err)
  628. }
  629. return nil
  630. }
  631. // Query Lender APIs and parse responses into MI structs
  632. func fetchMi(db *sql.DB, estimate *Estimate, pos int) []MI {
  633. var loan Loan = estimate.Loans[pos]
  634. var ltv = func(l float32) string {
  635. switch {
  636. case l > 95:
  637. return "LTV97"
  638. case l > 90:
  639. return "LTV95"
  640. case l > 85:
  641. return "LTV90"
  642. default:
  643. return "LTV85"
  644. }
  645. }
  646. var term = func(t int) string {
  647. switch {
  648. case t <= 10:
  649. return "A10"
  650. case t <= 15:
  651. return "A15"
  652. case t <= 20:
  653. return "A20"
  654. case t <= 25:
  655. return "A25"
  656. case t <= 30:
  657. return "A30"
  658. default:
  659. return "A40"
  660. }
  661. }
  662. var propertyCodes = map[string]string{
  663. "Single Attached": "SFO",
  664. "Single Detached": "SFO",
  665. "Condo Lo-rise": "CON",
  666. "Condo Hi-rise": "CON",
  667. }
  668. var purposeCodes = map[string]string{
  669. "Purchase": "PUR",
  670. "Refinance": "RRT",
  671. }
  672. body, err := json.Marshal(map[string]any{
  673. "zipCode": estimate.Zip,
  674. "stateCode": "CA",
  675. "address": "",
  676. "propertyTypeCode": propertyCodes[estimate.Property],
  677. "occupancyTypeCode": "PRS",
  678. "loanPurposeCode": purposeCodes[estimate.Transaction],
  679. "loanAmount": loan.Amount,
  680. "loanToValue": ltv(loan.Ltv),
  681. "amortizationTerm": term(loan.Term),
  682. "loanTypeCode": "FXD",
  683. "duLpDecisionCode": "DAE",
  684. "loanProgramCodes": []any{},
  685. "debtToIncome": loan.Dti,
  686. "wholesaleLoan": 0,
  687. "coveragePercentageCode": "L30",
  688. "productCode": "BPM",
  689. "renewalTypeCode": "CON",
  690. "numberOfBorrowers": 1,
  691. "coBorrowerCreditScores": []any{},
  692. "borrowerCreditScore": strconv.Itoa(estimate.Borrower.Credit),
  693. "masterPolicy": nil,
  694. "selfEmployedIndicator": false,
  695. "armType": "",
  696. "userId": 44504,
  697. })
  698. /*
  699. if err != nil {
  700. log.Printf("Could not marshal NationalMI body: \n%v\n%v\n",
  701. bytes.NewBuffer(body), err)
  702. func queryAddress(db *sql.DB, id int) ( Address, error ) {
  703. var address Address = Address{Id: id}
  704. var err error
  705. row := db.QueryRow(
  706. `SELECT id, full_address, street, city, region, country, zip
  707. FROM address WHERE id = ?`, id)
  708. err = row.Scan(
  709. &address.Id,
  710. &address.Full,
  711. &address.Street,
  712. &address.City,
  713. &address.Region,
  714. &address.Country,
  715. &address.Zip,
  716. )
  717. return address, err
  718. }
  719. }
  720. */
  721. req, err := http.NewRequest("POST",
  722. "https://rate-gps.nationalmi.com/rates/productRateQuote",
  723. bytes.NewBuffer(body))
  724. req.Header.Add("Content-Type", "application/json")
  725. req.AddCookie(&http.Cookie{
  726. Name: "nmirategps_email",
  727. Value: os.Getenv("NationalMIEmail")})
  728. resp, err := http.DefaultClient.Do(req)
  729. var res map[string]interface{}
  730. var result []MI
  731. if resp.StatusCode != 200 {
  732. log.Printf("the status: %v\nthe resp: %v\n the req: %v\n the body: %v\n",
  733. resp.Status, resp, req.Body, bytes.NewBuffer(body))
  734. } else {
  735. json.NewDecoder(resp.Body).Decode(&res)
  736. // estimate.Loans[pos].Mi = res
  737. // Parse res into result here
  738. }
  739. log.Println(err)
  740. return result
  741. }
  742. // Make comparison PDF
  743. func generatePDF(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  744. }
  745. func login(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  746. var id int
  747. var role string
  748. var err error
  749. var user User
  750. json.NewDecoder(r.Body).Decode(&user)
  751. row := db.QueryRow(
  752. `SELECT id, role FROM user WHERE email = ? AND password = sha2(?, 256)`,
  753. user.Email, user.Password,
  754. )
  755. err = row.Scan(&id, &role)
  756. if err != nil {
  757. http.Error(w, "Invalid Credentials.", http.StatusUnauthorized)
  758. return
  759. }
  760. err = setTokenCookie(id, role, w)
  761. if err != nil {
  762. http.Error(w, err.Error(), 500)
  763. }
  764. }
  765. func setTokenCookie(id int, role string, w http.ResponseWriter) error {
  766. token := jwt.NewWithClaims(jwt.SigningMethodHS256,
  767. UserClaims{Id: id, Role: role,
  768. Exp: time.Now().Add(time.Minute * 30).Format(time.UnixDate)})
  769. tokenStr, err := token.SignedString([]byte(os.Getenv("JWT_SECRET")))
  770. if err != nil {
  771. log.Println("Token could not be signed: ", err, tokenStr)
  772. return err
  773. }
  774. cookie := http.Cookie{Name: "skouter",
  775. Value: tokenStr,
  776. Path: "/",
  777. Expires: time.Now().Add(time.Hour * 1)}
  778. http.SetCookie(w, &cookie)
  779. return nil
  780. }
  781. func getToken(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  782. claims, _ := getClaims(r)
  783. err := setTokenCookie(claims.Id, claims.Role, w)
  784. if err != nil {
  785. http.Error(w,
  786. "Token generation error",
  787. http.StatusInternalServerError)
  788. }
  789. }
  790. func getClaims(r *http.Request) (UserClaims, error) {
  791. claims := new(UserClaims)
  792. _, tokenStr, found := strings.Cut(r.Header.Get("Authorization"), " ")
  793. if !found {
  794. return *claims, errors.New("Token not found")
  795. }
  796. // Pull token payload into UserClaims
  797. _, err := jwt.ParseWithClaims(tokenStr, claims,
  798. func(token *jwt.Token) (any, error) {
  799. return []byte(os.Getenv("JWT_SECRET")), nil
  800. })
  801. if err != nil {
  802. return *claims, err
  803. }
  804. if err = claims.Valid(); err != nil {
  805. return *claims, err
  806. }
  807. return *claims, nil
  808. }
  809. func guard(r *http.Request, required int) bool {
  810. claims, err := getClaims(r)
  811. if err != nil {
  812. return false
  813. }
  814. if roles[claims.Role] < required {
  815. return false
  816. }
  817. return true
  818. }
  819. // Inserts an address and returns it's ID along with any errors.
  820. func insertAddress(db *sql.DB, address Address) (int, error) {
  821. var query string
  822. var row *sql.Row
  823. var err error
  824. var id int // Inserted user's id
  825. query = `INSERT INTO address
  826. (
  827. full_address,
  828. street,
  829. city,
  830. region,
  831. country,
  832. zip
  833. )
  834. VALUES (?, ?, ?, ?, ?, ?)
  835. RETURNING id
  836. `
  837. row = db.QueryRow(query,
  838. address.Full,
  839. address.Street,
  840. address.City,
  841. address.Region,
  842. address.Country,
  843. address.Zip,
  844. )
  845. err = row.Scan(&id)
  846. return id, err
  847. }
  848. // Inserts an address and returns it's ID along with any errors.
  849. func insertBranch(db *sql.DB, branch Branch) (int, error) {
  850. var query string
  851. var row *sql.Row
  852. var err error
  853. var id int // Inserted user's id
  854. query = `INSERT INTO branch
  855. (
  856. name,
  857. type,
  858. letterhead,
  859. num,
  860. phone,
  861. address
  862. )
  863. VALUES (?, ?, ?, ?, ?, ?)
  864. RETURNING id
  865. `
  866. row = db.QueryRow(query,
  867. branch.Name,
  868. branch.Type,
  869. branch.Letterhead,
  870. branch.Num,
  871. branch.Phone,
  872. branch.Address.Id,
  873. )
  874. err = row.Scan(&id)
  875. return id, err
  876. }
  877. // Inserts an address and returns it's ID along with any errors.
  878. func insertLicense(db *sql.DB, license License) (int, error) {
  879. var query string
  880. var row *sql.Row
  881. var err error
  882. var id int // Inserted license's id
  883. query = `INSERT INTO license
  884. (
  885. user_id,
  886. type,
  887. num
  888. )
  889. VALUES (?, ?, ?)
  890. RETURNING id
  891. `
  892. row = db.QueryRow(query,
  893. license.UserId,
  894. license.Type,
  895. license.Num,
  896. )
  897. err = row.Scan(&id)
  898. return id, err
  899. }
  900. func queryLicense(db *sql.DB, user int) (License, error) {
  901. var license License = License{UserId: user}
  902. var err error
  903. row := db.QueryRow(
  904. `SELECT id, type, num FROM license WHERE user_id = ?`,
  905. user)
  906. err = row.Scan(
  907. &license.Id,
  908. &license.Type,
  909. &license.Num,
  910. )
  911. return license, err
  912. }
  913. func queryAddress(db *sql.DB, id int) (Address, error) {
  914. var address Address = Address{Id: id}
  915. var err error
  916. row := db.QueryRow(
  917. `SELECT id, full_address, street, city, region, country, zip
  918. FROM address WHERE id = ?`, id)
  919. err = row.Scan(
  920. &address.Id,
  921. &address.Full,
  922. &address.Street,
  923. &address.City,
  924. &address.Region,
  925. &address.Country,
  926. &address.Zip,
  927. )
  928. return address, err
  929. }
  930. func queryBranch(db *sql.DB, id int) (Branch, error) {
  931. var branch Branch = Branch{Id: id}
  932. var err error
  933. row := db.QueryRow(
  934. `SELECT id, name, type, letterhead, num, phone, address
  935. FROM branch WHERE id = ?`, id)
  936. err = row.Scan(
  937. &branch.Id,
  938. &branch.Name,
  939. &branch.Type,
  940. &branch.Letterhead,
  941. &branch.Num,
  942. &branch.Phone,
  943. &branch.Address.Id,
  944. )
  945. return branch, err
  946. }
  947. func queryUser(db *sql.DB, id int) (User, error) {
  948. var user User
  949. var query string
  950. var err error
  951. query = `SELECT
  952. u.id,
  953. u.email,
  954. u.first_name,
  955. u.last_name,
  956. coalesce(u.branch_id, 0),
  957. u.country,
  958. u.title,
  959. coalesce(u.status, ''),
  960. coalesce(u.customer_id, ''),
  961. u.verified,
  962. u.role,
  963. u.address,
  964. u.phone
  965. FROM user u WHERE u.id = ?
  966. `
  967. row := db.QueryRow(query, id)
  968. if err != nil {
  969. return user, err
  970. }
  971. err = row.Scan(
  972. &user.Id,
  973. &user.Email,
  974. &user.FirstName,
  975. &user.LastName,
  976. &user.Branch.Id,
  977. &user.Country,
  978. &user.Title,
  979. &user.Status,
  980. &user.CustomerId,
  981. &user.Verified,
  982. &user.Role,
  983. &user.Address.Id,
  984. &user.Phone,
  985. )
  986. if err != nil {
  987. return user, err
  988. }
  989. user.Address, err = queryAddress(db, user.Address.Id)
  990. if err != nil {
  991. return user, err
  992. }
  993. if user.Branch.Id > 0 {
  994. user.Branch, err = queryBranch(db, user.Branch.Id)
  995. if err != nil {
  996. return user, err
  997. }
  998. }
  999. return user, nil
  1000. }
  1001. func queryCustomer(db *sql.DB, id string) (User, error) {
  1002. var user User
  1003. var query string
  1004. var err error
  1005. query = `SELECT
  1006. u.id,
  1007. u.email,
  1008. u.first_name,
  1009. u.last_name,
  1010. coalesce(u.branch_id, 0),
  1011. u.country,
  1012. u.title,
  1013. coalesce(u.status, ''),
  1014. coalesce(u.customer_id, ''),
  1015. u.verified,
  1016. u.role,
  1017. u.address,
  1018. u.phone
  1019. FROM user u WHERE u.customer_id = ?
  1020. `
  1021. row := db.QueryRow(query, id)
  1022. if err != nil {
  1023. return user, err
  1024. }
  1025. err = row.Scan(
  1026. &user.Id,
  1027. &user.Email,
  1028. &user.FirstName,
  1029. &user.LastName,
  1030. &user.Branch.Id,
  1031. &user.Country,
  1032. &user.Title,
  1033. &user.Status,
  1034. &user.CustomerId,
  1035. &user.Verified,
  1036. &user.Role,
  1037. &user.Address.Id,
  1038. &user.Phone,
  1039. )
  1040. if err != nil {
  1041. return user, err
  1042. }
  1043. user.Address, err = queryAddress(db, user.Address.Id)
  1044. if err != nil {
  1045. return user, err
  1046. }
  1047. user.Branch, err = queryBranch(db, user.Branch.Id)
  1048. if err != nil {
  1049. return user, err
  1050. }
  1051. return user, nil
  1052. }
  1053. // Can probably be deleted.
  1054. func queryUsers(db *sql.DB, id int) ([]User, error) {
  1055. var users []User
  1056. var query string
  1057. var rows *sql.Rows
  1058. var err error
  1059. query = `SELECT
  1060. u.id,
  1061. u.email,
  1062. u.first_name,
  1063. u.last_name,
  1064. coalesce(u.branch_id, 0),
  1065. u.country,
  1066. u.title,
  1067. coalesce(u.status, ''),
  1068. u.verified,
  1069. u.role,
  1070. u.address,
  1071. u.phone
  1072. FROM user u WHERE u.id = CASE @e := ? WHEN 0 THEN u.id ELSE @e END
  1073. `
  1074. rows, err = db.Query(query, id)
  1075. if err != nil {
  1076. return users, err
  1077. }
  1078. defer rows.Close()
  1079. for rows.Next() {
  1080. var user User
  1081. if err := rows.Scan(
  1082. &user.Id,
  1083. &user.Email,
  1084. &user.FirstName,
  1085. &user.LastName,
  1086. &user.Branch.Id,
  1087. &user.Country,
  1088. &user.Title,
  1089. &user.Status,
  1090. &user.Verified,
  1091. &user.Role,
  1092. &user.Address.Id,
  1093. &user.Phone,
  1094. ); err != nil {
  1095. return users, err
  1096. }
  1097. user.Address, err = queryAddress(db, user.Address.Id)
  1098. if err != nil {
  1099. return users, err
  1100. }
  1101. user.Branch, err = queryBranch(db, user.Branch.Id)
  1102. if err != nil {
  1103. return users, err
  1104. }
  1105. users = append(users, user)
  1106. }
  1107. // Prevents runtime panics
  1108. if len(users) == 0 {
  1109. return users, errors.New("User not found.")
  1110. }
  1111. return users, nil
  1112. }
  1113. func querySub(db *sql.DB, id int) (Subscription, error) {
  1114. var query string
  1115. var err error
  1116. var s Subscription
  1117. query = `SELECT
  1118. id,
  1119. stripe_id,
  1120. user_id,
  1121. customer_id,
  1122. current_period_end,
  1123. current_period_start,
  1124. client_secret,
  1125. payment_status
  1126. FROM subscription WHERE id = ?
  1127. `
  1128. row := db.QueryRow(query, id)
  1129. err = row.Scan(
  1130. &s.Id,
  1131. &s.StripeId,
  1132. &s.CustomerId,
  1133. &s.End,
  1134. &s.Start,
  1135. &s.ClientSecret,
  1136. &s.PaymentStatus,
  1137. )
  1138. return s, err
  1139. }
  1140. func (user *User) querySub(db *sql.DB) error {
  1141. var query string
  1142. var err error
  1143. query = `SELECT
  1144. id,
  1145. stripe_id,
  1146. user_id,
  1147. customer_id,
  1148. price_id,
  1149. current_period_end,
  1150. current_period_start,
  1151. client_secret,
  1152. payment_status
  1153. FROM subscription WHERE user_id = ?
  1154. `
  1155. row := db.QueryRow(query, user.Id)
  1156. err = row.Scan(
  1157. &user.Sub.Id,
  1158. &user.Sub.StripeId,
  1159. &user.Sub.UserId,
  1160. &user.Sub.CustomerId,
  1161. &user.Sub.PriceId,
  1162. &user.Sub.End,
  1163. &user.Sub.Start,
  1164. &user.Sub.ClientSecret,
  1165. &user.Sub.PaymentStatus,
  1166. )
  1167. return err
  1168. }
  1169. func (estimate *Estimate) insertResults(db *sql.DB) error {
  1170. var query string
  1171. var row *sql.Row
  1172. var err error
  1173. var id int
  1174. query = `INSERT INTO estimate_result
  1175. (
  1176. loan_id,
  1177. loan_payment,
  1178. total_monthly,
  1179. total_fees,
  1180. total_credits,
  1181. cash_to_close
  1182. )
  1183. VALUES (?, ?, ?, ?, ?, ?)
  1184. RETURNING id
  1185. `
  1186. for i := range estimate.Loans {
  1187. r := estimate.Loans[i].Result
  1188. r.LoanId = estimate.Loans[i].Id
  1189. row = db.QueryRow(query,
  1190. r.LoanId,
  1191. r.LoanPayment,
  1192. r.TotalMonthly,
  1193. r.TotalFees,
  1194. r.TotalCredits,
  1195. r.CashToClose,
  1196. )
  1197. err = row.Scan(&id)
  1198. if err != nil {
  1199. return err
  1200. }
  1201. r.Id = id
  1202. }
  1203. return nil
  1204. }
  1205. // Insert user returning it's ID or any error
  1206. func insertUser(db *sql.DB, user User) (int, error) {
  1207. var query string
  1208. var row *sql.Row
  1209. var err error
  1210. var id int // Inserted user's id
  1211. user.Address.Id, err = insertAddress(db, user.Address)
  1212. if err != nil {
  1213. return 0, err
  1214. }
  1215. query = `INSERT INTO user
  1216. (
  1217. email,
  1218. first_name,
  1219. last_name,
  1220. password,
  1221. role,
  1222. title,
  1223. status,
  1224. verified,
  1225. address,
  1226. country,
  1227. branch_id,
  1228. phone,
  1229. created,
  1230. last_login
  1231. )
  1232. VALUES (?, ?, ?, sha2(?, 256), ?, ?, ?, ?, ?, ?,
  1233. CASE @b := ? WHEN 0 THEN NULL ELSE @b END,
  1234. ?, NOW(), NOW())
  1235. RETURNING id
  1236. `
  1237. row = db.QueryRow(query,
  1238. user.Email,
  1239. user.FirstName,
  1240. user.LastName,
  1241. user.Password,
  1242. user.Role,
  1243. user.Title,
  1244. user.Status,
  1245. user.Verified,
  1246. user.Address.Id,
  1247. user.Country,
  1248. user.Branch.Id,
  1249. user.Phone,
  1250. )
  1251. err = row.Scan(&id)
  1252. if err != nil {
  1253. return 0, err
  1254. }
  1255. user.Id = id
  1256. return id, nil
  1257. }
  1258. // Insert user returning it's ID or any error
  1259. func (sub *Subscription) insertSub(db *sql.DB) (error) {
  1260. var query string
  1261. var row *sql.Row
  1262. var err error
  1263. query = `INSERT INTO subscription
  1264. (
  1265. stripe_id,
  1266. user_id,
  1267. customer_id,
  1268. price_id,
  1269. current_period_end,
  1270. current_period_start,
  1271. client_secret,
  1272. payment_status
  1273. )
  1274. VALUES (?, ?, ?, ?, ?, ?, ?, ?)
  1275. RETURNING id
  1276. `
  1277. row = db.QueryRow(query,
  1278. sub.StripeId,
  1279. sub.UserId,
  1280. sub.CustomerId,
  1281. sub.PriceId,
  1282. sub.End,
  1283. sub.Start,
  1284. sub.ClientSecret,
  1285. sub.PaymentStatus,
  1286. )
  1287. err = row.Scan(&sub.Id)
  1288. return err
  1289. }
  1290. func (sub *Subscription) updateSub(db *sql.DB) error {
  1291. var query string
  1292. var err error
  1293. query = `UPDATE subscription
  1294. SET client_secret = ?, payment_status = ?
  1295. WHERE id = ?
  1296. `
  1297. s, err := subscription.Get(sub.StripeId, &stripe.SubscriptionParams{})
  1298. if err != nil { return err }
  1299. i, err := invoice.Get(s.LatestInvoice.ID, &stripe.InvoiceParams{})
  1300. if err != nil { return err }
  1301. p, err := paymentintent.Get(i.PaymentIntent.ID,
  1302. &stripe.PaymentIntentParams{})
  1303. if err != nil { return err }
  1304. _, err = db.Exec(query,
  1305. p.ClientSecret,
  1306. p.Status,
  1307. sub.Id,
  1308. )
  1309. if err != nil { return err }
  1310. sub.ClientSecret = p.ClientSecret
  1311. sub.PaymentStatus = string(p.Status)
  1312. return err
  1313. }
  1314. // Updates a user's stripe customer ID.
  1315. func (user *User) updateCustomerId(db *sql.DB, cid string) (error) {
  1316. var query string
  1317. var err error
  1318. query = `UPDATE user SET
  1319. customer_id = ?
  1320. WHERE id = ?
  1321. `
  1322. _, err = db.Exec(query,
  1323. cid,
  1324. user.Id,
  1325. )
  1326. if err != nil { return err }
  1327. user.CustomerId = cid
  1328. return nil
  1329. }
  1330. func updateAddress(address Address, db *sql.DB) error {
  1331. query := `
  1332. UPDATE address
  1333. SET
  1334. full_address = CASE @e := ? WHEN '' THEN full_address ELSE @e END,
  1335. street = CASE @fn := ? WHEN '' THEN street ELSE @fn END,
  1336. city = CASE @ln := ? WHEN '' THEN city ELSE @ln END,
  1337. region = CASE @r := ? WHEN '' THEN region ELSE @r END,
  1338. country = CASE @r := ? WHEN '' THEN country ELSE @r END,
  1339. zip = CASE @r := ? WHEN '' THEN zip ELSE @r END
  1340. WHERE id = ?
  1341. `
  1342. _, err := db.Exec(query,
  1343. address.Full,
  1344. address.Street,
  1345. address.City,
  1346. address.Region,
  1347. address.Country,
  1348. address.Zip,
  1349. address.Id,
  1350. )
  1351. return err
  1352. }
  1353. func updateUser(user User, db *sql.DB) error {
  1354. query := `
  1355. UPDATE user
  1356. SET
  1357. email = CASE @e := ? WHEN '' THEN email ELSE @e END,
  1358. first_name = CASE @fn := ? WHEN '' THEN first_name ELSE @fn END,
  1359. last_name = CASE @ln := ? WHEN '' THEN last_name ELSE @ln END,
  1360. role = CASE @r := ? WHEN '' THEN role ELSE @r END,
  1361. password = CASE @p := ? WHEN '' THEN password ELSE sha2(@p, 256) END
  1362. WHERE id = ?
  1363. `
  1364. _, err := db.Exec(query,
  1365. user.Email,
  1366. user.FirstName,
  1367. user.LastName,
  1368. user.Role,
  1369. user.Password,
  1370. user.Id,
  1371. )
  1372. return err
  1373. }
  1374. func getUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1375. claims, err := getClaims(r)
  1376. if err != nil {
  1377. w.WriteHeader(500)
  1378. return
  1379. }
  1380. user, err := queryUser(db, claims.Id)
  1381. if err != nil {
  1382. w.WriteHeader(422)
  1383. log.Println(err)
  1384. return
  1385. }
  1386. json.NewEncoder(w).Encode(user)
  1387. }
  1388. func getUsers(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1389. users, err := queryUsers(db, 0)
  1390. if err != nil {
  1391. w.WriteHeader(http.StatusInternalServerError)
  1392. return
  1393. }
  1394. json.NewEncoder(w).Encode(users)
  1395. }
  1396. // Updates a user using only specified values in the JSON body
  1397. func setUser(user User, db *sql.DB) error {
  1398. _, err := mail.ParseAddress(user.Email)
  1399. if err != nil {
  1400. return err
  1401. }
  1402. if roles[user.Role] == 0 {
  1403. return errors.New("Invalid role")
  1404. }
  1405. err = updateUser(user, db)
  1406. if err != nil {
  1407. return err
  1408. }
  1409. err = updateAddress(user.Address, db)
  1410. if err != nil {
  1411. return err
  1412. }
  1413. return nil
  1414. }
  1415. func patchUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1416. var user User
  1417. err := json.NewDecoder(r.Body).Decode(&user)
  1418. if err != nil {
  1419. http.Error(w, "Invalid fields", 422)
  1420. return
  1421. }
  1422. err = setUser(user, db)
  1423. if err != nil {
  1424. http.Error(w, err.Error(), 422)
  1425. return
  1426. }
  1427. }
  1428. // Update specified fields of the user specified in the claim
  1429. func patchSelf(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1430. claim, err := getClaims(r)
  1431. var user User
  1432. json.NewDecoder(r.Body).Decode(&user)
  1433. // First check that the target user to be updated is the same as the claim id, and
  1434. // their role is unchanged.
  1435. if err != nil || claim.Id != user.Id {
  1436. http.Error(w, "Target user's id does not match claim.", 401)
  1437. return
  1438. }
  1439. if claim.Role != user.Role && user.Role != "" {
  1440. http.Error(w, "Administrator required to escalate role.", 401)
  1441. return
  1442. }
  1443. err = setUser(user, db)
  1444. if err != nil {
  1445. http.Error(w, err.Error(), 422)
  1446. return
  1447. }
  1448. }
  1449. func deleteUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1450. var user User
  1451. err := json.NewDecoder(r.Body).Decode(&user)
  1452. if err != nil {
  1453. http.Error(w, "Invalid fields.", 422)
  1454. return
  1455. }
  1456. query := `DELETE FROM user WHERE id = ?`
  1457. _, err = db.Exec(query, user.Id)
  1458. if err != nil {
  1459. http.Error(w, "Could not delete.", 500)
  1460. }
  1461. }
  1462. // Checks if a user's entries are reasonable before database insertion.
  1463. // This function is very important because it is the only thing preventing
  1464. // anyone from creating an admin user. These error messages are displayed to
  1465. // the user.
  1466. func (user *User) validate() error {
  1467. _, err := mail.ParseAddress(user.Email)
  1468. if err != nil {
  1469. return errors.New("Invalid email.")
  1470. }
  1471. if roles[user.Role] == 0 {
  1472. return errors.New("Invalid role.")
  1473. }
  1474. if roles[user.Role] == roles["Admin"] {
  1475. return errors.New("New user cannot be an Admin.")
  1476. }
  1477. if user.FirstName == "" {
  1478. return errors.New("Given name cannot be empty.")
  1479. }
  1480. if user.LastName == "" {
  1481. return errors.New("Surname cannot be empty.")
  1482. }
  1483. if user.Password == "" {
  1484. return errors.New("Empty password")
  1485. }
  1486. return nil
  1487. }
  1488. func createUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1489. var user User
  1490. err := json.NewDecoder(r.Body).Decode(&user)
  1491. if err != nil {
  1492. http.Error(w, "Invalid fields.", 422)
  1493. return
  1494. }
  1495. user.Role = "User"
  1496. user.Status = "Trial"
  1497. err = user.validate()
  1498. if err != nil {
  1499. http.Error(w, err.Error(), 422)
  1500. return
  1501. }
  1502. user.Id, err = insertUser(db, user)
  1503. if err != nil {
  1504. http.Error(w, err.Error(), 422)
  1505. return
  1506. }
  1507. err = setTokenCookie(user.Id, user.Role, w)
  1508. if err != nil {
  1509. http.Error(w, err.Error(), 500)
  1510. return
  1511. }
  1512. json.NewEncoder(w).Encode(user)
  1513. }
  1514. func checkPassword(db *sql.DB, id int, pass string) bool {
  1515. var p string
  1516. query := `SELECT
  1517. password
  1518. FROM user WHERE user.id = ? AND password = sha2(?, 256)
  1519. `
  1520. row := db.QueryRow(query, id, pass)
  1521. err := row.Scan(&p)
  1522. if err != nil {
  1523. return false
  1524. }
  1525. return true
  1526. }
  1527. func setPassword(db *sql.DB, id int, pass string) error {
  1528. query := `UPDATE user
  1529. SET password = sha2(?, 256)
  1530. WHERE user.id = ?
  1531. `
  1532. _, err := db.Exec(query, pass, id)
  1533. if err != nil {
  1534. return errors.New("Could not insert password.")
  1535. }
  1536. return nil
  1537. }
  1538. func changePassword(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1539. var pass Password
  1540. claim, err := getClaims(r)
  1541. err = json.NewDecoder(r.Body).Decode(&pass)
  1542. if err != nil {
  1543. http.Error(w, "Bad fields.", 422)
  1544. return
  1545. }
  1546. if checkPassword(db, claim.Id, pass.Old) {
  1547. err = setPassword(db, claim.Id, pass.New)
  1548. } else {
  1549. http.Error(w, "Incorrect old password.", 401)
  1550. return
  1551. }
  1552. if err != nil {
  1553. http.Error(w, err.Error(), 500)
  1554. return
  1555. }
  1556. }
  1557. func fetchAvatar(db *sql.DB, user int) ([]byte, error) {
  1558. var img []byte
  1559. var query string
  1560. var err error
  1561. query = `SELECT
  1562. avatar
  1563. FROM user WHERE user.id = ?
  1564. `
  1565. row := db.QueryRow(query, user)
  1566. err = row.Scan(&img)
  1567. if err != nil {
  1568. return img, err
  1569. }
  1570. return img, nil
  1571. }
  1572. func insertAvatar(db *sql.DB, user int, img []byte) error {
  1573. query := `UPDATE user
  1574. SET avatar = ?
  1575. WHERE id = ?
  1576. `
  1577. _, err := db.Exec(query, img, user)
  1578. if err != nil {
  1579. return err
  1580. }
  1581. return nil
  1582. }
  1583. func fetchLetterhead(db *sql.DB, user int) ([]byte, error) {
  1584. var img []byte
  1585. var query string
  1586. var err error
  1587. query = `SELECT
  1588. letterhead
  1589. FROM user WHERE user.id = ?
  1590. `
  1591. row := db.QueryRow(query, user)
  1592. err = row.Scan(&img)
  1593. if err != nil {
  1594. return img, err
  1595. }
  1596. return img, nil
  1597. }
  1598. func insertLetterhead(db *sql.DB, user int, img []byte) error {
  1599. query := `UPDATE user
  1600. SET letterhead = ?
  1601. WHERE id = ?
  1602. `
  1603. _, err := db.Exec(query, img, user)
  1604. if err != nil {
  1605. return err
  1606. }
  1607. return nil
  1608. }
  1609. func setAvatar(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1610. var validTypes []string = []string{"image/png", "image/jpeg"}
  1611. var isValidType bool
  1612. claims, err := getClaims(r)
  1613. if err != nil {
  1614. http.Error(w, "Invalid token.", 422)
  1615. return
  1616. }
  1617. img, err := io.ReadAll(r.Body)
  1618. if err != nil {
  1619. http.Error(w, "Invalid file.", 422)
  1620. return
  1621. }
  1622. for _, v := range validTypes {
  1623. if v == http.DetectContentType(img) {
  1624. isValidType = true
  1625. }
  1626. }
  1627. if !isValidType {
  1628. http.Error(w, "Invalid file type.", 422)
  1629. return
  1630. }
  1631. err = insertAvatar(db, claims.Id, img)
  1632. if err != nil {
  1633. http.Error(w, "Could not insert.", 500)
  1634. return
  1635. }
  1636. }
  1637. func getAvatar(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1638. claims, err := getClaims(r)
  1639. if err != nil {
  1640. http.Error(w, "Invalid token.", 422)
  1641. return
  1642. }
  1643. img, err := fetchAvatar(db, claims.Id)
  1644. if err != nil {
  1645. http.Error(w, "Could not retrieve.", 500)
  1646. return
  1647. }
  1648. w.Header().Set("Content-Type", http.DetectContentType(img))
  1649. w.Write(img)
  1650. }
  1651. func setLetterhead(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1652. var validTypes []string = []string{"image/png", "image/jpeg"}
  1653. var isValidType bool
  1654. claims, err := getClaims(r)
  1655. if err != nil {
  1656. http.Error(w, "Invalid token.", 422)
  1657. return
  1658. }
  1659. img, err := io.ReadAll(r.Body)
  1660. if err != nil {
  1661. http.Error(w, "Invalid file.", 422)
  1662. return
  1663. }
  1664. for _, v := range validTypes {
  1665. if v == http.DetectContentType(img) {
  1666. isValidType = true
  1667. }
  1668. }
  1669. if !isValidType {
  1670. http.Error(w, "Invalid file type.", 422)
  1671. return
  1672. }
  1673. err = insertLetterhead(db, claims.Id, img)
  1674. if err != nil {
  1675. http.Error(w, "Could not insert.", 500)
  1676. return
  1677. }
  1678. }
  1679. func getLetterhead(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  1680. claims, err := getClaims(r)
  1681. if err != nil {
  1682. http.Error(w, "Invalid token.", 422)
  1683. return
  1684. }
  1685. img, err := fetchLetterhead(db, claims.Id)
  1686. if err != nil {
  1687. http.Error(w, "Could not retrieve.", 500)
  1688. return
  1689. }
  1690. w.Header().Set("Content-Type", http.DetectContentType(img))
  1691. w.Write(img)
  1692. }
  1693. func queryBorrower(db *sql.DB, id int) (Borrower, error) {
  1694. var borrower Borrower
  1695. var query string
  1696. var err error
  1697. query = `SELECT
  1698. l.id,
  1699. l.credit_score,
  1700. l.num,
  1701. l.monthly_income
  1702. FROM borrower l WHERE l.id = ?
  1703. `
  1704. row := db.QueryRow(query, id)
  1705. err = row.Scan(
  1706. borrower.Id,
  1707. borrower.Credit,
  1708. borrower.Num,
  1709. borrower.Income,
  1710. )
  1711. if err != nil {
  1712. return borrower, err
  1713. }
  1714. return borrower, nil
  1715. }
  1716. // Must have an estimate ID 'e', but not necessarily a loan id 'id'
  1717. func getResults(db *sql.DB, e int, id int) ([]Result, error) {
  1718. var results []Result
  1719. var query string
  1720. var rows *sql.Rows
  1721. var err error
  1722. query = `SELECT
  1723. r.id,
  1724. loan_id,
  1725. loan_payment,
  1726. total_monthly,
  1727. total_fees,
  1728. total_credits,
  1729. cash_to_close
  1730. FROM estimate_result r
  1731. INNER JOIN loan
  1732. ON r.loan_id = loan.id
  1733. WHERE r.id = CASE @e := ? WHEN 0 THEN r.id ELSE @e END
  1734. AND loan.estimate_id = ?
  1735. `
  1736. rows, err = db.Query(query, id, e)
  1737. if err != nil {
  1738. return results, err
  1739. }
  1740. defer rows.Close()
  1741. for rows.Next() {
  1742. var result Result
  1743. if err := rows.Scan(
  1744. &result.Id,
  1745. &result.LoanId,
  1746. &result.LoanPayment,
  1747. &result.TotalMonthly,
  1748. &result.TotalFees,
  1749. &result.TotalCredits,
  1750. &result.CashToClose,
  1751. ); err != nil {
  1752. return results, err
  1753. }
  1754. results = append(results, result)
  1755. }
  1756. // Prevents runtime panics
  1757. // if len(results) == 0 { return results, errors.New("Result not found.") }
  1758. return results, nil
  1759. }
  1760. // Retrieve an estimate result with a specified loan id
  1761. func getResult(db *sql.DB, loan int) (Result, error) {
  1762. var result Result
  1763. var query string
  1764. var err error
  1765. query = `SELECT
  1766. r.id,
  1767. loan_id,
  1768. loan_payment,
  1769. total_monthly,
  1770. total_fees,
  1771. total_credits,
  1772. cash_to_close
  1773. FROM estimate_result r
  1774. INNER JOIN loan
  1775. ON r.loan_id = loan.id
  1776. WHERE loan.Id = ?
  1777. `
  1778. row := db.QueryRow(query, loan)
  1779. err = row.Scan(
  1780. &result.Id,
  1781. &result.LoanId,
  1782. &result.LoanPayment,
  1783. &result.TotalMonthly,
  1784. &result.TotalFees,
  1785. &result.TotalCredits,
  1786. &result.CashToClose,
  1787. )
  1788. if err != nil {
  1789. return result, err
  1790. }
  1791. return result, nil
  1792. }
  1793. // Must have an estimate ID 'e', but not necessarily a loan id 'id'
  1794. func getLoans(db *sql.DB, e int, id int) ([]Loan, error) {
  1795. var loans []Loan
  1796. var query string
  1797. var rows *sql.Rows
  1798. var err error
  1799. query = `SELECT
  1800. l.id,
  1801. l.type_id,
  1802. l.estimate_id,
  1803. l.amount,
  1804. l.term,
  1805. l.interest,
  1806. l.ltv,
  1807. l.dti,
  1808. l.hoi,
  1809. l.tax,
  1810. l.name
  1811. FROM loan l WHERE l.id = CASE @e := ? WHEN 0 THEN l.id ELSE @e END AND
  1812. l.estimate_id = ?
  1813. `
  1814. rows, err = db.Query(query, id, e)
  1815. if err != nil {
  1816. return loans, err
  1817. }
  1818. defer rows.Close()
  1819. for rows.Next() {
  1820. var loan Loan
  1821. if err := rows.Scan(
  1822. &loan.Id,
  1823. &loan.Type.Id,
  1824. &loan.EstimateId,
  1825. &loan.Amount,
  1826. &loan.Term,
  1827. &loan.Interest,
  1828. &loan.Ltv,
  1829. &loan.Dti,
  1830. &loan.Hoi,
  1831. &loan.Tax,
  1832. &loan.Name,
  1833. ); err != nil {
  1834. return loans, err
  1835. }
  1836. mi, err := getMi(db, loan.Id)
  1837. if err != nil {
  1838. return loans, err
  1839. }
  1840. loan.Mi = mi
  1841. fees, err := getFees(db, loan.Id)
  1842. if err != nil {
  1843. return loans, err
  1844. }
  1845. loan.Fees = fees
  1846. loan.Result, err = getResult(db, loan.Id)
  1847. if err != nil {
  1848. return loans, err
  1849. }
  1850. loan.Type, err = getLoanType(db, loan.Type.Id)
  1851. if err != nil {
  1852. return loans, err
  1853. }
  1854. loans = append(loans, loan)
  1855. }
  1856. // Prevents runtime panics
  1857. if len(loans) == 0 {
  1858. return loans, errors.New("Loan not found.")
  1859. }
  1860. return loans, nil
  1861. }
  1862. func getEstimate(db *sql.DB, id int) (Estimate, error) {
  1863. estimates, err := getEstimates(db, id, 0)
  1864. if err != nil {
  1865. return Estimate{}, err
  1866. }
  1867. return estimates[0], nil
  1868. }
  1869. func getEstimates(db *sql.DB, id int, user int) ([]Estimate, error) {
  1870. var estimates []Estimate
  1871. var query string
  1872. var rows *sql.Rows
  1873. var err error
  1874. query = `SELECT
  1875. id,
  1876. user_id,
  1877. transaction,
  1878. price,
  1879. property,
  1880. occupancy,
  1881. zip,
  1882. pud
  1883. FROM estimate WHERE id = CASE @e := ? WHEN 0 THEN id ELSE @e END AND
  1884. user_id = CASE @e := ? WHEN 0 THEN user_id ELSE @e END
  1885. `
  1886. rows, err = db.Query(query, id, user)
  1887. if err != nil {
  1888. return estimates, err
  1889. }
  1890. defer rows.Close()
  1891. for rows.Next() {
  1892. var estimate Estimate
  1893. if err := rows.Scan(
  1894. &estimate.Id,
  1895. &estimate.User,
  1896. &estimate.Transaction,
  1897. &estimate.Price,
  1898. &estimate.Property,
  1899. &estimate.Occupancy,
  1900. &estimate.Zip,
  1901. &estimate.Pud,
  1902. ); err != nil {
  1903. return estimates, err
  1904. }
  1905. err := estimate.getBorrower(db)
  1906. if err != nil {
  1907. return estimates, err
  1908. }
  1909. estimates = append(estimates, estimate)
  1910. }
  1911. // Prevents runtime panics
  1912. if len(estimates) == 0 {
  1913. return estimates, errors.New("Estimate not found.")
  1914. }
  1915. for i := range estimates {
  1916. estimates[i].Loans, err = getLoans(db, estimates[i].Id, 0)
  1917. if err != nil {
  1918. return estimates, err
  1919. }
  1920. }
  1921. return estimates, nil
  1922. }
  1923. func queryETemplates(db *sql.DB, id int, user int) ([]ETemplate, error) {
  1924. var eTemplates []ETemplate
  1925. var query string
  1926. var rows *sql.Rows
  1927. var err error
  1928. query = `SELECT
  1929. id,
  1930. estimate_id,
  1931. user_id,
  1932. branch_id
  1933. FROM estimate_template WHERE id = CASE @e := ? WHEN 0 THEN id ELSE @e END AND
  1934. user_id = CASE @e := ? WHEN 0 THEN user_id ELSE @e END
  1935. `
  1936. rows, err = db.Query(query, id, user)
  1937. if err != nil {
  1938. return eTemplates, err
  1939. }
  1940. defer rows.Close()
  1941. for rows.Next() {
  1942. var e ETemplate
  1943. if err := rows.Scan(
  1944. &e.Id,
  1945. &e.Estimate.Id,
  1946. &e.UserId,
  1947. &e.BranchId,
  1948. ); err != nil {
  1949. return eTemplates, err
  1950. }
  1951. e.Estimate, err = getEstimate(db, e.Estimate.Id)
  1952. if err != nil {
  1953. return eTemplates, err
  1954. }
  1955. eTemplates = append(eTemplates, e)
  1956. }
  1957. return eTemplates, nil
  1958. }
  1959. // Accepts a borrower struct and returns the id of the inserted borrower and
  1960. // any related error.
  1961. func (estimate *Estimate) insertETemplate(db *sql.DB, user int, branch int) error {
  1962. var query string
  1963. var err error
  1964. query = `INSERT INTO estimate_template
  1965. (
  1966. estimate_id,
  1967. user_id,
  1968. branch_id
  1969. )
  1970. VALUES (?, ?, ?)
  1971. `
  1972. _, err = db.Exec(query,
  1973. estimate.Id,
  1974. user,
  1975. branch,
  1976. )
  1977. if err != nil {
  1978. return err
  1979. }
  1980. return nil
  1981. }
  1982. // Accepts a borrower struct and returns the id of the inserted borrower and
  1983. // any related error.
  1984. func (estimate *Estimate) insertBorrower(db *sql.DB) error {
  1985. var query string
  1986. var row *sql.Row
  1987. var err error
  1988. query = `INSERT INTO borrower
  1989. (
  1990. estimate_id,
  1991. credit_score,
  1992. monthly_income,
  1993. num
  1994. )
  1995. VALUES (?, ?, ?, ?)
  1996. RETURNING id
  1997. `
  1998. row = db.QueryRow(query,
  1999. estimate.Id,
  2000. estimate.Borrower.Credit,
  2001. estimate.Borrower.Income,
  2002. estimate.Borrower.Num,
  2003. )
  2004. err = row.Scan(&estimate.Borrower.Id)
  2005. if err != nil {
  2006. return err
  2007. }
  2008. return nil
  2009. }
  2010. func insertMi(db *sql.DB, mi MI) (int, error) {
  2011. var id int
  2012. query := `INSERT INTO mi
  2013. (
  2014. type,
  2015. label,
  2016. lender,
  2017. rate,
  2018. premium,
  2019. upfront,
  2020. five_year_total,
  2021. initial_premium,
  2022. initial_rate,
  2023. initial_amount
  2024. )
  2025. VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  2026. RETURNING id`
  2027. row := db.QueryRow(query,
  2028. &mi.Type,
  2029. &mi.Label,
  2030. &mi.Lender,
  2031. &mi.Rate,
  2032. &mi.Premium,
  2033. &mi.Upfront,
  2034. &mi.FiveYearTotal,
  2035. &mi.InitialAllInPremium,
  2036. &mi.InitialAllInRate,
  2037. &mi.InitialAmount,
  2038. )
  2039. err := row.Scan(&id)
  2040. if err != nil {
  2041. return 0, err
  2042. }
  2043. return id, nil
  2044. }
  2045. func insertFee(db *sql.DB, fee Fee) (int, error) {
  2046. var id int
  2047. query := `INSERT INTO fee
  2048. (loan_id, amount, perc, type, notes, name, category)
  2049. VALUES (?, ?, ?, ?, ?, ?, ?)
  2050. RETURNING id`
  2051. row := db.QueryRow(query,
  2052. fee.LoanId,
  2053. fee.Amount,
  2054. fee.Perc,
  2055. fee.Type,
  2056. fee.Notes,
  2057. fee.Name,
  2058. fee.Category,
  2059. )
  2060. err := row.Scan(&id)
  2061. if err != nil {
  2062. return 0, err
  2063. }
  2064. return id, nil
  2065. }
  2066. func insertLoanType(db *sql.DB, lt LoanType) (int, error) {
  2067. var id int
  2068. query := `INSERT INTO loan_type (branch_id, user_id, name)
  2069. VALUES (NULLIF(?, 0), NULLIF(?, 0), ?)
  2070. RETURNING id`
  2071. row := db.QueryRow(query,
  2072. lt.Branch,
  2073. lt.User,
  2074. lt.Name,
  2075. )
  2076. err := row.Scan(&id)
  2077. if err != nil {
  2078. return 0, err
  2079. }
  2080. return id, nil
  2081. }
  2082. func (loan *Loan) insertLoan(db *sql.DB) error {
  2083. var query string
  2084. var row *sql.Row
  2085. var err error
  2086. query = `INSERT INTO loan
  2087. (
  2088. estimate_id,
  2089. type_id,
  2090. amount,
  2091. term,
  2092. interest,
  2093. ltv,
  2094. dti,
  2095. hoi,
  2096. tax,
  2097. name
  2098. )
  2099. VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  2100. RETURNING id
  2101. `
  2102. row = db.QueryRow(query,
  2103. loan.EstimateId,
  2104. loan.Type.Id,
  2105. loan.Amount,
  2106. loan.Term,
  2107. loan.Interest,
  2108. loan.Ltv,
  2109. loan.Dti,
  2110. loan.Hoi,
  2111. loan.Tax,
  2112. loan.Name,
  2113. )
  2114. err = row.Scan(&loan.Id)
  2115. if err != nil {
  2116. return err
  2117. }
  2118. _, err = insertMi(db, loan.Mi)
  2119. if err != nil {
  2120. return err
  2121. }
  2122. for i := range loan.Fees {
  2123. loan.Fees[i].LoanId = loan.Id
  2124. _, err := insertFee(db, loan.Fees[i])
  2125. if err != nil {
  2126. return err
  2127. }
  2128. }
  2129. return nil
  2130. }
  2131. func (estimate *Estimate) insertEstimate(db *sql.DB) error {
  2132. var query string
  2133. var row *sql.Row
  2134. var err error
  2135. // var id int // Inserted estimate's id
  2136. query = `INSERT INTO estimate
  2137. (
  2138. user_id,
  2139. transaction,
  2140. price,
  2141. property,
  2142. occupancy,
  2143. zip,
  2144. pud
  2145. )
  2146. VALUES (?, ?, ?, ?, ?, ?, ?)
  2147. RETURNING id
  2148. `
  2149. row = db.QueryRow(query,
  2150. estimate.User,
  2151. estimate.Transaction,
  2152. estimate.Price,
  2153. estimate.Property,
  2154. estimate.Occupancy,
  2155. estimate.Zip,
  2156. estimate.Pud,
  2157. )
  2158. err = row.Scan(&estimate.Id)
  2159. if err != nil {
  2160. return err
  2161. }
  2162. err = estimate.insertBorrower(db)
  2163. if err != nil {
  2164. return err
  2165. }
  2166. for i := range estimate.Loans {
  2167. estimate.Loans[i].EstimateId = estimate.Id
  2168. err = estimate.Loans[i].insertLoan(db)
  2169. if err != nil {
  2170. return err
  2171. }
  2172. }
  2173. return nil
  2174. }
  2175. func (estimate *Estimate) del(db *sql.DB, user int) error {
  2176. var query string
  2177. var err error
  2178. query = `DELETE FROM estimate WHERE id = ? AND user_id = ?`
  2179. _, err = db.Exec(query, estimate.Id, user)
  2180. if err != nil {
  2181. return err
  2182. }
  2183. return nil
  2184. }
  2185. func (et *ETemplate) del(db *sql.DB, user int) error {
  2186. var query string
  2187. var err error
  2188. query = `DELETE FROM estimate_template WHERE id = ? AND user_id = ?`
  2189. _, err = db.Exec(query, et.Id, user)
  2190. if err != nil {
  2191. return err
  2192. }
  2193. return nil
  2194. }
  2195. func (eTemplate *ETemplate) insert(db *sql.DB) error {
  2196. var query string
  2197. var row *sql.Row
  2198. var err error
  2199. query = `INSERT INTO estimate_template
  2200. (
  2201. user_id,
  2202. branch_id,
  2203. estimate_id,
  2204. )
  2205. VALUES (?, ?, ?)
  2206. RETURNING id
  2207. `
  2208. row = db.QueryRow(query,
  2209. eTemplate.UserId,
  2210. eTemplate.BranchId,
  2211. eTemplate.Estimate.Id,
  2212. )
  2213. err = row.Scan(&eTemplate.Id)
  2214. if err != nil {
  2215. return err
  2216. }
  2217. return nil
  2218. }
  2219. func createEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2220. var estimate Estimate
  2221. err := json.NewDecoder(r.Body).Decode(&estimate)
  2222. if err != nil {
  2223. http.Error(w, "Invalid fields.", 422)
  2224. return
  2225. }
  2226. claims, err := getClaims(r)
  2227. estimate.User = claims.Id
  2228. err = estimate.insertEstimate(db)
  2229. if err != nil {
  2230. http.Error(w, err.Error(), 422)
  2231. return
  2232. }
  2233. estimate.makeResults()
  2234. err = estimate.insertResults(db)
  2235. if err != nil {
  2236. http.Error(w, err.Error(), 500)
  2237. return
  2238. }
  2239. e, err := getEstimates(db, estimate.Id, 0)
  2240. if err != nil {
  2241. http.Error(w, err.Error(), 500)
  2242. return
  2243. }
  2244. json.NewEncoder(w).Encode(e[0])
  2245. }
  2246. // Query all estimates that belong to the current user
  2247. func fetchEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2248. var estimates []Estimate
  2249. claims, err := getClaims(r)
  2250. estimates, err = getEstimates(db, 0, claims.Id)
  2251. if err != nil {
  2252. http.Error(w, err.Error(), 500)
  2253. return
  2254. }
  2255. json.NewEncoder(w).Encode(estimates)
  2256. }
  2257. func checkConventional(l Loan, b Borrower) error {
  2258. if b.Credit < 620 {
  2259. return errors.New("Credit score too low for conventional loan")
  2260. }
  2261. // Buyer needs to put down 20% to avoid mortgage insurance
  2262. if l.Ltv > 80 && l.Mi.Rate == 0 {
  2263. return errors.New(fmt.Sprintln(
  2264. l.Name,
  2265. "down payment must be 20% to avoid insurance",
  2266. ))
  2267. }
  2268. return nil
  2269. }
  2270. func checkFHA(l Loan, b Borrower) error {
  2271. if b.Credit < 500 {
  2272. return errors.New("Credit score too low for FHA loan")
  2273. }
  2274. if l.Ltv > 96.5 {
  2275. return errors.New("FHA down payment must be at least 3.5%")
  2276. }
  2277. if b.Credit < 580 && l.Ltv > 90 {
  2278. return errors.New("FHA down payment must be at least 10%")
  2279. }
  2280. // Debt-to-income must be below 45% if credit score is below 580.
  2281. if b.Credit < 580 && l.Dti > 45 {
  2282. return errors.New(fmt.Sprintln(
  2283. l.Name, "debt to income is too high for credit score.",
  2284. ))
  2285. }
  2286. return nil
  2287. }
  2288. // Loan option for veterans with no set rules. Mainly placeholder.
  2289. func checkVA(l Loan, b Borrower) error {
  2290. return nil
  2291. }
  2292. // Loan option for residents of rural areas with no set rules.
  2293. // Mainly placeholder.
  2294. func checkUSDA(l Loan, b Borrower) error {
  2295. return nil
  2296. }
  2297. // Should also check loan amount limit maybe with an API.
  2298. func checkEstimate(e Estimate) error {
  2299. if e.Property == "" {
  2300. return errors.New("Empty property type")
  2301. }
  2302. if e.Price == 0 {
  2303. return errors.New("Empty property price")
  2304. }
  2305. if e.Borrower.Num == 0 {
  2306. return errors.New("Missing number of borrowers")
  2307. }
  2308. if e.Borrower.Credit == 0 {
  2309. return errors.New("Missing borrower credit score")
  2310. }
  2311. if e.Borrower.Income == 0 {
  2312. return errors.New("Missing borrower credit income")
  2313. }
  2314. for _, l := range e.Loans {
  2315. if l.Amount == 0 {
  2316. return errors.New(fmt.Sprintln(l.Name, "loan amount cannot be zero"))
  2317. }
  2318. if l.Term == 0 {
  2319. return errors.New(fmt.Sprintln(l.Name, "loan term cannot be zero"))
  2320. }
  2321. if l.Interest == 0 {
  2322. return errors.New(fmt.Sprintln(l.Name, "loan interest cannot be zero"))
  2323. }
  2324. // Can be used to check rules for specific loan types
  2325. var err error
  2326. switch l.Type.Id {
  2327. case 1:
  2328. err = checkConventional(l, e.Borrower)
  2329. case 2:
  2330. err = checkFHA(l, e.Borrower)
  2331. case 3:
  2332. err = checkVA(l, e.Borrower)
  2333. case 4:
  2334. err = checkUSDA(l, e.Borrower)
  2335. default:
  2336. err = errors.New("Invalid loan type")
  2337. }
  2338. if err != nil {
  2339. return err
  2340. }
  2341. }
  2342. return nil
  2343. }
  2344. func validateEstimate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2345. var estimate Estimate
  2346. err := json.NewDecoder(r.Body).Decode(&estimate)
  2347. if err != nil {
  2348. http.Error(w, err.Error(), 422)
  2349. return
  2350. }
  2351. err = checkEstimate(estimate)
  2352. if err != nil {
  2353. http.Error(w, err.Error(), 406)
  2354. return
  2355. }
  2356. }
  2357. func checkPdf(w http.ResponseWriter, r *http.Request) {
  2358. db, err := sql.Open("mysql",
  2359. fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/skouter_dev",
  2360. os.Getenv("DBUser"),
  2361. os.Getenv("DBPass")))
  2362. // w.Header().Set("Content-Type", "application/json; charset=UTF-8")
  2363. err = db.Ping()
  2364. if err != nil {
  2365. fmt.Println("Bad database configuration: %v\n", err)
  2366. panic(err)
  2367. // maybe os.Exit(1) instead
  2368. }
  2369. estimates, err := getEstimates(db, 1, 0)
  2370. if err != nil {
  2371. w.WriteHeader(500)
  2372. return
  2373. }
  2374. // claims, err := getClaims(r)
  2375. if err != nil {
  2376. w.WriteHeader(500)
  2377. return
  2378. }
  2379. user, err := queryUser(db, 1)
  2380. info := Report{
  2381. Title: "test PDF",
  2382. Name: "idk-random-name",
  2383. User: user,
  2384. Estimate: estimates[0],
  2385. }
  2386. avatar, err := fetchAvatar(db, info.User.Id)
  2387. letterhead, err := fetchLetterhead(db, info.User.Id)
  2388. info.Avatar =
  2389. base64.StdEncoding.EncodeToString(avatar)
  2390. info.Letterhead =
  2391. base64.StdEncoding.EncodeToString(letterhead)
  2392. for l := range info.Estimate.Loans {
  2393. loan := info.Estimate.Loans[l]
  2394. for f := range info.Estimate.Loans[l].Fees {
  2395. if info.Estimate.Loans[l].Fees[f].Amount < 0 {
  2396. loan.Credits = append(loan.Credits, loan.Fees[f])
  2397. }
  2398. }
  2399. }
  2400. err = pages["report"].tpl.ExecuteTemplate(w, "master.tpl", info)
  2401. if err != nil {
  2402. fmt.Println(err)
  2403. }
  2404. }
  2405. func getETemplates(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2406. claims, err := getClaims(r)
  2407. et, err := queryETemplates(db, 0, claims.Id)
  2408. if err != nil {
  2409. http.Error(w, err.Error(), 500)
  2410. return
  2411. }
  2412. json.NewEncoder(w).Encode(et)
  2413. }
  2414. func createETemplate(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2415. var estimate Estimate
  2416. err := json.NewDecoder(r.Body).Decode(&estimate)
  2417. if err != nil {
  2418. http.Error(w, err.Error(), 422)
  2419. return
  2420. }
  2421. claims, err := getClaims(r)
  2422. if err != nil {
  2423. http.Error(w, err.Error(), 422)
  2424. return
  2425. }
  2426. err = estimate.insertETemplate(db, claims.Id, 0)
  2427. if err != nil {
  2428. http.Error(w, err.Error(), 500)
  2429. return
  2430. }
  2431. }
  2432. func getPdf(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2433. var estimate Estimate
  2434. err := json.NewDecoder(r.Body).Decode(&estimate)
  2435. cmd := exec.Command("wkhtmltopdf", "-", "-")
  2436. stdout, err := cmd.StdoutPipe()
  2437. if err != nil {
  2438. w.WriteHeader(500)
  2439. log.Println(err)
  2440. return
  2441. }
  2442. stdin, err := cmd.StdinPipe()
  2443. if err != nil {
  2444. w.WriteHeader(500)
  2445. log.Println(err)
  2446. return
  2447. }
  2448. if err := cmd.Start(); err != nil {
  2449. log.Fatal(err)
  2450. }
  2451. claims, err := getClaims(r)
  2452. if err != nil {
  2453. w.WriteHeader(500)
  2454. log.Println(err)
  2455. return
  2456. }
  2457. user, err := queryUser(db, claims.Id)
  2458. info := Report{
  2459. Title: "test PDF",
  2460. Name: "idk-random-name",
  2461. User: user,
  2462. Estimate: estimate,
  2463. }
  2464. avatar, err := fetchAvatar(db, info.User.Id)
  2465. letterhead, err := fetchLetterhead(db, info.User.Id)
  2466. if len(avatar) > 1 {
  2467. info.Avatar =
  2468. base64.StdEncoding.EncodeToString(avatar)
  2469. }
  2470. if len(letterhead) > 1 {
  2471. info.Letterhead =
  2472. base64.StdEncoding.EncodeToString(letterhead)
  2473. }
  2474. err = pages["report"].tpl.ExecuteTemplate(stdin, "master.tpl", info)
  2475. if err != nil {
  2476. w.WriteHeader(500)
  2477. log.Println(err)
  2478. return
  2479. }
  2480. stdin.Close()
  2481. buf, err := io.ReadAll(stdout)
  2482. if _, err := w.Write(buf); err != nil {
  2483. w.WriteHeader(500)
  2484. log.Println(err)
  2485. return
  2486. }
  2487. if err := cmd.Wait(); err != nil {
  2488. log.Println(err)
  2489. return
  2490. }
  2491. }
  2492. func clipLetterhead(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2493. var validTypes []string = []string{"image/png", "image/jpeg"}
  2494. var isValidType bool
  2495. var err error
  2496. // claims, err := getClaims(r)
  2497. if err != nil {
  2498. http.Error(w, "Invalid token.", 422)
  2499. return
  2500. }
  2501. img, t, err := image.Decode(r.Body)
  2502. if err != nil {
  2503. http.Error(w, "Invalid file, JPEG and PNG only.", 422)
  2504. return
  2505. }
  2506. for _, v := range validTypes {
  2507. if v == "image/"+t {
  2508. isValidType = true
  2509. }
  2510. }
  2511. if !isValidType {
  2512. http.Error(w, "Invalid file type.", 422)
  2513. return
  2514. }
  2515. g := gift.New(
  2516. gift.ResizeToFit(400, 200, gift.LanczosResampling),
  2517. )
  2518. dst := image.NewRGBA(g.Bounds(img.Bounds()))
  2519. g.Draw(dst, img)
  2520. w.Header().Set("Content-Type", "image/png")
  2521. err = png.Encode(w, dst)
  2522. if err != nil {
  2523. http.Error(w, "Error encoding.", 500)
  2524. return
  2525. }
  2526. }
  2527. func createCustomer(name string, email string, address Address) (
  2528. stripe.Customer, error) {
  2529. params := &stripe.CustomerParams{
  2530. Email: stripe.String(email),
  2531. Name: stripe.String(name),
  2532. Address: &stripe.AddressParams{
  2533. City: stripe.String(address.City),
  2534. Country: stripe.String(address.Country),
  2535. Line1: stripe.String(address.Street),
  2536. PostalCode: stripe.String(address.Zip),
  2537. State: stripe.String(address.Region),
  2538. },
  2539. };
  2540. result, err := customer.New(params)
  2541. return *result, err
  2542. }
  2543. // Initiates a new standard subscription using a given customer ID
  2544. func createSubscription(cid string) (*stripe.Subscription, error) {
  2545. // Automatically save the payment method to the subscription
  2546. // when the first payment is successful.
  2547. paymentSettings := &stripe.SubscriptionPaymentSettingsParams{
  2548. SaveDefaultPaymentMethod: stripe.String("on_subscription"),
  2549. }
  2550. // Create the subscription. Note we're expanding the Subscription's
  2551. // latest invoice and that invoice's payment_intent
  2552. // so we can pass it to the front end to confirm the payment
  2553. subscriptionParams := &stripe.SubscriptionParams{
  2554. Customer: stripe.String(cid),
  2555. Items: []*stripe.SubscriptionItemsParams{
  2556. {
  2557. Price: stripe.String(standardPriceId),
  2558. },
  2559. },
  2560. PaymentSettings: paymentSettings,
  2561. PaymentBehavior: stripe.String("default_incomplete"),
  2562. }
  2563. subscriptionParams.AddExpand("latest_invoice.payment_intent")
  2564. s, err := subscription.New(subscriptionParams)
  2565. return s, err
  2566. }
  2567. // Creates a new subscription instance for a new user or retrieves the
  2568. // existing instance if possible. It's main purpose is to supply a
  2569. // client secret used for sending billing information to stripe.
  2570. func subscribe(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2571. claims, err := getClaims(r)
  2572. user, err := queryUser(db, claims.Id)
  2573. if err != nil {
  2574. w.WriteHeader(422)
  2575. return
  2576. }
  2577. user.querySub(db)
  2578. var name string = user.FirstName + " " + user.LastName
  2579. if user.CustomerId == "" {
  2580. c, err := createCustomer(name, user.Email, user.Address)
  2581. if err != nil {
  2582. http.Error(w, err.Error(), 500)
  2583. return
  2584. }
  2585. err = user.updateCustomerId(db, c.ID)
  2586. if err != nil {
  2587. http.Error(w, err.Error(), 500)
  2588. return
  2589. }
  2590. }
  2591. if user.Sub.Id == 0 {
  2592. s, err := createSubscription(user.CustomerId)
  2593. if err != nil {
  2594. http.Error(w, err.Error(), 500)
  2595. return
  2596. }
  2597. user.Sub.UserId = user.Id
  2598. user.Sub.StripeId = s.ID
  2599. user.Sub.CustomerId = user.CustomerId
  2600. user.Sub.PriceId = standardPriceId
  2601. user.Sub.End = int(s.CurrentPeriodEnd)
  2602. user.Sub.Start = int(s.CurrentPeriodStart)
  2603. user.Sub.ClientSecret = s.LatestInvoice.PaymentIntent.ClientSecret
  2604. user.Sub.PaymentStatus = string(s.LatestInvoice.PaymentIntent.Status)
  2605. err = user.Sub.insertSub(db)
  2606. if err != nil {
  2607. http.Error(w, err.Error(), 500)
  2608. return
  2609. }
  2610. json.NewEncoder(w).Encode(user.Sub)
  2611. } else {
  2612. err = user.Sub.updateSub(db)
  2613. if err != nil {
  2614. http.Error(w, err.Error(), 500)
  2615. return
  2616. }
  2617. json.NewEncoder(w).Encode(user.Sub)
  2618. }
  2619. // Should check that subscription is still valid and has payment intent
  2620. // here
  2621. }
  2622. // A successful subscription payment should be confirmed by Stripe and
  2623. // Updated through this hook.
  2624. func invoicePaid(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2625. var invoice stripe.Invoice
  2626. b, err := io.ReadAll(r.Body)
  2627. if err != nil {
  2628. http.Error(w, err.Error(), http.StatusBadRequest)
  2629. log.Printf("io.ReadAll: %v", err)
  2630. return
  2631. }
  2632. event, err := webhook.ConstructEvent(b,
  2633. r.Header.Get("Stripe-Signature"),
  2634. hookKeys.InvoicePaid)
  2635. if err != nil {
  2636. http.Error(w, err.Error(), http.StatusBadRequest)
  2637. log.Printf("webhook.ConstructEvent: %v", err)
  2638. return
  2639. }
  2640. // OK should be sent before any processing to confirm with Stripe that
  2641. // the hook was received
  2642. w.WriteHeader(http.StatusOK)
  2643. if event.Type != "invoice.paid" {
  2644. log.Println("Invalid event type sent to invoice-paid.")
  2645. return
  2646. }
  2647. json.Unmarshal(event.Data.Raw, &invoice)
  2648. log.Println(event.Type, invoice.ID, invoice.Customer.ID)
  2649. user, err := queryCustomer(db, invoice.Customer.ID)
  2650. if err != nil {
  2651. log.Printf("Could not query customer: %v", err)
  2652. return
  2653. }
  2654. s, err := subscription.Get(invoice.Subscription.ID, nil)
  2655. if err != nil {
  2656. log.Printf("Could not fetch subscription: %v", err)
  2657. return
  2658. }
  2659. log.Println(user.Id, s.ID)
  2660. }
  2661. func invoiceFailed(w http.ResponseWriter, db *sql.DB, r *http.Request) {
  2662. b, err := io.ReadAll(r.Body)
  2663. if err != nil {
  2664. http.Error(w, err.Error(), http.StatusBadRequest)
  2665. log.Printf("io.ReadAll: %v", err)
  2666. return
  2667. }
  2668. event, err := webhook.ConstructEvent(b, r.Header.Get("Stripe-Signature"),
  2669. os.Getenv("STRIPE_SECRET_KEY"))
  2670. if err != nil {
  2671. http.Error(w, err.Error(), http.StatusBadRequest)
  2672. log.Printf("webhook.ConstructEvent: %v", err)
  2673. return
  2674. }
  2675. log.Println(event.Data)
  2676. }
  2677. func api(w http.ResponseWriter, r *http.Request) {
  2678. var args []string
  2679. p := r.URL.Path
  2680. db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s",
  2681. os.Getenv("DBUser"),
  2682. os.Getenv("DBPass"),
  2683. os.Getenv("DBName"),
  2684. ))
  2685. w.Header().Set("Content-Type", "application/json; charset=UTF-8")
  2686. err = db.Ping()
  2687. if err != nil {
  2688. fmt.Println("Bad database configuration: %v\n", err)
  2689. panic(err)
  2690. // maybe os.Exit(1) instead
  2691. }
  2692. switch {
  2693. case match(p, "/api/login", &args) &&
  2694. r.Method == http.MethodPost:
  2695. login(w, db, r)
  2696. case match(p, "/api/token", &args) &&
  2697. r.Method == http.MethodGet && guard(r, 1):
  2698. getToken(w, db, r)
  2699. case match(p, "/api/letterhead", &args) &&
  2700. r.Method == http.MethodPost && guard(r, 1):
  2701. clipLetterhead(w, db, r)
  2702. case match(p, "/api/users", &args) && // Array of all users
  2703. r.Method == http.MethodGet && guard(r, 3):
  2704. getUsers(w, db, r)
  2705. case match(p, "/api/user", &args) &&
  2706. r.Method == http.MethodGet && guard(r, 1):
  2707. getUser(w, db, r)
  2708. case match(p, "/api/user", &args) &&
  2709. r.Method == http.MethodPost:
  2710. createUser(w, db, r)
  2711. case match(p, "/api/user", &args) &&
  2712. r.Method == http.MethodPatch &&
  2713. guard(r, 3): // For admin to modify any user
  2714. patchUser(w, db, r)
  2715. case match(p, "/api/user", &args) &&
  2716. r.Method == http.MethodPatch &&
  2717. guard(r, 1): // For employees to modify own accounts
  2718. patchSelf(w, db, r)
  2719. case match(p, "/api/user", &args) &&
  2720. r.Method == http.MethodDelete &&
  2721. guard(r, 3):
  2722. deleteUser(w, db, r)
  2723. case match(p, "/api/user/avatar", &args) &&
  2724. r.Method == http.MethodGet &&
  2725. guard(r, 1):
  2726. getAvatar(w, db, r)
  2727. case match(p, "/api/user/avatar", &args) &&
  2728. r.Method == http.MethodPost &&
  2729. guard(r, 1):
  2730. setAvatar(w, db, r)
  2731. case match(p, "/api/user/letterhead", &args) &&
  2732. r.Method == http.MethodGet &&
  2733. guard(r, 1):
  2734. getLetterhead(w, db, r)
  2735. case match(p, "/api/user/letterhead", &args) &&
  2736. r.Method == http.MethodPost &&
  2737. guard(r, 1):
  2738. setLetterhead(w, db, r)
  2739. case match(p, "/api/user/password", &args) &&
  2740. r.Method == http.MethodPost &&
  2741. guard(r, 1):
  2742. changePassword(w, db, r)
  2743. case match(p, "/api/user/subscribe", &args) &&
  2744. r.Method == http.MethodPost &&
  2745. guard(r, 1):
  2746. subscribe(w, db, r)
  2747. case match(p, "/api/fees", &args) &&
  2748. r.Method == http.MethodGet &&
  2749. guard(r, 1):
  2750. getFeesTemp(w, db, r)
  2751. case match(p, "/api/fee", &args) &&
  2752. r.Method == http.MethodPost &&
  2753. guard(r, 1):
  2754. createFeesTemp(w, db, r)
  2755. case match(p, "/api/fee", &args) &&
  2756. r.Method == http.MethodDelete &&
  2757. guard(r, 1):
  2758. deleteFeeTemp(w, db, r)
  2759. case match(p, "/api/estimates", &args) &&
  2760. r.Method == http.MethodGet &&
  2761. guard(r, 1):
  2762. fetchEstimate(w, db, r)
  2763. case match(p, "/api/estimate", &args) &&
  2764. r.Method == http.MethodPost &&
  2765. guard(r, 1):
  2766. createEstimate(w, db, r)
  2767. case match(p, "/api/estimate", &args) &&
  2768. r.Method == http.MethodDelete &&
  2769. guard(r, 1):
  2770. deleteEstimate(w, db, r)
  2771. case match(p, "/api/estimate/validate", &args) &&
  2772. r.Method == http.MethodPost &&
  2773. guard(r, 1):
  2774. validateEstimate(w, db, r)
  2775. case match(p, "/api/estimate/summarize", &args) &&
  2776. r.Method == http.MethodPost &&
  2777. guard(r, 1):
  2778. summarize(w, db, r)
  2779. case match(p, "/api/templates", &args) &&
  2780. r.Method == http.MethodGet &&
  2781. guard(r, 1):
  2782. getETemplates(w, db, r)
  2783. case match(p, "/api/templates", &args) &&
  2784. r.Method == http.MethodPost &&
  2785. guard(r, 1):
  2786. createETemplate(w, db, r)
  2787. case match(p, "/api/templates", &args) &&
  2788. r.Method == http.MethodDelete &&
  2789. guard(r, 1):
  2790. deleteET(w, db, r)
  2791. case match(p, "/api/pdf", &args) &&
  2792. r.Method == http.MethodPost &&
  2793. guard(r, 1):
  2794. getPdf(w, db, r)
  2795. case match(p, "/api/stripe/invoice-paid", &args) &&
  2796. r.Method == http.MethodPost:
  2797. invoicePaid(w, db, r)
  2798. case match(p, "/api/stripe/invoice-payment-failed", &args) &&
  2799. r.Method == http.MethodPost:
  2800. invoiceFailed(w, db, r)
  2801. default:
  2802. http.Error(w, "Invalid route or token", 404)
  2803. }
  2804. db.Close()
  2805. }
  2806. func route(w http.ResponseWriter, r *http.Request) {
  2807. var page Page
  2808. var args []string
  2809. p := r.URL.Path
  2810. switch {
  2811. case r.Method == "GET" && match(p, "/", &args):
  2812. page = pages["home"]
  2813. case match(p, "/terms", &args):
  2814. page = pages["terms"]
  2815. case match(p, "/app", &args):
  2816. page = pages["app"]
  2817. default:
  2818. http.NotFound(w, r)
  2819. return
  2820. }
  2821. page.Render(w)
  2822. }
  2823. func serve() {
  2824. files := http.FileServer(http.Dir(""))
  2825. proxy, err := url.Parse("http://localhost:8002")
  2826. if err != nil {
  2827. log.Fatal("invalid origin server URL")
  2828. }
  2829. http.Handle("/assets/", files)
  2830. http.HandleFunc("/api/", api)
  2831. http.HandleFunc("/app", route)
  2832. http.Handle("/", httputil.NewSingleHostReverseProxy(proxy))
  2833. log.Fatal(http.ListenAndServe(address, nil))
  2834. }
  2835. func dbReset(db *sql.DB) {
  2836. b, err := os.ReadFile("migrations/reset.sql")
  2837. if err != nil {
  2838. log.Fatal(err)
  2839. }
  2840. _, err = db.Exec(string(b))
  2841. if err != nil {
  2842. log.Fatal(err)
  2843. }
  2844. b, err = os.ReadFile("migrations/0_29092022_setup_tables.sql")
  2845. if err != nil {
  2846. log.Fatal(err)
  2847. }
  2848. _, err = db.Exec(string(b))
  2849. if err != nil {
  2850. log.Fatal(err)
  2851. }
  2852. }
  2853. func generateFees(loan Loan) []Fee {
  2854. var fees []Fee
  2855. var fee Fee
  2856. p := gofakeit.Float32Range(0.5, 10)
  2857. size := gofakeit.Number(1, 10)
  2858. for f := 0; f < size; f++ {
  2859. fee = Fee{
  2860. Amount: int(float32(loan.Amount) * p / 100),
  2861. Perc: p,
  2862. Name: gofakeit.BuzzWord(),
  2863. Type: feeTypes[gofakeit.Number(0, len(feeTypes)-1)],
  2864. }
  2865. fees = append(fees, fee)
  2866. }
  2867. return fees
  2868. }
  2869. func generateCredits(loan Loan) []Fee {
  2870. var fees []Fee
  2871. var fee Fee
  2872. p := gofakeit.Float32Range(-10, -0.5)
  2873. size := gofakeit.Number(1, 10)
  2874. for f := 0; f < size; f++ {
  2875. fee = Fee{
  2876. Amount: int(float32(loan.Amount) * p / 100),
  2877. Perc: p,
  2878. Name: gofakeit.BuzzWord(),
  2879. Type: feeTypes[gofakeit.Number(0, len(feeTypes)-1)],
  2880. }
  2881. fees = append(fees, fee)
  2882. }
  2883. return fees
  2884. }
  2885. func seedAddresses(db *sql.DB) []Address {
  2886. addresses := make([]Address, 10)
  2887. for i, a := range addresses {
  2888. a.Street = gofakeit.Street()
  2889. a.City = gofakeit.City()
  2890. a.Region = gofakeit.State()
  2891. a.Country = "Canada"
  2892. a.Full = fmt.Sprintf("%s, %s %s", a.Street, a.City, a.Region)
  2893. id, err := insertAddress(db, a)
  2894. if err != nil {
  2895. log.Println(err)
  2896. break
  2897. }
  2898. addresses[i].Id = id
  2899. }
  2900. return addresses
  2901. }
  2902. func seedBranches(db *sql.DB, addresses []Address) []Branch {
  2903. branches := make([]Branch, 4)
  2904. for i := range branches {
  2905. branches[i].Name = gofakeit.Company()
  2906. branches[i].Type = "NMLS"
  2907. branches[i].Letterhead = gofakeit.ImagePng(400, 200)
  2908. branches[i].Num = gofakeit.HexUint8()
  2909. branches[i].Phone = gofakeit.Phone()
  2910. branches[i].Address.Id = gofakeit.Number(1, 5)
  2911. id, err := insertBranch(db, branches[i])
  2912. if err != nil {
  2913. log.Println(err)
  2914. break
  2915. }
  2916. branches[i].Id = id
  2917. }
  2918. return branches
  2919. }
  2920. func seedUsers(db *sql.DB, addresses []Address, branches []Branch) []User {
  2921. users := make([]User, 10)
  2922. for i := range users {
  2923. p := gofakeit.Person()
  2924. users[i].FirstName = p.FirstName
  2925. users[i].LastName = p.LastName
  2926. users[i].Email = p.Contact.Email
  2927. users[i].Phone = p.Contact.Phone
  2928. users[i].Branch = branches[gofakeit.Number(0, 3)]
  2929. users[i].Address = addresses[gofakeit.Number(1, 9)]
  2930. // users[i].Letterhead = gofakeit.ImagePng(400, 200)
  2931. // users[i].Avatar = gofakeit.ImagePng(200, 200)
  2932. users[i].Country = []string{"Canada", "USA"}[gofakeit.Number(0, 1)]
  2933. users[i].Password = "test123"
  2934. users[i].Verified = true
  2935. users[i].Title = "Loan Officer"
  2936. users[i].Status = "Subscribed"
  2937. users[i].Role = "User"
  2938. }
  2939. users[0].Email = "test@example.com"
  2940. users[0].Email = "test@example.com"
  2941. users[1].Email = "test2@example.com"
  2942. users[1].Status = "Branch"
  2943. users[1].Role = "Manager"
  2944. users[2].Email = "test3@example.com"
  2945. users[2].Status = "Free"
  2946. users[2].Role = "Admin"
  2947. for i := range users {
  2948. var err error
  2949. users[i].Id, err = insertUser(db, users[i])
  2950. if err != nil {
  2951. log.Println(err)
  2952. break
  2953. }
  2954. }
  2955. return users
  2956. }
  2957. func seedLicenses(db *sql.DB, users []User) []License {
  2958. licenses := make([]License, len(users))
  2959. for i := range licenses {
  2960. licenses[i].UserId = users[i].Id
  2961. licenses[i].Type = []string{"NMLS", "FSRA"}[gofakeit.Number(0, 1)]
  2962. licenses[i].Num = gofakeit.UUID()
  2963. id, err := insertLicense(db, licenses[i])
  2964. if err != nil {
  2965. log.Println(err)
  2966. break
  2967. }
  2968. licenses[i].Id = id
  2969. }
  2970. return licenses
  2971. }
  2972. func seedLoanTypes(db *sql.DB) []LoanType {
  2973. var loantypes []LoanType
  2974. var loantype LoanType
  2975. var err error
  2976. loantype = LoanType{Branch: 0, User: 0, Name: "Conventional"}
  2977. loantype.Id, err = insertLoanType(db, loantype)
  2978. if err != nil {
  2979. panic(err)
  2980. }
  2981. loantypes = append(loantypes, loantype)
  2982. loantype = LoanType{Branch: 0, User: 0, Name: "FHA"}
  2983. loantype.Id, err = insertLoanType(db, loantype)
  2984. if err != nil {
  2985. panic(err)
  2986. }
  2987. loantypes = append(loantypes, loantype)
  2988. loantype = LoanType{Branch: 0, User: 0, Name: "USDA"}
  2989. loantype.Id, err = insertLoanType(db, loantype)
  2990. if err != nil {
  2991. panic(err)
  2992. }
  2993. loantypes = append(loantypes, loantype)
  2994. loantype = LoanType{Branch: 0, User: 0, Name: "VA"}
  2995. loantype.Id, err = insertLoanType(db, loantype)
  2996. if err != nil {
  2997. panic(err)
  2998. }
  2999. loantypes = append(loantypes, loantype)
  3000. return loantypes
  3001. }
  3002. func seedEstimates(db *sql.DB, users []User, ltypes []LoanType) []Estimate {
  3003. var estimates []Estimate
  3004. var estimate Estimate
  3005. var l Loan
  3006. var err error
  3007. for i := 0; i < 15; i++ {
  3008. estimate = Estimate{}
  3009. estimate.User = users[gofakeit.Number(0, len(users)-1)].Id
  3010. estimate.Borrower = Borrower{
  3011. Credit: gofakeit.Number(600, 800),
  3012. Income: gofakeit.Number(1000000, 15000000),
  3013. Num: gofakeit.Number(1, 20),
  3014. }
  3015. estimate.Transaction = []string{"Purchase", "Refinance"}[gofakeit.Number(0, 1)]
  3016. estimate.Price = gofakeit.Number(50000, 200000000)
  3017. estimate.Property =
  3018. propertyTypes[gofakeit.Number(0, len(propertyTypes)-1)]
  3019. estimate.Occupancy =
  3020. []string{"Primary", "Secondary", "Investment"}[gofakeit.Number(0, 2)]
  3021. estimate.Zip = gofakeit.Zip()
  3022. lsize := gofakeit.Number(1, 6)
  3023. for j := 0; j < lsize; j++ {
  3024. l.Type = ltypes[gofakeit.Number(0, len(ltypes)-1)]
  3025. l.Amount = gofakeit.Number(
  3026. int(float32(estimate.Price)*0.5),
  3027. int(float32(estimate.Price)*0.93))
  3028. l.Term = gofakeit.Number(4, 30)
  3029. l.Hoi = gofakeit.Number(50000, 700000)
  3030. l.Hazard = gofakeit.Number(5000, 200000)
  3031. l.Tax = gofakeit.Number(5000, 200000)
  3032. l.Interest = gofakeit.Float32Range(0.5, 8)
  3033. l.Fees = generateFees(l)
  3034. l.Credits = generateCredits(l)
  3035. l.Name = gofakeit.AdjectiveDescriptive()
  3036. estimate.Loans = append(estimate.Loans, l)
  3037. }
  3038. estimates = append(estimates, estimate)
  3039. }
  3040. estimates[0].User = users[0].Id
  3041. estimates[1].User = users[0].Id
  3042. for i := range estimates {
  3043. err = estimates[i].insertEstimate(db)
  3044. if err != nil {
  3045. log.Println(err)
  3046. return estimates
  3047. }
  3048. }
  3049. return estimates
  3050. }
  3051. func seedResults(db *sql.DB, estimates []Estimate) error {
  3052. var err error
  3053. for i := range estimates {
  3054. estimates[i].makeResults()
  3055. err = estimates[i].insertResults(db)
  3056. if err != nil {
  3057. log.Println(err)
  3058. return err
  3059. }
  3060. }
  3061. return nil
  3062. }
  3063. func dbSeed(db *sql.DB) {
  3064. addresses := seedAddresses(db)
  3065. branches := seedBranches(db, addresses)
  3066. users := seedUsers(db, addresses, branches)
  3067. _ = seedLicenses(db, users)
  3068. loantypes := seedLoanTypes(db)
  3069. estimates := seedEstimates(db, users, loantypes)
  3070. _ = seedResults(db, estimates)
  3071. }
  3072. func dev(args []string) {
  3073. os.Setenv("DBName", "skouter_dev")
  3074. os.Setenv("DBUser", "tester")
  3075. os.Setenv("DBPass", "test123")
  3076. stripe.Key = os.Getenv("STRIPE_SECRET_KEY")
  3077. standardPriceId = "price_1OZLK9BPMoXn2pf9kuTAf8rs"
  3078. hookKeys = HookKeys{
  3079. InvoicePaid: os.Getenv("DEV_WEBHOOK_KEY"),
  3080. InvoiceFailed: os.Getenv("DEV_WEBHOOK_KEY"),
  3081. }
  3082. db, err := sql.Open("mysql",
  3083. fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s?multiStatements=true",
  3084. os.Getenv("DBUser"),
  3085. os.Getenv("DBPass"),
  3086. os.Getenv("DBName"),
  3087. ))
  3088. err = db.Ping()
  3089. if err != nil {
  3090. log.Println("Bad database configuration: %v", err)
  3091. panic(err)
  3092. // maybe os.Exit(1) instead
  3093. }
  3094. if len(args) == 0 {
  3095. serve()
  3096. return
  3097. }
  3098. switch args[0] {
  3099. case "seed":
  3100. dbSeed(db)
  3101. case "reset":
  3102. dbReset(db)
  3103. default:
  3104. return
  3105. }
  3106. db.Close()
  3107. }
  3108. func check(args []string) {
  3109. os.Setenv("DBName", "skouter_dev")
  3110. os.Setenv("DBUser", "tester")
  3111. os.Setenv("DBPass", "test123")
  3112. files := http.FileServer(http.Dir(""))
  3113. http.Handle("/assets/", files)
  3114. http.HandleFunc("/", checkPdf)
  3115. log.Fatal(http.ListenAndServe(address, nil))
  3116. }
  3117. func main() {
  3118. if len(os.Args) <= 1 {
  3119. serve()
  3120. return
  3121. }
  3122. switch os.Args[1] {
  3123. case "dev":
  3124. dev(os.Args[2:])
  3125. case "checkpdf":
  3126. check(os.Args[2:])
  3127. default:
  3128. return
  3129. }
  3130. }