A cloud-based application for generating invoices tailored for consultant services. Built with a React 19 frontend and an ASP.NET Core 10 Minimal API backend, deployed via Docker Compose with PostgreSQL.
- Google OAuth 2.0 social login
- Invoice creation with line items and PDF generation
- Client management (CRUD)
- View, filter, and search invoices by date
- Dark mode support
- Dockerized deployment with PostgreSQL database
| Layer | Technology |
|---|---|
| Frontend | React 19, TypeScript 5.7, Vite 6, Tailwind CSS 4, Radix UI / shadcn/ui |
| Backend | ASP.NET Core 10, FastEndpoints 8.0, Entity Framework Core 10 |
| Database | PostgreSQL 16 |
| Auth | Google OAuth 2.0 + JWT (HS256) |
| QuestPDF | |
| Deployment | Docker Compose, multi-stage Dockerfile |
- Docker and Docker Compose
- Node.js 22+ (for local frontend development)
- .NET 10 SDK (for local backend development)
- A Google Cloud Console project with OAuth 2.0 credentials
# Start PostgreSQL (if not using Docker Compose)
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres:16
# Create src/CloudInvoiceApp.Backend/appsettings.local.json with:
# - ConnectionStrings:invoice-db
# - Jwt:SecretKey (32+ chars)
# - Google:ClientId
# Run with hot reload
dotnet watch run --project src/CloudInvoiceApp.Backend/CloudInvoiceApp.Backend.csprojThe backend runs on https://localhost:5001 by default.
cd src/CloudInvoiceApp.Web
# Install dependencies
npm install
# Start dev server (hot reload on localhost:5173)
npm run dev# Apply migrations
dotnet ef database update --project src/CloudInvoiceApp.Backend/
# Create a new migration
dotnet ef migrations add MigrationName --project src/CloudInvoiceApp.Backend/ --context InvoiceDbContextMigrations are also auto-applied on startup.
cd deploy
# Copy and configure environment variables
cp .env.example .env
# Edit .env with your values (see Configuration section)
# Build and start all services
docker-compose up --build
# Stop services
docker-compose downThe application is available at http://localhost:<APP_PORT> (default 8080).
| Variable | Description | Default |
|---|---|---|
APP_IMAGE |
Docker image for the app | - |
APP_PORT |
Host port to expose | 8080 |
POSTGRES_PASSWORD |
PostgreSQL password | - |
POSTGRES_USER |
PostgreSQL user | postgres |
POSTGRES_DB |
PostgreSQL database name | InvoiceDb |
JWT_SECRET_KEY |
JWT signing secret (min 32 chars) | - |
JWT_ACCESS_EXPIRY_MINUTES |
Access token TTL in minutes | 15 |
JWT_REFRESH_EXPIRY_DAYS |
Refresh token TTL in days | 7 |
GOOGLE_CLIENT_ID |
Google OAuth client ID | - |
ALLOWED_ORIGINS |
CORS allowed origins (comma-separated) | empty (allow all) |
Configuration is loaded in this precedence order:
- Environment variables
appsettings.{Environment}.jsonappsettings.jsonappsettings.local.json
cloud-invoice-app/
├── deploy/
│ ├── Dockerfile # Multi-stage build (SDK + Node + runtime)
│ ├── docker-compose.yml # Container orchestration
│ ├── .env.example # Environment variable template
│ └── build-and-push.* # Deployment scripts (sh + ps1)
├── src/
│ ├── CloudInvoiceApp.sln
│ ├── CloudInvoiceApp.Backend/
│ │ ├── Program.cs # App bootstrap, DI, middleware
│ │ ├── Data/ # EF Core context and migrations
│ │ ├── Endpoints/ # FastEndpoints (Auth, Clients, Invoices)
│ │ ├── Models/ # Domain models
│ │ ├── Services/ # Business logic (Token, Audit, Cookie)
│ │ ├── Requests/ # Request DTOs
│ │ └── Responses/ # Response DTOs
│ ├── CloudInvoiceApp.Web/
│ │ ├── src/
│ │ │ ├── pages/ # Page components
│ │ │ ├── components/ # Reusable UI components
│ │ │ ├── contexts/ # AuthContext, ThemeContext
│ │ │ ├── services/ # API client, ConfigService
│ │ │ ├── layouts/ # AppLayout
│ │ │ └── types/ # TypeScript definitions
│ │ ├── package.json
│ │ └── vite.config.ts
│ ├── CloudInvoiceApp.AppHost/ # .NET Aspire orchestration
│ └── CloudInvoiceApp.ServiceDefaults/ # Shared service defaults
├── .github/workflows/ # CI/CD (placeholder)
├── CLAUDE.md # Project instructions for Claude Code
├── LICENSE # MIT License
└── README.md
- Frontend uses
@react-oauth/googlewith a client ID fetched at runtime from/api/config - Google ID token is sent to
POST /api/auth/google-login - Backend validates the token via Google APIs and issues a JWT access token + refresh token (httpOnly cookie)
- Access token is held in memory (React state); refresh token is stored as a Secure, SameSite=Strict httpOnly cookie
- Protected routes use
Authorization: Bearer <token>header
- PDF Generation: QuestPDF renders invoices to PDF on the backend
- Database: EF Core with PostgreSQL; migrations auto-applied on startup
- Static Serving: React production build is served from ASP.NET Core
wwwroot/ - Health Check: Available at
/health
- Fork the repository
- Create a feature branch (
git checkout -b feature-name) - Commit your changes (
git commit -m "Add feature") - Push to the branch (
git push origin feature-name) - Open a pull request
This project is licensed under the MIT License. See the LICENSE file for details.