From 3c04b5db2680defb2fdb49213b03cfc0a1791ccf Mon Sep 17 00:00:00 2001 From: Myx Date: Thu, 12 Mar 2026 13:36:39 +0100 Subject: [PATCH] Add webapp release --- .gitea/workflows/deploy-web-apps.yml | 43 +++++++++ public/web.config | 23 +++++ tools/deploy-web-apps.ps1 | 101 ++++++++++++++++++++ website/public/web.config | 23 +++++ website/src/app/services/release.service.ts | 49 ++++++++-- 5 files changed, 230 insertions(+), 9 deletions(-) create mode 100644 .gitea/workflows/deploy-web-apps.yml create mode 100644 public/web.config create mode 100644 tools/deploy-web-apps.ps1 create mode 100644 website/public/web.config diff --git a/.gitea/workflows/deploy-web-apps.yml b/.gitea/workflows/deploy-web-apps.yml new file mode 100644 index 0000000..cc0bac1 --- /dev/null +++ b/.gitea/workflows/deploy-web-apps.yml @@ -0,0 +1,43 @@ +name: Deploy Web Apps + +on: + push: + branches: [main, master] + workflow_dispatch: + +jobs: + deploy: + runs-on: windows + + defaults: + run: + shell: powershell + + steps: + - name: Checkout + uses: https://github.com/actions/checkout@v4 + + - name: Install root dependencies + env: + NODE_ENV: development + run: npm ci + + - name: Install website dependencies + env: + NODE_ENV: development + run: npm ci --prefix website + + - name: Build Toju web app + run: npm run build + + - name: Build Toju website + run: | + Push-Location website + npm run build + Pop-Location + + - name: Deploy both apps to IIS + run: > + ./tools/deploy-web-apps.ps1 + -WebsitePort 4341 + -AppPort 4492 diff --git a/public/web.config b/public/web.config new file mode 100644 index 0000000..0596310 --- /dev/null +++ b/public/web.config @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/deploy-web-apps.ps1 b/tools/deploy-web-apps.ps1 new file mode 100644 index 0000000..62fd6f3 --- /dev/null +++ b/tools/deploy-web-apps.ps1 @@ -0,0 +1,101 @@ +[CmdletBinding()] +param( + [string]$RepoRoot = (Split-Path -Parent $PSScriptRoot), + [string]$IisRoot = 'C:\inetpub\wwwroot', + [int]$WebsitePort = 4341, + [int]$AppPort = 4492 +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +try { + Import-Module WebAdministration -ErrorAction Stop +} catch { + throw 'The IIS WebAdministration module is required on the Windows runner.' +} + +function Invoke-RoboCopyMirror { + param( + [Parameter(Mandatory = $true)] + [string]$Source, + + [Parameter(Mandatory = $true)] + [string]$Destination + ) + + if (-not (Test-Path -LiteralPath $Source)) { + throw "Build output not found: $Source" + } + + New-Item -ItemType Directory -Path $Destination -Force | Out-Null + robocopy $Source $Destination /MIR /NFL /NDL /NJH /NJS /NP | Out-Null + + if ($LASTEXITCODE -gt 7) { + throw "robocopy failed from $Source to $Destination with exit code $LASTEXITCODE" + } +} + +function Ensure-AppPool { + param( + [Parameter(Mandatory = $true)] + [string]$Name + ) + + $appPoolPath = "IIS:\AppPools\$Name" + + if (-not (Test-Path -LiteralPath $appPoolPath)) { + New-WebAppPool -Name $Name | Out-Null + } + + Set-ItemProperty $appPoolPath -Name managedRuntimeVersion -Value '' +} + +function Publish-IisSite { + param( + [Parameter(Mandatory = $true)] + [string]$SiteName, + + [Parameter(Mandatory = $true)] + [string]$SourcePath, + + [Parameter(Mandatory = $true)] + [string]$DestinationPath, + + [Parameter(Mandatory = $true)] + [int]$Port + ) + + Ensure-AppPool -Name $SiteName + Invoke-RoboCopyMirror -Source $SourcePath -Destination $DestinationPath + + $existingSite = Get-Website -Name $SiteName -ErrorAction SilentlyContinue + if ($null -ne $existingSite) { + Stop-Website -Name $SiteName -ErrorAction SilentlyContinue + Remove-Website -Name $SiteName + } + + New-Website -Name $SiteName -PhysicalPath $DestinationPath -Port $Port -ApplicationPool $SiteName | Out-Null + Start-Website -Name $SiteName + + Write-Host "Deployed $SiteName to $DestinationPath on port $Port." +} + +$deployments = @( + @{ + SiteName = 'toju-website' + SourcePath = (Join-Path $RepoRoot 'website\dist\toju-website\browser') + DestinationPath = (Join-Path $IisRoot 'toju-website') + Port = $WebsitePort + }, + @{ + SiteName = 'toju-app' + SourcePath = (Join-Path $RepoRoot 'dist\client\browser') + DestinationPath = (Join-Path $IisRoot 'toju-app') + Port = $AppPort + } +) + +foreach ($deployment in $deployments) { + Publish-IisSite @deployment +} diff --git a/website/public/web.config b/website/public/web.config new file mode 100644 index 0000000..0596310 --- /dev/null +++ b/website/public/web.config @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/src/app/services/release.service.ts b/website/src/app/services/release.service.ts index 4b336f2..99722a9 100644 --- a/website/src/app/services/release.service.ts +++ b/website/src/app/services/release.service.ts @@ -55,6 +55,8 @@ const ARCHIVE_SUFFIXES = [ '.7z', '.rar' ]; +const DIRECT_RELEASES_API_URL = 'https://git.azaaxin.com/api/v1/repos/myxelium/Toju/releases'; +const PROXY_RELEASES_API_URL = '/api/releases'; function matchesAssetPattern(name: string, suffixes: string[], hints: string[] = []): boolean { if (suffixes.some((suffix) => name.endsWith(suffix))) { @@ -205,15 +207,7 @@ export class ReleaseService { private async fetchReleasesInternal(): Promise { try { - const response = await fetch('/api/releases', { - headers: { Accept: 'application/json' } - }); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}`); - } - - const data = await response.json(); + const data = await this.fetchReleasesFromAvailableEndpoints(); this.cachedReleases = Array.isArray(data) ? data : [data]; @@ -226,4 +220,41 @@ export class ReleaseService { this.fetchPromise = null; } } + + private async fetchReleasesFromAvailableEndpoints(): Promise { + let lastError: unknown = null; + + for (const endpoint of this.getReleaseEndpoints()) { + try { + const response = await fetch(endpoint, { + headers: { Accept: 'application/json' } + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}`); + } + + return await response.json(); + } catch (error) { + lastError = error; + } + } + + throw lastError instanceof Error + ? lastError + : new Error('Failed to fetch releases from all configured endpoints.'); + } + + private getReleaseEndpoints(): string[] { + if (!isPlatformBrowser(this.platformId)) { + return [PROXY_RELEASES_API_URL, DIRECT_RELEASES_API_URL]; + } + + const hostname = window.location.hostname; + const isLocalHost = hostname === 'localhost' || hostname === '127.0.0.1'; + + return isLocalHost + ? [PROXY_RELEASES_API_URL, DIRECT_RELEASES_API_URL] + : [DIRECT_RELEASES_API_URL, PROXY_RELEASES_API_URL]; + } }