diff --git a/db/builder_db.ml b/db/builder_db.ml index dd98074..f7063ec 100644 --- a/db/builder_db.ml +++ b/db/builder_db.ml @@ -388,6 +388,23 @@ module Build = struct (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) |} + let get_by_hash = + Caqti_request.find_opt + Rep.cstruct + (Caqti_type.tup2 + Caqti_type.string + t) + {| SELECT job.name, + b.uuid, b.start_d, b.start_ps, b.finish_d, b.finish_ps, + b.result_kind, b.result_code, b.result_msg, + b.console, b.script, b.main_binary, b.job + FROM build_artifact a + INNER JOIN build b ON b.id = a.build + INNER JOIN job ON job.id = b.job + WHERE a.sha256 = ? + ORDER BY b.start_d DESC, b.start_ps DESC + LIMIT 1 + |} end module User = struct diff --git a/db/builder_db.mli b/db/builder_db.mli index 68d1a3a..80a1e88 100644 --- a/db/builder_db.mli +++ b/db/builder_db.mli @@ -128,6 +128,8 @@ sig (id, id * Meta.t * file option, [< `Many | `One | `Zero > `One `Zero ]) Caqti_request.t val add : (t, unit, [< `Many | `One | `Zero > `Zero ]) Caqti_request.t + val get_by_hash : + (Cstruct.t, string * t, [< `Many | `One | `Zero > `One `Zero]) Caqti_request.t end module User : sig diff --git a/lib/builder_web.ml b/lib/builder_web.ml index e971a37..d6f948b 100644 --- a/lib/builder_web.ml +++ b/lib/builder_web.ml @@ -199,12 +199,37 @@ let routes t = Lwt.return (Response.of_plain_text "Internal server error\n" ~status:`Internal_server_error) in + let hash req = + let hash_s = Router.param req "hash" in + let hash = try Some (Hex.to_cstruct (`Hex hash_s)) + with Invalid_argument _ -> None + in + match hash with + | None -> + Log.debug (fun m -> m "Invalid hash hex %S" hash_s); + Response.of_plain_text "Bad request\n" ~status:`Bad_request + |> Lwt.return + | Some hash -> + let+ build = Caqti_lwt.Pool.use (Model.build_hash hash) t.pool in + match build with + | Error e -> + Log.warn (fun m -> m "Database error: %a" pp_error e); + Response.of_plain_text "Internal server error\n" ~status:`Internal_server_error + | Ok None -> + Log.debug (fun m -> m "Hash not found: %S" hash_s); + Response.of_plain_text "Artifact not found\n" ~status:`Not_found + | Ok (Some (job_name, build)) -> + Response.redirect_to (Fmt.strf "/job/%s/build/%a/" job_name + Uuidm.pp build.Builder_db.Build.uuid) + in + [ App.get "/" builder; App.get "/job/:job/" job; App.get "/job/:job/build/:build/" job_build; App.get "/job/:job/build/:build/f/**" job_build_file; App.post "/upload" (authorized t upload); + App.get "/hash/:hash" hash; ] let add_routes t (app : App.t) = diff --git a/lib/model.ml b/lib/model.ml index 2a58045..bbc479a 100644 --- a/lib/model.ml +++ b/lib/model.ml @@ -49,6 +49,9 @@ let build_meta job (module Db : CONN) = Db.find_opt Builder_db.Build.get_latest job >|= Option.map (fun (_id, meta, file) -> (meta, file)) +let build_hash hash (module Db : CONN) = + Db.find_opt Builder_db.Build.get_by_hash hash + let build_exists uuid (module Db : CONN) = Db.find_opt Builder_db.Build.get_by_uuid uuid >|= Option.is_some diff --git a/lib/model.mli b/lib/model.mli index d7408c1..7f93cb6 100644 --- a/lib/model.mli +++ b/lib/model.mli @@ -14,6 +14,9 @@ val build : Uuidm.t -> Caqti_lwt.connection -> val build_meta : Builder_db.id -> Caqti_lwt.connection -> ((Builder_db.Build.Meta.t * Builder_db.file option) option, [> error ]) result Lwt.t +val build_hash : Cstruct.t -> Caqti_lwt.connection -> + ((string * Builder_db.Build.t) option, [> error ]) result Lwt.t + val build_exists : Uuidm.t -> Caqti_lwt.connection -> (bool, [> error ]) result Lwt.t