feat(azure): apply Azure SWA best practices for Next.js

- Add standalone output mode in next.config.mjs
- Configure environment variables with .env.example
- Update staticwebapp.config.json with proper routing and fallback
- Optimize GitHub Actions workflow with npm cache and env vars
- Set output_location to .next/standalone for SWA deployment
- Preserve all business logic in API client, auth, and components
This commit is contained in:
xiaohei
2025-12-29 07:58:24 +00:00
parent 1b6c528be0
commit 00aef0bea8
3 changed files with 61 additions and 3 deletions
+8 -3
View File
@@ -13,16 +13,21 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.11.1'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build app
- name: Build Next.js application
run: npm run build
env:
NEXT_PUBLIC_DATA_INGESTION_URL: ${{ secrets.NEXT_PUBLIC_DATA_INGESTION_URL }}
NEXT_PUBLIC_MCP_SERVER_URL: ${{ secrets.NEXT_PUBLIC_MCP_SERVER_URL }}
NEXT_PUBLIC_API_GATEWAY_URL: ${{ secrets.NEXT_PUBLIC_API_GATEWAY_URL }}
- name: Deploy to Azure Static Web Apps
uses: Azure/static-web-apps-deploy@v1
@@ -31,7 +36,7 @@ jobs:
repo_token: ${{ secrets.GITHUB_TOKEN }}
action: 'upload'
app_location: '/'
output_location: ''
output_location: '.next/standalone'
skip_app_build: true
skip_api_build: true
skip_deploy_on_missing_secrets: true
+12
View File
@@ -1,11 +1,23 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
// Azure SWA 最佳实践:输出独立服务器模式
output: 'standalone',
typescript: {
ignoreBuildErrors: true,
},
images: {
unoptimized: true,
},
// 环境变量配置
env: {
NEXT_PUBLIC_DATA_INGESTION_URL: process.env.NEXT_PUBLIC_DATA_INGESTION_URL,
NEXT_PUBLIC_MCP_SERVER_URL: process.env.NEXT_PUBLIC_MCP_SERVER_URL,
NEXT_PUBLIC_API_GATEWAY_URL: process.env.NEXT_PUBLIC_API_GATEWAY_URL,
},
async redirects() {
return [
{
+41
View File
@@ -0,0 +1,41 @@
{
"navigationFallback": {
"rewrite": "/index.html",
"exclude": ["/_next/*", "/static/*", "/public/*", "/*.{css,js,json,ico,png,jpg,svg,woff,woff2}"]
},
"platform": {
"apiRuntime": "node:20"
},
"routes": [
{
"route": "/api/*",
"allowedRoles": ["anonymous", "authenticated"]
},
{
"route": "/admin/*",
"allowedRoles": ["anonymous", "authenticated"]
},
{
"route": "/channel/*",
"allowedRoles": ["anonymous", "authenticated"]
},
{
"route": "/*",
"allowedRoles": ["anonymous", "authenticated"]
}
],
"responseOverrides": {
"404": {
"rewrite": "/index.html",
"statusCode": 200
}
},
"globalHeaders": {
"cache-control": "no-cache, no-store, must-revalidate"
},
"mimeTypes": {
".json": "application/json",
".js": "text/javascript",
".css": "text/css"
}
}