package httpapp import ( "net/http" "strings" "gitea.avt.data-center.id/othman.suseno/atlas/internal/errors" ) // methodHandler routes requests based on HTTP method func methodHandler(get, post, put, delete, patch http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: if get != nil { get(w, r) } else { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } case http.MethodPost: if post != nil { post(w, r) } else { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } case http.MethodPut: if put != nil { put(w, r) } else { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } case http.MethodDelete: if delete != nil { delete(w, r) } else { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } case http.MethodPatch: if patch != nil { patch(w, r) } else { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } default: http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } } } // pathParam extracts the last segment from a path func pathParam(r *http.Request, prefix string) string { path := strings.TrimPrefix(r.URL.Path, prefix) path = strings.Trim(path, "/") parts := strings.Split(path, "/") if len(parts) > 0 { return parts[len(parts)-1] } return "" } // handlePoolOps routes pool operations by method func (a *App) handlePoolOps(w http.ResponseWriter, r *http.Request) { // Extract pool name from path like /api/v1/pools/tank name := pathParam(r, "/api/v1/pools/") if name == "" { writeError(w, errors.ErrBadRequest("pool name required")) return } if strings.HasSuffix(r.URL.Path, "/scrub") { if r.Method == http.MethodPost { a.handleScrubPool(w, r) } else if r.Method == http.MethodGet { a.handleGetScrubStatus(w, r) } else { writeError(w, errors.NewAPIError(errors.ErrCodeBadRequest, "method not allowed", http.StatusMethodNotAllowed)) } return } if strings.HasSuffix(r.URL.Path, "/export") { if r.Method == http.MethodPost { a.handleExportPool(w, r) } else { writeError(w, errors.NewAPIError(errors.ErrCodeBadRequest, "method not allowed", http.StatusMethodNotAllowed)) } return } methodHandler( func(w http.ResponseWriter, r *http.Request) { a.handleGetPool(w, r) }, nil, nil, func(w http.ResponseWriter, r *http.Request) { a.handleDeletePool(w, r) }, nil, )(w, r) } // handleDatasetOps routes dataset operations by method func (a *App) handleDatasetOps(w http.ResponseWriter, r *http.Request) { methodHandler( func(w http.ResponseWriter, r *http.Request) { a.handleGetDataset(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleCreateDataset(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleUpdateDataset(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleDeleteDataset(w, r) }, nil, )(w, r) } // handleZVOLOps routes ZVOL operations by method func (a *App) handleZVOLOps(w http.ResponseWriter, r *http.Request) { methodHandler( func(w http.ResponseWriter, r *http.Request) { a.handleGetZVOL(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleCreateZVOL(w, r) }, nil, func(w http.ResponseWriter, r *http.Request) { a.handleDeleteZVOL(w, r) }, nil, )(w, r) } // handleSnapshotOps routes snapshot operations by method func (a *App) handleSnapshotOps(w http.ResponseWriter, r *http.Request) { methodHandler( func(w http.ResponseWriter, r *http.Request) { a.handleGetSnapshot(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleCreateSnapshot(w, r) }, nil, func(w http.ResponseWriter, r *http.Request) { a.handleDeleteSnapshot(w, r) }, nil, )(w, r) } // handleSnapshotPolicyOps routes snapshot policy operations by method func (a *App) handleSnapshotPolicyOps(w http.ResponseWriter, r *http.Request) { methodHandler( func(w http.ResponseWriter, r *http.Request) { a.handleGetSnapshotPolicy(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleCreateSnapshotPolicy(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleUpdateSnapshotPolicy(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleDeleteSnapshotPolicy(w, r) }, nil, )(w, r) } // handleSMBShareOps routes SMB share operations by method func (a *App) handleSMBShareOps(w http.ResponseWriter, r *http.Request) { methodHandler( func(w http.ResponseWriter, r *http.Request) { a.handleGetSMBShare(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleCreateSMBShare(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleUpdateSMBShare(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleDeleteSMBShare(w, r) }, nil, )(w, r) } // handleNFSExportOps routes NFS export operations by method func (a *App) handleNFSExportOps(w http.ResponseWriter, r *http.Request) { methodHandler( func(w http.ResponseWriter, r *http.Request) { a.handleGetNFSExport(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleCreateNFSExport(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleUpdateNFSExport(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleDeleteNFSExport(w, r) }, nil, )(w, r) } // handleISCSITargetOps routes iSCSI target operations by method func (a *App) handleBackupOps(w http.ResponseWriter, r *http.Request) { backupID := pathParam(r, "/api/v1/backups/") if backupID == "" { writeError(w, errors.ErrBadRequest("backup id required")) return } switch r.Method { case http.MethodGet: // Check if it's a verify request if r.URL.Query().Get("verify") == "true" { a.handleVerifyBackup(w, r) } else { a.handleGetBackup(w, r) } case http.MethodPost: // Restore backup (POST /api/v1/backups/{id}/restore) if strings.HasSuffix(r.URL.Path, "/restore") { a.handleRestoreBackup(w, r) } else { writeError(w, errors.ErrBadRequest("invalid backup operation")) } case http.MethodDelete: a.handleDeleteBackup(w, r) default: writeError(w, errors.ErrBadRequest("method not allowed")) } } func (a *App) handleISCSITargetOps(w http.ResponseWriter, r *http.Request) { id := pathParam(r, "/api/v1/iscsi/targets/") if id == "" { writeError(w, errors.ErrBadRequest("target id required")) return } if strings.HasSuffix(r.URL.Path, "/connection") { if r.Method == http.MethodGet { a.handleGetISCSIConnectionInstructions(w, r) } else { writeError(w, errors.NewAPIError(errors.ErrCodeBadRequest, "method not allowed", http.StatusMethodNotAllowed)) } return } if strings.HasSuffix(r.URL.Path, "/luns") { if r.Method == http.MethodPost { a.handleAddLUN(w, r) return } writeError(w, errors.NewAPIError(errors.ErrCodeBadRequest, "method not allowed", http.StatusMethodNotAllowed)) return } if strings.HasSuffix(r.URL.Path, "/luns/remove") { if r.Method == http.MethodPost { a.handleRemoveLUN(w, r) return } writeError(w, errors.NewAPIError(errors.ErrCodeBadRequest, "method not allowed", http.StatusMethodNotAllowed)) return } methodHandler( func(w http.ResponseWriter, r *http.Request) { a.handleGetISCSITarget(w, r) }, nil, func(w http.ResponseWriter, r *http.Request) { a.handleUpdateISCSITarget(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleDeleteISCSITarget(w, r) }, nil, )(w, r) } // handleJobOps routes job operations by method func (a *App) handleJobOps(w http.ResponseWriter, r *http.Request) { if strings.HasSuffix(r.URL.Path, "/cancel") { if r.Method == http.MethodPost { a.handleCancelJob(w, r) return } http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } methodHandler( func(w http.ResponseWriter, r *http.Request) { a.handleGetJob(w, r) }, nil, nil, nil, nil, )(w, r) } // handleUserOps routes user operations by method func (a *App) handleUserOps(w http.ResponseWriter, r *http.Request) { methodHandler( func(w http.ResponseWriter, r *http.Request) { a.handleGetUser(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleCreateUser(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleUpdateUser(w, r) }, func(w http.ResponseWriter, r *http.Request) { a.handleDeleteUser(w, r) }, nil, )(w, r) }