commit 7be58a8f12bbb6c0db2c015bbf8f2a9e0cca0f65 Author: matu6968 Date: Thu Nov 21 00:45:22 2024 +0100 init diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..182f859 --- /dev/null +++ b/LICENSE @@ -0,0 +1,16 @@ +Copyright 2024 matu6968 + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the “Software”), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..128c266 --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# WebDesk 3rd party App Market client API for Go + +This allows you to make custom (known as 3rd party) App Market server clients for your intended purpose. + +## Features +- Listing currently uploaded apps +- Uploading new apps +- Editing app info +- Delete apps + +## Prerequisites + +- Go (1.23.1 or later, older will work with go.mod changes to the version) + +## Usage + +Import in your Go code: + ``` + import ( + "git.fluffy.pw/matu6968/webdesk-appmarket-golang" +) + ``` +# Configuration + +In the .env file this is the only thing you can set + +``` +AUTH_TOKEN=bearer-token-here # Put your token generated from the server .env file +``` +# API usage + +1. List uploaded apps: + +``` + // Create new client + c := client.NewClient("http://localhost:8080", os.Getenv("AUTH_TOKEN")) + + // List all apps + apps, err := c.GetApps() + if err != nil { + log.Fatal("Failed to get apps:", err) + } + fmt.Printf("Found %d apps\n", len(apps)) +``` +2. Upload an app: + +``` + // Upload new app + metadata := client.AppMetadata{ + Name: "Camera", + Ver: "6", + Info: "This is a camera app", + Pub: "matu6968", + } + newApp, err := c.UploadApp(metadata, "./index.js") + if err != nil { + log.Fatal("Failed to upload app:", err) + } + fmt.Printf("Uploaded app with ID: %s\n", newApp.AppID) +``` +3. Edit an app: + +``` + // Edit app + metadata.Ver = "7" // only specify what you want to edit + metadata.Name = "Camera" // only specify what you want to edit + metadata.Info = "This is a newer camera app" // only specify what you want to edit + metadata.Pub = "matu6968" // only specify what you want to edit + err = c.EditApp(newApp.AppID, metadata, "./index.js") // replace newApp.AppID with the app id you want to delete + if err != nil { + log.Fatal("Failed to edit app:", err) + } + fmt.Println("App updated successfully") +``` + +4. Delete an app: +``` + // Delete app + err = c.DeleteApp(newApp.AppID) // replace newApp.AppID with the app id you want to delete + if err != nil { + log.Fatal("Failed to delete app:", err) + } + fmt.Println("App deleted successfully") +``` + diff --git a/webdesk-appmarket-golang.go b/webdesk-appmarket-golang.go new file mode 100644 index 0000000..b39b200 --- /dev/null +++ b/webdesk-appmarket-golang.go @@ -0,0 +1,222 @@ +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"` +} + +// 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 +func (c *AppStoreClient) UploadApp(metadata AppMetadata, filePath string) (*App, error) { + // Create multipart form + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + // Add metadata + metadataBytes, err := json.Marshal(metadata) + 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 +} + +// 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 +}