2024-11-21 00:45:22 +01:00
|
|
|
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"`
|
|
|
|
}
|
|
|
|
|
2024-11-21 12:11:12 +01:00
|
|
|
// Extended metadata struct to include optional AppID
|
|
|
|
type uploadMetadata struct {
|
|
|
|
AppMetadata
|
|
|
|
AppID string `json:"appid,omitempty"`
|
|
|
|
}
|
|
|
|
|
2024-11-21 00:45:22 +01:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2024-11-21 12:11:12 +01:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2024-11-21 00:45:22 +01:00
|
|
|
// Create multipart form
|
|
|
|
body := &bytes.Buffer{}
|
|
|
|
writer := multipart.NewWriter(body)
|
|
|
|
|
|
|
|
// Add metadata
|
2024-11-21 12:11:12 +01:00
|
|
|
metadataBytes, err := json.Marshal(extendedMetadata)
|
2024-11-21 00:45:22 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-11-21 12:11:12 +01:00
|
|
|
// Backwards compatibility wrapper
|
|
|
|
func (c *AppStoreClient) UploadAppSimple(metadata AppMetadata, filePath string) (*App, error) {
|
|
|
|
return c.UploadApp(metadata, filePath, "")
|
|
|
|
}
|
|
|
|
|
2024-11-21 00:45:22 +01:00
|
|
|
// 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
|
|
|
|
}
|