diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 00000000..8f9fa708 --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,57 @@ +name: Preview + +on: + pull_request: + types: [opened, synchronize, reopened, closed] + branches: [main] + +concurrency: + group: preview-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + deploy_preview: + if: github.event.action != 'closed' + name: Deploy Preview + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + version: 10 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build web app (preview) + run: pnpm nx build web-app --configuration preview + + - name: Copy SWA routing config + run: cp apps/web-app/src/staticwebapp.config.json dist/apps/web-app/browser/ + + - name: Deploy to Azure Static Web Apps + id: deploy + uses: Azure/static-web-apps-deploy@v1 + with: + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} + repo_token: ${{ secrets.GITHUB_TOKEN }} + action: upload + app_location: dist/apps/web-app/browser + skip_app_build: true + + close_preview: + if: github.event.action == 'closed' + name: Close Preview + runs-on: ubuntu-latest + steps: + - name: Close staging environment + uses: Azure/static-web-apps-deploy@v1 + with: + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} + action: close diff --git a/apps/api/Api/Program.cs b/apps/api/Api/Program.cs index 6e3b0529..17b0b3f3 100644 --- a/apps/api/Api/Program.cs +++ b/apps/api/Api/Program.cs @@ -41,6 +41,17 @@ builder.Services.AddControllers(); +builder.Services.AddCors(options => + options.AddPolicy("SwaPreview", policy => + policy + .SetIsOriginAllowed(origin => + Uri.TryCreate(origin, UriKind.Absolute, out var uri) + && uri.Host.EndsWith(".azurestaticapps.net")) + .AllowAnyMethod() + .AllowAnyHeader() + ) +); + var app = builder.Build(); if (app.Environment.IsDevelopment()) @@ -85,6 +96,8 @@ app.UseRouting(); +app.UseCors("SwaPreview"); + app.UseAuthorization(); app.MapControllers(); diff --git a/apps/web-app/project.json b/apps/web-app/project.json index 4a79290f..b12854e1 100644 --- a/apps/web-app/project.json +++ b/apps/web-app/project.json @@ -50,6 +50,15 @@ "extractLicenses": false, "sourceMap": true, "namedChunks": true + }, + "preview": { + "fileReplacements": [ + { + "replace": "apps/web-app/src/environments/environment.ts", + "with": "apps/web-app/src/environments/environment.preview.ts" + } + ], + "outputHashing": "all" } }, "defaultConfiguration": "production" diff --git a/apps/web-app/src/app/api-base-url.interceptor.ts b/apps/web-app/src/app/api-base-url.interceptor.ts new file mode 100644 index 00000000..93620961 --- /dev/null +++ b/apps/web-app/src/app/api-base-url.interceptor.ts @@ -0,0 +1,10 @@ +import { HttpInterceptorFn } from '@angular/common/http'; + +import { environment } from '../environments/environment'; + +export const apiBaseUrlInterceptor: HttpInterceptorFn = (req, next) => { + if (!environment.apiBaseUrl || !req.url.startsWith('/api')) { + return next(req); + } + return next(req.clone({ url: environment.apiBaseUrl + req.url })); +}; diff --git a/apps/web-app/src/app/app.config.ts b/apps/web-app/src/app/app.config.ts index 1260909b..6c79d487 100644 --- a/apps/web-app/src/app/app.config.ts +++ b/apps/web-app/src/app/app.config.ts @@ -19,12 +19,16 @@ import { import { provideServiceWorker } from '@angular/service-worker'; import { authInterceptor } from '@myorg/auth'; +import { apiBaseUrlInterceptor } from './api-base-url.interceptor'; import { routes } from './app.routes'; export const appConfig: ApplicationConfig = { providers: [ provideZonelessChangeDetection(), - provideHttpClient(withFetch(), withInterceptors([authInterceptor])), + provideHttpClient( + withFetch(), + withInterceptors([apiBaseUrlInterceptor, authInterceptor]), + ), provideRouter( routes, withComponentInputBinding(), diff --git a/apps/web-app/src/environments/environment.preview.ts b/apps/web-app/src/environments/environment.preview.ts new file mode 100644 index 00000000..7fc918e7 --- /dev/null +++ b/apps/web-app/src/environments/environment.preview.ts @@ -0,0 +1,3 @@ +export const environment = { + apiBaseUrl: 'https://angularclinetcorengrxstarter.azurewebsites.net', +}; diff --git a/apps/web-app/src/environments/environment.ts b/apps/web-app/src/environments/environment.ts new file mode 100644 index 00000000..81af9b33 --- /dev/null +++ b/apps/web-app/src/environments/environment.ts @@ -0,0 +1,3 @@ +export const environment = { + apiBaseUrl: '', +}; diff --git a/apps/web-app/src/staticwebapp.config.json b/apps/web-app/src/staticwebapp.config.json new file mode 100644 index 00000000..a6c11734 --- /dev/null +++ b/apps/web-app/src/staticwebapp.config.json @@ -0,0 +1,6 @@ +{ + "navigationFallback": { + "rewrite": "/index.html", + "exclude": ["/*.{css,js,png,gif,ico,jpg,svg,webmanifest,woff,woff2,txt}"] + } +}