A production-ready .NET 10 backend for an AI-powered interview platform. Features resume analysis, mock AI interviews, job management, and company profiles β powered by Groq (primary) with Ollama as a local fallback.
- Deployment Guide: See DEPLOYMENT.md for production setup
- API Documentation: Swagger UI available at
/swaggerendpoint - Environment Setup: Copy
appsettings.Development.json.exampleand configure with your values
- .NET 10 / ASP.NET Core Web API
- PostgreSQL + Dapper
- ASP.NET Core Identity + JWT Authentication
- Groq API (llama-3.1-8b-instant) β primary AI
- Ollama (http://localhost:11434) β local AI fallback
- Clean layered architecture: Core β Application β Infrastructure β Server
- Docker & Docker Compose support
- Email templates with HTML formatting
βββ AIInterview.Core/ # DTOs, constants, shared models
βββ AIInterview.Application/ # Interfaces, services, business logic
βββ AIInterview.Infrastructure/ # Dapper repositories, DbContext, migrations
βββ AIInterview.Server/ # Controllers, middleware, configuration
βββ Templates/ # Email templates (HTML)
βββ docker-compose.yml # Development compose
βββ docker-compose.prod.yml # Production compose
βββ DEPLOYMENT.md # Production deployment guide
βββ deploy.sh / deploy.bat # Deployment automation scripts
- .NET 10 SDK (download)
- PostgreSQL 15+ (download)
- (Optional) Docker & Docker Compose for containerized development
- (Optional) Ollama for local AI fallback (download)
git clone <repo-url>
cd AIInterview.ServerOption A: Manual Setup
Copy and edit configuration:
cd AIInterview.Server
cp appsettings.Development.json.example appsettings.Development.json
# Edit appsettings.Development.json with your valuesOption B: Docker Setup
Use the development docker-compose:
docker-compose up -d
# This starts PostgreSQL, Ollama, and configures the databaseUpdate these required values:
{
"ConnectionStrings": {
"Default": "Host=localhost;Port=5432;Database=AI_Db;Username=postgres;Password=YOUR_PASSWORD"
},
"Jwt": {
"SecretKey": "YourSecretKeyWithAtLeast32CharactersForDevelopment"
},
"Google": {
"ClientId": "your-google-oauth-client-id.apps.googleusercontent.com"
},
"Groq": {
"ApiKey": "your-groq-api-key"
},
"CloudinarySettings": {
"CloudName": "your-cloudinary-cloud-name",
"ApiKey": "your-cloudinary-api-key",
"ApiSecret": "your-cloudinary-api-secret"
},
"EmailSettings": {
"Username": "your-email@gmail.com",
"Password": "your-app-specific-password"
}
}API Keys & Credentials:
- Groq API: Get free key at https://console.groq.com
- Google OAuth: Set up at https://console.cloud.google.com
- Cloudinary: Sign up at https://cloudinary.com
- Gmail App Password: Enable 2FA and generate at https://myaccount.google.com/apppasswords
# Apply migrations using EF Core
dotnet ef database update --project AIInterview.Infrastructure --startup-project AIInterview.Serverdotnet run --project AIInterview.ServerEndpoints:
- API:
https://localhost:7129 - Swagger UI:
https://localhost:7129/swagger - Health Check:
https://localhost:7129/health
Forgot-password emails use HTML template from Templates/ForgotPasswordTemplate.html with dynamic placeholders:
{{USER_EMAIL}}β User's email address{{RESET_URL}}β Password reset link{{RESET_CODE}}β Short reset reference code shown in email
Edit the template to customize styling and content.
Request
βββ FallbackAiService
βββ GroqAiService (primary) β api.groq.com
βββ OllamaAiService (fallback) β localhost:11434
If Groq fails for any reason (rate limit, network, etc.), the request automatically retries via Ollama. No code changes needed.
# Install from https://ollama.com
ollama serve
ollama pull llama3.2All endpoints require Authorization: Bearer <token> unless marked public.
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /api/account/register |
Register new user | Public |
| POST | /api/account/login |
Login, returns JWT | Public |
| POST | /api/account/refresh |
Refresh access token | Public |
| POST | /api/account/google-login |
Google OAuth login | Public |
| POST | /api/account/logout |
Logout | Required |
- Minimum length:
6characters - No required digit
- No required lowercase
- No required uppercase
- No required special character
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/jobseeker/profile |
Get profile |
| PUT | /api/jobseeker/profile |
Create or update profile |
| POST | /api/jobseeker/upload-avatar |
Upload avatar image |
| DELETE | /api/jobseeker/delete-avatar |
Delete avatar |
| POST | /api/jobseeker/upload-resume |
Upload resume (PDF/DOCX) |
| DELETE | /api/jobseeker/delete-resume |
Delete resume |
| GET | /api/jobseeker/download-resume |
Download resume |
| GET | /api/jobseeker/experience |
Get work experience |
| POST | /api/jobseeker/experience |
Add experience |
| PUT | /api/jobseeker/experience/{id} |
Update experience |
| DELETE | /api/jobseeker/experience/{id} |
Delete experience |
| GET | /api/jobseeker/education |
Get education |
| POST | /api/jobseeker/education |
Add education |
| PUT | /api/jobseeker/education/{id} |
Update education |
| DELETE | /api/jobseeker/education/{id} |
Delete education |
| GET | /api/jobseeker/skills |
Get user skills |
| PUT | /api/jobseeker/skills |
Sync skills |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/resume-enhancer/analyze |
Analyze uploaded resume file |
| POST | /api/resume-enhancer/analyze-from-profile |
Analyze resume from profile |
| GET | /api/resume-enhancer/result |
Get last cached analysis |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/mock-interview/start |
Start new interview session |
| POST | /api/mock-interview/message |
Send answer, get next question |
| GET | /api/mock-interview/session/{sessionId} |
Get session with full chat history |
| GET | /api/mock-interview/sessions |
Get all sessions for current user |
Start interview β request:
{
"skills": ["React", "TypeScript"]
}If
skillsis empty or omitted, skills are loaded from the user's profile automatically.
Start interview β response:
{
"sessionId": "abc-123...",
"skills": ["React", "TypeScript"],
"firstQuestion": "Can you explain the difference between props and state in React?"
}Send message β request:
{
"sessionId": "abc-123...",
"userMessage": "Props are read-only and passed from parent, state is managed internally."
}Send message β response:
{
"aiMessage": "Good answer! Next question: How does the useEffect hook work?",
"isCompleted": false,
"feedbackSummary": null
}After 5 questions isCompleted becomes true and feedbackSummary contains the overall performance summary.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/jobs/my-jobs |
Get employer's posted jobs |
| POST | /api/jobs |
Create job posting |
| PUT | /api/jobs/{id} |
Update job |
| DELETE | /api/jobs/{id} |
Delete job |
| PATCH | /api/jobs/{id}/close |
Close job |
| PATCH | /api/jobs/{id}/reopen |
Reopen job |
| GET | /api/jobs/{id}/applicants |
Get applicants |
| POST | /api/jobs/generate-description |
AI-generate job description |
Generate description β request:
{
"title": "Frontend Developer",
"skills": ["React", "TypeScript", "Redux"]
}| Method | Endpoint | Description |
|---|---|---|
| GET | /api/companyprofile |
Get company profile |
| PUT | /api/companyprofile |
Update company profile |
| POST | /api/companyprofile/upload-logo |
Upload company logo |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /api/lookup/skills |
Get all available skills | Public |
| GET | /api/lookup/job-types |
Get job types | Public |
| GET | /api/lookup/company-sizes |
Get company sizes | Public |
| Key | Description |
|---|---|
ConnectionStrings:Default |
PostgreSQL connection string |
Jwt:SecretKey |
JWT signing key (min 32 chars) |
Jwt:Issuer |
JWT issuer |
Jwt:Audience |
JWT audience |
Jwt:AccessTokenExpiration |
Access token TTL in minutes |
Jwt:RefreshTokenExpiration |
Refresh token TTL in minutes |
Google:ClientId |
Google OAuth client ID |
Groq:ApiKey |
Groq API key (console.groq.com) |
Ollama:BaseUrl |
Ollama base URL (default: http://localhost:11434) |
Ollama:Model |
Ollama model name (default: llama3.2) |
CloudinarySettings:CloudName |
Cloudinary cloud name |
CloudinarySettings:ApiKey |
Cloudinary API key |
CloudinarySettings:ApiSecret |
Cloudinary API secret |
Cors:AllowedOrigins |
Array of allowed frontend origins |