Skip to content

Commit 81565d5

Browse files
committed
Add production API URL interceptor and Azure setup script
- Add Angular environment files (dev: relative URLs, prod: Azure API URL) - Add apiBaseUrlInterceptor to rewrite /api/ to Azure App Service in production - Add fileReplacements in angular.json for production builds - Add staticwebapp.config.json for SPA navigation fallback - Add scripts/setup-azure.sh for seeding Azure SQL and MongoDB
1 parent a6e0d4b commit 81565d5

7 files changed

Lines changed: 174 additions & 2 deletions

File tree

scripts/setup-azure.sh

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
# =============================================================================
5+
# FitSpec Azure Database Setup
6+
# Seeds Azure SQL Database and Azure Cosmos DB (MongoDB API)
7+
#
8+
# Prerequisites:
9+
# - .NET SDK 10.0+ (for EF Core migrations)
10+
# - sqlcmd (install: https://learn.microsoft.com/en-us/sql/tools/sqlcmd)
11+
# - mongosh (install: https://www.mongodb.com/docs/mongodb-shell/install/)
12+
#
13+
# Usage:
14+
# ./scripts/setup-azure.sh \
15+
# --sql-connection "Server=yourserver.database.windows.net;Database=FitSpec;User Id=admin;Password=secret;Encrypt=true;TrustServerCertificate=false" \
16+
# --mongo-connection "mongodb://yourcosmosdb:password@yourcosmosdb.mongo.cosmos.azure.com:10255/fitspec?ssl=true&retrywrites=false"
17+
#
18+
# Or set environment variables:
19+
# AZURE_SQL_CONNECTION="..."
20+
# AZURE_MONGO_CONNECTION="..."
21+
# ./scripts/setup-azure.sh
22+
# =============================================================================
23+
24+
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
25+
cd "$ROOT_DIR"
26+
27+
SQL_CONN="${AZURE_SQL_CONNECTION:-}"
28+
MONGO_CONN="${AZURE_MONGO_CONNECTION:-}"
29+
30+
# Parse command line arguments
31+
while [[ $# -gt 0 ]]; do
32+
case $1 in
33+
--sql-connection) SQL_CONN="$2"; shift 2 ;;
34+
--mongo-connection) MONGO_CONN="$2"; shift 2 ;;
35+
--skip-sql) SKIP_SQL=1; shift ;;
36+
--skip-mongo) SKIP_MONGO=1; shift ;;
37+
--skip-migrations) SKIP_MIGRATIONS=1; shift ;;
38+
--help)
39+
echo "Usage: $0 [options]"
40+
echo ""
41+
echo "Options:"
42+
echo " --sql-connection STRING Azure SQL connection string"
43+
echo " --mongo-connection STRING Cosmos DB / MongoDB connection string"
44+
echo " --skip-sql Skip SQL seed scripts"
45+
echo " --skip-mongo Skip MongoDB seed scripts"
46+
echo " --skip-migrations Skip EF Core migrations (tables already exist)"
47+
echo " --help Show this help"
48+
exit 0
49+
;;
50+
*) echo "Unknown option: $1"; exit 1 ;;
51+
esac
52+
done
53+
54+
if [[ -z "$SQL_CONN" && -z "$SKIP_SQL" ]]; then
55+
echo "Error: Azure SQL connection string required."
56+
echo " Use --sql-connection or set AZURE_SQL_CONNECTION"
57+
exit 1
58+
fi
59+
60+
echo "=== FitSpec Azure Setup ==="
61+
echo ""
62+
63+
# -------------------------------------------------------------------------
64+
# Step 1: EF Core migrations (creates database schema)
65+
# -------------------------------------------------------------------------
66+
if [[ -z "$SKIP_MIGRATIONS" && -n "$SQL_CONN" ]]; then
67+
echo "[1/3] Applying EF Core migrations..."
68+
dotnet ef database update \
69+
--project src/FitSpec.Data \
70+
--startup-project src/FitSpec.API \
71+
--connection "$SQL_CONN"
72+
echo " Schema created."
73+
else
74+
echo "[1/3] Skipping EF Core migrations."
75+
fi
76+
77+
# -------------------------------------------------------------------------
78+
# Step 2: SQL seed scripts (stored procedures + data)
79+
# -------------------------------------------------------------------------
80+
if [[ -z "$SKIP_SQL" ]]; then
81+
echo "[2/3] Seeding Azure SQL Database..."
82+
83+
# Extract server, user, password from connection string for sqlcmd
84+
SERVER=$(echo "$SQL_CONN" | grep -oP 'Server=\K[^;]+')
85+
DATABASE=$(echo "$SQL_CONN" | grep -oP 'Database=\K[^;]+')
86+
USER=$(echo "$SQL_CONN" | grep -oP 'User Id=\K[^;]+')
87+
PASSWORD=$(echo "$SQL_CONN" | grep -oP 'Password=\K[^;]+')
88+
89+
if [[ -z "$SERVER" || -z "$DATABASE" || -z "$USER" || -z "$PASSWORD" ]]; then
90+
echo " Error: Could not parse connection string. Expected format:"
91+
echo " Server=...;Database=...;User Id=...;Password=..."
92+
exit 1
93+
fi
94+
95+
for f in infra/seed/[0-1][0-9]*.sql; do
96+
echo " Running $(basename "$f")..."
97+
sqlcmd -S "$SERVER" -U "$USER" -P "$PASSWORD" -d "$DATABASE" -i "$f" -I
98+
done
99+
echo " SQL seeding complete."
100+
else
101+
echo "[2/3] Skipping SQL seed scripts."
102+
fi
103+
104+
# -------------------------------------------------------------------------
105+
# Step 3: MongoDB seed scripts (reviews + catalog)
106+
# -------------------------------------------------------------------------
107+
if [[ -z "$SKIP_MONGO" && -n "$MONGO_CONN" ]]; then
108+
echo "[3/3] Seeding MongoDB (Cosmos DB)..."
109+
mongosh "$MONGO_CONN" < infra/seed/seed-mongo-reviews.js
110+
mongosh "$MONGO_CONN" < infra/seed/seed-mongo-catalog.js
111+
echo " MongoDB seeding complete."
112+
elif [[ -z "$SKIP_MONGO" && -z "$MONGO_CONN" ]]; then
113+
echo "[3/3] Skipping MongoDB — no connection string provided."
114+
echo " Use --mongo-connection or set AZURE_MONGO_CONNECTION"
115+
else
116+
echo "[3/3] Skipping MongoDB seed scripts."
117+
fi
118+
119+
echo ""
120+
echo "=== Azure setup complete! ==="
121+
echo ""
122+
echo "Verify your app settings include:"
123+
echo " ConnectionStrings__DefaultConnection = <your Azure SQL connection string>"
124+
echo " ConnectionStrings__MongoDb = <your Cosmos DB connection string>"
125+
echo " AllowedOrigins__0 = https://your-app.azurewebsites.net"
126+
echo " Claude__ApiKey = <your Anthropic API key> (optional)"

