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

skouter.go 84 KiB

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