···1616type device =
1717 { last_ip: string
1818 ; last_user_agent: string option [@default None]
1919- ; last_refreshed_at: int
1919+ ; last_refreshed_at: string
2020 ; is_current: bool }
2121[@@deriving json]
2222···6161 else "Unknown Browser"
6262 else "Unknown Browser"
6363 in
6464- os ^ " · " ^ browser
6565-6666-let format_date timestamp_ms =
6767- let date = Js.Date.fromFloat (float_of_int timestamp_ms) in
6868- let month = Js.Date.getMonth date |> int_of_float in
6969- let day = Js.Date.getDate date |> int_of_float in
7070- let year = Js.Date.getFullYear date |> int_of_float in
7171- Printf.sprintf "%d/%d/%d" year (month + 1) day
6464+ os ^ " \u{B7} " ^ browser
72657366let[@react.component] make
7467 ~props:
···180173 </div>
181174 <span className="text-sm text-mist-80">
182175 (string
183183- ( format_date device.last_refreshed_at
176176+ ( device.last_refreshed_at
184177 ^ " from " ^ device.last_ip ) )
185178 </span>
186179 </li> )
+8-2
pegasus/lib/api/account_/permissions.ml
···11+let format_date timestamp_ms =
22+ let ts = float_of_int timestamp_ms /. 1000.0 in
33+ let dt = Timedesc.of_timestamp_float_s_exn ts in
44+ Format.asprintf "%a" (Timedesc.pp ~format:"{year}/{mon:0X}/{day:0X}" ()) dt
55+16let get_client_host client_id =
27 let uri = Uri.of_string client_id in
38 Uri.host uri |> Option.value ~default:client_id
···4752 let%lwt device_rows =
4853 Oauth.Queries.get_distinct_devices_by_did ctx.db did
4954 in
5050- let current_ip = Dream.client ctx.req in
5555+ let current_ip = Util.request_ip ctx.req in
5156 let current_ua = Dream.header ctx.req "User-Agent" in
5257 let devices =
5358 List.map
5454- (fun (last_ip, last_user_agent, last_refreshed_at) ->
5959+ (fun (last_ip, last_user_agent, last_refreshed_ms) ->
5560 let is_current =
5661 last_ip = current_ip && last_user_agent = current_ua
5762 in
6363+ let last_refreshed_at = format_date last_refreshed_ms in
5864 ( {last_ip; last_user_agent; last_refreshed_at; is_current}
5965 : Frontend.AccountPermissionsPage.device ) )
6066 device_rows
+1-1
pegasus/lib/api/oauth_/token.ml
···44 Xrpc.handler ~auth:DPoP (fun ctx ->
55 let%lwt req = Xrpc.parse_body ctx.req Types.token_request_of_yojson in
66 let proof = Auth.get_dpop_proof_exn ctx.auth in
77- let ip = Dream.client ctx.req in
77+ let ip = Util.request_ip ctx.req in
88 let user_agent = Dream.header ctx.req "User-Agent" in
99 match req.grant_type with
1010 | "authorization_code" -> (
+1-1
pegasus/lib/api/server/createSession.ml
···2525 in
2626 let id = String.lowercase_ascii identifier in
2727 (* apply rate limits after parsing body so we can create key from identifier *)
2828- let key = id ^ "-" ^ Dream.client req in
2828+ let key = id ^ "-" ^ Util.request_ip req in
2929 let _ =
3030 Xrpc.consume_route_rate_limit ~name:"repo-write-hour"
3131 ~duration_ms:Util.day ~max_points:300 ~key ~consume_points
+6
pegasus/lib/util.ml
···388388 did_keys
389389 <> None
390390391391+let request_ip req =
392392+ Dream.header req "X-Forwarded-For"
393393+ |> Option.value ~default:(Dream.client req)
394394+ |> String.split_on_char ',' |> List.hd |> String.split_on_char ':' |> List.hd
395395+ |> String.trim
396396+391397let rec http_get ?(max_redirects = 5) ?headers uri =
392398 let%lwt ans = Cohttp_lwt_unix.Client.get ?headers uri in
393399 follow_redirect ~max_redirects uri ans