2024-07-26 10:37:13 +02:00
|
|
|
package controllers
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
2026-02-20 10:31:58 +01:00
|
|
|
"fmt"
|
2026-05-28 08:48:02 +02:00
|
|
|
"strconv"
|
2024-07-26 10:37:13 +02:00
|
|
|
|
|
|
|
|
oclib "cloud.o-forge.io/core/oc-lib"
|
2026-06-22 07:41:32 +02:00
|
|
|
"cloud.o-forge.io/core/oc-lib/models/workspace"
|
2024-08-30 10:19:40 +02:00
|
|
|
"cloud.o-forge.io/core/oc-lib/tools"
|
2026-06-22 07:41:32 +02:00
|
|
|
"oc-workspace/infrastructure"
|
2024-07-26 10:37:13 +02:00
|
|
|
beego "github.com/beego/beego/v2/server/web"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Operations about workspace
|
|
|
|
|
type WorkspaceController struct {
|
|
|
|
|
beego.Controller
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-02 14:15:58 +02:00
|
|
|
var paths = map[tools.DataType]map[tools.METHOD]string{
|
|
|
|
|
tools.PEER: { // paths to call to status of peers
|
2024-10-15 10:56:39 +02:00
|
|
|
tools.POST: "/status/",
|
2024-10-02 14:15:58 +02:00
|
|
|
},
|
|
|
|
|
tools.WORKSPACE: { // paths to call to delete/update workspace on peer destination
|
2024-10-15 10:56:39 +02:00
|
|
|
tools.PUT: "/:id?is_remote=true",
|
|
|
|
|
tools.DELETE: "/:id?is_remote=true",
|
2024-10-02 14:15:58 +02:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-08 08:38:40 +02:00
|
|
|
// @Title Search
|
|
|
|
|
// @Description search workspace
|
|
|
|
|
// @Param search path string true "the word search you want to get"
|
2025-01-17 17:18:29 +01:00
|
|
|
// @Param is_draft query string false "draft wished"
|
2026-05-28 08:48:02 +02:00
|
|
|
// @Param offset query string false "offset wished"
|
|
|
|
|
// @Param limit query string false "limit wished"
|
2024-08-08 08:38:40 +02:00
|
|
|
// @Success 200 {workspace} models.workspace
|
|
|
|
|
// @router /search/:search [get]
|
|
|
|
|
func (o *WorkspaceController) Search() {
|
2026-02-20 10:31:58 +01:00
|
|
|
// user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
|
2024-08-08 08:38:40 +02:00
|
|
|
// store and return Id or post with UUID
|
|
|
|
|
search := o.Ctx.Input.Param(":search")
|
2025-01-17 17:18:29 +01:00
|
|
|
isDraft := o.Ctx.Input.Query("is_draft")
|
2026-05-28 08:48:02 +02:00
|
|
|
offset, _ := strconv.Atoi(o.Ctx.Input.Query("offset"))
|
|
|
|
|
limit, _ := strconv.Atoi(o.Ctx.Input.Query("limit"))
|
2026-02-20 10:31:58 +01:00
|
|
|
// o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.WORKSPACE), user, peerID, groups, nil).Search(nil, search, isDraft == "true")
|
2026-05-28 08:48:02 +02:00
|
|
|
o.Data["json"] = oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.WORKSPACE), nil).Search(nil, search, isDraft == "true", int64(offset), int64(limit))
|
2026-02-20 10:31:58 +01:00
|
|
|
|
2024-08-08 08:38:40 +02:00
|
|
|
o.ServeJSON()
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-26 10:37:13 +02:00
|
|
|
// @Title Update
|
|
|
|
|
// @Description create workspaces
|
|
|
|
|
// @Param id path string true "the workspace id you want to get"
|
|
|
|
|
// @Param body body models.workspace true "The workspace content"
|
|
|
|
|
// @Success 200 {workspace} models.workspace
|
|
|
|
|
// @router /:id [put]
|
|
|
|
|
func (o *WorkspaceController) Put() {
|
2026-06-22 07:41:32 +02:00
|
|
|
caller := tools.NewHTTPCaller(paths)
|
2024-10-02 14:15:58 +02:00
|
|
|
caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true)
|
2024-07-26 10:37:13 +02:00
|
|
|
var res map[string]interface{}
|
|
|
|
|
id := o.Ctx.Input.Param(":id")
|
2026-06-22 07:41:32 +02:00
|
|
|
json.Unmarshal(o.Ctx.Input.CopyBody(2000000), &res)
|
|
|
|
|
|
|
|
|
|
// Snapshot resource IDs before the update to detect additions and removals.
|
|
|
|
|
oldResources := loadWorkspaceResourceMap(id)
|
|
|
|
|
|
2026-02-20 10:31:58 +01:00
|
|
|
o.Data["json"] = oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.WORKSPACE), caller).UpdateOne(res, id)
|
2026-06-22 07:41:32 +02:00
|
|
|
|
|
|
|
|
// Emit watch/unwatch events for resources that changed in this workspace.
|
|
|
|
|
go diffAndEmitWatchers(oldResources, extractResourceMap(res))
|
|
|
|
|
|
2024-07-26 10:37:13 +02:00
|
|
|
o.ServeJSON()
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 07:41:32 +02:00
|
|
|
// resourceEntry holds the resource ID and its creator's libp2p PeerID.
|
|
|
|
|
type resourceEntry struct {
|
|
|
|
|
creatorPeerID string
|
|
|
|
|
dataType tools.DataType
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// loadWorkspaceResourceMap returns the current resourceID→entry map for a workspace.
|
|
|
|
|
func loadWorkspaceResourceMap(id string) map[string]resourceEntry {
|
|
|
|
|
result := map[string]resourceEntry{}
|
|
|
|
|
data := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.WORKSPACE), nil).LoadOne(id)
|
|
|
|
|
if data.Data == nil {
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
w, ok := data.Data.(*workspace.Workspace)
|
|
|
|
|
if !ok {
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
for _, r := range w.DataResources { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.DATA_RESOURCE} }
|
|
|
|
|
for _, r := range w.ComputeResources { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.COMPUTE_RESOURCE} }
|
|
|
|
|
for _, r := range w.StorageResources { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.STORAGE_RESOURCE} }
|
|
|
|
|
for _, r := range w.ProcessingResources { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.PROCESSING_RESOURCE} }
|
|
|
|
|
for _, r := range w.WorkflowResources { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.WORKFLOW_RESOURCE} }
|
|
|
|
|
for _, r := range w.ServiceResources { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.SERVICE_RESOURCE} }
|
|
|
|
|
for _, r := range w.DynamicResources { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.DYNAMIC_RESOURCE} }
|
|
|
|
|
for _, r := range w.NativeTools { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.NATIVE_TOOL} }
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// extractResourceMap parses the PUT body to build a resourceID→entry map.
|
|
|
|
|
// creator_id is at the top level of each resource object (from Flutter toJSON()).
|
|
|
|
|
var resourceArrayKeys = map[string]tools.DataType{
|
|
|
|
|
"data_resources": tools.DATA_RESOURCE,
|
|
|
|
|
"compute_resources": tools.COMPUTE_RESOURCE,
|
|
|
|
|
"storage_resources": tools.STORAGE_RESOURCE,
|
|
|
|
|
"processing_resources": tools.PROCESSING_RESOURCE,
|
|
|
|
|
"workflow_resources": tools.WORKFLOW_RESOURCE,
|
|
|
|
|
"service_resources": tools.SERVICE_RESOURCE,
|
|
|
|
|
"dynamic_resources": tools.DYNAMIC_RESOURCE,
|
|
|
|
|
"native_tools": tools.NATIVE_TOOL,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func extractResourceMap(body map[string]interface{}) map[string]resourceEntry {
|
|
|
|
|
result := map[string]resourceEntry{}
|
|
|
|
|
for key, dt := range resourceArrayKeys {
|
|
|
|
|
arr, ok := body[key].([]interface{})
|
|
|
|
|
if !ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
for _, rawR := range arr {
|
|
|
|
|
r, ok := rawR.(map[string]interface{})
|
|
|
|
|
if !ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
id, _ := r["id"].(string)
|
|
|
|
|
creatorID, _ := r["creator_id"].(string)
|
|
|
|
|
if id != "" {
|
|
|
|
|
result[id] = resourceEntry{creatorPeerID: creatorID, dataType: dt}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// diffAndEmitWatchers emits PB_WATCH_RESOURCE for added non-self resources
|
|
|
|
|
// and PB_UNWATCH_RESOURCE for removed ones.
|
|
|
|
|
func diffAndEmitWatchers(old, new map[string]resourceEntry) {
|
|
|
|
|
for id, entry := range new {
|
|
|
|
|
if _, existed := old[id]; !existed {
|
|
|
|
|
infrastructure.EmitWatchResource(id, entry.creatorPeerID, entry.dataType)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for id, entry := range old {
|
|
|
|
|
if _, stillPresent := new[id]; !stillPresent {
|
|
|
|
|
infrastructure.EmitUnwatchResource(id, entry.creatorPeerID, entry.dataType)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-26 10:37:13 +02:00
|
|
|
// @Title Create
|
|
|
|
|
// @Description create workspace
|
|
|
|
|
// @Param data body json true "body for data content (Json format)"
|
|
|
|
|
// @Success 200 {workspace} models.workspace
|
|
|
|
|
// @router / [post]
|
|
|
|
|
func (o *WorkspaceController) Post() {
|
2026-02-20 10:31:58 +01:00
|
|
|
// user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
|
2024-08-30 16:00:49 +02:00
|
|
|
caller := tools.NewHTTPCaller(paths) // generate a http caller to send to peer shared workspace
|
2024-10-02 14:15:58 +02:00
|
|
|
caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true)
|
2024-07-26 10:37:13 +02:00
|
|
|
var res map[string]interface{}
|
|
|
|
|
json.Unmarshal(o.Ctx.Input.CopyBody(10000), &res)
|
2026-02-20 10:31:58 +01:00
|
|
|
// o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.WORKSPACE), user, peerID, groups, caller).StoreOne(res)
|
|
|
|
|
o.Data["json"] = oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.WORKSPACE), caller).StoreOne(res)
|
2024-07-26 10:37:13 +02:00
|
|
|
o.ServeJSON()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// @Title GetAll
|
|
|
|
|
// @Description find workspace by id
|
2025-01-17 17:18:29 +01:00
|
|
|
// @Param is_draft query string false "draft wished"
|
2026-05-28 08:48:02 +02:00
|
|
|
// @Param offset query string false "offset wished"
|
|
|
|
|
// @Param limit query string false "limit wished"
|
2024-07-26 10:37:13 +02:00
|
|
|
// @Success 200 {workspace} models.workspace
|
|
|
|
|
// @router / [get]
|
|
|
|
|
func (o *WorkspaceController) GetAll() {
|
2025-01-17 17:18:29 +01:00
|
|
|
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
|
2026-02-20 10:31:58 +01:00
|
|
|
fmt.Println(user, peerID, groups)
|
2025-01-17 17:18:29 +01:00
|
|
|
isDraft := o.Ctx.Input.Query("is_draft")
|
2026-02-20 10:31:58 +01:00
|
|
|
// o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.WORKSPACE), user, peerID, groups, nil).LoadAll(isDraft == "true")
|
2026-05-28 08:48:02 +02:00
|
|
|
offset, _ := strconv.Atoi(o.Ctx.Input.Query("offset"))
|
|
|
|
|
limit, _ := strconv.Atoi(o.Ctx.Input.Query("limit"))
|
|
|
|
|
o.Data["json"] = oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.WORKSPACE), nil).LoadAll(isDraft == "true", int64(offset), int64(limit))
|
2024-07-26 10:37:13 +02:00
|
|
|
o.ServeJSON()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// @Title Get
|
2025-01-17 17:18:29 +01:00
|
|
|
// @Description find workflow by idisDraft := o.Ctx.Input.Query("is_draft")
|
2024-07-26 10:37:13 +02:00
|
|
|
// @Param id path string true "the id you want to get"
|
|
|
|
|
// @Success 200 {workspace} models.workspace
|
|
|
|
|
// @router /:id [get]
|
|
|
|
|
func (o *WorkspaceController) Get() {
|
2026-02-20 10:31:58 +01:00
|
|
|
//user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
|
2024-07-26 10:37:13 +02:00
|
|
|
id := o.Ctx.Input.Param(":id")
|
2026-02-20 10:31:58 +01:00
|
|
|
// o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.WORKSPACE), user, peerID, groups, nil).LoadOne(id)
|
|
|
|
|
o.Data["json"] = oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.WORKSPACE), nil).LoadOne(id)
|
2024-07-26 10:37:13 +02:00
|
|
|
o.ServeJSON()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// @Title Delete
|
|
|
|
|
// @Description delete the workspace
|
|
|
|
|
// @Param id path string true "The id you want to delete"
|
|
|
|
|
// @Success 200 {workspace} delete success!
|
|
|
|
|
// @router /:id [delete]
|
|
|
|
|
func (o *WorkspaceController) Delete() {
|
2025-01-17 17:18:29 +01:00
|
|
|
user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
|
2024-07-26 10:37:13 +02:00
|
|
|
id := o.Ctx.Input.Param(":id")
|
2024-08-30 10:19:40 +02:00
|
|
|
caller := tools.NewHTTPCaller(paths) // generate a http caller to send to peer shared workspace
|
2024-10-02 14:15:58 +02:00
|
|
|
caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true)
|
2025-01-17 17:18:29 +01:00
|
|
|
o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.WORKSPACE), user, peerID, groups, caller).DeleteOne(id)
|
2024-07-26 10:37:13 +02:00
|
|
|
o.ServeJSON()
|
|
|
|
|
}
|
2026-06-22 07:41:32 +02:00
|
|
|
|