mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-06-04 04:22:57 +00:00
overhaul
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// AuthCmd represents the auth command
|
||||
var AuthCmd = &cobra.Command{
|
||||
Use: "auth",
|
||||
Short: "Authenticate with Containr API",
|
||||
Long: `Manage authentication with the Containr API.
|
||||
You can login, logout, and check your current authentication status.`,
|
||||
}
|
||||
|
||||
// loginCmd represents the login command
|
||||
var loginCmd = &cobra.Command{
|
||||
Use: "login [token]",
|
||||
Short: "Login to Containr",
|
||||
Long: `Login to Containr using your API token.
|
||||
You can get your token from the Containr web interface.`,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: runLogin,
|
||||
}
|
||||
|
||||
// logoutCmd represents the logout command
|
||||
var logoutCmd = &cobra.Command{
|
||||
Use: "logout",
|
||||
Short: "Logout from Containr",
|
||||
Long: `Remove stored authentication credentials.`,
|
||||
RunE: runLogout,
|
||||
}
|
||||
|
||||
// statusCmd represents the status command
|
||||
var statusCmd = &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Check authentication status",
|
||||
Long: `Check if you are currently authenticated with Containr.`,
|
||||
RunE: runStatus,
|
||||
}
|
||||
|
||||
func init() {
|
||||
AuthCmd.AddCommand(loginCmd)
|
||||
AuthCmd.AddCommand(logoutCmd)
|
||||
AuthCmd.AddCommand(statusCmd)
|
||||
}
|
||||
|
||||
func runLogin(cmd *cobra.Command, args []string) error {
|
||||
var token string
|
||||
|
||||
if len(args) > 0 {
|
||||
token = args[0]
|
||||
} else {
|
||||
// Prompt for token
|
||||
fmt.Print("Enter your Containr API token: ")
|
||||
fmt.Scanln(&token)
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
return fmt.Errorf("token is required")
|
||||
}
|
||||
|
||||
// Store token in config
|
||||
viper.Set("token", token)
|
||||
if err := viper.WriteConfig(); err != nil {
|
||||
return fmt.Errorf("failed to save token: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("✓ Successfully logged in to Containr")
|
||||
return nil
|
||||
}
|
||||
|
||||
func runLogout(cmd *cobra.Command, args []string) error {
|
||||
// Remove token from config
|
||||
viper.Set("token", "")
|
||||
if err := viper.WriteConfig(); err != nil {
|
||||
return fmt.Errorf("failed to remove token: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("✓ Successfully logged out from Containr")
|
||||
return nil
|
||||
}
|
||||
|
||||
func runStatus(cmd *cobra.Command, args []string) error {
|
||||
token := viper.GetString("token")
|
||||
if token == "" {
|
||||
fmt.Println("❌ Not authenticated")
|
||||
fmt.Println("Run 'containr auth login <token>' to authenticate")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println("✓ Authenticated with Containr")
|
||||
|
||||
// TODO: Verify token with API
|
||||
apiURL := viper.GetString("api-url")
|
||||
if apiURL != "" {
|
||||
fmt.Printf("API URL: %s\n", apiURL)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"containr/internal/pkg/utils"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Project represents a Containr project
|
||||
type Project struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
OwnerID string `json:"owner_id"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ProjectsCmd represents the projects command
|
||||
var ProjectsCmd = &cobra.Command{
|
||||
Use: "projects",
|
||||
Short: "Manage projects",
|
||||
Long: `Manage your Containr projects.
|
||||
You can list, create, update, and delete projects.`,
|
||||
}
|
||||
|
||||
// listProjectsCmd represents the list command
|
||||
var listProjectsCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List all projects",
|
||||
Long: `List all your Containr projects.`,
|
||||
RunE: runListProjects,
|
||||
}
|
||||
|
||||
// createProjectCmd represents the create command
|
||||
var createProjectCmd = &cobra.Command{
|
||||
Use: "create [name]",
|
||||
Short: "Create a new project",
|
||||
Long: `Create a new Containr project.
|
||||
Provide a name and optional description.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runCreateProject,
|
||||
}
|
||||
|
||||
// deleteProjectCmd represents the delete command
|
||||
var deleteProjectCmd = &cobra.Command{
|
||||
Use: "delete [project-id]",
|
||||
Short: "Delete a project",
|
||||
Long: `Delete a Containr project by ID.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runDeleteProject,
|
||||
}
|
||||
|
||||
var projectDescription string
|
||||
|
||||
// getAPIURL constructs the full API URL for a given endpoint
|
||||
func getAPIURL(endpoint string) string {
|
||||
baseURL := viper.GetString("api-url")
|
||||
if baseURL == "" {
|
||||
baseURL = "http://localhost:8080/api/v1" // Default for development
|
||||
}
|
||||
|
||||
// Ensure baseURL doesn't end with / and endpoint starts with /
|
||||
baseURL = strings.TrimSuffix(baseURL, "/")
|
||||
if !strings.HasPrefix(endpoint, "/") {
|
||||
endpoint = "/" + endpoint
|
||||
}
|
||||
|
||||
return baseURL + endpoint
|
||||
}
|
||||
|
||||
func init() {
|
||||
ProjectsCmd.AddCommand(listProjectsCmd)
|
||||
ProjectsCmd.AddCommand(createProjectCmd)
|
||||
ProjectsCmd.AddCommand(deleteProjectCmd)
|
||||
|
||||
// Add flags
|
||||
createProjectCmd.Flags().StringVarP(&projectDescription, "description", "d", "", "Project description")
|
||||
}
|
||||
|
||||
func runListProjects(cmd *cobra.Command, args []string) error {
|
||||
apiURL := getAPIURL("/projects")
|
||||
|
||||
req, err := http.NewRequest("GET", apiURL, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", "Bearer "+viper.GetString("token"))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("API request failed: %s - %s", resp.Status, string(body))
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read response: %w", err)
|
||||
}
|
||||
|
||||
var projects []Project
|
||||
if err := json.Unmarshal(body, &projects); err != nil {
|
||||
return fmt.Errorf("failed to parse response: %w", err)
|
||||
}
|
||||
|
||||
if len(projects) == 0 {
|
||||
fmt.Println("No projects found")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println("Your Projects:")
|
||||
fmt.Println()
|
||||
for _, project := range projects {
|
||||
fmt.Printf("📦 %s (%s)\n", project.Name, project.ID)
|
||||
if project.Description != "" {
|
||||
fmt.Printf(" %s\n", project.Description)
|
||||
}
|
||||
fmt.Printf(" Created: %s\n", utils.FormatTime(project.CreatedAt))
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runCreateProject(cmd *cobra.Command, args []string) error {
|
||||
name := args[0]
|
||||
|
||||
projectData := map[string]interface{}{
|
||||
"name": name,
|
||||
}
|
||||
|
||||
if projectDescription != "" {
|
||||
projectData["description"] = projectDescription
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(projectData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal project data: %w", err)
|
||||
}
|
||||
|
||||
apiURL := getAPIURL("/projects")
|
||||
|
||||
req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", "Bearer "+viper.GetString("token"))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("API request failed: %s - %s", resp.Status, string(body))
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read response: %w", err)
|
||||
}
|
||||
|
||||
var project Project
|
||||
if err := json.Unmarshal(body, &project); err != nil {
|
||||
return fmt.Errorf("failed to parse response: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Project '%s' created successfully!\n", project.Name)
|
||||
fmt.Printf("ID: %s\n", project.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runDeleteProject(cmd *cobra.Command, args []string) error {
|
||||
projectID := args[0]
|
||||
|
||||
apiURL := getAPIURL("/projects/" + projectID)
|
||||
|
||||
req, err := http.NewRequest("DELETE", apiURL, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", "Bearer "+viper.GetString("token"))
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusNoContent {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("API request failed: %s - %s", resp.Status, string(body))
|
||||
}
|
||||
|
||||
fmt.Printf("✓ Project '%s' deleted successfully!\n", projectID)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user