@@ -2,23 +2,24 @@ use crate::settings::GitHub;
22use crate :: settings:: WhoAmI ;
33use crate :: update:: update_github;
44use crate :: userid:: UserId ;
5+ use actix_cors:: Cors ;
56use actix_session:: CookieSession ;
67use actix_session:: Session ;
78use actix_web:: client:: Client ;
89use actix_web:: cookie:: SameSite ;
910use actix_web:: dev:: HttpServiceFactory ;
1011use actix_web:: http;
11- use actix_web:: middleware:: cors:: Cors ;
1212use actix_web:: web;
1313use actix_web:: Error ;
1414use actix_web:: HttpResponse ;
1515use actix_web:: Responder ;
1616use cis_client:: getby:: GetBy ;
1717use cis_client:: AsyncCisClientTrait ;
1818use cis_profile:: schema:: Profile ;
19- use future:: IntoFuture ;
2019use futures:: future;
20+ use futures:: future:: Either ;
2121use futures:: Future ;
22+ use futures:: IntoFuture ;
2223use oauth2:: basic:: BasicClient ;
2324use oauth2:: prelude:: * ;
2425use oauth2:: AuthUrl ;
@@ -31,12 +32,17 @@ use oauth2::Scope;
3132use oauth2:: TokenResponse ;
3233use oauth2:: TokenUrl ;
3334use std:: sync:: Arc ;
35+ use std:: sync:: RwLock ;
36+ use std:: time:: Duration ;
37+ use ttl_cache:: TtlCache ;
3438use url:: Url ;
3539
3640const AUTH_URL : & str = "https://github.com/login/oauth/authorize" ;
3741const TOKEN_URL : & str = "https://github.com/login/oauth/access_token" ;
3842const USER_URL : & str = "https://api.github.com/user" ;
3943
44+ const CACHE_DURATION : Duration = Duration :: from_secs ( 24 * 60 * 60 ) ;
45+
4046#[ derive( Deserialize ) ]
4147pub struct Auth {
4248 code : String ,
@@ -56,21 +62,40 @@ pub struct GitHubUser {
5662 node_id : String ,
5763}
5864
59- fn id_to_username ( id : web:: Path < String > ) -> impl Future < Item = HttpResponse , Error = Error > {
60- Client :: default ( )
61- . get ( format ! ( "{}/{}" , USER_URL , id) )
62- . header ( http:: header:: USER_AGENT , "whoami" )
63- . send ( )
64- . map_err ( Into :: into)
65- . and_then ( |mut res| {
66- info ! ( "status: {}" , res. status( ) ) ;
67- res. json :: < GitHubUser > ( ) . map_err ( Into :: into)
68- } )
69- . and_then ( |user| {
70- HttpResponse :: Ok ( ) . json ( GitHubUsername {
71- username : user. login ,
72- } )
73- } )
65+ fn id_to_username (
66+ id : web:: Path < String > ,
67+ cache : web:: Data < Arc < RwLock < TtlCache < String , String > > > > ,
68+ ) -> impl Future < Item = HttpResponse , Error = Error > {
69+ if let Some ( username) = cache. read ( ) . ok ( ) . and_then ( |c| c. get ( & * id) . cloned ( ) ) {
70+ info ! ( "serving {} → {} from cache" , & * id, & username) ;
71+ Either :: A ( Ok ( HttpResponse :: Ok ( ) . json ( GitHubUsername { username } ) ) . into_future ( ) )
72+ } else {
73+ let cache = Arc :: clone ( & * cache) ;
74+ let cache_id = ( * id) . clone ( ) ;
75+ Either :: B (
76+ Client :: default ( )
77+ . get ( format ! ( "{}/{}" , USER_URL , id) )
78+ . header ( http:: header:: USER_AGENT , "whoami" )
79+ . send ( )
80+ . map_err ( Into :: into)
81+ . and_then ( |mut res| {
82+ info ! ( "status: {}" , res. status( ) ) ;
83+ res. json :: < GitHubUser > ( ) . map_err ( Into :: into)
84+ } )
85+ . map ( move |user| {
86+ if let Ok ( mut c) = cache. write ( ) {
87+ info ! ( "caching {} → {}" , & cache_id, & user. login) ;
88+ c. insert ( cache_id, user. login . clone ( ) , CACHE_DURATION ) ;
89+ }
90+ user
91+ } )
92+ . and_then ( |user| {
93+ HttpResponse :: Ok ( ) . json ( GitHubUsername {
94+ username : user. login ,
95+ } )
96+ } ) ,
97+ )
98+ }
7499}
75100
76101fn redirect ( client : web:: Data < Arc < BasicClient > > , session : Session ) -> impl Responder {
@@ -172,6 +197,7 @@ pub fn github_app<T: AsyncCisClientTrait + 'static>(
172197 let github_client_secret = ClientSecret :: new ( github. client_secret . clone ( ) ) ;
173198 let auth_url = AuthUrl :: new ( Url :: parse ( AUTH_URL ) . expect ( "Invalid authorization endpoint URL" ) ) ;
174199 let token_url = TokenUrl :: new ( Url :: parse ( TOKEN_URL ) . expect ( "Invalid token endpoint URL" ) ) ;
200+ let ttl_cache = Arc :: new ( RwLock :: new ( TtlCache :: < String , String > :: new ( 2000 ) ) ) ;
175201
176202 let client = Arc :: new (
177203 BasicClient :: new (
@@ -207,6 +233,7 @@ pub fn github_app<T: AsyncCisClientTrait + 'static>(
207233 )
208234 . data ( client)
209235 . data ( cis_client)
236+ . data ( ttl_cache)
210237 . service ( web:: resource ( "/add" ) . route ( web:: get ( ) . to ( redirect) ) )
211238 . service ( web:: resource ( "/auth" ) . route ( web:: get ( ) . to_async ( auth :: < T > ) ) )
212239 . service ( web:: resource ( "/username/{id}" ) . route ( web:: get ( ) . to_async ( id_to_username) ) )
0 commit comments