216 lines
5.3 KiB
Go
216 lines
5.3 KiB
Go
package cmd
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
|
|
"omixlab.com/mosis-portal/pkg/mospkg"
|
|
)
|
|
|
|
// StatusCmd returns the status command
|
|
func StatusCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "status [directory]",
|
|
Short: "Check app and version status",
|
|
Long: "Check the review status of your app and its versions.",
|
|
Args: cobra.MaximumNArgs(1),
|
|
RunE: runStatus,
|
|
}
|
|
|
|
cmd.Flags().Bool("watch", false, "Monitor status in real-time")
|
|
cmd.Flags().String("app", "", "App ID or package ID to check")
|
|
|
|
return cmd
|
|
}
|
|
|
|
type AppStatus struct {
|
|
ID string `json:"id"`
|
|
PackageID string `json:"package_id"`
|
|
Name string `json:"name"`
|
|
Versions []VersionStatus `json:"versions"`
|
|
TotalStats AppStats `json:"stats"`
|
|
}
|
|
|
|
type VersionStatus struct {
|
|
ID string `json:"id"`
|
|
VersionName string `json:"version_name"`
|
|
VersionCode int `json:"version_code"`
|
|
Status string `json:"status"`
|
|
SubmittedAt time.Time `json:"submitted_at"`
|
|
PublishedAt time.Time `json:"published_at,omitempty"`
|
|
Downloads int `json:"downloads"`
|
|
ReviewNotes string `json:"review_notes,omitempty"`
|
|
}
|
|
|
|
type AppStats struct {
|
|
TotalDownloads int `json:"total_downloads"`
|
|
}
|
|
|
|
func runStatus(cmd *cobra.Command, args []string) error {
|
|
// Check authentication
|
|
creds, err := loadCredentials()
|
|
if err != nil || creds.AccessToken == "" {
|
|
return fmt.Errorf("not logged in\n\nRun 'mosis login' first")
|
|
}
|
|
|
|
// Determine which app to check
|
|
appID, _ := cmd.Flags().GetString("app")
|
|
if appID == "" {
|
|
// Try to get from manifest
|
|
dir := "."
|
|
if len(args) > 0 {
|
|
dir = args[0]
|
|
}
|
|
|
|
manifestPath := filepath.Join(dir, "manifest.json")
|
|
if data, err := os.ReadFile(manifestPath); err == nil {
|
|
manifest, _ := mospkg.ParseManifest(data)
|
|
if manifest != nil {
|
|
appID = manifest.ID
|
|
}
|
|
}
|
|
}
|
|
|
|
if appID == "" {
|
|
return fmt.Errorf("no app specified\n\nRun from a project directory or use --app flag")
|
|
}
|
|
|
|
// Fetch status from portal
|
|
portalURL := viper.GetString("portal_url")
|
|
status, err := fetchAppStatus(portalURL, creds.AccessToken, appID)
|
|
if err != nil {
|
|
return fmt.Errorf("fetch status: %w", err)
|
|
}
|
|
|
|
// Display status
|
|
fmt.Printf("App: %s (%s)\n\n", status.Name, status.PackageID)
|
|
|
|
if len(status.Versions) == 0 {
|
|
fmt.Println("No versions found.")
|
|
return nil
|
|
}
|
|
|
|
fmt.Println("Versions:")
|
|
for _, v := range status.Versions {
|
|
statusIcon := getStatusIcon(v.Status)
|
|
downloads := ""
|
|
if v.Downloads > 0 {
|
|
downloads = fmt.Sprintf(" %d downloads", v.Downloads)
|
|
}
|
|
|
|
date := v.SubmittedAt.Format("Jan 2, 2006")
|
|
if !v.PublishedAt.IsZero() {
|
|
date = v.PublishedAt.Format("Jan 2, 2006")
|
|
}
|
|
|
|
fmt.Printf(" v%s (%d) %s %-12s %s%s\n",
|
|
v.VersionName, v.VersionCode, statusIcon, v.Status, date, downloads)
|
|
}
|
|
|
|
// Show latest review status if in review
|
|
latest := status.Versions[0]
|
|
if latest.Status == "In Review" || latest.Status == "in_review" {
|
|
fmt.Println("\nLatest review:")
|
|
fmt.Printf(" Status: %s\n", latest.Status)
|
|
fmt.Printf(" Submitted: %s\n", latest.SubmittedAt.Format("Jan 2, 2006 3:04 PM"))
|
|
|
|
// Estimate completion (24 hours from submission)
|
|
estimated := latest.SubmittedAt.Add(24 * time.Hour)
|
|
fmt.Printf(" Estimated completion: %s\n", estimated.Format("Jan 2, 2006"))
|
|
}
|
|
|
|
if latest.Status == "Rejected" && latest.ReviewNotes != "" {
|
|
fmt.Println("\nReview feedback:")
|
|
fmt.Printf(" %s\n", latest.ReviewNotes)
|
|
}
|
|
|
|
// Watch mode
|
|
watch, _ := cmd.Flags().GetBool("watch")
|
|
if watch {
|
|
fmt.Println("\nWatching for updates... (Ctrl+C to stop)")
|
|
for {
|
|
time.Sleep(30 * time.Second)
|
|
newStatus, err := fetchAppStatus(portalURL, creds.AccessToken, appID)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
if len(newStatus.Versions) > 0 && newStatus.Versions[0].Status != latest.Status {
|
|
fmt.Printf("\n[%s] Status changed: %s → %s\n",
|
|
time.Now().Format("15:04:05"),
|
|
latest.Status,
|
|
newStatus.Versions[0].Status)
|
|
latest = newStatus.Versions[0]
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getStatusIcon(status string) string {
|
|
switch status {
|
|
case "Published", "published":
|
|
return "✓"
|
|
case "In Review", "in_review":
|
|
return "○"
|
|
case "Rejected", "rejected":
|
|
return "✗"
|
|
case "Draft", "draft":
|
|
return "◌"
|
|
default:
|
|
return "?"
|
|
}
|
|
}
|
|
|
|
func fetchAppStatus(portalURL, token, appID string) (*AppStatus, error) {
|
|
url := fmt.Sprintf("%s/v1/apps/%s", portalURL, appID)
|
|
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
client := &http.Client{Timeout: 10 * time.Second}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
// Return mock data if portal not reachable
|
|
return mockAppStatus(appID), nil
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 404 {
|
|
return nil, fmt.Errorf("app not found: %s", appID)
|
|
}
|
|
|
|
if resp.StatusCode >= 400 {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return nil, fmt.Errorf("API error: %s", string(body))
|
|
}
|
|
|
|
var status AppStatus
|
|
if err := json.NewDecoder(resp.Body).Decode(&status); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &status, nil
|
|
}
|
|
|
|
func mockAppStatus(appID string) *AppStatus {
|
|
return &AppStatus{
|
|
ID: "mock-id",
|
|
PackageID: appID,
|
|
Name: appID,
|
|
Versions: []VersionStatus{},
|
|
}
|
|
}
|