src/fitspec-ui/angular.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,13 @@
7272
"maximumError": "8kB"
7373
}
7474
],
75-
"outputHashing": "all"
75+
"outputHashing": "all",
76+
"fileReplacements": [
77+
{
78+
"replace": "src/environments/environment.ts",
79+
"with": "src/environments/environment.prod.ts"
80+
}
81+
]
7682
},
7783
"development": {
7884
"optimization": false,
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"navigationFallback": {
3+
"rewrite": "/index.html",
4+
"exclude": ["/assets/*", "/icons/*", "/*.js", "/*.css", "/*.ico", "/*.webmanifest"]
5+
},
6+
"routes": [
7+
{
8+
"route": "/api/*",
9+
"allowedRoles": ["anonymous"]
10+
}
11+
],
12+
"globalHeaders": {
13+
"X-Content-Type-Options": "nosniff",
14+
"X-Frame-Options": "DENY",
15+
"Referrer-Policy": "strict-origin-when-cross-origin"
16+
},
17+
"responseOverrides": {
18+
"404": {
19+
"rewrite": "/index.html"
20+
}
21+
}
22+
}

src/fitspec-ui/src/app/app.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import { provideHttpClient, withInterceptors } from '@angular/common/http';
44
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
55
import { routes } from './app.routes';
66
import { errorInterceptor } from './core/interceptors/error.interceptor';
7+
import { apiBaseUrlInterceptor } from './core/interceptors/api-base-url.interceptor';
78

89
export const appConfig: ApplicationConfig = {
910
providers: [
1011
provideZoneChangeDetection({ eventCoalescing: true }),
1112
provideRouter(routes),
12-
provideHttpClient(withInterceptors([errorInterceptor])),
13+
provideHttpClient(withInterceptors([apiBaseUrlInterceptor, errorInterceptor])),
1314
provideAnimationsAsync(),
1415
]
1516
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { HttpInterceptorFn } from '@angular/common/http';
2+
import { environment } from '../../../environments/environment';
3+
4+
export const apiBaseUrlInterceptor: HttpInterceptorFn = (req, next) => {
5+
if (environment.apiBaseUrl && req.url.startsWith('/api')) {
6+
return next(req.clone({ url: `${environment.apiBaseUrl}${req.url}` }));
7+
}
8+
return next(req);
9+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const environment = {
2+
production: true,
3+
apiBaseUrl: 'https://fitspec-api.azurewebsites.net'
4+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const environment = {
2+
production: false,
3+
apiBaseUrl: ''
4+
};

0 commit comments

Comments
 (0)