codex: Start showing archives for user

Currently only under group/user, since most archive access is only
gained through the group.
This commit is contained in:
2026-01-15 16:12:55 +01:00
parent f6ec692ebf
commit 15d91d87bf
4 changed files with 128 additions and 55 deletions

View File

@@ -97,7 +97,8 @@ type Archives =
| Ok archives ->
setArchives archives
setError None
| Error err -> setError (Some "Error fetching archives")
| Error err ->
setError (Some "Error fetching archives")
setLoading false
)
@@ -196,4 +197,4 @@ type Archives =
]
]
]
]
]

View File

@@ -5,6 +5,7 @@ module Utils =
open Fable.Core
open Fable.Core.JsInterop
open Fable.Remoting.Client
open FsToolkit.ErrorHandling
open Oceanbox.Codex
open Oceanbox.Codex.Types
@@ -71,3 +72,52 @@ module Utils =
return Error "Error fetching archive count"
}
let private fetchArchmaesterArchives (group: string) : JS.Promise<Result<Archmaester.Dto.ArchiveProps array, string>> =
promise {
let filter : Archmaester.Dto.ArchiveFilter = {
id = None
searchTerm = None
archiveType = None
owner = None
user = None
groups = Some [| group |]
}
let! result = Remoting.adminApi.getArchives 0 -1 filter |> Async.StartAsPromise
return result
}
let fetchGroupArchiveProps (group: string) : JS.Promise<Types.Archive array> =
promise {
let fgaUser = sprintf "group:%s#member" (Groups.Utils.canonicalizeName group)
let! archivesRes = fetchArchmaesterArchives group
let! viewsRes =
OpenFGA.fetchObjects(fgaUser, "view", "archive", context = {| time = System.DateTime.Now |})
let! execsRes =
OpenFGA.fetchObjects(fgaUser, "exec", "archive", context = {| task = "*"; usage = -1; time = System.DateTime.Now |})
let res =
result {
// TODO: Create specific exception
let! props = archivesRes |> Result.mapError System.Exception
let! views = viewsRes |> Result.mapError System.Exception
let! execs = execsRes |> Result.mapError System.Exception
let viewArchiveIds = views |> Array.choose extractFgaArchiveId
let execArchiveIds = execs |> Array.choose extractFgaArchiveId
let archives =
props
|> Array.map (fun prop -> {
Props = prop
CanView = viewArchiveIds |> Array.contains prop.archiveId
CanExec = execArchiveIds |> Array.contains prop.archiveId
})
return archives
}
match res with
| Ok archives ->
return archives
| Error ex ->
raise ex
return [||]
}

View File

@@ -3,7 +3,6 @@ namespace Oceanbox.Codex
module Group =
open Browser
open Fable.Core
open FsToolkit.ErrorHandling
open Oceanbox.Codex.Types
@@ -17,59 +16,9 @@ module Group =
return Error (sprintf "Error fetching users for group %s" group)
}
let private fetchArchmaesterArchives (group: string) : JS.Promise<Result<Archmaester.Dto.ArchiveProps array, string>> =
promise {
let filter : Archmaester.Dto.ArchiveFilter = {
id = None
searchTerm = None
archiveType = None
owner = None
user = None
groups = Some [| group |]
}
let! result = Remoting.adminApi.getArchives 0 -1 filter |> Async.StartAsPromise
return result
}
module private Elmish =
open Elmish
let fetchArchiveProps (group: string) : JS.Promise<Types.Archive array> =
promise {
let fgaUser = sprintf "group:%s#member" (Groups.Utils.canonicalizeName group)
let! archivesRes = fetchArchmaesterArchives group
let! viewsRes =
OpenFGA.fetchObjects(fgaUser, "view", "archive", context = {| time = System.DateTime.Now |})
let! execsRes =
OpenFGA.fetchObjects(fgaUser, "exec", "archive", context = {| task = "*"; usage = -1; time = System.DateTime.Now |})
let res =
result {
// TODO: Create specific exception
let! props = archivesRes |> Result.mapError System.Exception
let! views = viewsRes |> Result.mapError System.Exception
let! execs = execsRes |> Result.mapError System.Exception
let viewArchiveIds = views |> Array.choose Archives.Utils.extractFgaArchiveId
let execArchiveIds = execs |> Array.choose Archives.Utils.extractFgaArchiveId
let archives =
props
|> Array.map (fun prop -> {
Props = prop
CanView = viewArchiveIds |> Array.contains prop.archiveId
CanExec = execArchiveIds |> Array.contains prop.archiveId
})
return archives
}
match res with
| Ok archives ->
return archives
| Error ex ->
raise ex
return [||]
}
type Msg =
| FetchArchives of string
| SetArchiveAdding of bool
@@ -97,7 +46,7 @@ module Group =
let update msg model =
match msg with
| FetchArchives group ->
model, Cmd.OfPromise.either fetchArchiveProps group SetArchives HandleExn
model, Cmd.OfPromise.either Archives.Utils.fetchGroupArchiveProps group SetArchives HandleExn
| HandleExn ex ->
let msg =
match ex with
@@ -470,4 +419,4 @@ module Group =
]
]
]
]
]

View File

@@ -4,6 +4,71 @@ open Feliz
open Feliz.Router
module GroupUser =
[<ReactComponent>]
let private ArchiveList key (group: string) (title: string) (archives: Types.Archive array) =
Html.div [
prop.style [
style.flexBasis (length.px 256)
]
prop.children [
Html.h3 title
Html.ul [
prop.children (
archives
|> Array.sortBy _.Props.name
|> Array.map (fun archive ->
let text = Archives.Utils.archiveName archive
Html.li [
prop.children [
Html.a [
prop.href (Router.format("groups", group, "archives", string archive.Props.archiveId))
prop.text text
]
]
]
)
)
]
]
]
[<ReactComponent>]
let private ArchiveLists (group: string) =
let archives, setArchives = React.useState<Types.Archive array> [||]
React.useEffect (
(fun () ->
Archives.Utils.fetchGroupArchiveProps group
|> Promise.iter (fun res ->
setArchives res
)
),
[| box group |]
)
let fvcom =
archives
|> Array.filter (fun archive ->
match archive.Props.archiveType with
| Archmaester.Dto.Fvcom _ -> true
| _ -> false
)
let drifters =
archives
|> Array.filter (fun archive ->
match archive.Props.archiveType with
| Archmaester.Dto.Drifters _ -> true
| _ -> false
)
Html.div [
prop.classes ["flex-row-start"; "gap-8"]
prop.children [
ArchiveList "fvcom-ul-list" group "FVCOM" fvcom
ArchiveList "drifters-ul-list" group "Drifters" drifters
]
]
[<ReactComponent>]
let View (group: string) (user: string) =
let fgaUser = sprintf "user:%s" user
@@ -128,4 +193,12 @@ module GroupUser =
]
]
]
Html.section [
prop.children [
Html.h2 "Archives via group"
ArchiveLists group
]
]
]