Note: This project was generated by AI (GitHub Copilot) based on a detailed specification prompt.
A .NET 10 MVC web service that ingests analytics from Google Play, Apple App Store, and Huawei AppGallery, stores normalized data in PostgreSQL, and provides a dashboard with Chart.js visualizations.
┌──────────────────────────────────────────────────────────────┐
│ Web Layer │
│ Controllers │ Views (Razor + Chart.js) │ API Key Middleware │
├──────────────────────────────────────────────────────────────┤
│ Application Layer │
│ DTOs │ Service Interfaces │ DashboardService │ Orchestrator │
├──────────────────────────────────────────────────────────────┤
│ Infrastructure Layer │
│ EF Core │ Repositories │ API Clients │ Quartz Jobs │
├──────────────────────────────────────────────────────────────┤
│ Domain Layer │
│ Entities │ Enums │ Repository Interfaces │ Unit of Work │
└──────────────────────────────────────────────────────────────┘
│
┌──────┴──────┐
│ PostgreSQL │
└─────────────┘
docker compose up --buildThe web app will be available at http://localhost:8080.
PostgreSQL runs on port 5432. Migrations apply automatically on startup.
- Windows Server with IIS enabled
- .NET 10 Hosting Bundle installed on the server
- PostgreSQL 16 accessible from the server
dotnet publish src/Web/AppStoreAnalytics.Web.csproj -c Release -o ./publishRun this once from your development machine (or the server) pointing at your external database:
dotnet ef database update \
--project src/Infrastructure/AppStoreAnalytics.Infrastructure.csproj \
--startup-project src/Web/AppStoreAnalytics.Web.csproj \
--connection "Host=YOUR_DB_SERVER;Port=5432;Database=appstoreanalytics;Username=YOUR_USER;Password=YOUR_PASSWORD"Edit publish/appsettings.Production.json:
{
"ConnectionStrings": {
"DefaultConnection": "Host=YOUR_DB_SERVER;Port=5432;Database=appstoreanalytics;Username=YOUR_USER;Password=YOUR_PASSWORD;SSL Mode=Prefer;Trust Server Certificate=true"
}
}Or set it as an environment variable in IIS:
ConnectionStrings__DefaultConnection
- Copy the
publishfolder to your server (e.g.C:\inetpub\appstoreanalytics) - Open IIS Manager → Add Website
- Site name:
AppStoreAnalytics - Physical path:
C:\inetpub\appstoreanalytics - Binding: choose your port/hostname
- Site name:
- Set the Application Pool:
- Select the pool → Basic Settings → .NET CLR version: No Managed Code
- Create the
logsfolder inside the publish directory for stdout logging - Browse to the site — the dashboard will load at
/
- Dashboard:
https://your-server/ - API test:
curl -H "X-Api-Key: YOUR_KEY" https://your-server/api/ingestion/run-all
- .NET 10 SDK
- PostgreSQL 16 (local install or any accessible server)
Edit src/Web/appsettings.Development.json with your PostgreSQL connection:
{
"ConnectionStrings": {
"DefaultConnection": "Host=YOUR_SERVER;Port=5432;Database=appstoreanalytics;Username=YOUR_USER;Password=YOUR_PASSWORD"
}
}Migrations apply automatically in Development mode.
cd src/Web
dotnet rundotnet testEdit src/Web/appsettings.json or set environment variables:
- Create a service account in Google Cloud Console
- Grant access in Google Play Console → API Access
- Set
AppStoreCredentials__GooglePlay__ServiceAccountJsonPathto the JSON key file path
- Generate an API key in App Store Connect → Users → Keys
- Set
IssuerId,KeyId, andPrivateKeyPathin configuration
- Go to AppGallery Connect → API Client
- Create credentials and set
ClientIdandClientSecret
Set IngestionApiKey__ApiKey to a secure random string. All /api/* endpoints require the X-Api-Key header.
# Ingest all stores for all apps
curl -X POST http://localhost:8080/api/ingestion/run-all \
-H "X-Api-Key: change-me-to-a-secure-random-key"
# Ingest specific store for specific app
curl -X POST http://localhost:8080/api/ingestion/run/GooglePlay/1 \
-H "X-Api-Key: change-me-to-a-secure-random-key"Store types: GooglePlay (1), Apple (2), Huawei (3).
Quartz.NET runs DailyIngestionJob at 2:00 AM UTC daily. To change the schedule, modify the cron expression in src/Infrastructure/DependencyInjection.cs:
q.AddTrigger(opts => opts
.ForJob(jobKey)
.WithIdentity("DailyIngestionTrigger")
.WithCronSchedule("0 0 2 * * ?")); // cron expression- Add entity (if needed) in
src/Domain/Enums/StoreType.cs - Create API client implementing
IStoreApiClientinsrc/Infrastructure/ApiClients/ - Create ingestion service extending
BaseIngestionServiceinsrc/Infrastructure/Services/ - Register in
src/Infrastructure/DependencyInjection.cs:services.AddHttpClient<NewStoreApiClient>(); services.AddScoped<IStoreApiClient, NewStoreApiClient>(); services.AddScoped<IIngestionService, NewStoreIngestionService>();
- Seed store record in
AppDbContext.OnModelCreating
| Table | Description |
|---|---|
apps |
Registered applications |
stores |
Store definitions (seeded) |
daily_installs |
Daily install counts per store/country |
ratings |
Rating snapshots per store |
reviews |
Individual reviews |
countries |
Country reference data |
store_credentials |
Encrypted API credentials |
ingestion_logs |
Ingestion run history |
/src
/Domain → Entities, enums, repository interfaces
/Application → DTOs, service interfaces, business logic
/Infrastructure → EF Core, API clients, Quartz jobs
/Web → MVC controllers, Razor views, middleware
/tests
/UnitTests → xUnit + Moq tests for services
/IntegrationTests → API endpoint tests (requires DB)
MIT