param( [string]$BaseUrl = 'https://argus.kusoft.xyz', [Parameter(Mandatory = $true)] [string]$Username, [Parameter(Mandatory = $true)] [string]$Password, [Parameter(Mandatory = $true)] [string]$Slug, [Parameter(Mandatory = $true)] [string]$Name, [Parameter(Mandatory = $true)] [string]$Summary, [Parameter(Mandatory = $true)] [string]$Description, [Parameter(Mandatory = $true)] [string]$AndroidPackageName, [Parameter(Mandatory = $true)] [string]$Version, [Parameter(Mandatory = $true)] [ValidateRange(1, [int]::MaxValue)] [int]$AndroidVersionCode, [Parameter(Mandatory = $true)] [string]$PackagePath, [string]$Channel = 'stable', [string]$Platform = 'android', [string]$PackageKind = 'apk', [string]$Notes, [string]$RepositoryUrl, [string]$HomepageUrl = 'https://qsfera.kusoft.xyz', [switch]$Unlisted ) $ErrorActionPreference = 'Stop' Add-Type -AssemblyName System.Net.Http function Read-HttpBody([System.Net.Http.HttpResponseMessage]$Response) { if ($null -eq $Response.Content) { return '' } return $Response.Content.ReadAsStringAsync().GetAwaiter().GetResult() } function Ensure-Success( [System.Net.Http.HttpResponseMessage]$Response, [string]$Operation ) { if ($Response.IsSuccessStatusCode) { return } $body = Read-HttpBody $Response throw "$Operation failed with status $([int]$Response.StatusCode) ($($Response.ReasonPhrase)). Body: $body" } function New-JsonContent([string]$Json) { return [System.Net.Http.StringContent]::new($Json, [System.Text.Encoding]::UTF8, 'application/json') } $resolvedPackagePath = (Resolve-Path -LiteralPath $PackagePath).Path if (-not (Test-Path $resolvedPackagePath)) { throw "Package file was not found: $resolvedPackagePath" } $baseUri = $BaseUrl.TrimEnd('/') $cookieContainer = [System.Net.CookieContainer]::new() $handler = [System.Net.Http.HttpClientHandler]::new() $handler.CookieContainer = $cookieContainer $handler.UseCookies = $true $client = [System.Net.Http.HttpClient]::new($handler) $client.Timeout = [TimeSpan]::FromMinutes(15) try { $loginPayload = @{ username = $Username password = $Password } | ConvertTo-Json -Compress $loginResponse = $client.PostAsync( "$baseUri/api/admin/session/login", (New-JsonContent $loginPayload) ).GetAwaiter().GetResult() Ensure-Success $loginResponse 'Argus login' $appPayload = @{ name = $Name summary = $Summary description = $Description androidPackageName = $AndroidPackageName repositoryUrl = if ([string]::IsNullOrWhiteSpace($RepositoryUrl)) { $null } else { $RepositoryUrl } homepageUrl = if ([string]::IsNullOrWhiteSpace($HomepageUrl)) { $null } else { $HomepageUrl } isListed = (-not $Unlisted) } | ConvertTo-Json -Compress $appResponse = $client.PutAsync( "$baseUri/api/admin/apps/$Slug", (New-JsonContent $appPayload) ).GetAwaiter().GetResult() Ensure-Success $appResponse 'Argus app metadata update' $form = [System.Net.Http.MultipartFormDataContent]::new() $fields = [ordered]@{ Version = $Version Channel = $Channel Platform = $Platform PackageKind = $PackageKind AndroidVersionCode = [string]$AndroidVersionCode } foreach ($entry in $fields.GetEnumerator()) { $form.Add([System.Net.Http.StringContent]::new($entry.Value, [System.Text.Encoding]::UTF8), $entry.Key) } if (-not [string]::IsNullOrWhiteSpace($Notes)) { $form.Add([System.Net.Http.StringContent]::new($Notes, [System.Text.Encoding]::UTF8), 'Notes') } $fileStream = [System.IO.File]::OpenRead($resolvedPackagePath) try { $fileContent = [System.Net.Http.StreamContent]::new($fileStream) $fileContent.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse('application/vnd.android.package-archive') $form.Add($fileContent, 'Package', [System.IO.Path]::GetFileName($resolvedPackagePath)) $releaseResponse = $client.PostAsync( "$baseUri/api/admin/apps/$Slug/releases", $form ).GetAwaiter().GetResult() Ensure-Success $releaseResponse 'Argus release upload' $releaseBody = Read-HttpBody $releaseResponse if (-not [string]::IsNullOrWhiteSpace($releaseBody)) { Write-Output $releaseBody } } finally { $fileStream.Dispose() $form.Dispose() } } finally { $client.Dispose() $handler.Dispose() }