package Ġcom . w br awner . twig s . server Ċ Ċ import Ġch . qos . log back . classic . Level Ċ import Ġcom . w br awner . twig s .* Ċ import Ġcom . w br awner . twig s . db .* Ċ import Ġcom . w br awner . twig s . model . Session Ċ import Ġcom . w br awner . twig s . storage .* Ċ import Ġcom . w br awner . twig s . web . web Routes Ċ import Ġcom . zaxxer . hikari . HikariConfig Ċ import Ġcom . zaxxer . hikari . Hikari DataSource Ċ import Ġio . ktor . application .* Ċ import Ġio . ktor . auth .* Ċ import Ġio . ktor . features .* Ċ import Ġio . ktor . http .* Ċ import Ġio . ktor . response .* Ċ import Ġio . ktor . serialization .* Ċ import Ġio . ktor . sessions .* Ċ import Ġkotlinx . coroutines . current Coroutine Context Ċ import Ġkotlinx . coroutines . delay Ċ import Ġkotlinx . coroutines . isActive Ċ import Ġkotlinx . coroutines . launch Ċ import Ġkotlinx . serialization . json . Json Ċ import Ġorg . slf 4 j . LoggerFactory Ċ import Ġjava . util . concurrent . TimeUnit Ċ Ċ fun Ġmain ( args : ĠArray < String >): ĠUnit Ġ= Ġio . ktor . server . c io . Engine Main . main ( args ) Ċ Ċ private Ġconst Ġval ĠDATABASE _ VERSION Ġ= Ġ 3 Ċ Ċ fun ĠApplication . module () Ġ{ ĊĠĠĠ Ġval Ġdb Host Ġ= Ġenvironment . config . property OrNull (" twig s . database . host ") ?. getString () Ġ?: Ġ" localhost " ĊĠĠĠ Ġval Ġdb Port Ġ= Ġenvironment . config . property OrNull (" twig s . database . port ") ?. getString () Ġ?: Ġ" 5 4 3 2 " ĊĠĠĠ Ġval Ġdb Name Ġ= Ġenvironment . config . property OrNull (" twig s . database . name ") ?. getString () Ġ?: Ġ" twig s " ĊĠĠĠ Ġval Ġdb User Ġ= Ġenvironment . config . property OrNull (" twig s . database . user ") ?. getString () Ġ?: Ġ" twig s " ĊĠĠĠ Ġval Ġdb Pass Ġ= Ġenvironment . config . property OrNull (" twig s . database . password ") ?. getString () Ġ?: Ġ" twig s " ĊĠĠĠ Ġval Ġjdbc Url Ġ= Ġ" jdbc : postgresql :// $ db Host :$ db Port /$ dbName ? string type = un specified " ĊĠĠĠ Ġ( LoggerFactory . getLogger (" com . zaxxer . hikari ") Ġas Ġch . qos . log back . classic . Logger ). level Ġ= ĠLevel . ERROR ĊĠĠĠ ĠH ikari DataSource ( HikariConfig (). apply Ġ{ ĊĠĠĠĠĠĠĠ Ġset Jdbc Url ( jdbc Url ) ĊĠĠĠĠĠĠĠ Ġusername Ġ= Ġdb User ĊĠĠĠĠĠĠĠ Ġpassword Ġ= Ġdb Pass ĊĠĠĠ Ġ}). also Ġ{ ĊĠĠĠĠĠĠĠ Ġmodule With Dependencies ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġemail Service Ġ= ĠSm tp Email Service ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġfrom Ġ= Ġenvironment . config . property OrNull (" twig s . smtp . from ") ?. getString (), ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġhost Ġ= Ġenvironment . config . property OrNull (" twig s . smtp . host ") ?. getString (), ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġport Ġ= Ġenvironment . config . property OrNull (" twig s . smtp . port ") ?. getString ()?. toInt OrNull (), ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġusername Ġ= Ġenvironment . config . property OrNull (" twig s . smtp . user ") ?. getString (), ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġpassword Ġ= Ġenvironment . config . property OrNull (" twig s . smtp . pass ") ?. getString (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmetadata Repository Ġ= ĠMetadata Repository ( it ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġbudget Repository Ġ= ĠJdbc Budget Repository ( it ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġcategory Repository Ġ= ĠJdbc Category Repository ( it ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpassword Reset Repository Ġ= ĠJdbc Password Reset Repository ( it ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpermission Repository Ġ= ĠJdbc Permission Repository ( it ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġrecur ring Transaction Repository Ġ= ĠJdbc Recurring Transaction Repository ( it ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsession Repository Ġ= ĠJdbc Session Repository ( it ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtransaction Repository Ġ= ĠJdbc Transaction Repository ( it ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġuser Repository Ġ= ĠJdbc UserRepository ( it ) ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠ Ġ} Ċ } Ċ Ċ fun ĠApplication . module With Dependencies ( ĊĠĠĠ Ġemail Service : ĠEmail Service , ĊĠĠĠ Ġmetadata Repository : ĠMetadata Repository , ĊĠĠĠ Ġbudget Repository : ĠBudget Repository , ĊĠĠĠ Ġcategory Repository : ĠCategory Repository , ĊĠĠĠ Ġpassword Reset Repository : ĠPassword Reset Repository , ĊĠĠĠ Ġpermission Repository : ĠPermission Repository , ĊĠĠĠ Ġrecur ring Transaction Repository : ĠRe cur ring Transaction Repository , ĊĠĠĠ Ġsession Repository : ĠSession Repository , ĊĠĠĠ Ġtransaction Repository : ĠTransaction Repository , ĊĠĠĠ Ġuser Repository : ĠUser Repository Ċ ) Ġ{ ĊĠĠĠ Ġinstall ( Call Logging ) ĊĠĠĠ Ġinstall ( Authentication ) Ġ{ ĊĠĠĠĠĠĠĠ Ġsession < Session > Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġchallenge Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcall . respond ( HttpStatusCode . Unauthorized ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġvalidate Ġ{ Ġsession Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġenvironment . log . info (" Valid ating Ġsession ") ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġstored Session Ġ= Ġsession Repository . findAll ( session . token ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. first OrNull () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( stored Session Ġ== Ġnull ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġenvironment . log . info (" Did Ġnot Ġfind Ġsession !") ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġreturn @ validate Ġnull ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġelse Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġenvironment . log . info (" Found Ġsession !") ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġreturn @ validate Ġif Ġ( two Week s From Now . is After ( stored Session . expiration )) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġsession Repository . save ( stored Session . copy ( expiration Ġ= Ġtwo Week s From Now )) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġelse Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġnull ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĠĠĠ Ġinstall ( Sessions ) Ġ{ ĊĠĠĠĠĠĠĠ Ġheader < Session >(" Authorization ") Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġserializer Ġ= Ġobject Ġ: ĠSession Serializer < Session > Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġdeserialize ( text : ĠString ): ĠSession Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġenvironment . log . info (" Deserial izing Ġsession !") ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġreturn ĠSession ( token Ġ= Ġtext . substring After (" Bearer Ġ")) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġserialize ( session : ĠSession ): ĠString Ġ= Ġsession . token ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĠĠĠ Ġinstall ( Content Neg otiation ) Ġ{ ĊĠĠĠĠĠĠĠ Ġjson ( json Ġ= ĠJson Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġignore Unknown Keys Ġ= Ġtrue ĊĠĠĠĠĠĠĠĠĠĠĠ Ġencode Defaults Ġ= Ġtrue ĊĠĠĠĠĠĠĠĠĠĠĠ Ġexplicit Null s Ġ= Ġfalse ĊĠĠĠĠĠĠĠĠĠĠĠ Ġis Len ient Ġ= Ġtrue ĊĠĠĠĠĠĠĠĠĠĠĠ Ġallow Special Floating Point Values Ġ= Ġtrue ĊĠĠĠĠĠĠĠĠĠĠĠ Ġallow Structured Map Keys Ġ= Ġtrue ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpretty Print Ġ= Ġfalse ĊĠĠĠĠĠĠĠĠĠĠĠ Ġuse Array Pol ym orphism Ġ= Ġtrue ĊĠĠĠĠĠĠĠ Ġ}) ĊĠĠĠ Ġ} ĊĠĠĠ Ġinstall ( C ORS ) Ġ{ ĊĠĠĠĠĠĠĠ Ġhost (" twig s . w br awner . com ", ĠlistOf (" http ", Ġ" https ")) Ġ// ĠTODO : ĠMake Ġconfigurable ĊĠĠĠĠĠĠĠ Ġmethod ( HttpMethod . Options ) ĊĠĠĠĠĠĠĠ Ġmethod ( HttpMethod . Put ) ĊĠĠĠĠĠĠĠ Ġmethod ( HttpMethod . Delete ) ĊĠĠĠĠĠĠĠ Ġheader ( HttpHeaders . Authorization ) ĊĠĠĠĠĠĠĠ Ġheader ( HttpHeaders . Accept ) ĊĠĠĠĠĠĠĠ Ġheader ( HttpHeaders . Accept Encoding ) ĊĠĠĠĠĠĠĠ Ġheader ( HttpHeaders . Accept Language ) ĊĠĠĠĠĠĠĠ Ġheader ( HttpHeaders . Connection ) ĊĠĠĠĠĠĠĠ Ġheader ( HttpHeaders . ContentType ) ĊĠĠĠĠĠĠĠ Ġheader ( HttpHeaders . Host ) ĊĠĠĠĠĠĠĠ Ġheader ( HttpHeaders . Origin ) ĊĠĠĠĠĠĠĠ Ġheader ( HttpHeaders . AccessControl Request Headers ) ĊĠĠĠĠĠĠĠ Ġheader ( HttpHeaders . AccessControl Request Method ) ĊĠĠĠĠĠĠĠ Ġheader (" Sec - Fetch - Dest ") ĊĠĠĠĠĠĠĠ Ġheader (" Sec - Fetch - Mode ") ĊĠĠĠĠĠĠĠ Ġheader (" Sec - Fetch - Site ") ĊĠĠĠĠĠĠĠ Ġheader (" sec - ch - ua ") ĊĠĠĠĠĠĠĠ Ġheader (" sec - ch - ua - mobile ") ĊĠĠĠĠĠĠĠ Ġheader (" sec - ch - ua - platform ") ĊĠĠĠĠĠĠĠ Ġheader ( HttpHeaders . UserAgent ) ĊĠĠĠĠĠĠĠ Ġheader (" D NT ") ĊĠĠĠĠĠĠĠ Ġallow Credentials Ġ= Ġtrue ĊĠĠĠ Ġ} ĊĠĠĠ Ġbudget Routes ( budget Repository , Ġpermission Repository ) ĊĠĠĠ Ġcategory Routes ( category Repository , Ġpermission Repository ) ĊĠĠĠ Ġrecur ring Transaction Routes ( recurring Transaction Repository , Ġpermission Repository ) ĊĠĠĠ Ġtransaction Routes ( transaction Repository , Ġpermission Repository ) ĊĠĠĠ Ġuser Routes ( email Service , Ġpassword Reset Repository , Ġpermission Repository , Ġsession Repository , Ġuser Repository ) ĊĠĠĠ Ġweb Routes () ĊĠĠĠ Ġlaunch Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġmetadata Ġ= Ġ( metadata Repository . findAll (). first OrNull () Ġ?: ĠDatabase Metadata ()) ĊĠĠĠĠĠĠĠ Ġvar Ġversion Ġ= Ġmetadata . version ĊĠĠĠĠĠĠĠ Ġwhile Ġ( current Coroutine Context (). isActive Ġ&& Ġversion ++ Ġ< ĠDATABASE _ VERSION ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmetadata Repository . run Migration ( version ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmetadata Repository . save ( metadata . copy ( version Ġ= Ġversion )) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġsalt Ġ= Ġmetadata . salt . if Empty Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmetadata Repository . save ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġmetadata . copy ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġsalt Ġ= Ġenvironment . config ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. property OrNull (" twig s . password . salt ") ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ? . getString () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ?: Ġrandom String ( 1 6 ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ). salt ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġval Ġjobs Ġ= ĠlistOf ( ĊĠĠĠĠĠĠĠĠĠĠĠ ĠSession Cleanup Job ( session Repository ), ĊĠĠĠĠĠĠĠĠĠĠĠ ĠRe cur ring Transaction Processing Job ( recurring Transaction Repository , Ġtransaction Repository ) ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġwhile Ġ( current Coroutine Context (). isActive ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġjobs . forEach Ġ{ Ġit . run () Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdelay ( TimeUnit . HOUR S . to Millis ( 1 )) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} Ċ } Ċ Ċ interface ĠJob Ġ{ ĊĠĠĠ Ġsuspend Ġfun Ġrun () Ċ }