objective categorical abstract machine language personal data server

Break up util file into file modules

futur.blue 09aff094 2c82eb97

verified
+1026 -1027
+12 -12
bin/main.ml
··· 2 2 open Dream 3 3 4 4 let () = 5 - Rate_limiter.Shared.register ~name:"repo-write-hour" ~duration_ms:Util.hour 5 + Rate_limiter.Shared.register ~name:"repo-write-hour" ~duration_ms:Util.Time.hour 6 6 ~points:5000 ; 7 - Rate_limiter.Shared.register ~name:"repo-write-day" ~duration_ms:Util.day 7 + Rate_limiter.Shared.register ~name:"repo-write-day" ~duration_ms:Util.Time.day 8 8 ~points:35000 9 9 10 10 let handlers = ··· 35 35 ; (post, "/account/security", Api.Account_.Security.Index.post_handler) 36 36 ; ( get 37 37 , "/account/security/backup-codes" 38 - , Api.Account_.Security.Backup_codes.count_handler ) 38 + , Api.Account_.Security.Security_backup_codes.count_handler ) 39 39 ; ( post 40 40 , "/account/security/backup-codes/regenerate" 41 - , Api.Account_.Security.Backup_codes.regenerate_handler ) 41 + , Api.Account_.Security.Security_backup_codes.regenerate_handler ) 42 42 ; ( get 43 43 , "/account/security/totp/setup" 44 - , Api.Account_.Security.Totp.setup_handler ) 44 + , Api.Account_.Security.Security_totp.setup_handler ) 45 45 ; ( post 46 46 , "/account/security/totp/verify" 47 - , Api.Account_.Security.Totp.verify_handler ) 47 + , Api.Account_.Security.Security_totp.verify_handler ) 48 48 ; ( post 49 49 , "/account/security/totp/disable" 50 - , Api.Account_.Security.Totp.disable_handler ) 50 + , Api.Account_.Security.Security_totp.disable_handler ) 51 51 ; ( get 52 52 , "/account/security/keys" 53 - , Api.Account_.Security.Security_key.list_handler ) 53 + , Api.Account_.Security.Security_keys.list_handler ) 54 54 ; ( post 55 55 , "/account/security/keys/setup" 56 - , Api.Account_.Security.Security_key.setup_handler ) 56 + , Api.Account_.Security.Security_keys.setup_handler ) 57 57 ; ( post 58 58 , "/account/security/keys/:id/verify" 59 - , Api.Account_.Security.Security_key.verify_handler ) 59 + , Api.Account_.Security.Security_keys.verify_handler ) 60 60 ; ( post 61 61 , "/account/security/keys/:id/resync" 62 - , Api.Account_.Security.Security_key.resync_handler ) 62 + , Api.Account_.Security.Security_keys.resync_handler ) 63 63 ; ( delete 64 64 , "/account/security/keys/:id" 65 - , Api.Account_.Security.Security_key.delete_handler ) 65 + , Api.Account_.Security.Security_keys.delete_handler ) 66 66 ; (get, "/account/permissions", Api.Account_.Permissions.get_handler) 67 67 ; (post, "/account/permissions", Api.Account_.Permissions.post_handler) 68 68 ; (get, "/account/identity", Api.Account_.Identity.get_handler)
+10 -10
pegasus/bench/bench_repository.ml
··· 50 50 51 51 let setup_test_db () : (User_store.t * string) Lwt.t = 52 52 let path, uri = create_temp_db () in 53 - let%lwt pool = Util.connect_sqlite ~create:true ~write:true uri in 53 + let%lwt pool = Util.Sqlite.connect ~create:true ~write:true uri in 54 54 let%lwt () = Migrations.run_migrations User_store pool in 55 55 let db : User_store.t = {did= "did:plc:bench"; db= pool} in 56 56 Lwt.return (db, path) ··· 175 175 let blocks = generate_blocks size in 176 176 let%lwt r1 = 177 177 time_it (Printf.sprintf "Bulk.put_blocks (%d blocks)" size) (fun () -> 178 - Util.use_pool db.db (fun conn -> User_store.Bulk.put_blocks blocks conn) 178 + Util.Sqlite.use_pool db.db (fun conn -> User_store.Bulk.put_blocks blocks conn) 179 179 >|= fun _ -> () ) 180 180 in 181 181 print_result r1 ; ··· 184 184 let records = generate_record_data size in 185 185 let%lwt r2 = 186 186 time_it (Printf.sprintf "Bulk.put_records (%d records)" size) (fun () -> 187 - Util.use_pool db2.db (fun conn -> User_store.Bulk.put_records records conn) 187 + Util.Sqlite.use_pool db2.db (fun conn -> User_store.Bulk.put_records records conn) 188 188 >|= fun _ -> () ) 189 189 in 190 190 print_result r2 ; ··· 200 200 let records = generate_record_data size in 201 201 let kv_pairs = List.map (fun (path, cid, _, _) -> (path, cid)) records in 202 202 let%lwt () = 203 - Util.use_pool db.db (fun conn -> User_store.Bulk.put_records records conn) 203 + Util.Sqlite.use_pool db.db (fun conn -> User_store.Bulk.put_records records conn) 204 204 >|= fun _ -> () 205 205 in 206 206 let%lwt r1 = ··· 231 231 let initial_records = generate_record_data initial_size in 232 232 let initial_kv = List.map (fun (path, cid, _, _) -> (path, cid)) initial_records in 233 233 let%lwt () = 234 - Util.use_pool db.db (fun conn -> User_store.Bulk.put_records initial_records conn) 234 + Util.Sqlite.use_pool db.db (fun conn -> User_store.Bulk.put_records initial_records conn) 235 235 >|= fun _ -> () 236 236 in 237 237 let%lwt mst = Mst.of_assoc db initial_kv in 238 238 let add_records = generate_record_data add_count in 239 239 let add_kv = List.map (fun (path, cid, _, _) -> (path, cid)) add_records in 240 240 let%lwt () = 241 - Util.use_pool db.db (fun conn -> User_store.Bulk.put_records add_records conn) 241 + Util.Sqlite.use_pool db.db (fun conn -> User_store.Bulk.put_records add_records conn) 242 242 >|= fun _ -> () 243 243 in 244 244 let%lwt r1 = ··· 332 332 let%lwt db, path = setup_test_db () in 333 333 let records = generate_record_data size in 334 334 let%lwt () = 335 - Util.use_pool db.db (fun conn -> User_store.Bulk.put_records records conn) 335 + Util.Sqlite.use_pool db.db (fun conn -> User_store.Bulk.put_records records conn) 336 336 >|= fun _ -> () 337 337 in 338 338 let%lwt r1 = ··· 352 352 let num_ops = 500 in 353 353 let initial_records = generate_record_data initial_size in 354 354 let%lwt () = 355 - Util.use_pool db.db (fun conn -> User_store.Bulk.put_records initial_records conn) 355 + Util.Sqlite.use_pool db.db (fun conn -> User_store.Bulk.put_records initial_records conn) 356 356 >|= fun _ -> () 357 357 in 358 358 let initial_kv = List.map (fun (path, cid, _, _) -> (path, cid)) initial_records in 359 359 let%lwt mst = Mst.of_assoc db initial_kv in 360 360 let extra_records = generate_record_data num_ops in 361 361 let%lwt () = 362 - Util.use_pool db.db (fun conn -> User_store.Bulk.put_records extra_records conn) 362 + Util.Sqlite.use_pool db.db (fun conn -> User_store.Bulk.put_records extra_records conn) 363 363 >|= fun _ -> () 364 364 in 365 365 let existing = ref (shuffle initial_records) in ··· 409 409 let size = 20000 in 410 410 let blocks = generate_blocks size in 411 411 let%lwt () = 412 - Util.use_pool db.db (fun conn -> User_store.Bulk.put_blocks blocks conn) >|= fun _ -> () 412 + Util.Sqlite.use_pool db.db (fun conn -> User_store.Bulk.put_blocks blocks conn) >|= fun _ -> () 413 413 in 414 414 let cids = List.map fst blocks in 415 415 let shuffled_cids = shuffle cids in
+1 -1
pegasus/lib/api/account_/identity.ml
··· 31 31 Lwt.return_none 32 32 else Lwt.return_none 33 33 in 34 - Util.render_html ~title:"Identity" 34 + Util.Html.render_page ~title:"Identity" 35 35 (module Frontend.AccountIdentityPage) 36 36 ~props: 37 37 { current_user
+8 -8
pegasus/lib/api/account_/index.ml
··· 1 1 let has_valid_delete_code (actor : Data_store.Types.actor) = 2 2 match (actor.auth_code, actor.auth_code_expires_at) with 3 3 | Some code, Some expires_at -> 4 - String.starts_with ~prefix:"del-" code && expires_at > Util.now_ms () 4 + String.starts_with ~prefix:"del-" code && expires_at > Util.Time.now_ms () 5 5 | _ -> 6 6 false 7 7 8 8 let has_valid_email_change_code (actor : Data_store.Types.actor) = 9 9 match (actor.auth_code, actor.auth_code_expires_at, actor.pending_email) with 10 10 | Some _, Some expires_at, Some _ -> 11 - expires_at > Util.now_ms () 11 + expires_at > Util.Time.now_ms () 12 12 | _ -> 13 13 false 14 14 15 15 let has_valid_email_confirmation_code (actor : Data_store.Types.actor) = 16 16 match (actor.auth_code, actor.auth_code_expires_at, actor.pending_email) with 17 17 | Some _, Some expires_at, None -> 18 - expires_at > Util.now_ms () 18 + expires_at > Util.Time.now_ms () 19 19 | _ -> 20 20 false 21 21 ··· 47 47 let email_change_pending = has_valid_email_change_code actor in 48 48 let pending_email = actor.pending_email in 49 49 let delete_pending = has_valid_delete_code actor in 50 - Util.render_html ~title:"Account" 50 + Util.Html.render_page ~title:"Account" 51 51 (module Frontend.AccountPage) 52 52 ~props: 53 53 { current_user ··· 101 101 let email_change_pending = has_valid_email_change_code actor in 102 102 let pending_email = actor.pending_email in 103 103 let delete_pending = has_valid_delete_code actor in 104 - Util.render_html ~title:"Account" 104 + Util.Html.render_page ~title:"Account" 105 105 (module Frontend.AccountPage) 106 106 ~props: 107 107 { current_user= {current_user with handle= actor.handle} ··· 133 133 (* update handle if changed *) 134 134 let%lwt handle_result = 135 135 if new_handle <> actor.handle then 136 - Identity.UpdateHandle.update_handle ~did 136 + Identity_util.update_handle ~did 137 137 ~handle:new_handle ctx.db 138 138 else Lwt.return_ok () 139 139 in ··· 177 177 | Some code, Some expires_at 178 178 when String.starts_with ~prefix:"del-" code 179 179 && code = token 180 - && expires_at > Util.now_ms () -> 180 + && expires_at > Util.Time.now_ms () -> 181 181 let%lwt _ = 182 182 Server.DeleteAccount.delete_account ~did ctx.db 183 183 in ··· 222 222 match%lwt 223 223 match (actor.auth_code, actor.auth_code_expires_at) with 224 224 | Some code, Some expiry 225 - when Some code = token && expiry > Util.now_ms () -> 225 + when Some code = token && expiry > Util.Time.now_ms () -> 226 226 Server.UpdateEmail.update_email ~token actor ctx.db 227 227 | _ -> 228 228 Lwt.return_error Server.UpdateEmail.InvalidToken
+7 -7
pegasus/lib/api/account_/login.ml
··· 2 2 Xrpc.handler (fun ctx -> 3 3 let redirect_url = 4 4 if List.length @@ Dream.all_queries ctx.req > 0 then 5 - Uri.make ~path:"/oauth/authorize" ~query:(Util.copy_query ctx.req) () 5 + Uri.make ~path:"/oauth/authorize" ~query:(Util.Http.copy_query ctx.req) () 6 6 |> Uri.to_string 7 7 else "/account" 8 8 in 9 9 let csrf_token = Dream.csrf_token ctx.req in 10 - Util.render_html ~title:"Login" 10 + Util.Html.render_page ~title:"Login" 11 11 (module Frontend.LoginPage) 12 12 ~props: 13 13 { redirect_url ··· 69 69 with 70 70 | None -> 71 71 let error = "Session expired. Please try again." in 72 - Util.render_html ~status:`Unauthorized ~title:"Login" 72 + Util.Html.render_page ~status:`Unauthorized ~title:"Login" 73 73 (module Frontend.LoginPage) 74 74 ~props: 75 75 { redirect_url ··· 95 95 let%lwt methods = 96 96 Two_factor.get_available_methods ~did:pending.did ctx.db 97 97 in 98 - Util.render_html ~status:`Unauthorized ~title:"Login" 98 + Util.Html.render_page ~status:`Unauthorized ~title:"Login" 99 99 (module Frontend.LoginPage) 100 100 ~props: 101 101 { redirect_url ··· 115 115 let error = 116 116 "Invalid username or password. Please try again." 117 117 in 118 - Util.render_html ~status:`Unauthorized ~title:"Login" 118 + Util.Html.render_page ~status:`Unauthorized ~title:"Login" 119 119 (module Frontend.LoginPage) 120 120 ~props: 121 121 { redirect_url ··· 145 145 Lwt.return () 146 146 else Lwt.return () 147 147 in 148 - Util.render_html ~title:"Login" 148 + Util.Html.render_page ~title:"Login" 149 149 (module Frontend.LoginPage) 150 150 ~props: 151 151 { redirect_url ··· 160 160 | _ -> 161 161 let redirect_url = "/account" in 162 162 let error = "Something went wrong, go back and try again." in 163 - Util.render_html ~status:`Unauthorized ~title:"Login" 163 + Util.Html.render_page ~status:`Unauthorized ~title:"Login" 164 164 (module Frontend.LoginPage) 165 165 ~props: 166 166 { redirect_url
+25 -25
pegasus/lib/api/account_/migrate/migrate.ml
··· 36 36 let render_error ~csrf_token ~invite_required ~hostname 37 37 ?(step = "enter_credentials") ?did ?handle ?old_pds ?identifier ?invite_code 38 38 error = 39 - Util.render_html ~status:`Bad_Request ~title:"Migrate Account" 39 + Util.Html.render_page ~status:`Bad_Request ~title:"Migrate Account" 40 40 (module Frontend.MigratePage) 41 41 ~props: 42 42 (make_props ~csrf_token ~invite_required ~hostname ~step ?did ?handle ··· 95 95 log "migration %s: failed to deactivate old account: %s" did e ) ; 96 96 (false, Some e) 97 97 in 98 - Util.render_html ~title:"Migrate Account" 98 + Util.Html.render_page ~title:"Migrate Account" 99 99 (module Frontend.MigratePage) 100 100 ~props: 101 101 (make_props ~csrf_token ~invite_required ~hostname ~step:"complete" ··· 106 106 identity is pointing to this PDS." 107 107 () ) 108 108 | _ -> 109 - Util.render_html ~title:"Migrate Account" 109 + Util.Html.render_page ~title:"Migrate Account" 110 110 (module Frontend.MigratePage) 111 111 ~props: 112 112 (make_props ~csrf_token ~invite_required ~hostname ~step:"error" ··· 123 123 else 124 124 match session with 125 125 | None -> 126 - Util.render_html ~status:`Internal_Server_Error ~title:"Migrate Account" 126 + Util.Html.render_page ~status:`Internal_Server_Error ~title:"Migrate Account" 127 127 (module Frontend.MigratePage) 128 128 ~props: 129 129 (make_props ~csrf_token ~invite_required ~hostname ~step:"error" ··· 145 145 ; blobs_cursor= "" 146 146 ; plc_requested= true } 147 147 in 148 - Util.render_html ~title:"Migrate Account" 148 + Util.Html.render_page ~title:"Migrate Account" 149 149 (module Frontend.MigratePage) 150 150 ~props: 151 151 (make_props ~csrf_token ~invite_required ~hostname ··· 172 172 ; blobs_cursor= "" 173 173 ; plc_requested= true } 174 174 in 175 - Util.render_html ~title:"Migrate Account" 175 + Util.Html.render_page ~title:"Migrate Account" 176 176 (module Frontend.MigratePage) 177 177 ~props: 178 178 (make_props ~csrf_token ~invite_required ~hostname ··· 243 243 in 244 244 match step with 245 245 | "resume_available" -> 246 - Util.render_html ~title:"Migrate Account" 246 + Util.Html.render_page ~title:"Migrate Account" 247 247 (module Frontend.MigratePage) 248 248 ~props: 249 249 (make_props ~csrf_token ~invite_required ~hostname ··· 262 262 ; blobs_cursor= "" 263 263 ; plc_requested= false } 264 264 in 265 - Util.render_html ~title:"Migrate Account" 265 + Util.Html.render_page ~title:"Migrate Account" 266 266 (module Frontend.MigratePage) 267 267 ~props: 268 268 (make_props ~csrf_token ~invite_required ~hostname ··· 282 282 ; blobs_cursor= "" 283 283 ; plc_requested= true } 284 284 in 285 - Util.render_html ~title:"Migrate Account" 285 + Util.Html.render_page ~title:"Migrate Account" 286 286 (module Frontend.MigratePage) 287 287 ~props: 288 288 (make_props ~csrf_token ~invite_required ~hostname ··· 294 294 code." 295 295 () ) 296 296 | "complete" -> 297 - Util.render_html ~title:"Migrate Account" 297 + Util.Html.render_page ~title:"Migrate Account" 298 298 (module Frontend.MigratePage) 299 299 ~props: 300 300 (make_props ~csrf_token ~invite_required ~hostname ~step:"complete" ··· 302 302 ~blobs_failed:0 ~old_account_deactivated:true 303 303 ~message:"Your account has been successfully migrated!" () ) 304 304 | "complete_deactivation_failed" -> 305 - Util.render_html ~title:"Migrate Account" 305 + Util.Html.render_page ~title:"Migrate Account" 306 306 (module Frontend.MigratePage) 307 307 ~props: 308 308 (make_props ~csrf_token ~invite_required ~hostname ~step:"complete" ··· 312 312 "Failed to deactivate old account (401): Unauthorized" 313 313 ~message:"Your account has been successfully migrated!" () ) 314 314 | "error" | _ -> 315 - Util.render_html ~title:"Migrate Account" 315 + Util.Html.render_page ~title:"Migrate Account" 316 316 (module Frontend.MigratePage) 317 317 ~props: 318 318 (make_props ~csrf_token ~invite_required ~hostname ~step:"error" ()) ··· 329 329 | Remote.AuthError e -> 330 330 render_err e 331 331 | Remote.AuthNeeds2FA -> 332 - Util.render_html ~title:"Migrate Account" 332 + Util.Html.render_page ~title:"Migrate Account" 333 333 (module Frontend.MigratePage) 334 334 ~props: 335 335 (make_props ~csrf_token ~invite_required ~hostname ~step:"enter_2fa" ··· 380 380 render_err ~did ~handle ~old_pds e 381 381 | Ok State.AlreadyActive -> 382 382 let%lwt () = Session.log_in_did ctx.req did in 383 - Util.render_html ~title:"Migrate Account" 383 + Util.Html.render_page ~title:"Migrate Account" 384 384 (module Frontend.MigratePage) 385 385 ~props: 386 386 (make_props ~csrf_token ~invite_required ~hostname ~step:"complete" ··· 400 400 log "migration %s: failed to deactivate old account: %s" did err ) ; 401 401 (false, Some err) 402 402 in 403 - Util.render_html ~title:"Migrate Account" 403 + Util.Html.render_page ~title:"Migrate Account" 404 404 (module Frontend.MigratePage) 405 405 ~props: 406 406 (make_props ~csrf_token ~invite_required ~hostname ~step:"complete" ··· 484 484 ; blobs_cursor= cursor 485 485 ; plc_requested= false } 486 486 in 487 - Util.render_html ~title:"Migrate Account" 487 + Util.Html.render_page ~title:"Migrate Account" 488 488 (module Frontend.MigratePage) 489 489 ~props: 490 490 (make_props ~csrf_token ~invite_required ~hostname ··· 543 543 ; blobs_failed= new_failed 544 544 ; blobs_cursor= new_cursor } 545 545 in 546 - Util.render_html ~title:"Migrate Account" 546 + Util.Html.render_page ~title:"Migrate Account" 547 547 (module Frontend.MigratePage) 548 548 ~props: 549 549 (make_props ~csrf_token ~invite_required ~hostname ··· 662 662 state.did e ) ; 663 663 (false, Some e) 664 664 in 665 - Util.render_html ~title:"Migrate Account" 665 + Util.Html.render_page ~title:"Migrate Account" 666 666 (module Frontend.MigratePage) 667 667 ~props: 668 668 (make_props ~csrf_token ~invite_required ~hostname ··· 689 689 | Ok old_client -> ( 690 690 match%lwt Remote.request_plc_signature old_client with 691 691 | Error e -> 692 - Util.render_html ~title:"Migrate Account" 692 + Util.Html.render_page ~title:"Migrate Account" 693 693 (module Frontend.MigratePage) 694 694 ~props: 695 695 (make_props ~csrf_token ~invite_required ~hostname 696 696 ~step:"enter_plc_token" ~did:state.did ~handle:state.handle 697 697 ~old_pds:state.old_pds ~error:("Failed to resend: " ^ e) () ) 698 698 | Ok () -> 699 - Util.render_html ~title:"Migrate Account" 699 + Util.Html.render_page ~title:"Migrate Account" 700 700 (module Frontend.MigratePage) 701 701 ~props: 702 702 (make_props ~csrf_token ~invite_required ~hostname ··· 811 811 | Remote.AuthError e -> 812 812 render_err ~step:"resume_available" e 813 813 | Remote.AuthNeeds2FA -> 814 - Util.render_html ~title:"Migrate Account" 814 + Util.Html.render_page ~title:"Migrate Account" 815 815 (module Frontend.MigratePage) 816 816 ~props: 817 817 (make_props ~csrf_token ~invite_required ~hostname ··· 834 834 render_err ~step:"resume_available" ~did ~handle ~old_pds e 835 835 | Ok State.AlreadyActive -> 836 836 let%lwt () = Session.log_in_did ctx.req did in 837 - Util.render_html ~title:"Migrate Account" 837 + Util.Html.render_page ~title:"Migrate Account" 838 838 (module Frontend.MigratePage) 839 839 ~props: 840 840 (make_props ~csrf_token ~invite_required ~hostname ··· 860 860 did e ) ; 861 861 (false, Some e) 862 862 in 863 - Util.render_html ~title:"Migrate Account" 863 + Util.Html.render_page ~title:"Migrate Account" 864 864 (module Frontend.MigratePage) 865 865 ~props: 866 866 (make_props ~csrf_token ~invite_required ~hostname ··· 919 919 ; blobs_cursor= cursor 920 920 ; plc_requested= false } 921 921 in 922 - Util.render_html ~title:"Migrate Account" 922 + Util.Html.render_page ~title:"Migrate Account" 923 923 (module Frontend.MigratePage) 924 924 ~props: 925 925 (make_props ~csrf_token ~invite_required ~hostname ··· 949 949 ~old_pds:state.old_pds ~blobs_imported:state.blobs_imported 950 950 ~blobs_failed:state.blobs_failed () 951 951 in 952 - Util.render_html ~title:"Migrate Account" 952 + Util.Html.render_page ~title:"Migrate Account" 953 953 (module Frontend.MigratePage) 954 954 ~props ) 955 955
+8 -8
pegasus/lib/api/account_/password_reset.ml
··· 4 4 let step = 5 5 Dream.query ctx.req "step" |> Option.value ~default:"request" 6 6 in 7 - Util.render_html ~title:"Reset Password" 7 + Util.Html.render_page ~title:"Reset Password" 8 8 (module Frontend.PasswordResetPage) 9 9 ~props:{csrf_token; step; email_sent_to= None; error= None} ) 10 10 ··· 25 25 List.assoc_opt "password" fields |> Option.value ~default:"" 26 26 in 27 27 if String.length token = 0 then 28 - Util.render_html ~status:`Bad_Request ~title:"Reset Password" 28 + Util.Html.render_page ~status:`Bad_Request ~title:"Reset Password" 29 29 (module Frontend.PasswordResetPage) 30 30 ~props: 31 31 { csrf_token ··· 33 33 ; email_sent_to= None 34 34 ; error= Some "Please enter the reset code." } 35 35 else if String.length password < 8 then 36 - Util.render_html ~status:`Bad_Request ~title:"Reset Password" 36 + Util.Html.render_page ~status:`Bad_Request ~title:"Reset Password" 37 37 (module Frontend.PasswordResetPage) 38 38 ~props: 39 39 { csrf_token ··· 45 45 Server.ResetPassword.reset_password ~token ~password ctx.db 46 46 with 47 47 | Ok _ -> 48 - Util.render_html ~title:"Reset Password" 48 + Util.Html.render_page ~title:"Reset Password" 49 49 (module Frontend.PasswordResetPage) 50 50 ~props: 51 51 { csrf_token ··· 54 54 ; error= None } 55 55 | Error Server.ResetPassword.InvalidToken 56 56 | Error Server.ResetPassword.ExpiredToken -> 57 - Util.render_html ~status:`Bad_Request ~title:"Reset Password" 57 + Util.Html.render_page ~status:`Bad_Request ~title:"Reset Password" 58 58 (module Frontend.PasswordResetPage) 59 59 ~props: 60 60 { csrf_token ··· 71 71 |> String.lowercase_ascii 72 72 in 73 73 if String.length email = 0 then 74 - Util.render_html ~status:`Bad_Request ~title:"Reset Password" 74 + Util.Html.render_page ~status:`Bad_Request ~title:"Reset Password" 75 75 (module Frontend.PasswordResetPage) 76 76 ~props: 77 77 { csrf_token ··· 87 87 | None -> 88 88 Lwt.return_unit 89 89 in 90 - Util.render_html ~title:"Reset Password" 90 + Util.Html.render_page ~title:"Reset Password" 91 91 (module Frontend.PasswordResetPage) 92 92 ~props: 93 93 { csrf_token ··· 95 95 ; email_sent_to= Some email 96 96 ; error= None } ) 97 97 | _ -> 98 - Util.render_html ~status:`Bad_Request ~title:"Reset Password" 98 + Util.Html.render_page ~status:`Bad_Request ~title:"Reset Password" 99 99 (module Frontend.PasswordResetPage) 100 100 ~props: 101 101 { csrf_token
+1 -1
pegasus/lib/api/account_/permissions.ml
··· 68 68 : Frontend.AccountPermissionsPage.device ) ) 69 69 device_rows 70 70 in 71 - Util.render_html ~title:"Permissions" 71 + Util.Html.render_page ~title:"Permissions" 72 72 (module Frontend.AccountPermissionsPage) 73 73 ~props: 74 74 { current_user
pegasus/lib/api/account_/security/backup_codes.ml pegasus/lib/api/account_/security/security_backup_codes.ml
+1 -1
pegasus/lib/api/account_/security/index.ml
··· 53 53 let%lwt two_fa_status = Two_factor.get_status ~did ctx.db in 54 54 let error = Dream.query ctx.req "error" in 55 55 let success = Dream.query ctx.req "success" in 56 - Util.render_html ~title:"Security" 56 + Util.Html.render_page ~title:"Security" 57 57 (module Frontend.AccountSecurityPage) 58 58 ~props: 59 59 { current_user
pegasus/lib/api/account_/security/security_key.ml pegasus/lib/api/account_/security/security_keys.ml
pegasus/lib/api/account_/security/totp.ml pegasus/lib/api/account_/security/security_totp.ml
+11 -11
pegasus/lib/api/account_/signup.ml
··· 17 17 if String.contains handle_input '.' then handle_input 18 18 else handle_input ^ hostname_suffix 19 19 in 20 - let validation_result = Util.validate_handle handle in 20 + let validation_result = Identity_util.validate_handle handle in 21 21 match validation_result with 22 22 | Error (InvalidFormat e) | Error (TooLong e) | Error (TooShort e) -> 23 23 Dream.json @@ Yojson.Safe.to_string ··· 44 44 let csrf_token = Dream.csrf_token ctx.req in 45 45 let invite_required = Env.invite_required in 46 46 let hostname = Env.hostname in 47 - Util.render_html ~title:"Sign Up" 47 + Util.Html.render_page ~title:"Sign Up" 48 48 (module Frontend.SignupPage) 49 49 ~props:{csrf_token; invite_required; hostname; error= None} ) 50 50 ··· 85 85 ?invite_code ctx.db 86 86 with 87 87 | Error Server.CreateAccount.InviteCodeRequired -> 88 - Util.render_html ~status:`Bad_Request ~title:"Sign Up" 88 + Util.Html.render_page ~status:`Bad_Request ~title:"Sign Up" 89 89 (module Frontend.SignupPage) 90 90 ~props: 91 91 { props with 92 92 error= Some "An invite code is required to sign up." } 93 93 | Error Server.CreateAccount.InvalidInviteCode -> 94 - Util.render_html ~status:`Bad_Request ~title:"Sign Up" 94 + Util.Html.render_page ~status:`Bad_Request ~title:"Sign Up" 95 95 (module Frontend.SignupPage) 96 96 ~props:{props with error= Some "Invalid invite code."} 97 97 | Error (Server.CreateAccount.InvalidHandle e) -> 98 - Util.render_html ~status:`Bad_Request ~title:"Sign Up" 98 + Util.Html.render_page ~status:`Bad_Request ~title:"Sign Up" 99 99 (module Frontend.SignupPage) 100 100 ~props:{props with error= Some e} 101 101 | Error Server.CreateAccount.EmailAlreadyExists -> 102 - Util.render_html ~status:`Bad_Request ~title:"Sign Up" 102 + Util.Html.render_page ~status:`Bad_Request ~title:"Sign Up" 103 103 (module Frontend.SignupPage) 104 104 ~props: 105 105 { props with 106 106 error= Some "An account with that email already exists." } 107 107 | Error Server.CreateAccount.HandleAlreadyExists -> 108 - Util.render_html ~status:`Bad_Request ~title:"Sign Up" 108 + Util.Html.render_page ~status:`Bad_Request ~title:"Sign Up" 109 109 (module Frontend.SignupPage) 110 110 ~props: 111 111 { props with 112 112 error= Some "An account with that handle already exists." } 113 113 | Error Server.CreateAccount.DidAlreadyExists -> 114 - Util.render_html ~status:`Bad_Request ~title:"Sign Up" 114 + Util.Html.render_page ~status:`Bad_Request ~title:"Sign Up" 115 115 (module Frontend.SignupPage) 116 116 ~props: 117 117 { props with 118 118 error= Some "An account with that DID already exists." } 119 119 | Error (Server.CreateAccount.PlcError _) -> 120 - Util.render_html ~status:`Internal_Server_Error ~title:"Sign Up" 120 + Util.Html.render_page ~status:`Internal_Server_Error ~title:"Sign Up" 121 121 (module Frontend.SignupPage) 122 122 ~props: 123 123 { props with ··· 126 126 "Failed to create your identity. Please try again \ 127 127 later." } 128 128 | Error Server.CreateAccount.InviteUseFailure -> 129 - Util.render_html ~status:`Internal_Server_Error ~title:"Sign Up" 129 + Util.Html.render_page ~status:`Internal_Server_Error ~title:"Sign Up" 130 130 (module Frontend.SignupPage) 131 131 ~props: 132 132 { props with ··· 137 137 let%lwt () = Session.log_in_did ctx.req did in 138 138 Dream.redirect ctx.req "/account" ) 139 139 | _ -> 140 - Util.render_html ~status:`Bad_Request ~title:"Sign Up" 140 + Util.Html.render_page ~status:`Bad_Request ~title:"Sign Up" 141 141 (module Frontend.SignupPage) 142 142 ~props: 143 143 { props with
+3 -3
pegasus/lib/api/admin/getAccountInfo.ml
··· 4 4 { did= actor.did 5 5 ; handle= actor.handle 6 6 ; email= Some actor.email 7 - ; email_confirmed_at= Option.map Util.ms_to_iso8601 actor.email_confirmed_at 8 - ; indexed_at= Util.ms_to_iso8601 actor.created_at 9 - ; deactivated_at= Option.map Util.ms_to_iso8601 actor.deactivated_at 7 + ; email_confirmed_at= Option.map Util.Time.ms_to_iso8601 actor.email_confirmed_at 8 + ; indexed_at= Util.Time.ms_to_iso8601 actor.created_at 9 + ; deactivated_at= Option.map Util.Time.ms_to_iso8601 actor.deactivated_at 10 10 ; related_records= None 11 11 ; invited_by= None 12 12 ; invites= None
+2 -2
pegasus/lib/api/admin/updateAccountHandle.ml
··· 7 7 | None -> 8 8 Errors.invalid_request "account not found" 9 9 | Some _ -> ( 10 - match%lwt Identity.UpdateHandle.update_handle ~did ~handle db with 10 + match%lwt Identity_util.update_handle ~did ~handle db with 11 11 | Ok () -> 12 12 Dream.empty `OK 13 13 | Error e -> 14 14 Errors.invalid_request ~name:"InvalidHandle" 15 - (Identity.UpdateHandle.update_handle_error_to_string e) ) ) 15 + (Identity_util.update_handle_error_to_string e) ) )
+2 -2
pegasus/lib/api/admin_/blobs.ml
··· 114 114 let%lwt blobs, next_cursor = list_all_blobs ~limit ~cursor ctx.db in 115 115 let blobs = List.map blob_to_view blobs in 116 116 let csrf_token = Dream.csrf_token ctx.req in 117 - Util.render_html ~title:"Admin / Blobs" 117 + Util.Html.render_page ~title:"Admin / Blobs" 118 118 (module Frontend.AdminBlobsPage) 119 119 ~props: 120 120 { blobs ··· 172 172 let limit = 50 in 173 173 let%lwt blobs, next_cursor = list_all_blobs ~limit ~cursor ctx.db in 174 174 let blobs = List.map blob_to_view blobs in 175 - Util.render_html ~title:"Admin / Blobs" 175 + Util.Html.render_page ~title:"Admin / Blobs" 176 176 (module Frontend.AdminBlobsPage) 177 177 ~props:{blobs; csrf_token; cursor; next_cursor; error; success} 178 178 in
+2 -2
pegasus/lib/api/admin_/invites.ml
··· 11 11 let%lwt invites = Data_store.list_invites ~limit:100 ctx.db in 12 12 let invites = List.map invite_to_view invites in 13 13 let csrf_token = Dream.csrf_token ctx.req in 14 - Util.render_html ~title:"Admin / Invite Codes" 14 + Util.Html.render_page ~title:"Admin / Invite Codes" 15 15 (module Frontend.AdminInvitesPage) 16 16 ~props:{invites; csrf_token; error= None; success= None} ) 17 17 ··· 25 25 let render_page ?error ?success () = 26 26 let%lwt invites = Data_store.list_invites ~limit:100 ctx.db in 27 27 let invites = List.map invite_to_view invites in 28 - Util.render_html ~title:"Admin / Invite Codes" 28 + Util.Html.render_page ~title:"Admin / Invite Codes" 29 29 (module Frontend.AdminInvitesPage) 30 30 ~props:{invites; csrf_token; error; success} 31 31 in
+3 -3
pegasus/lib/api/admin_/login.ml
··· 5 5 Dream.redirect ctx.req "/admin/users" 6 6 | false -> 7 7 let csrf_token = Dream.csrf_token ctx.req in 8 - Util.render_html ~title:"Admin Login" 8 + Util.Html.render_page ~title:"Admin Login" 9 9 (module Frontend.AdminLoginPage) 10 10 ~props:{csrf_token; error= None} ) 11 11 ··· 21 21 let%lwt () = Session.set_admin_authenticated ctx.req true in 22 22 Dream.redirect ctx.req "/admin/users" 23 23 else 24 - Util.render_html ~status:`Unauthorized ~title:"Admin Login" 24 + Util.Html.render_page ~status:`Unauthorized ~title:"Admin Login" 25 25 (module Frontend.AdminLoginPage) 26 26 ~props:{csrf_token; error= Some "Invalid password."} 27 27 | _ -> 28 - Util.render_html ~status:`Unauthorized ~title:"Admin Login" 28 + Util.Html.render_page ~status:`Unauthorized ~title:"Admin Login" 29 29 (module Frontend.AdminLoginPage) 30 30 ~props:{csrf_token; error= Some "Invalid form submission."} )
+4 -4
pegasus/lib/api/admin_/users.ml
··· 49 49 let actors = List.map actor_to_view actors in 50 50 let csrf_token = Dream.csrf_token ctx.req in 51 51 let hostname = Env.hostname in 52 - Util.render_html ~title:"Admin / Users" 52 + Util.Html.render_page ~title:"Admin / Users" 53 53 (module Frontend.AdminUsersPage) 54 54 ~props: 55 55 { actors ··· 96 96 None 97 97 else None 98 98 in 99 - Util.render_html ~title:"Admin / Users" 99 + Util.Html.render_page ~title:"Admin / Users" 100 100 (module Frontend.AdminUsersPage) 101 101 ~props: 102 102 { actors ··· 137 137 if String.contains handle_input '.' then handle_input 138 138 else handle_input ^ hostname_suffix 139 139 in 140 - match Util.validate_handle handle with 140 + match Identity_util.validate_handle handle with 141 141 | Error (InvalidFormat e) 142 142 | Error (TooLong e) 143 143 | Error (TooShort e) -> ··· 204 204 List.assoc_opt "handle" fields |> Option.value ~default:"" 205 205 in 206 206 match%lwt 207 - Identity.UpdateHandle.update_handle ~did ~handle ctx.db 207 + Identity_util.update_handle ~did ~handle ctx.db 208 208 with 209 209 | Ok () -> 210 210 render_page ~success:"Handle updated." ()
+1 -1
pegasus/lib/api/identity/requestPlcOperationSignature.ml
··· 2 2 Xrpc.handler ~auth:Authorization (fun {auth; db; _} -> 3 3 let did = Auth.get_authed_did_exn auth in 4 4 let code = Util.make_code () in 5 - let expires_at = Util.now_ms () + (60 * 60 * 1000) in 5 + let expires_at = Util.Time.now_ms () + (60 * 60 * 1000) in 6 6 let%lwt () = Data_store.set_auth_code ~did ~code ~expires_at db in 7 7 let%lwt {email; handle; _} = 8 8 Data_store.get_actor_by_identifier did db |> Lwt.map Option.get
+2 -2
pegasus/lib/api/identity/signPlcOperation.ml
··· 13 13 | Some actor -> ( 14 14 match (actor.auth_code, actor.auth_code_expires_at) with 15 15 | auth_code, Some auth_expires_at 16 - when input.token = auth_code && Util.now_ms () < auth_expires_at -> ( 16 + when input.token = auth_code && Util.Time.now_ms () < auth_expires_at -> ( 17 17 match%lwt Plc.get_audit_log did with 18 18 | Ok log -> 19 19 let latest = Mist.Util.last log |> Option.get in ··· 21 21 Option.map 22 22 (fun v -> 23 23 try 24 - Util.Did_doc_types.string_map_of_yojson v |> Result.get_ok 24 + Util.Types.string_map_of_yojson v |> Result.get_ok 25 25 with _ -> Errors.invalid_request "invalid request body" ) 26 26 input.verification_methods 27 27 in
+4 -85
pegasus/lib/api/identity/updateHandle.ml
··· 1 1 open Lexicons.Com.Atproto.Identity.UpdateHandle.Main 2 2 3 - type update_handle_error = 4 - | InvalidFormat of string 5 - | HandleTaken 6 - | TooShort of string 7 - | TooLong of string 8 - | InternalServerError of string 9 - 10 - let update_handle_error_to_string = function 11 - | InvalidFormat m | TooShort m | TooLong m -> 12 - "handle " ^ m 13 - | HandleTaken -> 14 - "handle already taken" 15 - | InternalServerError msg -> 16 - msg 17 - 18 - let update_handle ~did ~handle db = 19 - match Util.validate_handle handle with 20 - | Error (InvalidFormat e) -> 21 - Lwt.return_error (InvalidFormat e) 22 - | Error (TooShort e) -> 23 - Lwt.return_error (TooShort e) 24 - | Error (TooLong e) -> 25 - Lwt.return_error (TooLong e) 26 - | Ok () -> ( 27 - match%lwt Data_store.get_actor_by_identifier handle db with 28 - | Some _ -> 29 - Lwt.return_error HandleTaken 30 - | None -> ( 31 - let%lwt {handle= prev_handle; _} = 32 - Data_store.get_actor_by_identifier did db |> Lwt.map Option.get 33 - in 34 - let%lwt () = Data_store.update_actor_handle ~did ~handle db in 35 - let%lwt plc_result = 36 - if String.starts_with ~prefix:"did:plc:" did then 37 - match%lwt Plc.get_audit_log did with 38 - | Error e -> 39 - Lwt.return_error 40 - (InternalServerError ("failed to fetch did doc: " ^ e)) 41 - | Ok log -> ( 42 - let latest = List.rev log |> List.hd in 43 - let aka = 44 - match 45 - List.mem ("at://" ^ handle) latest.operation.also_known_as 46 - with 47 - | true -> 48 - latest.operation.also_known_as 49 - | false -> 50 - ("at://" ^ handle) :: latest.operation.also_known_as 51 - in 52 - let aka = 53 - List.filter (fun x -> x <> "at://" ^ prev_handle) aka 54 - in 55 - let signed = 56 - Plc.sign_operation Env.rotation_key 57 - (Operation 58 - { type'= "plc_operation" 59 - ; prev= Some latest.cid 60 - ; also_known_as= aka 61 - ; rotation_keys= latest.operation.rotation_keys 62 - ; verification_methods= 63 - latest.operation.verification_methods 64 - ; services= latest.operation.services } ) 65 - in 66 - match%lwt Plc.submit_operation did signed with 67 - | Ok _ -> 68 - Lwt.return_ok () 69 - | Error (status, msg) -> 70 - Lwt.return_error 71 - (InternalServerError 72 - (Printf.sprintf "failed to submit plc operation: %d %s" 73 - status msg ) ) ) 74 - else Lwt.return_ok () 75 - in 76 - match plc_result with 77 - | Error e -> 78 - Lwt.return_error e 79 - | Ok () -> 80 - let () = Ttl_cache.String_cache.remove Id_resolver.Did.cache did in 81 - let%lwt _ = Sequencer.sequence_identity db ~did ~handle () in 82 - Lwt.return_ok () ) ) 83 - 84 3 let calc_key_did ctx = Some (Auth.get_authed_did_exn ctx.Xrpc.auth) 85 4 86 5 let handler = 87 6 Xrpc.handler ~auth:Authorization 88 7 ~rate_limits: 89 8 [ Route 90 - { duration_ms= 5 * Util.minute 9 + { duration_ms= 5 * Util.Time.minute 91 10 ; points= 10 92 11 ; calc_key= Some calc_key_did 93 12 ; calc_points= None } 94 13 ; Route 95 - { duration_ms= Util.day 14 + { duration_ms= Util.Time.day 96 15 ; points= 50 97 16 ; calc_key= Some calc_key_did 98 17 ; calc_points= None } ] ··· 100 19 Auth.assert_identity_scope auth ~attr:Oauth.Scopes.Handle ; 101 20 let did = Auth.get_authed_did_exn auth in 102 21 let%lwt {handle} = Xrpc.parse_body req input_of_yojson in 103 - match%lwt update_handle ~did ~handle db with 22 + match%lwt Identity_util.update_handle ~did ~handle db with 104 23 | Ok () -> 105 24 Dream.empty `OK 106 25 | Error e -> 107 - let msg = update_handle_error_to_string e in 26 + let msg = Identity_util.update_handle_error_to_string e in 108 27 Log.err (fun log -> log "%s" msg) ; 109 28 Errors.invalid_request ~name:"InvalidHandle" msg )
+4 -4
pegasus/lib/api/oauth_/authorize.ml
··· 4 4 let get_handler = 5 5 Xrpc.handler (fun ctx -> 6 6 let login_redirect = 7 - Uri.make ~path:"/account/login" ~query:(Util.copy_query ctx.req) () 7 + Uri.make ~path:"/account/login" ~query:(Util.Http.copy_query ctx.req) () 8 8 |> Uri.to_string |> Dream.redirect ctx.req 9 9 in 10 10 let client_id = Dream.query ctx.req "client_id" in ··· 45 45 ^ Uuidm.to_string 46 46 (Uuidm.v4_gen (Random.State.make_self_init ()) ()) 47 47 in 48 - let expires_at = Util.now_ms () + Constants.code_expiry_ms in 48 + let expires_at = Util.Time.now_ms () + Constants.code_expiry_ms in 49 49 let%lwt () = 50 50 Queries.insert_auth_code ctx.db 51 51 { code ··· 136 136 Option.value current_user 137 137 ~default:(List.hd logged_in_users) 138 138 in 139 - Util.render_html ~title:("Authorizing " ^ host) 139 + Util.Html.render_page ~title:("Authorizing " ^ host) 140 140 (module Frontend.OauthAuthorizePage) 141 141 ~props: 142 142 { client_url ··· 191 191 Errors.invalid_request "code already authorized" 192 192 else if code_rec.used then 193 193 Errors.invalid_request "code already used" 194 - else if Util.now_ms () > code_rec.expires_at then 194 + else if Util.Time.now_ms () > code_rec.expires_at then 195 195 Errors.invalid_request "code expired" 196 196 else if code_rec.request_id <> request_id then 197 197 Errors.invalid_request "code not for this request"
+2 -2
pegasus/lib/api/oauth_/par.ml
··· 23 23 ^ Uuidm.to_string (Uuidm.v4_gen (Random.State.make_self_init ()) ()) 24 24 in 25 25 let request_uri = Constants.request_uri_prefix ^ request_id in 26 - let expires_at = Util.now_ms () + Constants.par_request_ttl_ms in 26 + let expires_at = Util.Time.now_ms () + Constants.par_request_ttl_ms in 27 27 let request : oauth_request = 28 28 { request_id 29 29 ; client_id= req.client_id 30 30 ; request_data= Yojson.Safe.to_string (par_request_to_yojson req) 31 31 ; dpop_jkt= Some proof.jkt 32 32 ; expires_at 33 - ; created_at= Util.now_ms () } 33 + ; created_at= Util.Time.now_ms () } 34 34 in 35 35 let%lwt () = Queries.insert_par_request ctx.db request in 36 36 Dream.json ~status:`Created
+2 -2
pegasus/lib/api/oauth_/token.ml
··· 17 17 | None -> 18 18 Errors.invalid_request "invalid code" 19 19 | Some code_rec -> ( 20 - if Util.now_ms () > code_rec.expires_at then 20 + if Util.Time.now_ms () > code_rec.expires_at then 21 21 Errors.invalid_request "code expired" 22 22 else 23 23 match code_rec.authorized_by with ··· 80 80 () ) 81 81 in 82 82 let now_sec = int_of_float (Unix.gettimeofday ()) in 83 - let now_ms = Util.now_ms () in 83 + let now_ms = Util.Time.now_ms () in 84 84 let expires_in = 85 85 Constants.access_token_expiry_ms / 1000 86 86 in
+1 -1
pegasus/lib/api/proxy/appBskyFeedGetFeed.ml
··· 7 7 let handler = 8 8 Xrpc.handler ~auth:Authorization (fun ctx -> 9 9 let input = Xrpc.parse_query ctx.req query_of_yojson in 10 - match Util.parse_at_uri input.feed with 10 + match Util.Syntax.parse_at_uri input.feed with 11 11 | None -> 12 12 Errors.invalid_request ("invalid feed URI " ^ input.feed) 13 13 | Some {repo; collection; rkey; _} -> (
+2 -2
pegasus/lib/api/repo/getRecord.ml
··· 9 9 match input_did with 10 10 | Ok input_did -> ( 11 11 let uri = 12 - Util.make_at_uri ~repo:input_did ~collection:input.collection 12 + Util.Syntax.make_at_uri ~repo:input_did ~collection:input.collection 13 13 ~rkey:input.rkey ~fragment:None 14 14 in 15 15 let%lwt repo = Repository.load ~ensure_active:true input_did in ··· 68 68 Errors.internal_error ~name:"RecordNotFound" 69 69 ~msg: 70 70 ( "could not find record " 71 - ^ Util.make_at_uri ~repo:input.repo ~collection:input.collection 71 + ^ Util.Syntax.make_at_uri ~repo:input.repo ~collection:input.collection 72 72 ~rkey:input.rkey ~fragment:None ) 73 73 () ) )
+1 -1
pegasus/lib/api/root.ml
··· 1 1 let handler = 2 2 Xrpc.handler (fun _ -> 3 - Util.render_html ~title:"Pegasus" (module Frontend.RootPage) ~props:() ) 3 + Util.Html.render_page ~title:"Pegasus" (module Frontend.RootPage) ~props:() )
+2 -2
pegasus/lib/api/server/confirmEmail.ml
··· 9 9 else 10 10 match (actor.auth_code, actor.auth_code_expires_at) with 11 11 | Some auth_code, Some expires_at 12 - when auth_code = token && Util.now_ms () < expires_at -> 12 + when auth_code = token && Util.Time.now_ms () < expires_at -> 13 13 let%lwt () = Data_store.confirm_email ~did:actor.did db in 14 14 Lwt.return_ok () 15 - | Some _, Some expires_at when Util.now_ms () >= expires_at -> 15 + | Some _, Some expires_at when Util.Time.now_ms () >= expires_at -> 16 16 Lwt.return_error ExpiredToken 17 17 | _ -> 18 18 Lwt.return_error InvalidToken
+1 -1
pegasus/lib/api/server/createAccount.ml
··· 33 33 Lwt.return_error e 34 34 | Ok () -> ( 35 35 (* validate handle *) 36 - match Util.validate_handle handle with 36 + match Identity_util.validate_handle handle with 37 37 | Error (InvalidFormat e) | Error (TooLong e) | Error (TooShort e) -> 38 38 Lwt.return_error (InvalidHandle ("handle " ^ e)) 39 39 | Ok _ -> (
+2 -2
pegasus/lib/api/server/createSession.ml
··· 35 35 let key = id ^ "-" ^ Util.request_ip req in 36 36 let _ = 37 37 Xrpc.consume_route_rate_limit ~name:"repo-write-hour" 38 - ~duration_ms:Util.day ~max_points:300 ~key ~consume_points 38 + ~duration_ms:Util.Time.day ~max_points:300 ~key ~consume_points 39 39 in 40 40 let _ = 41 41 Xrpc.consume_route_rate_limit ~name:"repo-write-day" 42 - ~duration_ms:(5 * Util.minute) ~max_points:30 ~key ~consume_points 42 + ~duration_ms:(5 * Util.Time.minute) ~max_points:30 ~key ~consume_points 43 43 in 44 44 match%lwt 45 45 Lwt_result.catch @@ fun () -> Data_store.try_login ~id ~password db
+3 -3
pegasus/lib/api/server/deleteAccount.ml
··· 10 10 let delete_account ~did db = 11 11 let%lwt () = 12 12 try%lwt 13 - Util.use_pool db (fun conn -> 14 - Util.transact conn (fun () -> 13 + Util.Sqlite.use_pool db (fun conn -> 14 + Util.Sqlite.transact conn (fun () -> 15 15 let open Util.Syntax in 16 16 let$! () = 17 17 Data_store.Queries.delete_reserved_keys_by_did ~did conn ··· 45 45 | Some auth_code, Some auth_expires_at 46 46 when String.starts_with ~prefix:"del-" auth_code 47 47 && token = auth_code 48 - && Util.now_ms () < auth_expires_at -> 48 + && Util.Time.now_ms () < auth_expires_at -> 49 49 let%lwt _ = delete_account ~did db in 50 50 Dream.empty `OK 51 51 | None, _ | _, None ->
+4 -4
pegasus/lib/api/server/requestAccountDelete.ml
··· 4 4 "del-" 5 5 ^ String.sub 6 6 Digestif.SHA256.( 7 - digest_string (did ^ Int.to_string @@ Util.now_ms ()) |> to_hex ) 7 + digest_string (did ^ Int.to_string @@ Util.Time.now_ms ()) |> to_hex ) 8 8 0 8 9 9 in 10 - let expires_at = Util.now_ms () + (15 * 60 * 1000) in 10 + let expires_at = Util.Time.now_ms () + (15 * 60 * 1000) in 11 11 let%lwt () = Data_store.set_auth_code ~did ~code ~expires_at db in 12 12 Util.send_email_or_log ~recipients:[To actor.email] 13 13 ~subject:(Printf.sprintf "Account deletion request for %s" actor.handle) ··· 19 19 Xrpc.handler ~auth:Authorization 20 20 ~rate_limits: 21 21 [ Route 22 - { duration_ms= Util.day 22 + { duration_ms= Util.Time.day 23 23 ; points= 15 24 24 ; calc_key= Some calc_key_did 25 25 ; calc_points= None } 26 26 ; Route 27 - { duration_ms= Util.hour 27 + { duration_ms= Util.Time.hour 28 28 ; points= 5 29 29 ; calc_key= Some calc_key_did 30 30 ; calc_points= None } ]
+3 -3
pegasus/lib/api/server/requestEmailConfirmation.ml
··· 6 6 Lwt.return_error AlreadyConfirmed 7 7 | None -> 8 8 let code = Util.make_code () in 9 - let expires_at = Util.now_ms () + (10 * 60 * 1000) in 9 + let expires_at = Util.Time.now_ms () + (10 * 60 * 1000) in 10 10 let%lwt () = 11 11 Data_store.set_auth_code ~did:actor.did ~code ~expires_at db 12 12 in ··· 23 23 Xrpc.handler ~auth:Authorization 24 24 ~rate_limits: 25 25 [ Route 26 - { duration_ms= Util.day 26 + { duration_ms= Util.Time.day 27 27 ; points= 15 28 28 ; calc_key= Some calc_key_did 29 29 ; calc_points= None } 30 30 ; Route 31 - { duration_ms= Util.hour 31 + { duration_ms= Util.Time.hour 32 32 ; points= 5 33 33 ; calc_key= Some calc_key_did 34 34 ; calc_points= None } ]
+3 -3
pegasus/lib/api/server/requestEmailUpdate.ml
··· 8 8 if token_required then 9 9 let did = actor.did in 10 10 let code = Util.make_code () in 11 - let expires_at = Util.now_ms () + (10 * 60 * 1000) in 11 + let expires_at = Util.Time.now_ms () + (10 * 60 * 1000) in 12 12 let%lwt () = 13 13 match pending_email with 14 14 | Some pending_email -> ··· 41 41 Xrpc.handler ~auth:Authorization 42 42 ~rate_limits: 43 43 [ Route 44 - { duration_ms= Util.day 44 + { duration_ms= Util.Time.day 45 45 ; points= 15 46 46 ; calc_key= Some calc_key_did 47 47 ; calc_points= None } 48 48 ; Route 49 - { duration_ms= Util.hour 49 + { duration_ms= Util.Time.hour 50 50 ; points= 5 51 51 ; calc_key= Some calc_key_did 52 52 ; calc_points= None } ]
+3 -3
pegasus/lib/api/server/requestPasswordReset.ml
··· 3 3 let request_password_reset (actor : Data_store.Types.actor) db = 4 4 let did = actor.did in 5 5 let code = Util.make_code () in 6 - let expires_at = Util.now_ms () + (10 * 60 * 1000) in 6 + let expires_at = Util.Time.now_ms () + (10 * 60 * 1000) in 7 7 let%lwt () = Data_store.set_auth_code ~did ~code ~expires_at db in 8 8 Util.send_email_or_log ~recipients:[To actor.email] 9 9 ~subject:(Printf.sprintf "Password reset for %s" actor.handle) ··· 13 13 Xrpc.handler 14 14 ~rate_limits: 15 15 [ Route 16 - {duration_ms= Util.day; points= 50; calc_key= None; calc_points= None} 16 + {duration_ms= Util.Time.day; points= 50; calc_key= None; calc_points= None} 17 17 ; Route 18 - {duration_ms= Util.hour; points= 15; calc_key= None; calc_points= None} 18 + {duration_ms= Util.Time.hour; points= 15; calc_key= None; calc_points= None} 19 19 ] 20 20 (fun {req; auth; db; _} -> 21 21 let%lwt actor_opt =
+2 -2
pegasus/lib/api/server/resetPassword.ml
··· 9 9 | Some actor -> ( 10 10 match (actor.auth_code, actor.auth_code_expires_at) with 11 11 | Some auth_code, Some auth_expires_at 12 - when token = auth_code && Util.now_ms () < auth_expires_at -> 12 + when token = auth_code && Util.Time.now_ms () < auth_expires_at -> 13 13 let%lwt () = Data_store.update_password ~did:actor.did ~password db in 14 14 Lwt.return_ok actor.did 15 15 | _ -> ··· 19 19 Xrpc.handler 20 20 ~rate_limits: 21 21 [ Route 22 - { duration_ms= 5 * Util.minute 22 + { duration_ms= 5 * Util.Time.minute 23 23 ; points= 50 24 24 ; calc_key= None 25 25 ; calc_points= None } ]
+2 -2
pegasus/lib/api/server/updateEmail.ml
··· 25 25 | Some token -> ( 26 26 match (actor.auth_code, actor.auth_code_expires_at) with 27 27 | Some auth_code, Some expires_at 28 - when auth_code = token && Util.now_ms () < expires_at -> 28 + when auth_code = token && Util.Time.now_ms () < expires_at -> 29 29 let%lwt () = Data_store.update_email ~did ~email db in 30 30 Lwt.return_ok email 31 - | Some _, Some expires_at when Util.now_ms () >= expires_at -> 31 + | Some _, Some expires_at when Util.Time.now_ms () >= expires_at -> 32 32 Lwt.return_error ExpiredToken 33 33 | _ -> 34 34 Lwt.return_error InvalidToken ) )
+43 -43
pegasus/lib/data_store.ml
··· 319 319 {sql| INSERT INTO revoked_tokens (did, jti, revoked_at) VALUES (%string{did}, %string{jti}, %int{now}) |sql}] 320 320 end 321 321 322 - type t = Util.caqti_pool 322 + type t = Util.Sqlite.caqti_pool 323 323 324 324 let pool : t option ref = ref None 325 325 ··· 339 339 if create = Some true then 340 340 Util.mkfile_p Util.Constants.pegasus_db_filepath ~perm:0o644 ; 341 341 let%lwt db = 342 - Util.connect_sqlite ?create ~write:true 342 + Util.Sqlite.connect ?create ~write:true 343 343 Util.Constants.pegasus_db_location 344 344 in 345 345 let%lwt () = Migrations.run_migrations Data_store db in ··· 350 350 if create = Some true then 351 351 Util.mkfile_p Util.Constants.pegasus_db_filepath ~perm:0o644 ; 352 352 let%lwt db = 353 - Util.connect_sqlite ?create ~write:false Util.Constants.pegasus_db_location 353 + Util.Sqlite.connect ?create ~write:false Util.Constants.pegasus_db_location 354 354 in 355 355 let%lwt () = Migrations.run_migrations Data_store db in 356 356 Lwt.return db 357 357 358 358 let create_actor ~did ~handle ~email ~password ~signing_key conn = 359 359 let password_hash = Bcrypt.hash password |> Bcrypt.string_of_hash in 360 - let now = Util.now_ms () in 361 - Util.use_pool conn 360 + let now = Util.Time.now_ms () in 361 + Util.Sqlite.use_pool conn 362 362 @@ Queries.create_actor ~did ~handle ~email ~password_hash ~signing_key 363 363 ~created_at:now 364 364 ~preferences:(Yojson.Safe.from_string "[]") 365 365 366 366 let get_actor_by_identifier id conn = 367 - Util.use_pool conn @@ Queries.get_actor_by_identifier ~id 367 + Util.Sqlite.use_pool conn @@ Queries.get_actor_by_identifier ~id 368 368 369 - let activate_actor did conn = Util.use_pool conn @@ Queries.activate_actor ~did 369 + let activate_actor did conn = Util.Sqlite.use_pool conn @@ Queries.activate_actor ~did 370 370 371 371 let deactivate_actor did conn = 372 - let deactivated_at = Util.now_ms () in 373 - Util.use_pool conn @@ Queries.deactivate_actor ~did ~deactivated_at 372 + let deactivated_at = Util.Time.now_ms () in 373 + Util.Sqlite.use_pool conn @@ Queries.deactivate_actor ~did ~deactivated_at 374 374 375 - let delete_actor did conn = Util.use_pool conn @@ Queries.delete_actor ~did 375 + let delete_actor did conn = Util.Sqlite.use_pool conn @@ Queries.delete_actor ~did 376 376 377 377 let update_actor_handle ~did ~handle conn = 378 - Util.use_pool conn @@ Queries.update_actor_handle ~did ~handle 378 + Util.Sqlite.use_pool conn @@ Queries.update_actor_handle ~did ~handle 379 379 380 380 let try_login ~id ~password conn = 381 381 match%lwt get_actor_by_identifier id conn with ··· 390 390 Lwt.return_none ) 391 391 392 392 let list_actors ?(cursor = "") ?(limit = 100) conn = 393 - Util.use_pool conn @@ Queries.list_actors ~cursor ~limit 393 + Util.Sqlite.use_pool conn @@ Queries.list_actors ~cursor ~limit 394 394 395 395 let put_preferences ~did ~prefs conn = 396 - Util.use_pool conn @@ Queries.put_preferences ~did ~preferences:prefs 396 + Util.Sqlite.use_pool conn @@ Queries.put_preferences ~did ~preferences:prefs 397 397 398 398 (* invite codes *) 399 399 let create_invite ~code ~did ~remaining conn = 400 - Util.use_pool conn @@ Queries.create_invite ~code ~did ~remaining 400 + Util.Sqlite.use_pool conn @@ Queries.create_invite ~code ~did ~remaining 401 401 402 - let get_invite ~code conn = Util.use_pool conn @@ Queries.get_invite ~code 402 + let get_invite ~code conn = Util.Sqlite.use_pool conn @@ Queries.get_invite ~code 403 403 404 - let use_invite ~code conn = Util.use_pool conn @@ Queries.use_invite ~code 404 + let use_invite ~code conn = Util.Sqlite.use_pool conn @@ Queries.use_invite ~code 405 405 406 406 let list_invites ?(limit = 100) conn = 407 - Util.use_pool conn @@ Queries.list_invites ~limit 407 + Util.Sqlite.use_pool conn @@ Queries.list_invites ~limit 408 408 409 - let delete_invite ~code conn = Util.use_pool conn @@ Queries.delete_invite ~code 409 + let delete_invite ~code conn = Util.Sqlite.use_pool conn @@ Queries.delete_invite ~code 410 410 411 411 let update_invite ~code ~did ~remaining conn = 412 - Util.use_pool conn @@ Queries.update_invite ~code ~did ~remaining 412 + Util.Sqlite.use_pool conn @@ Queries.update_invite ~code ~did ~remaining 413 413 414 414 let list_actors_filtered ?(cursor = "") ?(limit = 100) ~filter conn = 415 415 if String.length filter = 0 then 416 - Util.use_pool conn @@ Queries.list_all_actors ~cursor ~limit 417 - else Util.use_pool conn @@ Queries.list_actors_filtered ~filter ~cursor ~limit 416 + Util.Sqlite.use_pool conn @@ Queries.list_all_actors ~cursor ~limit 417 + else Util.Sqlite.use_pool conn @@ Queries.list_actors_filtered ~filter ~cursor ~limit 418 418 419 419 (* reserved keys *) 420 420 let create_reserved_key ~key_did ~did ~private_key conn = 421 - let created_at = Util.now_ms () in 422 - Util.use_pool conn 421 + let created_at = Util.Time.now_ms () in 422 + Util.Sqlite.use_pool conn 423 423 @@ Queries.create_reserved_key ~key_did ~did ~private_key ~created_at 424 424 425 425 let get_reserved_key_by_did ~did conn = 426 - Util.use_pool conn @@ Queries.get_reserved_key_by_did ~did 426 + Util.Sqlite.use_pool conn @@ Queries.get_reserved_key_by_did ~did 427 427 428 428 let get_reserved_key ~key_did conn = 429 - Util.use_pool conn @@ Queries.get_reserved_key ~key_did 429 + Util.Sqlite.use_pool conn @@ Queries.get_reserved_key ~key_did 430 430 431 431 let delete_reserved_key ~key_did conn = 432 - Util.use_pool conn @@ Queries.delete_reserved_key ~key_did 432 + Util.Sqlite.use_pool conn @@ Queries.delete_reserved_key ~key_did 433 433 434 434 let delete_reserved_keys_by_did ~did conn = 435 - Util.use_pool conn @@ Queries.delete_reserved_keys_by_did ~did 435 + Util.Sqlite.use_pool conn @@ Queries.delete_reserved_keys_by_did ~did 436 436 437 437 (* 2fa *) 438 438 let set_auth_code ~did ~code ~expires_at conn = 439 - Util.use_pool conn @@ Queries.set_auth_code ~did ~code ~expires_at 439 + Util.Sqlite.use_pool conn @@ Queries.set_auth_code ~did ~code ~expires_at 440 440 441 441 let set_pending_email ~did ~code ~expires_at ~pending_email conn = 442 - Util.use_pool conn 442 + Util.Sqlite.use_pool conn 443 443 @@ Queries.set_pending_email ~did ~code ~expires_at ~pending_email 444 444 445 445 let clear_auth_code ~did conn = 446 - Util.use_pool conn @@ Queries.clear_auth_code ~did 446 + Util.Sqlite.use_pool conn @@ Queries.clear_auth_code ~did 447 447 448 448 let get_actor_by_auth_code ~code conn = 449 - Util.use_pool conn @@ Queries.get_actor_by_auth_code ~code 449 + Util.Sqlite.use_pool conn @@ Queries.get_actor_by_auth_code ~code 450 450 451 451 let update_password ~did ~password conn = 452 452 let password_hash = Bcrypt.hash password |> Bcrypt.string_of_hash in 453 - Util.use_pool conn @@ Queries.update_password ~did ~password_hash 453 + Util.Sqlite.use_pool conn @@ Queries.update_password ~did ~password_hash 454 454 455 455 let update_email ~did ~email conn = 456 - Util.use_pool conn @@ Queries.update_email ~did ~email 456 + Util.Sqlite.use_pool conn @@ Queries.update_email ~did ~email 457 457 458 458 let confirm_email ~did conn = 459 - let confirmed_at = Util.now_ms () in 460 - Util.use_pool conn @@ Queries.confirm_email ~did ~confirmed_at 459 + let confirmed_at = Util.Time.now_ms () in 460 + Util.Sqlite.use_pool conn @@ Queries.confirm_email ~did ~confirmed_at 461 461 462 462 (* firehose helpers *) 463 463 let append_firehose_event conn ~time ~t ~data : int Lwt.t = 464 - Util.use_pool conn @@ Queries.firehose_insert ~time ~t ~data 464 + Util.Sqlite.use_pool conn @@ Queries.firehose_insert ~time ~t ~data 465 465 466 466 let list_firehose_since conn ~since ~limit : firehose_event list Lwt.t = 467 - Util.use_pool conn @@ Queries.firehose_since ~since ~limit 467 + Util.Sqlite.use_pool conn @@ Queries.firehose_since ~since ~limit 468 468 469 469 let next_firehose_event conn ~cursor : firehose_event option Lwt.t = 470 - Util.use_pool conn @@ Queries.firehose_next ~cursor 470 + Util.Sqlite.use_pool conn @@ Queries.firehose_next ~cursor 471 471 472 472 let earliest_firehose_after_time conn ~time : firehose_event option Lwt.t = 473 - Util.use_pool conn @@ Queries.firehose_earliest_after ~time 473 + Util.Sqlite.use_pool conn @@ Queries.firehose_earliest_after ~time 474 474 475 475 let latest_firehose_seq conn : int option Lwt.t = 476 - Util.use_pool conn @@ Queries.firehose_latest_seq 476 + Util.Sqlite.use_pool conn @@ Queries.firehose_latest_seq 477 477 478 478 let next_firehose_seq conn : int Lwt.t = 479 - let%lwt seq = Util.use_pool conn Queries.firehose_latest_seq in 479 + let%lwt seq = Util.Sqlite.use_pool conn Queries.firehose_latest_seq in 480 480 Option.map succ seq |> Option.value ~default:0 |> Lwt.return 481 481 482 482 (* jwts *) 483 483 let is_token_revoked conn ~did ~jti = 484 - Util.use_pool conn @@ Queries.get_revoked_token ~did ~jti 484 + Util.Sqlite.use_pool conn @@ Queries.get_revoked_token ~did ~jti 485 485 486 486 let revoke_token conn ~did ~jti = 487 - Util.use_pool conn @@ Queries.revoke_token ~did ~jti ~now:(Util.now_ms ()) 487 + Util.Sqlite.use_pool conn @@ Queries.revoke_token ~did ~jti ~now:(Util.Time.now_ms ())
+4 -4
pegasus/lib/id_resolver.ml
··· 8 8 let uri = 9 9 Uri.of_string ("https://" ^ handle ^ "/.well-known/atproto-did") 10 10 in 11 - let%lwt {status; _}, body = Util.http_get uri in 11 + let%lwt {status; _}, body = Util.Http.get uri in 12 12 match status with 13 13 | `OK -> 14 14 let%lwt did = Body.to_string body in ··· 77 77 end 78 78 79 79 module Did = struct 80 - open Util.Did_doc_types 80 + open Util.Types 81 81 82 82 module Document = struct 83 83 type service = ··· 175 175 ~path:(Uri.pct_encode did) () 176 176 in 177 177 let%lwt {status; _}, body = 178 - Util.http_get uri 178 + Util.Http.get uri 179 179 ~headers:(Cohttp.Header.of_list [("Accept", "application/json")]) 180 180 in 181 181 match status with ··· 197 197 ~path:"/.well-known/did.json" () 198 198 in 199 199 let%lwt {status; _}, body = 200 - Util.http_get uri 200 + Util.Http.get uri 201 201 ~headers:(Cohttp.Header.of_list [("Accept", "application/json")]) 202 202 in 203 203 match status with
+107
pegasus/lib/identity_util.ml
··· 1 + type validate_handle_error = 2 + | InvalidFormat of string 3 + | TooShort of string 4 + | TooLong of string 5 + 6 + let validate_handle handle = 7 + (* if it's a custom domain, just check that it contains a period *) 8 + if not (String.ends_with ~suffix:("." ^ Env.hostname) handle) then 9 + if not (String.contains handle '.') then 10 + Error (InvalidFormat ("must end with " ^ "." ^ Env.hostname)) 11 + else Ok () 12 + else 13 + let front = 14 + String.sub handle 0 15 + (String.length handle - (String.length Env.hostname + 1)) 16 + in 17 + if String.contains front '.' then 18 + Error (InvalidFormat "can't contain periods") 19 + else 20 + match String.length front with 21 + | l when l < 3 -> 22 + Error (TooShort "must be at least 3 characters") 23 + | l when l > 18 -> 24 + Error (TooLong "must be at most 18 characters") 25 + | _ -> 26 + Ok () 27 + 28 + type update_handle_error = 29 + | InvalidFormat of string 30 + | HandleTaken 31 + | TooShort of string 32 + | TooLong of string 33 + | InternalServerError of string 34 + 35 + let update_handle_error_to_string = function 36 + | InvalidFormat m | TooShort m | TooLong m -> 37 + "handle " ^ m 38 + | HandleTaken -> 39 + "handle already taken" 40 + | InternalServerError msg -> 41 + msg 42 + 43 + let update_handle ~did ~handle db = 44 + match validate_handle handle with 45 + | Error (InvalidFormat e) -> 46 + Lwt.return_error (InvalidFormat e) 47 + | Error (TooShort e) -> 48 + Lwt.return_error (TooShort e) 49 + | Error (TooLong e) -> 50 + Lwt.return_error (TooLong e) 51 + | Ok () -> ( 52 + match%lwt Data_store.get_actor_by_identifier handle db with 53 + | Some _ -> 54 + Lwt.return_error HandleTaken 55 + | None -> ( 56 + let%lwt {handle= prev_handle; _} = 57 + Data_store.get_actor_by_identifier did db |> Lwt.map Option.get 58 + in 59 + let%lwt () = Data_store.update_actor_handle ~did ~handle db in 60 + let%lwt plc_result = 61 + if String.starts_with ~prefix:"did:plc:" did then 62 + match%lwt Plc.get_audit_log did with 63 + | Error e -> 64 + Lwt.return_error 65 + (InternalServerError ("failed to fetch did doc: " ^ e)) 66 + | Ok log -> ( 67 + let latest = List.rev log |> List.hd in 68 + let aka = 69 + match 70 + List.mem ("at://" ^ handle) latest.operation.also_known_as 71 + with 72 + | true -> 73 + latest.operation.also_known_as 74 + | false -> 75 + ("at://" ^ handle) :: latest.operation.also_known_as 76 + in 77 + let aka = 78 + List.filter (fun x -> x <> "at://" ^ prev_handle) aka 79 + in 80 + let signed = 81 + Plc.sign_operation Env.rotation_key 82 + (Operation 83 + { type'= "plc_operation" 84 + ; prev= Some latest.cid 85 + ; also_known_as= aka 86 + ; rotation_keys= latest.operation.rotation_keys 87 + ; verification_methods= 88 + latest.operation.verification_methods 89 + ; services= latest.operation.services } ) 90 + in 91 + match%lwt Plc.submit_operation did signed with 92 + | Ok _ -> 93 + Lwt.return_ok () 94 + | Error (status, msg) -> 95 + Lwt.return_error 96 + (InternalServerError 97 + (Printf.sprintf "failed to submit plc operation: %d %s" 98 + status msg ) ) ) 99 + else Lwt.return_ok () 100 + in 101 + match plc_result with 102 + | Error e -> 103 + Lwt.return_error e 104 + | Ok () -> 105 + let () = Ttl_cache.String_cache.remove Id_resolver.Did.cache did in 106 + let%lwt _ = Sequencer.sequence_identity db ~did ~handle () in 107 + Lwt.return_ok () ) )
+2 -2
pegasus/lib/lexicon_resolver.ml
··· 24 24 [@@deriving yojson {strict= false}] 25 25 26 26 let cache : permission_set Ttl_cache.String_cache.t = 27 - Ttl_cache.String_cache.create (3 * Util.hour) () 27 + Ttl_cache.String_cache.create (3 * Util.Time.hour) () 28 28 29 29 (* reuse dns client from id_resolver *) 30 30 let dns_client = Id_resolver.Handle.dns_client 31 31 32 32 (* resolve did authority for nsid *) 33 33 let resolve_did_authority nsid = 34 - let authority = Util.nsid_authority nsid in 34 + let authority = Util.Syntax.nsid_authority nsid in 35 35 try%lwt 36 36 let%lwt result = 37 37 Dns_client_lwt.getaddrinfo dns_client Dns.Rr_map.Txt
+5 -5
pegasus/lib/migrations/migrations.ml
··· 62 62 with _ -> None 63 63 64 64 let run_migration db (id, name, sql) = 65 - Util.use_pool db (fun conn -> 66 - Util.transact conn (fun () -> 65 + Util.Sqlite.use_pool db (fun conn -> 66 + Util.Sqlite.transact conn (fun () -> 67 67 let open Lwt_result.Infix in 68 68 execute_raw conn sql 69 69 >>= fun () -> 70 - let applied_at = Util.now_ms () in 70 + let applied_at = Util.Time.now_ms () in 71 71 Queries.record_migration ~id ~name ~applied_at conn ) ) 72 72 73 73 type migration_type = Data_store | User_store ··· 80 80 | User_store -> 81 81 User_store_migrations_sql.(read, file_list) 82 82 in 83 - let%lwt () = Util.use_pool conn Queries.create_migrations_table in 83 + let%lwt () = Util.Sqlite.use_pool conn Queries.create_migrations_table in 84 84 let%lwt applied = 85 - Util.use_pool conn Queries.get_applied_migrations 85 + Util.Sqlite.use_pool conn Queries.get_applied_migrations 86 86 >|= List.map (fun m -> m.id) 87 87 in 88 88 let pending =
+1 -1
pegasus/lib/oauth/client.ml
··· 1 1 open Types 2 2 3 3 let fetch_client_metadata client_id : client_metadata Lwt.t = 4 - let%lwt {status; _}, res = Util.http_get (Uri.of_string client_id) in 4 + let%lwt {status; _}, res = Util.Http.get (Uri.of_string client_id) in 5 5 if status <> `OK then 6 6 let%lwt () = Cohttp_lwt.Body.drain_body res in 7 7 failwith
+3 -3
pegasus/lib/oauth/dpop.ml
··· 15 15 Hashtbl.create Constants.jti_cache_size 16 16 17 17 let cleanup_jti_cache () = 18 - let now = Util.now_ms () in 18 + let now = Util.Time.now_ms () in 19 19 Hashtbl.filter_map_inplace 20 20 (fun _ expires_at -> if expires_at > now then Some expires_at else None) 21 21 jti_cache ··· 50 50 |> to_raw_string |> Jwt.b64_encode ) 51 51 52 52 let create_nonce_state secret = 53 - let counter = Util.now_ms () / Constants.dpop_rotation_interval_ms in 53 + let counter = Util.Time.now_ms () / Constants.dpop_rotation_interval_ms in 54 54 { secret 55 55 ; counter 56 56 ; prev= compute_nonce secret (pred counter) ··· 60 60 let nonce_state = ref (create_nonce_state Env.dpop_nonce_secret) 61 61 62 62 let next_nonce () = 63 - let now_counter = Util.now_ms () / Constants.dpop_rotation_interval_ms in 63 + let now_counter = Util.Time.now_ms () / Constants.dpop_rotation_interval_ms in 64 64 let diff = now_counter - !nonce_state.counter in 65 65 ( match diff with 66 66 | 0 ->
+18 -18
pegasus/lib/oauth/queries.ml
··· 3 3 open Types 4 4 5 5 let insert_par_request conn req = 6 - Util.use_pool conn 6 + Util.Sqlite.use_pool conn 7 7 @@ [%rapper 8 8 execute 9 9 {sql| ··· 14 14 req 15 15 16 16 let get_par_request conn request_id = 17 - Util.use_pool conn 17 + Util.Sqlite.use_pool conn 18 18 @@ [%rapper 19 19 get_opt 20 20 {sql| ··· 25 25 AND expires_at > %int{now} 26 26 |sql} 27 27 record_out] 28 - ~request_id ~now:(Util.now_ms ()) 28 + ~request_id ~now:(Util.Time.now_ms ()) 29 29 30 30 let insert_auth_code conn code = 31 - Util.use_pool conn 31 + Util.Sqlite.use_pool conn 32 32 @@ [%rapper 33 33 execute 34 34 {sql| ··· 39 39 code 40 40 41 41 let get_auth_code conn code = 42 - Util.use_pool conn 42 + Util.Sqlite.use_pool conn 43 43 @@ [%rapper 44 44 get_opt 45 45 {sql| ··· 53 53 ~code 54 54 55 55 let activate_auth_code conn code did ~ip ~user_agent = 56 - let authorized_at = Util.now_ms () in 57 - Util.use_pool conn 56 + let authorized_at = Util.Time.now_ms () in 57 + Util.Sqlite.use_pool conn 58 58 @@ [%rapper 59 59 execute 60 60 {sql| ··· 68 68 ~did ~authorized_at ~ip ~user_agent ~code 69 69 70 70 let consume_auth_code conn code = 71 - Util.use_pool conn 71 + Util.Sqlite.use_pool conn 72 72 @@ [%rapper 73 73 get_opt 74 74 {sql| ··· 83 83 ~code 84 84 85 85 let insert_oauth_token conn token = 86 - Util.use_pool conn 86 + Util.Sqlite.use_pool conn 87 87 @@ [%rapper 88 88 execute 89 89 {sql| ··· 94 94 token 95 95 96 96 let get_oauth_token_by_refresh conn refresh_token = 97 - Util.use_pool conn 97 + Util.Sqlite.use_pool conn 98 98 @@ [%rapper 99 99 get_opt 100 100 {sql| ··· 108 108 ~refresh_token 109 109 110 110 let update_oauth_token conn ~old_refresh_token ~new_refresh_token ~expires_at = 111 - let now_ms = Util.now_ms () in 112 - Util.use_pool conn 111 + let now_ms = Util.Time.now_ms () in 112 + Util.Sqlite.use_pool conn 113 113 @@ [%rapper 114 114 execute 115 115 {sql| ··· 122 122 ~new_refresh_token ~expires_at ~now_ms ~old_refresh_token 123 123 124 124 let delete_oauth_token_by_refresh conn refresh_token = 125 - Util.use_pool conn 125 + Util.Sqlite.use_pool conn 126 126 @@ [%rapper 127 127 execute 128 128 {sql| ··· 131 131 ~refresh_token 132 132 133 133 let get_oauth_tokens_by_did conn did = 134 - Util.use_pool conn 134 + Util.Sqlite.use_pool conn 135 135 @@ [%rapper 136 136 get_many 137 137 {sql| ··· 146 146 ~did 147 147 148 148 let get_distinct_clients_by_did conn did = 149 - Util.use_pool conn 149 + Util.Sqlite.use_pool conn 150 150 @@ [%rapper 151 151 get_many 152 152 {sql| ··· 159 159 ~did 160 160 161 161 let get_distinct_devices_by_did conn did = 162 - Util.use_pool conn 162 + Util.Sqlite.use_pool conn 163 163 @@ [%rapper 164 164 get_many 165 165 {sql| ··· 173 173 ~did 174 174 175 175 let delete_oauth_tokens_by_client conn ~did ~client_id = 176 - Util.use_pool conn 176 + Util.Sqlite.use_pool conn 177 177 @@ [%rapper 178 178 execute 179 179 {sql| ··· 183 183 ~did ~client_id 184 184 185 185 let delete_oauth_tokens_by_device conn ~did ~last_ip ~last_user_agent = 186 - Util.use_pool conn 186 + Util.Sqlite.use_pool conn 187 187 @@ [%rapper 188 188 execute 189 189 {sql|
+2 -2
pegasus/lib/oauth/scopes.ml
··· 69 69 70 70 (* check if permission_nsid is under include_nsid's authority *) 71 71 let is_parent_authority_of ~include_nsid ~permission_nsid = 72 - let include_authority = Util.nsid_authority include_nsid in 73 - let permission_authority = Util.nsid_authority permission_nsid in 72 + let include_authority = Util.Syntax.nsid_authority include_nsid in 73 + let permission_authority = Util.Syntax.nsid_authority permission_nsid in 74 74 String.equal include_authority permission_authority 75 75 || String.starts_with ~prefix:(include_authority ^ ".") permission_authority 76 76
+13 -13
pegasus/lib/passkey.ml
··· 126 126 127 127 let create_challenge ?did ~challenge_type db = 128 128 let _challenge_obj, challenge_b64 = Webauthn.generate_challenge () in 129 - let now = Util.now_ms () in 129 + let now = Util.Time.now_ms () in 130 130 let expires_at = now + challenge_expiry_ms in 131 131 let challenge_type_str = 132 132 match challenge_type with ··· 136 136 "authenticate" 137 137 in 138 138 let%lwt () = 139 - Util.use_pool db 139 + Util.Sqlite.use_pool db 140 140 @@ Queries.insert_challenge ~challenge:challenge_b64 ~did 141 141 ~challenge_type:challenge_type_str ~expires_at ~created_at:now 142 142 in 143 143 Lwt.return challenge_b64 144 144 145 145 let verify_challenge ~challenge ~challenge_type db = 146 - let now = Util.now_ms () in 146 + let now = Util.Time.now_ms () in 147 147 let expected_type = 148 148 match challenge_type with 149 149 | `Register -> ··· 151 151 | `Authenticate -> 152 152 "authenticate" 153 153 in 154 - match%lwt Util.use_pool db @@ Queries.get_challenge challenge now with 154 + match%lwt Util.Sqlite.use_pool db @@ Queries.get_challenge challenge now with 155 155 | Some c when c.challenge_type = expected_type -> 156 156 Lwt.return_some c 157 157 | _ -> 158 158 Lwt.return_none 159 159 160 160 let delete_challenge ~challenge db = 161 - Util.use_pool db @@ Queries.delete_challenge ~challenge 161 + Util.Sqlite.use_pool db @@ Queries.delete_challenge ~challenge 162 162 163 163 let store_credential ~did ~credential_id ~public_key ~name db = 164 - let now = Util.now_ms () in 165 - Util.use_pool db 164 + let now = Util.Time.now_ms () in 165 + Util.Sqlite.use_pool db 166 166 @@ Queries.insert_passkey ~did ~credential_id ~public_key ~sign_count:0 ~name 167 167 ~created_at:now 168 168 169 169 let get_credentials_for_user ~did db = 170 - Util.use_pool db @@ Queries.get_passkeys_by_did ~did 170 + Util.Sqlite.use_pool db @@ Queries.get_passkeys_by_did ~did 171 171 172 172 let get_credential_by_id ~credential_id db = 173 - Util.use_pool db @@ Queries.get_passkey_by_credential_id ~credential_id 173 + Util.Sqlite.use_pool db @@ Queries.get_passkey_by_credential_id ~credential_id 174 174 175 175 let update_sign_count ~credential_id ~sign_count db = 176 - let now = Util.now_ms () in 177 - Util.use_pool db 176 + let now = Util.Time.now_ms () in 177 + Util.Sqlite.use_pool db 178 178 @@ Queries.update_passkey_sign_count ~credential_id ~sign_count 179 179 ~last_used_at:now 180 180 181 181 let delete_credential ~id ~did db = 182 - let%lwt () = Util.use_pool db @@ Queries.delete_passkey ~id ~did in 182 + let%lwt () = Util.Sqlite.use_pool db @@ Queries.delete_passkey ~id ~did in 183 183 Lwt.return_true 184 184 185 185 let rename_credential ~id ~did ~name db = 186 - let%lwt () = Util.use_pool db @@ Queries.rename_passkey ~id ~did ~name in 186 + let%lwt () = Util.Sqlite.use_pool db @@ Queries.rename_passkey ~id ~did ~name in 187 187 Lwt.return_true 188 188 189 189 let generate_registration_options ~did ~email ~existing_credentials db =
+2 -2
pegasus/lib/plc.ml
··· 1 1 open Cohttp 2 2 open Cohttp_lwt 3 3 open Cohttp_lwt_unix 4 - open Util.Did_doc_types 4 + open Util.Types 5 5 6 6 let default_endpoint = "https://plc.directory" 7 7 ··· 278 278 did 279 279 in 280 280 let headers = Http.Header.init_with "Accept" "application/json" in 281 - let%lwt res, body = Util.http_get ~headers uri in 281 + let%lwt res, body = Util.Http.get ~headers uri in 282 282 match res.status with 283 283 | `OK -> 284 284 let%lwt body = Body.to_string body in
+2 -2
pegasus/lib/repository.ml
··· 577 577 in 578 578 let record_data = List.rev record_data in 579 579 let%lwt _ = 580 - Util.use_pool ~timeout:600. t.db.db (fun conn -> 581 - Util.transact conn (fun () -> 580 + Util.Sqlite.use_pool ~timeout:600. t.db.db (fun conn -> 581 + Util.Sqlite.transact conn (fun () -> 582 582 let$! _ = User_store.Queries.put_commit root commit_bytes conn in 583 583 let$! () = User_store.Queries.clear_mst conn in 584 584 let$! () = User_store.Bulk.put_blocks mst_blocks conn in
+16 -16
pegasus/lib/security_key.ml
··· 182 182 183 183 let setup_security_key ~did ~name db = 184 184 let secret = generate_secret () in 185 - let now = Util.now_ms () in 185 + let now = Util.Time.now_ms () in 186 186 let%lwt () = 187 - Util.use_pool db 187 + Util.Sqlite.use_pool db 188 188 @@ Queries.insert_security_key ~did ~name ~secret ~counter:0 ~created_at:now 189 189 in 190 - let%lwt id = Util.use_pool db @@ Queries.get_last_insert_id () in 190 + let%lwt id = Util.Sqlite.use_pool db @@ Queries.get_last_insert_id () in 191 191 let issuer = "Pegasus PDS (" ^ Env.hostname ^ ")" in 192 192 let uri = make_provisioning_uri ~secret ~account:did ~issuer in 193 193 let secret_b32 = ··· 196 196 Lwt.return (id, secret_b32, uri) 197 197 198 198 let verify_setup ~id ~did ~code db = 199 - match%lwt Util.use_pool db @@ Queries.get_security_key_by_id id did with 199 + match%lwt Util.Sqlite.use_pool db @@ Queries.get_security_key_by_id id did with 200 200 | None -> 201 201 Lwt.return_error "Security key not found" 202 202 | Some sk -> ( ··· 209 209 | Error msg -> 210 210 Lwt.return_error msg 211 211 | Ok new_counter -> 212 - let now = Util.now_ms () in 212 + let now = Util.Time.now_ms () in 213 213 let%lwt () = 214 - Util.use_pool db 214 + Util.Sqlite.use_pool db 215 215 @@ Queries.verify_security_key ~id ~did ~verified_at:now 216 216 ~counter:new_counter 217 217 in ··· 219 219 220 220 let verify_login ~did ~code db = 221 221 let%lwt keys = 222 - Util.use_pool db @@ Queries.get_verified_security_keys_by_did ~did 222 + Util.Sqlite.use_pool db @@ Queries.get_verified_security_keys_by_did ~did 223 223 in 224 224 let rec try_keys = function 225 225 | [] -> ··· 229 229 | Error _ -> 230 230 try_keys rest 231 231 | Ok new_counter -> 232 - let now = Util.now_ms () in 232 + let now = Util.Time.now_ms () in 233 233 let%lwt () = 234 - Util.use_pool db 234 + Util.Sqlite.use_pool db 235 235 @@ Queries.update_counter_and_last_used ~id:sk.id 236 236 ~counter:new_counter ~last_used_at:now 237 237 in ··· 240 240 try_keys keys 241 241 242 242 let resync_key ~id ~did ~code1 ~code2 db = 243 - match%lwt Util.use_pool db @@ Queries.get_security_key_by_id id did with 243 + match%lwt Util.Sqlite.use_pool db @@ Queries.get_security_key_by_id id did with 244 244 | None -> 245 245 Lwt.return_error "Security key not found" 246 246 | Some sk -> ( ··· 254 254 Lwt.return_error msg 255 255 | Ok new_counter -> 256 256 let%lwt () = 257 - Util.use_pool db 257 + Util.Sqlite.use_pool db 258 258 @@ Queries.update_counter ~id:sk.id ~counter:new_counter 259 259 in 260 260 Lwt.return_ok () ) 261 261 262 262 let get_keys_for_user ~did db = 263 - Util.use_pool db @@ Queries.get_security_keys_by_did ~did 263 + Util.Sqlite.use_pool db @@ Queries.get_security_keys_by_did ~did 264 264 265 265 let delete_key ~id ~did db = 266 - let%lwt () = Util.use_pool db @@ Queries.delete_security_key ~id ~did in 266 + let%lwt () = Util.Sqlite.use_pool db @@ Queries.delete_security_key ~id ~did in 267 267 Lwt.return_true 268 268 269 269 let has_security_keys ~did db = 270 - match%lwt Util.use_pool db @@ Queries.has_security_keys ~did with 270 + match%lwt Util.Sqlite.use_pool db @@ Queries.has_security_keys ~did with 271 271 | Some _ -> 272 272 Lwt.return_true 273 273 | None -> 274 274 Lwt.return_false 275 275 276 276 let count_security_keys ~did db = 277 - Util.use_pool db @@ Queries.count_security_keys ~did 277 + Util.Sqlite.use_pool db @@ Queries.count_security_keys ~did 278 278 279 279 let count_verified_security_keys ~did db = 280 - Util.use_pool db @@ Queries.count_verified_security_keys ~did 280 + Util.Sqlite.use_pool db @@ Queries.count_verified_security_keys ~did
+11 -11
pegasus/lib/sequencer.ml
··· 444 444 in 445 445 match kind_result with 446 446 | Ok kind -> 447 - Ok {seq= dbe.seq; time= Util.ms_to_iso8601 dbe.time; kind} 447 + Ok {seq= dbe.seq; time= Util.Time.ms_to_iso8601 dbe.time; kind} 448 448 | Error e -> 449 449 Error ("failed to parse event: " ^ e) ) 450 450 | Error _ -> ··· 458 458 459 459 let queue_max = 1000 460 460 461 - let notify_interval = 20 * Util.minute 461 + let notify_interval = 20 * Util.Time.minute 462 462 463 463 let ring : item array = Array.make ring_size {seq= 0; bytes= Bytes.empty} 464 464 ··· 486 486 head_seq := it.seq ; 487 487 ring.(it.seq mod ring_size) <- it ; 488 488 if !count < ring_size then incr count ; 489 - let now = Util.now_ms () in 489 + let now = Util.Time.now_ms () in 490 490 if now - !last_notified > notify_interval then begin 491 491 last_notified := now ; 492 492 List.iter ··· 726 726 let sequence_commit (conn : Data_store.t) ~(did : string) ~(commit : Cid.t) 727 727 ~(rev : string) ?since ~(blocks : bytes) ~(ops : commit_evt_op list) 728 728 ?(prev_data : Cid.t option) () : int Lwt.t = 729 - let time_ms = Util.now_ms () in 730 - let time_iso = Util.ms_to_iso8601 time_ms in 729 + let time_ms = Util.Time.now_ms () in 730 + let time_iso = Util.Time.ms_to_iso8601 time_ms in 731 731 let evt : commit_evt = 732 732 { rebase= false 733 733 ; too_big= false ··· 748 748 749 749 let sequence_sync (conn : Data_store.t) ~(did : string) ~(rev : string) 750 750 ~(blocks : bytes) () : int Lwt.t = 751 - let time_ms = Util.now_ms () in 752 - let time_iso = Util.ms_to_iso8601 time_ms in 751 + let time_ms = Util.Time.now_ms () in 752 + let time_iso = Util.Time.ms_to_iso8601 time_ms in 753 753 let evt : sync_evt = {did; rev; blocks} in 754 754 let raw = Dag_cbor.encode_yojson @@ Encode.format_sync evt in 755 755 let%lwt seq = DB.append_event conn ~t:`Sync ~time:time_ms ~data:raw in ··· 759 759 760 760 let sequence_identity (conn : Data_store.t) ~(did : string) 761 761 ?(handle : string option) () : int Lwt.t = 762 - let time_ms = Util.now_ms () in 763 - let time_iso = Util.ms_to_iso8601 time_ms in 762 + let time_ms = Util.Time.now_ms () in 763 + let time_iso = Util.Time.ms_to_iso8601 time_ms in 764 764 let evt : identity_evt = {did; handle} in 765 765 let raw = Dag_cbor.encode_yojson @@ Encode.format_identity evt in 766 766 let%lwt seq = DB.append_event conn ~t:`Identity ~time:time_ms ~data:raw in ··· 770 770 771 771 let sequence_account (conn : Data_store.t) ~(did : string) ~(active : bool) 772 772 ?(status : account_status option) () : int Lwt.t = 773 - let time_ms = Util.now_ms () in 774 - let time_iso = Util.ms_to_iso8601 time_ms in 773 + let time_ms = Util.Time.now_ms () in 774 + let time_iso = Util.Time.ms_to_iso8601 time_ms in 775 775 let evt : account_evt = {did; active; status} in 776 776 let raw = Dag_cbor.encode_yojson @@ Encode.format_account evt in 777 777 let%lwt seq = DB.append_event conn ~t:`Account ~time:time_ms ~data:raw in
+1 -1
pegasus/lib/session.ml
··· 198 198 Lwt.return_some 199 199 { actor with 200 200 avatar_data_uri= 201 - Some (Util.make_data_uri ~mimetype ~data) } 201 + Some (Util.Html.make_data_uri ~mimetype ~data) } 202 202 | _ -> 203 203 Lwt.return_some actor ) 204 204 | _ ->
+13 -13
pegasus/lib/totp.ml
··· 97 97 with _ -> false 98 98 99 99 let store_codes ~did ~codes db = 100 - let now = Util.now_ms () in 100 + let now = Util.Time.now_ms () in 101 101 Lwt_list.iter_s 102 102 (fun code -> 103 103 let code_hash = hash_code code in 104 - Util.use_pool db 104 + Util.Sqlite.use_pool db 105 105 @@ Queries.insert_backup_code ~did ~code_hash ~created_at:now ) 106 106 codes 107 107 108 108 let regenerate ~did db = 109 - let%lwt () = Util.use_pool db @@ Queries.delete_backup_codes_by_did ~did in 109 + let%lwt () = Util.Sqlite.use_pool db @@ Queries.delete_backup_codes_by_did ~did in 110 110 let codes = generate_codes () in 111 111 let%lwt () = store_codes ~did ~codes db in 112 112 Lwt.return (List.map format_code codes) ··· 114 114 let verify_and_consume ~did ~code db = 115 115 let normalized_code = normalize_code code in 116 116 let%lwt codes = 117 - Util.use_pool db @@ Queries.get_unused_backup_codes_by_did ~did 117 + Util.Sqlite.use_pool db @@ Queries.get_unused_backup_codes_by_did ~did 118 118 in 119 119 let rec check = function 120 120 | [] -> 121 121 Lwt.return_false 122 122 | c :: rest -> 123 123 if verify_code_hash normalized_code c.code_hash then 124 - let now = Util.now_ms () in 124 + let now = Util.Time.now_ms () in 125 125 let%lwt () = 126 - Util.use_pool db 126 + Util.Sqlite.use_pool db 127 127 @@ Queries.mark_code_used ~id:c.id ~did ~used_at:now 128 128 in 129 129 Lwt.return_true ··· 132 132 check codes 133 133 134 134 let get_remaining_count ~did db = 135 - Util.use_pool db @@ Queries.count_unused_backup_codes ~did 135 + Util.Sqlite.use_pool db @@ Queries.count_unused_backup_codes ~did 136 136 137 137 let has_backup_codes ~did db = 138 138 let%lwt count = get_remaining_count ~did db in ··· 244 244 check 0 245 245 246 246 let create_secret ~did ~secret db = 247 - Util.use_pool db @@ Queries.set_totp_secret ~did ~secret 247 + Util.Sqlite.use_pool db @@ Queries.set_totp_secret ~did ~secret 248 248 249 249 let get_secret ~did db = 250 - match%lwt Util.use_pool db @@ Queries.get_totp_secret ~did with 250 + match%lwt Util.Sqlite.use_pool db @@ Queries.get_totp_secret ~did with 251 251 | Some (Some secret, verified_at) -> 252 252 Lwt.return_some (secret, verified_at) 253 253 | _ -> ··· 261 261 Lwt.return_error "TOTP is already enabled" 262 262 | Some (secret, None) -> 263 263 if verify_code ~secret ~code then 264 - let now = Util.now_ms () in 264 + let now = Util.Time.now_ms () in 265 265 let%lwt () = 266 - Util.use_pool db @@ Queries.verify_totp_secret ~did ~verified_at:now 266 + Util.Sqlite.use_pool db @@ Queries.verify_totp_secret ~did ~verified_at:now 267 267 in 268 268 Lwt.return_ok () 269 269 else Lwt.return_error "Invalid verification code" 270 270 271 - let disable ~did db = Util.use_pool db @@ Queries.clear_totp_secret ~did 271 + let disable ~did db = Util.Sqlite.use_pool db @@ Queries.clear_totp_secret ~did 272 272 273 273 let is_enabled ~did db = 274 - match%lwt Util.use_pool db @@ Queries.is_totp_enabled ~did with 274 + match%lwt Util.Sqlite.use_pool db @@ Queries.is_totp_enabled ~did with 275 275 | Some _ -> 276 276 Lwt.return_true 277 277 | None ->
+1 -1
pegasus/lib/ttl_cache.ml
··· 10 10 11 11 let default_initial_capacity = 16 12 12 13 - let[@inline] _now_ms () : time_ms = Util.now_ms () 13 + let[@inline] _now_ms () : time_ms = Util.Time.now_ms () 14 14 15 15 let create ?capacity ?(initial_capacity = default_initial_capacity) 16 16 default_ttl () : 'a t =
+16 -16
pegasus/lib/two_factor.ml
··· 104 104 Base64.(encode_string ~alphabet:uri_safe_alphabet ~pad:false token) 105 105 106 106 let is_2fa_enabled ~did db = 107 - match%lwt Util.use_pool db @@ Queries.is_2fa_enabled ~did with 107 + match%lwt Util.Sqlite.use_pool db @@ Queries.is_2fa_enabled ~did with 108 108 | Some 1 -> 109 109 Lwt.return_true 110 110 | _ -> ··· 113 113 let get_status ~did db = 114 114 let%lwt totp_enabled = Totp.is_enabled ~did db in 115 115 let%lwt email_2fa = 116 - match%lwt Util.use_pool db @@ Queries.get_email_2fa_enabled ~did with 116 + match%lwt Util.Sqlite.use_pool db @@ Queries.get_email_2fa_enabled ~did with 117 117 | Some 1 -> 118 118 Lwt.return_true 119 119 | _ -> ··· 132 132 let get_available_methods ~did db = 133 133 let%lwt totp_enabled = Totp.is_enabled ~did db in 134 134 let%lwt email_2fa = 135 - match%lwt Util.use_pool db @@ Queries.get_email_2fa_enabled ~did with 135 + match%lwt Util.Sqlite.use_pool db @@ Queries.get_email_2fa_enabled ~did with 136 136 | Some 1 -> 137 137 Lwt.return_true 138 138 | _ -> ··· 149 149 (* create a pending 2FA session after password verification *) 150 150 let create_pending_session ~did db = 151 151 let session_token = generate_session_token () in 152 - let now = Util.now_ms () in 152 + let now = Util.Time.now_ms () in 153 153 let expires_at = now + pending_session_expiry_ms in 154 154 let%lwt () = 155 - Util.use_pool db 155 + Util.Sqlite.use_pool db 156 156 @@ Queries.insert_pending_2fa ~session_token ~did ~password_verified_at:now 157 157 ~expires_at ~created_at:now 158 158 in 159 159 Lwt.return session_token 160 160 161 161 let get_pending_session ~session_token db = 162 - let now = Util.now_ms () in 163 - Util.use_pool db @@ Queries.get_pending_2fa session_token now 162 + let now = Util.Time.now_ms () in 163 + Util.Sqlite.use_pool db @@ Queries.get_pending_2fa session_token now 164 164 165 165 let get_pending_session_for_did ~did db = 166 - let now = Util.now_ms () in 167 - Util.use_pool db @@ Queries.get_pending_2fa_for_did did now 166 + let now = Util.Time.now_ms () in 167 + Util.Sqlite.use_pool db @@ Queries.get_pending_2fa_for_did did now 168 168 169 169 let delete_pending_session ~session_token db = 170 - Util.use_pool db @@ Queries.delete_pending_2fa ~session_token 170 + Util.Sqlite.use_pool db @@ Queries.delete_pending_2fa ~session_token 171 171 172 172 let send_email_code ~session_token ~actor db = 173 173 let code = Util.make_code () in 174 - let now = Util.now_ms () in 174 + let now = Util.Time.now_ms () in 175 175 let expires_at = now + email_code_expiry_ms in 176 176 let%lwt () = 177 - Util.use_pool db 177 + Util.Sqlite.use_pool db 178 178 @@ Queries.update_email_code ~session_token ~email_code:code 179 179 ~email_code_expires_at:expires_at 180 180 in ··· 189 189 let _verify_email_code ~code ~session = 190 190 match (session.email_code, session.email_code_expires_at) with 191 191 | Some stored_code, Some expires_at -> 192 - let now = Util.now_ms () in 192 + let now = Util.Time.now_ms () in 193 193 if now > expires_at then Lwt.return_error "Email code expired" 194 194 else if stored_code = code then Lwt.return_ok session.did 195 195 else Lwt.return_error "Invalid code" ··· 231 231 else Lwt.return_error "Invalid backup code" 232 232 233 233 let enable_email_2fa ~did db = 234 - Util.use_pool db @@ Queries.set_email_2fa_enabled ~did ~enabled:1 234 + Util.Sqlite.use_pool db @@ Queries.set_email_2fa_enabled ~did ~enabled:1 235 235 236 236 let disable_email_2fa ~did db = 237 - Util.use_pool db @@ Queries.set_email_2fa_enabled ~did ~enabled:0 237 + Util.Sqlite.use_pool db @@ Queries.set_email_2fa_enabled ~did ~enabled:0 238 238 239 239 let is_email_2fa_enabled ~did db = 240 - match%lwt Util.use_pool db @@ Queries.get_email_2fa_enabled ~did with 240 + match%lwt Util.Sqlite.use_pool db @@ Queries.get_email_2fa_enabled ~did with 241 241 | Some 1 -> 242 242 Lwt.return_true 243 243 | _ ->
+40 -40
pegasus/lib/user_store.ml
··· 457 457 process_chunks chunks 458 458 end 459 459 460 - type t = {did: string; db: Util.caqti_pool} 460 + type t = {did: string; db: Util.Sqlite.caqti_pool} 461 461 462 462 let pool_cache : (string, t) Hashtbl.t = Hashtbl.create 64 463 463 ··· 475 475 Lwt.return cached 476 476 | None -> 477 477 let%lwt db = 478 - Util.connect_sqlite ?create ~write:true 478 + Util.Sqlite.connect ?create ~write:true 479 479 (Util.Constants.user_db_location did) 480 480 in 481 481 let%lwt () = Migrations.run_migrations User_store db in ··· 487 487 if create = Some true then 488 488 Util.mkfile_p (Util.Constants.user_db_filepath did) ~perm:0o644 ; 489 489 let%lwt db = 490 - Util.connect_sqlite ?create ~write:false 490 + Util.Sqlite.connect ?create ~write:false 491 491 (Util.Constants.user_db_location did) 492 492 in 493 493 let%lwt () = Migrations.run_migrations User_store db in ··· 496 496 (* mst blocks; implements Writable_blockstore *) 497 497 498 498 let get_bytes t cid : Blob.t option Lwt.t = 499 - Util.use_pool t.db @@ Queries.get_block cid 499 + Util.Sqlite.use_pool t.db @@ Queries.get_block cid 500 500 >|= function Some {data; _} -> Some data | None -> None 501 501 502 502 let get_blocks t cids : Block_map.with_missing Lwt.t = 503 503 if List.is_empty cids then 504 504 Lwt.return ({blocks= Block_map.empty; missing= []} : Block_map.with_missing) 505 505 else 506 - let%lwt blocks = Util.use_pool t.db @@ Queries.get_blocks cids in 506 + let%lwt blocks = Util.Sqlite.use_pool t.db @@ Queries.get_blocks cids in 507 507 let found_map = 508 508 List.fold_left 509 509 (fun acc ({cid; data} : block) -> Block_map.set cid data acc) ··· 521 521 cids ) 522 522 523 523 let has t cid : bool Lwt.t = 524 - Util.use_pool t.db @@ Queries.has_block cid 524 + Util.Sqlite.use_pool t.db @@ Queries.has_block cid 525 525 >|= function Some _ -> true | None -> false 526 526 527 527 let put_block t cid block : (bool, exn) Lwt_result.t = 528 528 Lwt_result.catch 529 529 @@ fun () -> 530 - match%lwt Util.use_pool t.db @@ Queries.put_block cid block with 530 + match%lwt Util.Sqlite.use_pool t.db @@ Queries.put_block cid block with 531 531 | Some _ -> 532 532 Lwt.return true 533 533 | None -> ··· 539 539 else 540 540 Lwt_result.catch (fun () -> 541 541 let%lwt () = 542 - Util.use_pool t.db (fun conn -> Bulk.put_blocks entries conn) 542 + Util.Sqlite.use_pool t.db (fun conn -> Bulk.put_blocks entries conn) 543 543 in 544 544 Lwt.return (List.length entries) ) 545 545 546 546 let delete_block t cid : (bool, exn) Lwt_result.t = 547 547 Lwt_result.catch 548 - @@ fun () -> Util.use_pool t.db @@ Queries.delete_block cid >|= fun _ -> true 548 + @@ fun () -> Util.Sqlite.use_pool t.db @@ Queries.delete_block cid >|= fun _ -> true 549 549 550 550 let delete_many t cids : (int, exn) Lwt_result.t = 551 551 Lwt_result.catch 552 - @@ fun () -> Util.use_pool t.db @@ Queries.delete_blocks cids >|= List.length 552 + @@ fun () -> Util.Sqlite.use_pool t.db @@ Queries.delete_blocks cids >|= List.length 553 553 554 554 let clear_mst t : unit Lwt.t = 555 - let%lwt () = Util.use_pool t.db Queries.clear_mst in 555 + let%lwt () = Util.Sqlite.use_pool t.db Queries.clear_mst in 556 556 Lwt.return_unit 557 557 558 558 (* mst misc *) 559 559 560 - let count_blocks t : int Lwt.t = Util.use_pool t.db @@ Queries.count_blocks () 560 + let count_blocks t : int Lwt.t = Util.Sqlite.use_pool t.db @@ Queries.count_blocks () 561 561 562 562 (* repo commit *) 563 563 564 564 let get_commit t : (Cid.t * signed_commit) option Lwt.t = 565 - let%lwt commit = Util.use_pool t.db Queries.get_commit in 565 + let%lwt commit = Util.Sqlite.use_pool t.db Queries.get_commit in 566 566 Lwt.return 567 567 @@ Option.map 568 568 (fun (cid, data) -> ··· 575 575 let data = commit |> signed_commit_to_yojson |> Dag_cbor.encode_yojson in 576 576 let cid = Cid.create Dcbor data in 577 577 ( Lwt_result.catch 578 - @@ fun () -> Util.use_pool t.db @@ Queries.put_commit cid data ) 578 + @@ fun () -> Util.Sqlite.use_pool t.db @@ Queries.put_commit cid data ) 579 579 |> Lwt_result.map (fun () -> cid) 580 580 581 581 (* records *) 582 582 583 583 let get_record t path : record option Lwt.t = 584 - Util.use_pool t.db @@ Queries.get_record ~path 584 + Util.Sqlite.use_pool t.db @@ Queries.get_record ~path 585 585 >|= Option.map (fun (cid, data, since) -> 586 586 {path; cid; value= Lex.of_cbor data; since} ) 587 587 588 588 let get_record_cid t path : Cid.t option Lwt.t = 589 - Util.use_pool t.db @@ Queries.get_record_cid ~path 589 + Util.Sqlite.use_pool t.db @@ Queries.get_record_cid ~path 590 590 591 591 let get_all_record_cids t : (string * Cid.t) list Lwt.t = 592 - Util.use_pool t.db Queries.get_all_record_cids 592 + Util.Sqlite.use_pool t.db Queries.get_all_record_cids 593 593 594 594 let get_records_by_cids t cids : (Cid.t * Blob.t) list Lwt.t = 595 595 if List.is_empty cids then Lwt.return [] 596 596 else 597 - Util.use_pool t.db @@ Queries.get_records_by_cids cids 597 + Util.Sqlite.use_pool t.db @@ Queries.get_records_by_cids cids 598 598 >|= List.map (fun ({cid; data} : block) -> (cid, data)) 599 599 600 600 let list_records t ?(limit = 100) ?(cursor = "") ?(reverse = false) collection : ··· 602 602 let fn = 603 603 if reverse then Queries.list_records_reverse else Queries.list_records 604 604 in 605 - Util.use_pool t.db @@ fn ~collection ~limit ~cursor 605 + Util.Sqlite.use_pool t.db @@ fn ~collection ~limit ~cursor 606 606 >|= List.map (fun (path, cid, data, since) -> 607 607 {path; cid; value= Lex.of_cbor data; since} ) 608 608 609 - let count_records t : int Lwt.t = Util.use_pool t.db @@ Queries.count_records () 609 + let count_records t : int Lwt.t = Util.Sqlite.use_pool t.db @@ Queries.count_records () 610 610 611 611 let list_collections t : string list Lwt.t = 612 - Util.use_pool t.db @@ Queries.list_collections 612 + Util.Sqlite.use_pool t.db @@ Queries.list_collections 613 613 614 614 let put_record t record path : (Cid.t * bytes) Lwt.t = 615 615 let cid, data = Lex.to_cbor_block record in 616 616 let since = Tid.now () in 617 617 let%lwt () = 618 - Util.use_pool t.db @@ Queries.put_record ~path ~cid ~data ~since 618 + Util.Sqlite.use_pool t.db @@ Queries.put_record ~path ~cid ~data ~since 619 619 in 620 620 Lwt.return (cid, data) 621 621 622 622 let put_record_raw t ~path ~cid ~data ~since : unit Lwt.t = 623 - Util.use_pool t.db @@ Queries.put_record ~path ~cid ~data ~since 623 + Util.Sqlite.use_pool t.db @@ Queries.put_record ~path ~cid ~data ~since 624 624 625 625 let delete_record t path : unit Lwt.t = 626 - Util.use_pool t.db (fun conn -> 627 - Util.transact conn (fun () -> 626 + Util.Sqlite.use_pool t.db (fun conn -> 627 + Util.Sqlite.transact conn (fun () -> 628 628 let del = Queries.delete_record path conn in 629 629 let$! () = del in 630 630 let$! deleted_blobs = ··· 642 642 (* blobs *) 643 643 644 644 let get_blob t cid : blob_with_contents option Lwt.t = 645 - match%lwt Util.use_pool t.db @@ Queries.get_blob ~cid with 645 + match%lwt Util.Sqlite.use_pool t.db @@ Queries.get_blob ~cid with 646 646 | None -> 647 647 Lwt.return_none 648 648 | Some (cid, mimetype, storage_str) -> ( ··· 655 655 Lwt.return_none ) 656 656 657 657 let get_blob_metadata t cid : blob option Lwt.t = 658 - match%lwt Util.use_pool t.db @@ Queries.get_blob ~cid with 658 + match%lwt Util.Sqlite.use_pool t.db @@ Queries.get_blob ~cid with 659 659 | None -> 660 660 Lwt.return_none 661 661 | Some (cid, mimetype, storage_str) -> ··· 663 663 Lwt.return_some {cid; mimetype; storage} 664 664 665 665 let list_blobs ?since t ~limit ~cursor : Cid.t list Lwt.t = 666 - Util.use_pool t.db 666 + Util.Sqlite.use_pool t.db 667 667 @@ 668 668 match since with 669 669 | Some since -> ··· 673 673 674 674 let list_missing_blobs ?(limit = 500) ?(cursor = "") t : 675 675 (string * Cid.t) list Lwt.t = 676 - Util.use_pool t.db @@ Queries.list_missing_blobs ~limit ~cursor 676 + Util.Sqlite.use_pool t.db @@ Queries.list_missing_blobs ~limit ~cursor 677 677 678 - let count_blobs t : int Lwt.t = Util.use_pool t.db @@ Queries.count_blobs () 678 + let count_blobs t : int Lwt.t = Util.Sqlite.use_pool t.db @@ Queries.count_blobs () 679 679 680 680 let count_referenced_blobs t : int Lwt.t = 681 - Util.use_pool t.db @@ Queries.count_referenced_blobs () 681 + Util.Sqlite.use_pool t.db @@ Queries.count_referenced_blobs () 682 682 683 683 let put_blob t cid mimetype data : Cid.t Lwt.t = 684 684 let%lwt storage = Blob_store.put ~did:t.did ~cid ~data in 685 685 let storage_str = Blob_store.storage_to_string storage in 686 - Util.use_pool t.db @@ Queries.put_blob cid mimetype storage_str 686 + Util.Sqlite.use_pool t.db @@ Queries.put_blob cid mimetype storage_str 687 687 688 688 let delete_blob t cid : unit Lwt.t = 689 689 let%lwt blob_opt = get_blob_metadata t cid in ··· 692 692 delete_blob_file ~did:t.did ~cid ~storage 693 693 | None -> 694 694 () ) ; 695 - Util.use_pool t.db @@ Queries.delete_blob cid 695 + Util.Sqlite.use_pool t.db @@ Queries.delete_blob cid 696 696 697 697 let delete_orphaned_blobs_by_record_path t path : 698 698 (Cid.t * Blob_store.storage) list Lwt.t = 699 699 let%lwt results = 700 - Util.use_pool t.db @@ Queries.delete_orphaned_blobs_by_record_path path 700 + Util.Sqlite.use_pool t.db @@ Queries.delete_orphaned_blobs_by_record_path path 701 701 in 702 702 Lwt.return 703 703 @@ List.map ··· 706 706 results 707 707 708 708 let list_blob_refs t path : Cid.t list Lwt.t = 709 - Util.use_pool t.db @@ Queries.list_blob_refs path 709 + Util.Sqlite.use_pool t.db @@ Queries.list_blob_refs path 710 710 711 711 let put_blob_ref t path cid : unit Lwt.t = 712 - Util.use_pool t.db @@ Queries.put_blob_ref path cid 712 + Util.Sqlite.use_pool t.db @@ Queries.put_blob_ref path cid 713 713 714 714 let put_blob_refs t path cids : (unit, exn) Lwt_result.t = 715 715 if List.is_empty cids then Lwt.return_ok () 716 716 else 717 717 Lwt_result.map (fun _ -> ()) 718 - @@ Util.multi_query t.db 718 + @@ Util.Sqlite.multi_query t.db 719 719 (List.map (fun cid -> Queries.put_blob_ref cid path) cids) 720 720 721 721 let clear_blob_refs t path cids : unit Lwt.t = 722 722 if List.is_empty cids then Lwt.return_unit 723 - else Util.use_pool t.db @@ Queries.clear_blob_refs path cids 723 + else Util.Sqlite.use_pool t.db @@ Queries.clear_blob_refs path cids 724 724 725 725 let update_blob_storage t cid storage : unit Lwt.t = 726 726 let storage_str = Blob_store.storage_to_string storage in 727 - Util.use_pool t.db @@ Queries.update_blob_storage cid storage_str 727 + Util.Sqlite.use_pool t.db @@ Queries.update_blob_storage cid storage_str 728 728 729 729 let list_blobs_by_storage t ~storage ~limit ~cursor : 730 730 (Cid.t * string) list Lwt.t = 731 731 let storage_str = Blob_store.storage_to_string storage in 732 - Util.use_pool t.db 732 + Util.Sqlite.use_pool t.db 733 733 @@ Queries.list_blobs_by_storage ~storage:storage_str ~limit ~cursor
-600
pegasus/lib/util.ml
··· 1 - module Constants = struct 2 - let data_dir = 3 - Core.Filename.to_absolute_exn Env.data_dir 4 - ~relative_to:(Core_unix.getcwd ()) 5 - 6 - let pegasus_db_filepath = Filename.concat data_dir "pegasus.db" 7 - 8 - let pegasus_db_location = "sqlite3://" ^ pegasus_db_filepath |> Uri.of_string 9 - 10 - let user_db_filepath did = 11 - let dirname = Filename.concat data_dir "store" in 12 - let filename = Str.global_replace (Str.regexp ":") "_" did in 13 - Filename.concat dirname filename ^ ".db" 14 - 15 - let user_db_location did = 16 - "sqlite3://" ^ user_db_filepath did |> Uri.of_string 17 - 18 - let user_blobs_location did = 19 - did 20 - |> Str.global_replace (Str.regexp ":") "_" 21 - |> (Filename.concat data_dir "blobs" |> Filename.concat) 22 - end 23 - 24 - module Syntax = struct 25 - let unwrap m = 26 - match%lwt m with 27 - | Ok x -> 28 - Lwt.return x 29 - | Error e -> 30 - raise (Caqti_error.Exn e) 31 - 32 - (* unwraps an Lwt result, raising an exception if there's an error *) 33 - let ( let$! ) m f = 34 - match%lwt m with Ok x -> f x | Error e -> raise (Caqti_error.Exn e) 35 - 36 - (* unwraps an Lwt result, raising an exception if there's an error *) 37 - let ( >$! ) m f = 38 - match%lwt m with 39 - | Ok x -> 40 - Lwt.return (f x) 41 - | Error e -> 42 - raise (Caqti_error.Exn e) 43 - end 44 - 45 - module Rapper = struct 46 - module CID : Rapper.CUSTOM with type t = Cid.t = struct 47 - type t = Cid.t 48 - 49 - let t = 50 - let encode cid = 51 - try Ok (Cid.to_string cid) with e -> Error (Printexc.to_string e) 52 - in 53 - Caqti_type.(custom ~encode ~decode:Cid.of_string string) 54 - end 55 - 56 - module Blob : Rapper.CUSTOM with type t = bytes = struct 57 - type t = bytes 58 - 59 - let t = 60 - let encode blob = 61 - try Ok (Bytes.to_string blob) with e -> Error (Printexc.to_string e) 62 - in 63 - let decode blob = 64 - try Ok (Bytes.of_string blob) with e -> Error (Printexc.to_string e) 65 - in 66 - Caqti_type.(custom ~encode ~decode string) 67 - end 68 - 69 - module Json : Rapper.CUSTOM with type t = Yojson.Safe.t = struct 70 - type t = Yojson.Safe.t 71 - 72 - let t = 73 - let encode json = 74 - try Ok (Yojson.Safe.to_string json ~std:true) 75 - with e -> Error (Printexc.to_string e) 76 - in 77 - let decode json = 78 - try Ok (Yojson.Safe.from_string json) 79 - with e -> Error (Printexc.to_string e) 80 - in 81 - Caqti_type.(custom ~encode ~decode string) 82 - end 83 - end 84 - 85 - module Did_doc_types = struct 86 - type string_or_null = string option 87 - 88 - let string_or_null_to_yojson = function Some s -> `String s | None -> `Null 89 - 90 - let string_or_null_of_yojson = function 91 - | `String s -> 92 - Ok (Some s) 93 - | `Null -> 94 - Ok None 95 - | _ -> 96 - Error "invalid field value" 97 - 98 - type string_or_strings = [`String of string | `Strings of string list] 99 - 100 - let string_or_strings_to_yojson = function 101 - | `String c -> 102 - `String c 103 - | `Strings cs -> 104 - `List (List.map (fun c -> `String c) cs) 105 - 106 - let string_or_strings_of_yojson = function 107 - | `String c -> 108 - Ok (`Strings [c]) 109 - | `List cs -> 110 - Ok (`Strings (Yojson.Safe.Util.filter_string cs)) 111 - | _ -> 112 - Error "invalid field value" 113 - 114 - type string_map = (string * string) list 115 - 116 - let string_map_to_yojson = function 117 - | [] -> 118 - `Assoc [] 119 - | m -> 120 - `Assoc (List.map (fun (k, v) -> (k, `String v)) m) 121 - 122 - let string_map_of_yojson = function 123 - | `Null -> 124 - Ok [] 125 - | `Assoc m -> 126 - Ok 127 - (List.filter_map 128 - (fun (k, v) -> 129 - match (k, v) with _, `String s -> Some (k, s) | _, _ -> None ) 130 - m ) 131 - | _ -> 132 - Error "invalid field value" 133 - 134 - type string_or_string_map = [`String of string | `String_map of string_map] 135 - 136 - let string_or_string_map_to_yojson = function 137 - | `String c -> 138 - `String c 139 - | `String_map m -> 140 - `Assoc (List.map (fun (k, v) -> (k, `String v)) m) 141 - 142 - let string_or_string_map_of_yojson = function 143 - | `String c -> 144 - Ok (`String c) 145 - | `Assoc m -> 146 - string_map_of_yojson (`Assoc m) |> Result.map (fun m -> `String_map m) 147 - | _ -> 148 - Error "invalid field value" 149 - 150 - type string_or_string_map_or_either_list = 151 - [ `String of string 152 - | `String_map of string_map 153 - | `List of string_or_string_map list ] 154 - 155 - let string_or_string_map_or_either_list_to_yojson = function 156 - | `String c -> 157 - `String c 158 - | `String_map m -> 159 - `Assoc (List.map (fun (k, v) -> (k, `String v)) m) 160 - | `List l -> 161 - `List (List.map string_or_string_map_to_yojson l) 162 - 163 - let string_or_string_map_or_either_list_of_yojson = function 164 - | `String c -> 165 - Ok (`String c) 166 - | `Assoc m -> 167 - string_map_of_yojson (`Assoc m) |> Result.map (fun m -> `String_map m) 168 - | `List l -> 169 - Ok 170 - (`List 171 - ( List.map string_or_string_map_of_yojson l 172 - |> List.filter_map (function Ok x -> Some x | Error _ -> None) ) ) 173 - | _ -> 174 - Error "invalid field value" 175 - end 176 - 177 - type caqti_pool = (Caqti_lwt.connection, Caqti_error.t) Caqti_lwt_unix.Pool.t 178 - 179 - (* turns a caqti error into an exception *) 180 - let caqti_result_exn = function 181 - | Ok x -> 182 - Ok x 183 - | Error caqti_err -> 184 - Error (Caqti_error.Exn caqti_err) 185 - 186 - let _init_connection (module Db : Rapper_helper.CONNECTION) : 187 - (unit, Caqti_error.t) Lwt_result.t = 188 - let open Lwt_result.Syntax in 189 - let open Caqti_request.Infix in 190 - let open Caqti_type in 191 - let* _ = 192 - Db.find (((unit ->! string) ~oneshot:true) "PRAGMA journal_mode=WAL") () 193 - in 194 - let* _ = 195 - Db.exec (((unit ->. unit) ~oneshot:true) "PRAGMA foreign_keys=ON") () 196 - in 197 - let* _ = 198 - Db.exec (((unit ->. unit) ~oneshot:true) "PRAGMA synchronous=NORMAL") () 199 - in 200 - let* _ = 201 - Db.find (((unit ->! int) ~oneshot:true) "PRAGMA busy_timeout=5000") () 202 - in 203 - Lwt.return_ok () 204 - 205 - (* creates an sqlite pool *) 206 - let connect_sqlite ?(create = false) ?(write = true) db_uri : caqti_pool Lwt.t = 207 - let uri = 208 - Uri.add_query_params' db_uri 209 - [("create", string_of_bool create); ("write", string_of_bool write)] 210 - in 211 - let pool_config = Caqti_pool_config.create ~max_size:16 ~max_idle_size:4 () in 212 - match 213 - Caqti_lwt_unix.connect_pool ~pool_config ~post_connect:_init_connection uri 214 - with 215 - | Ok pool -> 216 - Lwt.return pool 217 - | Error e -> 218 - raise (Caqti_error.Exn e) 219 - 220 - let with_connection db_uri f = 221 - match%lwt 222 - Caqti_lwt_unix.with_connection db_uri (fun conn -> 223 - match%lwt _init_connection conn with 224 - | Ok () -> 225 - f conn 226 - | Error e -> 227 - Lwt.return_error e ) 228 - with 229 - | Ok result -> 230 - Lwt.return result 231 - | Error e -> 232 - raise (Caqti_error.Exn e) 233 - 234 - let use_pool ?(timeout = 60.0) pool 235 - (f : Caqti_lwt.connection -> ('a, Caqti_error.t) Lwt_result.t) : 'a Lwt.t = 236 - match%lwt 237 - Lwt_unix.with_timeout timeout (fun () -> Caqti_lwt_unix.Pool.use f pool) 238 - with 239 - | Ok res -> 240 - Lwt.return res 241 - | Error e -> 242 - raise (Caqti_error.Exn e) 243 - 244 - let transact conn fn : (unit, 'e) Lwt_result.t = 245 - let module C = (val conn : Caqti_lwt.CONNECTION) in 246 - match%lwt C.start () with 247 - | Ok () -> ( 248 - try%lwt 249 - match%lwt fn () with 250 - | Ok _ -> ( 251 - match%lwt C.commit () with 252 - | Ok () -> 253 - Lwt.return_ok () 254 - | Error e -> ( 255 - match%lwt C.rollback () with 256 - | Ok () -> 257 - Lwt.return_error e 258 - | Error e -> 259 - Lwt.return_error e ) ) 260 - | Error e -> ( 261 - match%lwt C.rollback () with 262 - | Ok () -> 263 - Lwt.return_error e 264 - | Error e -> 265 - Lwt.return_error e ) 266 - with e -> ( 267 - match%lwt C.rollback () with 268 - | Ok () -> 269 - Lwt.return_error 270 - ( match e with 271 - | Caqti_error.Exn e -> 272 - e 273 - | e -> 274 - Caqti_error.request_failed ~query:"unknown" 275 - ~uri:(Uri.of_string "//unknown") 276 - (Caqti_error.Msg (Printexc.to_string e)) ) 277 - | Error e -> 278 - Lwt.return_error e ) ) 279 - | Error e -> 280 - Lwt.return_error e 281 - 282 - (* runs a bunch of queries in a transaction, catches duplicate insertion, returning how many succeeded *) 283 - let multi_query pool 284 - (queries : (Caqti_lwt.connection -> ('a, Caqti_error.t) Lwt_result.t) list) 285 - : (int, exn) Lwt_result.t = 286 - let open Syntax in 287 - Lwt_result.catch (fun () -> 288 - use_pool pool (fun connection -> 289 - let module C = (val connection : Caqti_lwt.CONNECTION) in 290 - let$! () = C.start () in 291 - let is_ignorable_error e = 292 - match (e : Caqti_error.t) with 293 - | `Request_failed qe | `Response_failed qe -> ( 294 - match Caqti_error.cause (`Request_failed qe) with 295 - | `Not_null_violation | `Unique_violation -> 296 - true 297 - | _ -> 298 - false ) 299 - | _ -> 300 - false 301 - in 302 - let rec aux acc queries = 303 - match acc with 304 - | Error e -> 305 - Lwt.return_error e 306 - | Ok count -> ( 307 - match queries with 308 - | [] -> 309 - Lwt.return (Ok count) 310 - | query :: rest -> ( 311 - let%lwt result = query connection in 312 - match result with 313 - | Ok _ -> 314 - aux (Ok (count + 1)) rest 315 - | Error e -> 316 - if is_ignorable_error e then aux (Ok count) rest 317 - else Lwt.return_error e ) ) 318 - in 319 - let%lwt result = aux (Ok 0) queries in 320 - match result with 321 - | Ok count -> 322 - let$! () = C.commit () in 323 - Lwt.return_ok count 324 - | Error e -> 325 - let%lwt _ = C.rollback () in 326 - Lwt.return_error e ) ) 327 - 328 - let minute = 60 * 1000 329 - 330 - let hour = 60 * minute 331 - 332 - let day = 24 * hour 333 - 334 - (* unix timestamp *) 335 - let now_ms () : int = int_of_float (Unix.gettimeofday () *. 1000.) 336 - 337 - let ms_to_iso8601 ms = 338 - let s = float_of_int ms /. 1000. in 339 - Timedesc.(of_timestamp_float_s_exn s |> to_iso8601) 340 - 341 - (* returns all blob refs in a record *) 342 - let rec find_blob_refs (record : Mist.Lex.repo_record) : Mist.Blob_ref.t list = 343 - let rec aux acc entries = 344 - List.fold_left 345 - (fun acc value -> 346 - match value with 347 - | `BlobRef blob -> 348 - blob :: acc 349 - | `LexMap map -> 350 - find_blob_refs map @ acc 351 - | `LexArray arr -> 352 - aux acc (Array.to_list arr) @ acc 353 - | _ -> 354 - acc ) 355 - acc entries 356 - in 357 - aux [] (Mist.Lex.String_map.bindings record |> List.map snd) 358 - |> List.sort_uniq (fun (r1 : Mist.Blob_ref.t) r2 -> Cid.compare r1.ref r2.ref) 359 - 360 - type validate_handle_error = 361 - | InvalidFormat of string 362 - | TooShort of string 363 - | TooLong of string 364 - 365 - let validate_handle handle = 366 - (* if it's a custom domain, just check that it contains a period *) 367 - if not (String.ends_with ~suffix:("." ^ Env.hostname) handle) then 368 - if not (String.contains handle '.') then 369 - Error (InvalidFormat ("must end with " ^ "." ^ Env.hostname)) 370 - else Ok () 371 - else 372 - let front = 373 - String.sub handle 0 374 - (String.length handle - (String.length Env.hostname + 1)) 375 - in 376 - if String.contains front '.' then 377 - Error (InvalidFormat "can't contain periods") 378 - else 379 - match String.length front with 380 - | l when l < 3 -> 381 - Error (TooShort "must be at least 3 characters") 382 - | l when l > 18 -> 383 - Error (TooLong "must be at most 18 characters") 384 - | _ -> 385 - Ok () 386 - 387 - let mkfile_p path ~perm = 388 - Core_unix.mkdir_p (Filename.dirname path) ~perm:0o755 ; 389 - Core_unix.openfile ~mode:[O_CREAT; O_WRONLY] ~perm path |> Core_unix.close 390 - 391 - let sig_matches_some_did_key ~did_keys ~signature ~msg = 392 - List.find_opt 393 - (fun key -> 394 - let raw, (module Curve) = 395 - Kleidos.parse_multikey_str (String.sub key 8 (String.length key - 8)) 396 - in 397 - let valid = 398 - Curve.verify ~pubkey:(Curve.normalize_pubkey_to_raw raw) ~signature ~msg 399 - in 400 - valid ) 401 - did_keys 402 - <> None 403 - 404 - let request_ip req = 405 - Dream.header req "X-Forwarded-For" 406 - |> Option.value ~default:(Dream.client req) 407 - |> String.split_on_char ',' |> List.hd |> String.split_on_char ':' |> List.hd 408 - |> String.trim 409 - 410 - let rec http_get ?(max_redirects = 5) ?(no_drain = false) ?headers uri = 411 - let ua = "pegasus (" ^ Env.host_endpoint ^ ")" in 412 - let headers = 413 - match headers with 414 - | Some headers -> 415 - Http.Header.add_unless_exists headers "User-Agent" ua 416 - | None -> 417 - Http.Header.of_list [("User-Agent", ua)] 418 - in 419 - let%lwt ans = Cohttp_lwt_unix.Client.get ~headers uri in 420 - follow_redirect ~max_redirects ~no_drain uri ans 421 - 422 - and follow_redirect ~max_redirects ~no_drain request_uri (response, body) = 423 - let status = Http.Response.status response in 424 - (* the unconsumed body would otherwise leak memory *) 425 - let%lwt () = 426 - if status <> `OK && not no_drain then Cohttp_lwt.Body.drain_body body 427 - else Lwt.return_unit 428 - in 429 - match status with 430 - | `Permanent_redirect | `Moved_permanently -> 431 - handle_redirect ~permanent:true ~max_redirects request_uri response 432 - | `Found | `Temporary_redirect -> 433 - handle_redirect ~permanent:false ~max_redirects request_uri response 434 - | _ -> 435 - Lwt.return (response, body) 436 - 437 - and handle_redirect ~permanent ~max_redirects request_uri response = 438 - if max_redirects <= 0 then failwith "too many redirects" 439 - else 440 - let headers = Http.Response.headers response in 441 - let location = Http.Header.get headers "location" in 442 - match location with 443 - | None -> 444 - failwith "redirection without Location header" 445 - | Some url -> 446 - let uri = Uri.of_string url in 447 - let%lwt () = 448 - if permanent then 449 - Logs_lwt.warn (fun m -> 450 - m "Permanent redirection from %s to %s" 451 - (Uri.to_string request_uri) 452 - url ) 453 - else Lwt.return_unit 454 - in 455 - http_get uri ~max_redirects:(max_redirects - 1) 456 - 457 - let copy_query req = Dream.all_queries req |> List.map (fun (k, v) -> (k, [v])) 458 - 459 - let make_headers headers = 460 - List.fold_left 461 - (fun headers (k, v) -> 462 - match v with 463 - | Some value -> 464 - Http.Header.add headers k value 465 - | None -> 466 - headers ) 467 - (Http.Header.init ()) headers 468 - 469 - let str_contains ~affix str = 470 - let re = Str.regexp_string affix in 471 - try 472 - ignore (Str.search_forward re str 0) ; 473 - true 474 - with Not_found -> false 475 - 476 - let make_code () = 477 - let () = Mirage_crypto_rng_unix.use_default () in 478 - let token = 479 - Multibase.Base32.encode_string ~pad:false 480 - @@ Mirage_crypto_rng_unix.getrandom 8 481 - in 482 - String.sub token 0 5 ^ "-" ^ String.sub token 5 5 483 - 484 - module type Template = sig 485 - type props 486 - 487 - val props_of_json : Yojson.Basic.t -> props 488 - 489 - val props_to_json : props -> Yojson.Basic.t 490 - 491 - val make : ?key:string -> props:props -> unit -> React.element 492 - end 493 - 494 - let render_html ?status ?title (type props) 495 - (template : (module Template with type props = props)) ~props = 496 - let module Template = (val template : Template with type props = props) in 497 - let props_json = Template.props_to_json props |> Yojson.Basic.to_string in 498 - let page_data = Printf.sprintf "window.__PAGE__ = {props: %s};" props_json in 499 - let app = Template.make ~props () in 500 - let page = 501 - Frontend.Layout.make ?title ~favicon:Env.favicon_url ~children:app () 502 - in 503 - Dream.stream ?status 504 - ~headers:[("Content-Type", "text/html")] 505 - (fun stream -> 506 - [%lwt 507 - let html, subscribe = 508 - ReactServerDOM.render_html ~skipRoot:false 509 - ~bootstrapScriptContent:page_data 510 - ~bootstrapScripts:["/public/client.js"] page 511 - in 512 - [%lwt 513 - let () = Dream.write stream html in 514 - [%lwt 515 - let () = Dream.flush stream in 516 - [%lwt 517 - let () = 518 - subscribe (fun chunk -> 519 - [%lwt 520 - let () = Dream.write stream chunk in 521 - Dream.flush stream] ) 522 - in 523 - Dream.flush stream]]]] ) 524 - 525 - let make_data_uri ~mimetype ~data = 526 - let base64_data = data |> Bytes.to_string |> Base64.encode_string in 527 - Printf.sprintf "data:%s;base64,%s" mimetype base64_data 528 - 529 - let at_uri_regexp = 530 - Re.Pcre.re 531 - {|^at:\/\/([a-zA-Z0-9._:%-]+)(?:\/([a-zA-Z0-9-.]+)(?:\/([a-zA-Z0-9._~:@!$&%')(*+,;=-]+))?)?(?:#(\/[a-zA-Z0-9._~:@!$&%')(*+,;=\-[\]\/\\]*))?$|} 532 - |> Re.compile 533 - 534 - type at_uri = 535 - {repo: string; collection: string; rkey: string; fragment: string option} 536 - 537 - let parse_at_uri uri = 538 - match Re.exec_opt at_uri_regexp uri with 539 - | None -> 540 - None 541 - | Some m -> ( 542 - try 543 - Some 544 - { repo= Re.Group.get m 1 545 - ; collection= Re.Group.get m 2 546 - ; rkey= Re.Group.get m 3 547 - ; fragment= Re.Group.get_opt m 4 } 548 - with _ -> None ) 549 - 550 - let make_at_uri ~repo ~collection ~rkey ~fragment = 551 - Printf.sprintf "at://%s/%s/%s%s" repo collection rkey 552 - (Option.value ~default:"" fragment) 553 - 554 - let nsid_authority nsid = 555 - match String.rindex_opt nsid '.' with 556 - | None -> 557 - nsid 558 - | Some idx -> 559 - String.sub nsid 0 idx 560 - 561 - let send_email_or_log ~(recipients : Letters.recipient list) ~subject 562 - ~(body : Letters.body) = 563 - let log_email () = 564 - match body with 565 - | Plain text | Html text | Mixed (text, _, _) -> 566 - let to_addr = 567 - List.find_map 568 - (fun (r : Letters.recipient) -> 569 - match r with To addr -> Some addr | _ -> None ) 570 - recipients 571 - |> Option.get 572 - in 573 - Log.info (fun log -> log "email to %s: %s" to_addr text) 574 - in 575 - match (Env.smtp_config, Env.smtp_sender) with 576 - | Some config, Some sender -> ( 577 - match Letters.create_email ~from:sender ~recipients ~subject ~body () with 578 - | Error e -> 579 - failwith (Printf.sprintf "failed to construct email: %s" e) 580 - | Ok message -> ( 581 - try%lwt Letters.send ~config ~sender ~recipients ~message 582 - with e -> 583 - Log.log_exn e ; 584 - Lwt.return (log_email ()) ) ) 585 - | _ -> 586 - Lwt.return (log_email ()) 587 - 588 - let s3_error_to_string : Aws_s3_lwt.S3.error -> string = function 589 - | Redirect endpoint -> 590 - "redirect to " ^ endpoint.host 591 - | Throttled -> 592 - "throttled" 593 - | Unknown (code, msg) -> 594 - Printf.sprintf "unknown error %d: %s" code msg 595 - | Failed exn -> 596 - Printf.sprintf "failed: %s" (Printexc.to_string exn) 597 - | Forbidden -> 598 - "forbidden" 599 - | Not_found -> 600 - "not found"
+20
pegasus/lib/util/constants.ml
··· 1 + let data_dir = 2 + Core.Filename.to_absolute_exn Env.data_dir 3 + ~relative_to:(Core_unix.getcwd ()) 4 + 5 + let pegasus_db_filepath = Filename.concat data_dir "pegasus.db" 6 + 7 + let pegasus_db_location = "sqlite3://" ^ pegasus_db_filepath |> Uri.of_string 8 + 9 + let user_db_filepath did = 10 + let dirname = Filename.concat data_dir "store" in 11 + let filename = Str.global_replace (Str.regexp ":") "_" did in 12 + Filename.concat dirname filename ^ ".db" 13 + 14 + let user_db_location did = 15 + "sqlite3://" ^ user_db_filepath did |> Uri.of_string 16 + 17 + let user_blobs_location did = 18 + did 19 + |> Str.global_replace (Str.regexp ":") "_" 20 + |> (Filename.concat data_dir "blobs" |> Filename.concat)
+44
pegasus/lib/util/html.ml
··· 1 + module type Template = sig 2 + type props 3 + 4 + val props_of_json : Yojson.Basic.t -> props 5 + 6 + val props_to_json : props -> Yojson.Basic.t 7 + 8 + val make : ?key:string -> props:props -> unit -> React.element 9 + end 10 + 11 + let render_page ?status ?title (type props) 12 + (template : (module Template with type props = props)) ~props = 13 + let module Template = (val template : Template with type props = props) in 14 + let props_json = Template.props_to_json props |> Yojson.Basic.to_string in 15 + let page_data = Printf.sprintf "window.__PAGE__ = {props: %s};" props_json in 16 + let app = Template.make ~props () in 17 + let page = 18 + Frontend.Layout.make ?title ~favicon:Env.favicon_url ~children:app () 19 + in 20 + Dream.stream ?status 21 + ~headers:[("Content-Type", "text/html")] 22 + (fun stream -> 23 + [%lwt 24 + let html, subscribe = 25 + ReactServerDOM.render_html ~skipRoot:false 26 + ~bootstrapScriptContent:page_data 27 + ~bootstrapScripts:["/public/client.js"] page 28 + in 29 + [%lwt 30 + let () = Dream.write stream html in 31 + [%lwt 32 + let () = Dream.flush stream in 33 + [%lwt 34 + let () = 35 + subscribe (fun chunk -> 36 + [%lwt 37 + let () = Dream.write stream chunk in 38 + Dream.flush stream] ) 39 + in 40 + Dream.flush stream]]]] ) 41 + 42 + let make_data_uri ~mimetype ~data = 43 + let base64_data = data |> Bytes.to_string |> Base64.encode_string in 44 + Printf.sprintf "data:%s;base64,%s" mimetype base64_data
+58
pegasus/lib/util/http_.ml
··· 1 + let rec get ?(max_redirects = 5) ?(no_drain = false) ?headers uri = 2 + let ua = "pegasus (" ^ Env.host_endpoint ^ ")" in 3 + let headers = 4 + match headers with 5 + | Some headers -> 6 + Http.Header.add_unless_exists headers "User-Agent" ua 7 + | None -> 8 + Http.Header.of_list [("User-Agent", ua)] 9 + in 10 + let%lwt ans = Cohttp_lwt_unix.Client.get ~headers uri in 11 + follow_redirect ~max_redirects ~no_drain uri ans 12 + 13 + and follow_redirect ~max_redirects ~no_drain request_uri (response, body) = 14 + let status = Http.Response.status response in 15 + (* the unconsumed body would otherwise leak memory *) 16 + let%lwt () = 17 + if status <> `OK && not no_drain then Cohttp_lwt.Body.drain_body body 18 + else Lwt.return_unit 19 + in 20 + match status with 21 + | `Permanent_redirect | `Moved_permanently -> 22 + handle_redirect ~permanent:true ~max_redirects request_uri response 23 + | `Found | `Temporary_redirect -> 24 + handle_redirect ~permanent:false ~max_redirects request_uri response 25 + | _ -> 26 + Lwt.return (response, body) 27 + 28 + and handle_redirect ~permanent ~max_redirects request_uri response = 29 + if max_redirects <= 0 then failwith "too many redirects" 30 + else 31 + let headers = Http.Response.headers response in 32 + let location = Http.Header.get headers "location" in 33 + match location with 34 + | None -> 35 + failwith "redirection without Location header" 36 + | Some url -> 37 + let uri = Uri.of_string url in 38 + let%lwt () = 39 + if permanent then 40 + Logs_lwt.warn (fun m -> 41 + m "Permanent redirection from %s to %s" 42 + (Uri.to_string request_uri) 43 + url ) 44 + else Lwt.return_unit 45 + in 46 + get uri ~max_redirects:(max_redirects - 1) 47 + 48 + let copy_query req = Dream.all_queries req |> List.map (fun (k, v) -> (k, [v])) 49 + 50 + let make_headers headers = 51 + List.fold_left 52 + (fun headers (k, v) -> 53 + match v with 54 + | Some value -> 55 + Http.Header.add headers k value 56 + | None -> 57 + headers ) 58 + (Http.Header.init ()) headers
+37
pegasus/lib/util/rapper_.ml
··· 1 + module CID : Rapper.CUSTOM with type t = Cid.t = struct 2 + type t = Cid.t 3 + 4 + let t = 5 + let encode cid = 6 + try Ok (Cid.to_string cid) with e -> Error (Printexc.to_string e) 7 + in 8 + Caqti_type.(custom ~encode ~decode:Cid.of_string string) 9 + end 10 + 11 + module Blob : Rapper.CUSTOM with type t = bytes = struct 12 + type t = bytes 13 + 14 + let t = 15 + let encode blob = 16 + try Ok (Bytes.to_string blob) with e -> Error (Printexc.to_string e) 17 + in 18 + let decode blob = 19 + try Ok (Bytes.of_string blob) with e -> Error (Printexc.to_string e) 20 + in 21 + Caqti_type.(custom ~encode ~decode string) 22 + end 23 + 24 + module Json : Rapper.CUSTOM with type t = Yojson.Safe.t = struct 25 + type t = Yojson.Safe.t 26 + 27 + let t = 28 + let encode json = 29 + try Ok (Yojson.Safe.to_string json ~std:true) 30 + with e -> Error (Printexc.to_string e) 31 + in 32 + let decode json = 33 + try Ok (Yojson.Safe.from_string json) 34 + with e -> Error (Printexc.to_string e) 35 + in 36 + Caqti_type.(custom ~encode ~decode string) 37 + end
+150
pegasus/lib/util/sqlite_.ml
··· 1 + type caqti_pool = (Caqti_lwt.connection, Caqti_error.t) Caqti_lwt_unix.Pool.t 2 + 3 + (* turns a caqti error into an exception *) 4 + let caqti_result_exn = function 5 + | Ok x -> 6 + Ok x 7 + | Error caqti_err -> 8 + Error (Caqti_error.Exn caqti_err) 9 + 10 + let _init_connection (module Db : Rapper_helper.CONNECTION) : 11 + (unit, Caqti_error.t) Lwt_result.t = 12 + let open Lwt_result.Syntax in 13 + let open Caqti_request.Infix in 14 + let open Caqti_type in 15 + let* _ = 16 + Db.find (((unit ->! string) ~oneshot:true) "PRAGMA journal_mode=WAL") () 17 + in 18 + let* _ = 19 + Db.exec (((unit ->. unit) ~oneshot:true) "PRAGMA foreign_keys=ON") () 20 + in 21 + let* _ = 22 + Db.exec (((unit ->. unit) ~oneshot:true) "PRAGMA synchronous=NORMAL") () 23 + in 24 + let* _ = 25 + Db.find (((unit ->! int) ~oneshot:true) "PRAGMA busy_timeout=5000") () 26 + in 27 + Lwt.return_ok () 28 + 29 + (* creates an sqlite pool *) 30 + let connect ?(create = false) ?(write = true) db_uri : caqti_pool Lwt.t = 31 + let uri = 32 + Uri.add_query_params' db_uri 33 + [("create", string_of_bool create); ("write", string_of_bool write)] 34 + in 35 + let pool_config = Caqti_pool_config.create ~max_size:16 ~max_idle_size:4 () in 36 + match 37 + Caqti_lwt_unix.connect_pool ~pool_config ~post_connect:_init_connection uri 38 + with 39 + | Ok pool -> 40 + Lwt.return pool 41 + | Error e -> 42 + raise (Caqti_error.Exn e) 43 + 44 + let with_connection db_uri f = 45 + match%lwt 46 + Caqti_lwt_unix.with_connection db_uri (fun conn -> 47 + match%lwt _init_connection conn with 48 + | Ok () -> 49 + f conn 50 + | Error e -> 51 + Lwt.return_error e ) 52 + with 53 + | Ok result -> 54 + Lwt.return result 55 + | Error e -> 56 + raise (Caqti_error.Exn e) 57 + 58 + let use_pool ?(timeout = 60.0) pool 59 + (f : Caqti_lwt.connection -> ('a, Caqti_error.t) Lwt_result.t) : 'a Lwt.t = 60 + match%lwt 61 + Lwt_unix.with_timeout timeout (fun () -> Caqti_lwt_unix.Pool.use f pool) 62 + with 63 + | Ok res -> 64 + Lwt.return res 65 + | Error e -> 66 + raise (Caqti_error.Exn e) 67 + 68 + let transact conn fn : (unit, 'e) Lwt_result.t = 69 + let module C = (val conn : Caqti_lwt.CONNECTION) in 70 + match%lwt C.start () with 71 + | Ok () -> ( 72 + try%lwt 73 + match%lwt fn () with 74 + | Ok _ -> ( 75 + match%lwt C.commit () with 76 + | Ok () -> 77 + Lwt.return_ok () 78 + | Error e -> ( 79 + match%lwt C.rollback () with 80 + | Ok () -> 81 + Lwt.return_error e 82 + | Error e -> 83 + Lwt.return_error e ) ) 84 + | Error e -> ( 85 + match%lwt C.rollback () with 86 + | Ok () -> 87 + Lwt.return_error e 88 + | Error e -> 89 + Lwt.return_error e ) 90 + with e -> ( 91 + match%lwt C.rollback () with 92 + | Ok () -> 93 + Lwt.return_error 94 + ( match e with 95 + | Caqti_error.Exn e -> 96 + e 97 + | e -> 98 + Caqti_error.request_failed ~query:"unknown" 99 + ~uri:(Uri.of_string "//unknown") 100 + (Caqti_error.Msg (Printexc.to_string e)) ) 101 + | Error e -> 102 + Lwt.return_error e ) ) 103 + | Error e -> 104 + Lwt.return_error e 105 + 106 + (* runs a bunch of queries in a transaction, catches duplicate insertion, returning how many succeeded *) 107 + let multi_query pool 108 + (queries : (Caqti_lwt.connection -> ('a, Caqti_error.t) Lwt_result.t) list) 109 + : (int, exn) Lwt_result.t = 110 + let open Syntax in 111 + Lwt_result.catch (fun () -> 112 + use_pool pool (fun connection -> 113 + let module C = (val connection : Caqti_lwt.CONNECTION) in 114 + let$! () = C.start () in 115 + let is_ignorable_error e = 116 + match (e : Caqti_error.t) with 117 + | `Request_failed qe | `Response_failed qe -> ( 118 + match Caqti_error.cause (`Request_failed qe) with 119 + | `Not_null_violation | `Unique_violation -> 120 + true 121 + | _ -> 122 + false ) 123 + | _ -> 124 + false 125 + in 126 + let rec aux acc queries = 127 + match acc with 128 + | Error e -> 129 + Lwt.return_error e 130 + | Ok count -> ( 131 + match queries with 132 + | [] -> 133 + Lwt.return (Ok count) 134 + | query :: rest -> ( 135 + let%lwt result = query connection in 136 + match result with 137 + | Ok _ -> 138 + aux (Ok (count + 1)) rest 139 + | Error e -> 140 + if is_ignorable_error e then aux (Ok count) rest 141 + else Lwt.return_error e ) ) 142 + in 143 + let%lwt result = aux (Ok 0) queries in 144 + match result with 145 + | Ok count -> 146 + let$! () = C.commit () in 147 + Lwt.return_ok count 148 + | Error e -> 149 + let%lwt _ = C.rollback () in 150 + Lwt.return_error e ) )
+50
pegasus/lib/util/syntax.ml
··· 1 + let unwrap m = 2 + match%lwt m with 3 + | Ok x -> 4 + Lwt.return x 5 + | Error e -> 6 + raise (Caqti_error.Exn e) 7 + 8 + (* unwraps an Lwt result, raising an exception if there's an error *) 9 + let ( let$! ) m f = 10 + match%lwt m with Ok x -> f x | Error e -> raise (Caqti_error.Exn e) 11 + 12 + (* unwraps an Lwt result, raising an exception if there's an error *) 13 + let ( >$! ) m f = 14 + match%lwt m with 15 + | Ok x -> 16 + Lwt.return (f x) 17 + | Error e -> 18 + raise (Caqti_error.Exn e) 19 + 20 + let at_uri_regexp = 21 + Re.Pcre.re 22 + {|^at:\/\/([a-zA-Z0-9._:%-]+)(?:\/([a-zA-Z0-9-.]+)(?:\/([a-zA-Z0-9._~:@!$&%')(*+,;=-]+))?)?(?:#(\/[a-zA-Z0-9._~:@!$&%')(*+,;=\-[\]\/\\]*))?$|} 23 + |> Re.compile 24 + 25 + type at_uri = 26 + {repo: string; collection: string; rkey: string; fragment: string option} 27 + 28 + let parse_at_uri uri = 29 + match Re.exec_opt at_uri_regexp uri with 30 + | None -> 31 + None 32 + | Some m -> ( 33 + try 34 + Some 35 + { repo= Re.Group.get m 1 36 + ; collection= Re.Group.get m 2 37 + ; rkey= Re.Group.get m 3 38 + ; fragment= Re.Group.get_opt m 4 } 39 + with _ -> None ) 40 + 41 + let make_at_uri ~repo ~collection ~rkey ~fragment = 42 + Printf.sprintf "at://%s/%s/%s%s" repo collection rkey 43 + (Option.value ~default:"" fragment) 44 + 45 + let nsid_authority nsid = 46 + match String.rindex_opt nsid '.' with 47 + | None -> 48 + nsid 49 + | Some idx -> 50 + String.sub nsid 0 idx
+12
pegasus/lib/util/time.ml
··· 1 + let minute = 60 * 1000 2 + 3 + let hour = 60 * minute 4 + 5 + let day = 24 * hour 6 + 7 + (* unix timestamp *) 8 + let now_ms () : int = int_of_float (Unix.gettimeofday () *. 1000.) 9 + 10 + let ms_to_iso8601 ms = 11 + let s = float_of_int ms /. 1000. in 12 + Timedesc.(of_timestamp_float_s_exn s |> to_iso8601)
+89
pegasus/lib/util/types.ml
··· 1 + type string_or_null = string option 2 + 3 + let string_or_null_to_yojson = function Some s -> `String s | None -> `Null 4 + 5 + let string_or_null_of_yojson = function 6 + | `String s -> 7 + Ok (Some s) 8 + | `Null -> 9 + Ok None 10 + | _ -> 11 + Error "invalid field value" 12 + 13 + type string_or_strings = [`String of string | `Strings of string list] 14 + 15 + let string_or_strings_to_yojson = function 16 + | `String c -> 17 + `String c 18 + | `Strings cs -> 19 + `List (List.map (fun c -> `String c) cs) 20 + 21 + let string_or_strings_of_yojson = function 22 + | `String c -> 23 + Ok (`Strings [c]) 24 + | `List cs -> 25 + Ok (`Strings (Yojson.Safe.Util.filter_string cs)) 26 + | _ -> 27 + Error "invalid field value" 28 + 29 + type string_map = (string * string) list 30 + 31 + let string_map_to_yojson = function 32 + | [] -> 33 + `Assoc [] 34 + | m -> 35 + `Assoc (List.map (fun (k, v) -> (k, `String v)) m) 36 + 37 + let string_map_of_yojson = function 38 + | `Null -> 39 + Ok [] 40 + | `Assoc m -> 41 + Ok 42 + (List.filter_map 43 + (fun (k, v) -> 44 + match (k, v) with _, `String s -> Some (k, s) | _, _ -> None ) 45 + m ) 46 + | _ -> 47 + Error "invalid field value" 48 + 49 + type string_or_string_map = [`String of string | `String_map of string_map] 50 + 51 + let string_or_string_map_to_yojson = function 52 + | `String c -> 53 + `String c 54 + | `String_map m -> 55 + `Assoc (List.map (fun (k, v) -> (k, `String v)) m) 56 + 57 + let string_or_string_map_of_yojson = function 58 + | `String c -> 59 + Ok (`String c) 60 + | `Assoc m -> 61 + string_map_of_yojson (`Assoc m) |> Result.map (fun m -> `String_map m) 62 + | _ -> 63 + Error "invalid field value" 64 + 65 + type string_or_string_map_or_either_list = 66 + [ `String of string 67 + | `String_map of string_map 68 + | `List of string_or_string_map list ] 69 + 70 + let string_or_string_map_or_either_list_to_yojson = function 71 + | `String c -> 72 + `String c 73 + | `String_map m -> 74 + `Assoc (List.map (fun (k, v) -> (k, `String v)) m) 75 + | `List l -> 76 + `List (List.map string_or_string_map_to_yojson l) 77 + 78 + let string_or_string_map_or_either_list_of_yojson = function 79 + | `String c -> 80 + Ok (`String c) 81 + | `Assoc m -> 82 + string_map_of_yojson (`Assoc m) |> Result.map (fun m -> `String_map m) 83 + | `List l -> 84 + Ok 85 + (`List 86 + ( List.map string_or_string_map_of_yojson l 87 + |> List.filter_map (function Ok x -> Some x | Error _ -> None) ) ) 88 + | _ -> 89 + Error "invalid field value"
+113
pegasus/lib/util/util.ml
··· 1 + module Constants = Constants 2 + 3 + module Syntax = Syntax 4 + 5 + module Rapper = Rapper_ 6 + 7 + module Types = Types 8 + 9 + module Sqlite = Sqlite_ 10 + 11 + module Time = Time 12 + 13 + module Http = Http_ 14 + 15 + module Html = Html 16 + 17 + (* returns all blob refs in a record *) 18 + let rec find_blob_refs (record : Mist.Lex.repo_record) : Mist.Blob_ref.t list = 19 + let rec aux acc entries = 20 + List.fold_left 21 + (fun acc value -> 22 + match value with 23 + | `BlobRef blob -> 24 + blob :: acc 25 + | `LexMap map -> 26 + find_blob_refs map @ acc 27 + | `LexArray arr -> 28 + aux acc (Array.to_list arr) @ acc 29 + | _ -> 30 + acc ) 31 + acc entries 32 + in 33 + aux [] (Mist.Lex.String_map.bindings record |> List.map snd) 34 + |> List.sort_uniq (fun (r1 : Mist.Blob_ref.t) r2 -> Cid.compare r1.ref r2.ref) 35 + 36 + let mkfile_p path ~perm = 37 + Core_unix.mkdir_p (Filename.dirname path) ~perm:0o755 ; 38 + Core_unix.openfile ~mode:[O_CREAT; O_WRONLY] ~perm path |> Core_unix.close 39 + 40 + let sig_matches_some_did_key ~did_keys ~signature ~msg = 41 + List.find_opt 42 + (fun key -> 43 + let raw, (module Curve) = 44 + Kleidos.parse_multikey_str (String.sub key 8 (String.length key - 8)) 45 + in 46 + let valid = 47 + Curve.verify ~pubkey:(Curve.normalize_pubkey_to_raw raw) ~signature ~msg 48 + in 49 + valid ) 50 + did_keys 51 + <> None 52 + 53 + let request_ip req = 54 + Dream.header req "X-Forwarded-For" 55 + |> Option.value ~default:(Dream.client req) 56 + |> String.split_on_char ',' |> List.hd |> String.split_on_char ':' |> List.hd 57 + |> String.trim 58 + 59 + let str_contains ~affix str = 60 + let re = Str.regexp_string affix in 61 + try 62 + ignore (Str.search_forward re str 0) ; 63 + true 64 + with Not_found -> false 65 + 66 + let make_code () = 67 + let () = Mirage_crypto_rng_unix.use_default () in 68 + let token = 69 + Multibase.Base32.encode_string ~pad:false 70 + @@ Mirage_crypto_rng_unix.getrandom 8 71 + in 72 + String.sub token 0 5 ^ "-" ^ String.sub token 5 5 73 + 74 + let send_email_or_log ~(recipients : Letters.recipient list) ~subject 75 + ~(body : Letters.body) = 76 + let log_email () = 77 + match body with 78 + | Plain text | Html text | Mixed (text, _, _) -> 79 + let to_addr = 80 + List.find_map 81 + (fun (r : Letters.recipient) -> 82 + match r with To addr -> Some addr | _ -> None ) 83 + recipients 84 + |> Option.get 85 + in 86 + Log.info (fun log -> log "email to %s: %s" to_addr text) 87 + in 88 + match (Env.smtp_config, Env.smtp_sender) with 89 + | Some config, Some sender -> ( 90 + match Letters.create_email ~from:sender ~recipients ~subject ~body () with 91 + | Error e -> 92 + failwith (Printf.sprintf "failed to construct email: %s" e) 93 + | Ok message -> ( 94 + try%lwt Letters.send ~config ~sender ~recipients ~message 95 + with e -> 96 + Log.log_exn e ; 97 + Lwt.return (log_email ()) ) ) 98 + | _ -> 99 + Lwt.return (log_email ()) 100 + 101 + let s3_error_to_string : Aws_s3_lwt.S3.error -> string = function 102 + | Redirect endpoint -> 103 + "redirect to " ^ endpoint.host 104 + | Throttled -> 105 + "throttled" 106 + | Unknown (code, msg) -> 107 + Printf.sprintf "unknown error %d: %s" code msg 108 + | Failed exn -> 109 + Printf.sprintf "failed: %s" (Printexc.to_string exn) 110 + | Forbidden -> 111 + "forbidden" 112 + | Not_found -> 113 + "not found"
+3 -3
pegasus/lib/xrpc.ml
··· 276 276 let signing_key = Kleidos.parse_multikey_str signing_multikey in 277 277 let jwt = Jwt.generate_service_jwt ~did ~aud ~lxm ~signing_key in 278 278 let path, _ = Dream.split_target (Dream.target ctx.req) in 279 - let query = Util.copy_query ctx.req in 279 + let query = Util.Http.copy_query ctx.req in 280 280 let uri = Uri.make ~scheme ~host ~path ~query () in 281 281 let headers = 282 - Util.make_headers 282 + Util.Http.make_headers 283 283 [ ("accept-language", Dream.header ctx.req "accept-language") 284 284 ; ("content-type", Dream.header ctx.req "content-type") 285 285 ; ( "atproto-accept-labelers" ··· 291 291 Lwt_unix.with_timeout 30.0 (fun () -> 292 292 match Dream.method_ ctx.req with 293 293 | `GET -> 294 - Util.http_get uri ~headers ~no_drain:true 294 + Util.Http.get uri ~headers ~no_drain:true 295 295 | `POST -> 296 296 let%lwt req_body = Dream.body ctx.req in 297 297 Client.post uri ~headers ~body:(Body.of_string req_body)
+3 -3
pegasus/test/test_scopes.ml
··· 5 5 6 6 let test_nsid_authority () = 7 7 check test_string "three segments" "com.example" 8 - (Pegasus.Util.nsid_authority "com.example.foo") ; 8 + (Pegasus.Util.Syntax.nsid_authority "com.example.foo") ; 9 9 check test_string "four segments" "com.example.app" 10 - (Pegasus.Util.nsid_authority "com.example.app.auth") ; 10 + (Pegasus.Util.Syntax.nsid_authority "com.example.app.auth") ; 11 11 check test_string "two segments" "com" 12 - (Pegasus.Util.nsid_authority "com.example") 12 + (Pegasus.Util.Syntax.nsid_authority "com.example") 13 13 14 14 let test_is_parent_authority () = 15 15 check bool "same authority" true
+3 -3
pegasus/test/test_sequencer.ml
··· 24 24 let with_db (f : Data_store.t -> unit Lwt.t) : unit Lwt.t = 25 25 let tmp = Filename.temp_file "pegasus_sequencer_test" ".db" in 26 26 let%lwt pool = 27 - Util.connect_sqlite ~create:true ~write:true 27 + Util.Sqlite.connect ~create:true ~write:true 28 28 (Uri.of_string ("sqlite3://" ^ tmp)) 29 29 in 30 30 let%lwt () = Migrations.run_migrations Data_store pool in ··· 79 79 with_db (fun conn -> 80 80 let did = "did:example:bob" in 81 81 (* add 3 identity events to db without publishing to bus *) 82 - let time0 = Util.now_ms () in 82 + let time0 = Util.Time.now_ms () in 83 83 let mk_raw did = 84 84 let evt : Sequencer.Types.identity_evt = {did; handle= None} in 85 85 Dag_cbor.encode_yojson @@ Sequencer.Encode.format_identity evt ··· 136 136 let test_gap_healing () = 137 137 with_db (fun conn -> 138 138 let did = "did:example:carol" in 139 - let time0 = Util.now_ms () in 139 + let time0 = Util.Time.now_ms () in 140 140 (* add 2 identity events to db without publishing *) 141 141 let mk_raw did = 142 142 let evt : Sequencer.Types.identity_evt = {did; handle= None} in