Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support app install and uninstall #496

Merged
merged 5 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions restapi/api/app_endpoints.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package api

import (
"log"
"net/http"
"os"

"github.com/danielpaulus/go-ios/ios"
"github.com/danielpaulus/go-ios/ios/installationproxy"
"github.com/danielpaulus/go-ios/ios/instruments"
"github.com/danielpaulus/go-ios/ios/zipconduit"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)

// List apps on a device
Expand Down Expand Up @@ -138,3 +142,95 @@ func KillApp(c *gin.Context) {

c.JSON(http.StatusOK, GenericResponse{Message: bundleID + " is not running"})
}

// Install app on a device
// @Summary Install app on a device
// @Description Install app on a device by uploading an ipa file
// @Tags apps
// @Produce json
// @Param file formData file true "ipa file to install"
// @Success 200 {object} GenericResponse
// @Failure 500 {object} GenericResponse
// @Router /device/{udid}/apps/install [post]
func InstallApp(c *gin.Context) {
device := c.MustGet(IOS_KEY).(ios.DeviceEntry)
file, err := c.FormFile("file")

log.Printf("Received file: %s", file.Filename)

if err != nil {
c.JSON(http.StatusUnprocessableEntity, GenericResponse{Error: "file form-data is missing"})
return
}

if file.Size == 0 { // 100 MB limit
c.JSON(http.StatusRequestEntityTooLarge, GenericResponse{Error: "uploaded file is empty"})
return
}

if file.Size > 200*1024*1024 { // 100 MB limit
c.JSON(http.StatusRequestEntityTooLarge, GenericResponse{Error: "file size exceeds the 200MB limit"})
return
}

appDownloadFolder := os.Getenv("APP_DOWNLOAD_FOLDER")
if appDownloadFolder == "" {
appDownloadFolder = "/tmp"
aluedeke marked this conversation as resolved.
Show resolved Hide resolved
}

dst := appDownloadFolder + "/" + uuid.New().String() + ".ipa"
aluedeke marked this conversation as resolved.
Show resolved Hide resolved
defer func() {
if err := os.Remove(dst); err != nil {
c.JSON(http.StatusInternalServerError, GenericResponse{Error: "failed to delete temporary file"})
}
}()

c.SaveUploadedFile(file, dst)

conn, err := zipconduit.New(device)
if err != nil {
c.JSON(http.StatusInternalServerError, GenericResponse{Error: "Unable to setup ZipConduit connection"})
return
}

err = conn.SendFile(dst)
if err != nil {
c.JSON(http.StatusInternalServerError, GenericResponse{Error: "Unable to install uploaded app"})
return
}

c.JSON(http.StatusOK, GenericResponse{Message: "App installed successfully"})
}

// Uninstall app on a device
// @Summary Uninstall app on a device
// @Description Uninstall app on a device by provided bundleID
// @Tags apps
// @Produce json
// @Param bundleID query string true "bundle identifier of the targeted app"
// @Success 200 {object} GenericResponse
// @Failure 500 {object} GenericResponse
func UninstallApp(c *gin.Context) {
device := c.MustGet(IOS_KEY).(ios.DeviceEntry)

bundleID := c.Query("bundleID")
if bundleID == "" {
c.JSON(http.StatusUnprocessableEntity, GenericResponse{Error: "bundleID query param is missing"})
return
}

svc, err := installationproxy.New(device)
if err != nil {
c.JSON(http.StatusInternalServerError, GenericResponse{Error: err.Error()})
return
}
defer svc.Close()

err = svc.Uninstall(bundleID)
if err != nil {
c.JSON(http.StatusInternalServerError, GenericResponse{Error: err.Error()})
return
}

c.JSON(http.StatusOK, GenericResponse{Message: bundleID + " uninstalled successfully"})
}
2 changes: 2 additions & 0 deletions restapi/api/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,6 @@ func appRoutes(group *gin.RouterGroup) {
router.GET("/", ListApps)
router.POST("/launch", LaunchApp)
router.POST("/kill", KillApp)
router.POST("/install", InstallApp)
router.POST("/uninstall", UninstallApp)
}
Loading