package httpapp import ( "encoding/json" "fmt" "log" "net/http" "gitea.avt.data-center.id/othman.suseno/atlas/internal/models" ) // pathParam is now in router_helpers.go // Disk Handlers func (a *App) handleListDisks(w http.ResponseWriter, r *http.Request) { disks, err := a.zfs.ListDisks() if err != nil { log.Printf("list disks error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, disks) } // ZFS Pool Handlers func (a *App) handleListPools(w http.ResponseWriter, r *http.Request) { pools, err := a.zfs.ListPools() if err != nil { log.Printf("list pools error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, pools) } func (a *App) handleCreatePool(w http.ResponseWriter, r *http.Request) { var req struct { Name string `json:"name"` VDEVs []string `json:"vdevs"` Options map[string]string `json:"options,omitempty"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"}) return } if req.Name == "" || len(req.VDEVs) == 0 { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "name and vdevs are required"}) return } if req.Options == nil { req.Options = make(map[string]string) } if err := a.zfs.CreatePool(req.Name, req.VDEVs, req.Options); err != nil { log.Printf("create pool error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } pool, err := a.zfs.GetPool(req.Name) if err != nil { writeJSON(w, http.StatusCreated, map[string]string{"message": "pool created", "name": req.Name}) return } writeJSON(w, http.StatusCreated, pool) } func (a *App) handleGetPool(w http.ResponseWriter, r *http.Request) { name := pathParam(r, "/api/v1/pools/") if name == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "pool name required"}) return } pool, err := a.zfs.GetPool(name) if err != nil { writeJSON(w, http.StatusNotFound, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, pool) } func (a *App) handleDeletePool(w http.ResponseWriter, r *http.Request) { name := pathParam(r, "/api/v1/pools/") if name == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "pool name required"}) return } if err := a.zfs.DestroyPool(name); err != nil { log.Printf("destroy pool error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, map[string]string{"message": "pool destroyed", "name": name}) } func (a *App) handleScrubPool(w http.ResponseWriter, r *http.Request) { name := pathParam(r, "/api/v1/pools/") if name == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "pool name required"}) return } if err := a.zfs.ScrubPool(name); err != nil { log.Printf("scrub pool error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, map[string]string{"message": "scrub started", "pool": name}) } // Dataset Handlers func (a *App) handleListDatasets(w http.ResponseWriter, r *http.Request) { pool := r.URL.Query().Get("pool") datasets, err := a.zfs.ListDatasets(pool) if err != nil { log.Printf("list datasets error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, datasets) } func (a *App) handleCreateDataset(w http.ResponseWriter, r *http.Request) { var req struct { Name string `json:"name"` Options map[string]string `json:"options,omitempty"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"}) return } if req.Name == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "dataset name is required"}) return } if req.Options == nil { req.Options = make(map[string]string) } if err := a.zfs.CreateDataset(req.Name, req.Options); err != nil { log.Printf("create dataset error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusCreated, map[string]string{"message": "dataset created", "name": req.Name}) } func (a *App) handleGetDataset(w http.ResponseWriter, r *http.Request) { name := pathParam(r, "/api/v1/datasets/") if name == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "dataset name required"}) return } datasets, err := a.zfs.ListDatasets("") if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } for _, ds := range datasets { if ds.Name == name { writeJSON(w, http.StatusOK, ds) return } } writeJSON(w, http.StatusNotFound, map[string]string{"error": "dataset not found"}) } func (a *App) handleUpdateDataset(w http.ResponseWriter, r *http.Request) { name := pathParam(r, "/api/v1/datasets/") // TODO: Implement dataset property updates writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "name": name}) } func (a *App) handleDeleteDataset(w http.ResponseWriter, r *http.Request) { name := pathParam(r, "/api/v1/datasets/") if name == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "dataset name required"}) return } recursive := r.URL.Query().Get("recursive") == "true" if err := a.zfs.DestroyDataset(name, recursive); err != nil { log.Printf("destroy dataset error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, map[string]string{"message": "dataset destroyed", "name": name}) } // ZVOL Handlers func (a *App) handleListZVOLs(w http.ResponseWriter, r *http.Request) { pool := r.URL.Query().Get("pool") zvols, err := a.zfs.ListZVOLs(pool) if err != nil { log.Printf("list zvols error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, zvols) } func (a *App) handleCreateZVOL(w http.ResponseWriter, r *http.Request) { var req struct { Name string `json:"name"` Size uint64 `json:"size"` // in bytes Options map[string]string `json:"options,omitempty"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"}) return } if req.Name == "" || req.Size == 0 { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "name and size are required"}) return } if req.Options == nil { req.Options = make(map[string]string) } if err := a.zfs.CreateZVOL(req.Name, req.Size, req.Options); err != nil { log.Printf("create zvol error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusCreated, map[string]string{"message": "zvol created", "name": req.Name}) } func (a *App) handleGetZVOL(w http.ResponseWriter, r *http.Request) { name := pathParam(r, "/api/v1/zvols/") if name == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "zvol name required"}) return } zvols, err := a.zfs.ListZVOLs("") if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } for _, zvol := range zvols { if zvol.Name == name { writeJSON(w, http.StatusOK, zvol) return } } writeJSON(w, http.StatusNotFound, map[string]string{"error": "zvol not found"}) } func (a *App) handleDeleteZVOL(w http.ResponseWriter, r *http.Request) { name := pathParam(r, "/api/v1/zvols/") if name == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "zvol name required"}) return } if err := a.zfs.DestroyZVOL(name); err != nil { log.Printf("destroy zvol error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, map[string]string{"message": "zvol destroyed", "name": name}) } // Snapshot Handlers func (a *App) handleListSnapshots(w http.ResponseWriter, r *http.Request) { dataset := r.URL.Query().Get("dataset") snapshots, err := a.zfs.ListSnapshots(dataset) if err != nil { log.Printf("list snapshots error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, snapshots) } func (a *App) handleCreateSnapshot(w http.ResponseWriter, r *http.Request) { var req struct { Dataset string `json:"dataset"` Name string `json:"name"` Recursive bool `json:"recursive,omitempty"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"}) return } if req.Dataset == "" || req.Name == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "dataset and name are required"}) return } if err := a.zfs.CreateSnapshot(req.Dataset, req.Name, req.Recursive); err != nil { log.Printf("create snapshot error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } snapshotName := fmt.Sprintf("%s@%s", req.Dataset, req.Name) snap, err := a.zfs.GetSnapshot(snapshotName) if err != nil { writeJSON(w, http.StatusCreated, map[string]string{"message": "snapshot created", "name": snapshotName}) return } writeJSON(w, http.StatusCreated, snap) } func (a *App) handleGetSnapshot(w http.ResponseWriter, r *http.Request) { name := pathParam(r, "/api/v1/snapshots/") if name == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "snapshot name required"}) return } snap, err := a.zfs.GetSnapshot(name) if err != nil { writeJSON(w, http.StatusNotFound, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, snap) } func (a *App) handleDeleteSnapshot(w http.ResponseWriter, r *http.Request) { name := pathParam(r, "/api/v1/snapshots/") if name == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "snapshot name required"}) return } recursive := r.URL.Query().Get("recursive") == "true" if err := a.zfs.DestroySnapshot(name, recursive); err != nil { log.Printf("destroy snapshot error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, map[string]string{"message": "snapshot destroyed", "name": name}) } // Snapshot Policy Handlers func (a *App) handleListSnapshotPolicies(w http.ResponseWriter, r *http.Request) { dataset := r.URL.Query().Get("dataset") var policies []models.SnapshotPolicy if dataset != "" { policies = a.snapshotPolicy.ListForDataset(dataset) } else { policies = a.snapshotPolicy.List() } writeJSON(w, http.StatusOK, policies) } func (a *App) handleCreateSnapshotPolicy(w http.ResponseWriter, r *http.Request) { var policy models.SnapshotPolicy if err := json.NewDecoder(r.Body).Decode(&policy); err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"}) return } if policy.Dataset == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "dataset is required"}) return } a.snapshotPolicy.Set(&policy) writeJSON(w, http.StatusCreated, policy) } func (a *App) handleGetSnapshotPolicy(w http.ResponseWriter, r *http.Request) { dataset := pathParam(r, "/api/v1/snapshot-policies/") if dataset == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "dataset name required"}) return } policy, err := a.snapshotPolicy.Get(dataset) if err != nil { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } if policy == nil { writeJSON(w, http.StatusNotFound, map[string]string{"error": "policy not found"}) return } writeJSON(w, http.StatusOK, policy) } func (a *App) handleUpdateSnapshotPolicy(w http.ResponseWriter, r *http.Request) { dataset := pathParam(r, "/api/v1/snapshot-policies/") if dataset == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "dataset name required"}) return } var policy models.SnapshotPolicy if err := json.NewDecoder(r.Body).Decode(&policy); err != nil { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"}) return } // Ensure dataset matches URL parameter policy.Dataset = dataset a.snapshotPolicy.Set(&policy) writeJSON(w, http.StatusOK, policy) } func (a *App) handleDeleteSnapshotPolicy(w http.ResponseWriter, r *http.Request) { dataset := pathParam(r, "/api/v1/snapshot-policies/") if dataset == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "dataset name required"}) return } if err := a.snapshotPolicy.Delete(dataset); err != nil { log.Printf("delete snapshot policy error: %v", err) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, map[string]string{"message": "policy deleted", "dataset": dataset}) } // SMB Share Handlers func (a *App) handleListSMBShares(w http.ResponseWriter, r *http.Request) { shares := []models.SMBShare{} // Stub writeJSON(w, http.StatusOK, shares) } func (a *App) handleCreateSMBShare(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented"}) } func (a *App) handleGetSMBShare(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/shares/smb/") writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "id": id}) } func (a *App) handleUpdateSMBShare(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/shares/smb/") writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "id": id}) } func (a *App) handleDeleteSMBShare(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/shares/smb/") writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "id": id}) } // NFS Export Handlers func (a *App) handleListNFSExports(w http.ResponseWriter, r *http.Request) { exports := []models.NFSExport{} // Stub writeJSON(w, http.StatusOK, exports) } func (a *App) handleCreateNFSExport(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented"}) } func (a *App) handleGetNFSExport(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/exports/nfs/") writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "id": id}) } func (a *App) handleUpdateNFSExport(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/exports/nfs/") writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "id": id}) } func (a *App) handleDeleteNFSExport(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/exports/nfs/") writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "id": id}) } // iSCSI Handlers func (a *App) handleListISCSITargets(w http.ResponseWriter, r *http.Request) { targets := []models.ISCSITarget{} // Stub writeJSON(w, http.StatusOK, targets) } func (a *App) handleCreateISCSITarget(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented"}) } func (a *App) handleGetISCSITarget(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/iscsi/targets/") writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "id": id}) } func (a *App) handleUpdateISCSITarget(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/iscsi/targets/") writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "id": id}) } func (a *App) handleDeleteISCSITarget(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/iscsi/targets/") writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "id": id}) } func (a *App) handleAddLUN(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/iscsi/targets/") writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "id": id}) } func (a *App) handleRemoveLUN(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/iscsi/targets/") writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "id": id}) } // Job Handlers func (a *App) handleListJobs(w http.ResponseWriter, r *http.Request) { status := models.JobStatus(r.URL.Query().Get("status")) jobs := a.jobManager.List(status) writeJSON(w, http.StatusOK, jobs) } func (a *App) handleGetJob(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/jobs/") if id == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "job id required"}) return } job, err := a.jobManager.Get(id) if err != nil { writeJSON(w, http.StatusNotFound, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, job) } func (a *App) handleCancelJob(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/jobs/") if id == "" { writeJSON(w, http.StatusBadRequest, map[string]string{"error": "job id required"}) return } if err := a.jobManager.Cancel(id); err != nil { log.Printf("cancel job error: %v", err) writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()}) return } writeJSON(w, http.StatusOK, map[string]string{"message": "job cancelled", "id": id}) } // Auth Handlers (stubs) func (a *App) handleLogin(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented"}) } func (a *App) handleLogout(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, map[string]string{"message": "logged out"}) } // User Handlers func (a *App) handleListUsers(w http.ResponseWriter, r *http.Request) { users := []models.User{} // Stub writeJSON(w, http.StatusOK, users) } func (a *App) handleCreateUser(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented"}) } func (a *App) handleGetUser(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/users/") writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "id": id}) } func (a *App) handleUpdateUser(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/users/") writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "id": id}) } func (a *App) handleDeleteUser(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/users/") writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "id": id}) } // Audit Log Handlers func (a *App) handleListAuditLogs(w http.ResponseWriter, r *http.Request) { logs := []models.AuditLog{} // Stub writeJSON(w, http.StatusOK, logs) }