package client import ( "bytes" "encoding/json" "fmt" "io" "mime/multipart" "net/http" "os" "path/filepath" ) type AppStoreClient struct { BaseURL string AuthToken string HttpClient *http.Client } type App struct { Name string `json:"name"` Ver string `json:"ver"` AppID string `json:"appid"` Info string `json:"info"` Pub string `json:"pub"` Path string `json:"path"` } type AppMetadata struct { Name string `json:"name"` Ver string `json:"ver"` Info string `json:"info"` Pub string `json:"pub"` } // Extended metadata struct to include optional AppID type uploadMetadata struct { AppMetadata AppID string `json:"appid,omitempty"` } // NewClient creates a new AppStore client func NewClient(baseURL, authToken string) *AppStoreClient { return &AppStoreClient{ BaseURL: baseURL, AuthToken: authToken, HttpClient: &http.Client{}, } } // GetApps retrieves all apps from the store func (c *AppStoreClient) GetApps() ([]App, error) { resp, err := c.HttpClient.Get(c.BaseURL + "/apps") if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) } var apps []App if err := json.NewDecoder(resp.Body).Decode(&apps); err != nil { return nil, err } return apps, nil } // UploadApp uploads a new app to the store with an optional custom app ID func (c *AppStoreClient) UploadApp(metadata AppMetadata, filePath string, customAppID string) (*App, error) { // Create extended metadata with optional AppID extendedMetadata := uploadMetadata{ AppMetadata: metadata, AppID: customAppID, // Will be omitted from JSON if empty } // Create multipart form body := &bytes.Buffer{} writer := multipart.NewWriter(body) // Add metadata metadataBytes, err := json.Marshal(extendedMetadata) if err != nil { return nil, err } if err := writer.WriteField("metadata", string(metadataBytes)); err != nil { return nil, err } // Add file file, err := os.Open(filePath) if err != nil { return nil, err } defer file.Close() part, err := writer.CreateFormFile("file", filepath.Base(filePath)) if err != nil { return nil, err } if _, err := io.Copy(part, file); err != nil { return nil, err } if err := writer.Close(); err != nil { return nil, err } // Create request req, err := http.NewRequest("POST", c.BaseURL+"/uploadapp", body) if err != nil { return nil, err } req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Authorization", "Bearer "+c.AuthToken) // Send request resp, err := c.HttpClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) } var newApp App if err := json.NewDecoder(resp.Body).Decode(&newApp); err != nil { return nil, err } return &newApp, nil } // Backwards compatibility wrapper func (c *AppStoreClient) UploadAppSimple(metadata AppMetadata, filePath string) (*App, error) { return c.UploadApp(metadata, filePath, "") } // EditApp updates an existing app func (c *AppStoreClient) EditApp(appID string, metadata AppMetadata, filePath string) error { body := &bytes.Buffer{} writer := multipart.NewWriter(body) // Add metadata updateData := struct { AppID string `json:"appid"` App AppMetadata `json:"app"` }{ AppID: appID, App: metadata, } metadataBytes, err := json.Marshal(updateData) if err != nil { return err } if err := writer.WriteField("metadata", string(metadataBytes)); err != nil { return err } // Add file if provided if filePath != "" { file, err := os.Open(filePath) if err != nil { return err } defer file.Close() part, err := writer.CreateFormFile("file", filepath.Base(filePath)) if err != nil { return err } if _, err := io.Copy(part, file); err != nil { return err } } if err := writer.Close(); err != nil { return err } req, err := http.NewRequest("PUT", c.BaseURL+"/editapp", body) if err != nil { return err } req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Authorization", "Bearer "+c.AuthToken) resp, err := c.HttpClient.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("unexpected status code: %d", resp.StatusCode) } return nil } // DeleteApp removes an app from the store func (c *AppStoreClient) DeleteApp(appID string) error { data := struct { AppID string `json:"appid"` }{ AppID: appID, } jsonData, err := json.Marshal(data) if err != nil { return err } req, err := http.NewRequest("DELETE", c.BaseURL+"/deleteapp", bytes.NewBuffer(jsonData)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+c.AuthToken) resp, err := c.HttpClient.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("unexpected status code: %d", resp.StatusCode) } return nil }