Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

skouter.go 87 KiB

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