A modern, full-stack Portfolio Management System built with ASP.NET Core 10, Next.js 16, and Clean Architecture.
Getting Started Β· Features Β· Architecture Β· Screenshots Β· Contributing Β· License
PortfolioCMS is an open-source, full-stack content management system designed to let developers and creatives build, manage, and showcase their portfolio through a beautiful admin dashboard and a public-facing portfolio website.
The backend is built on ASP.NET Core 10 with a strict Clean Architecture separation of concerns, providing a secure REST API. The frontend is a Next.js 16 application with React 19, Tailwind CSS 4, and a premium dark-themed admin dashboard β giving you a complete, production-ready portfolio platform out of the box.
- Admin Dashboard β Premium dark-themed UI with real-time stats, animated charts, and glassmorphism design
- Blog Management β Full markdown editor with toolbar, live preview, inline image uploads, and keyboard shortcuts (Ctrl+S, Ctrl+B, Ctrl+I)
- Blog Search & Filtering β Search posts by title/summary, filter by category, and filter by publish status
- Blog Categories β Create, edit, and delete categories with a tabbed interface (Posts | Categories)
- Profile Editor β Two-column layout with a live profile preview card showing avatar, name, headline, status, visibility, and a profile completeness tracker
- Portfolio Sections β Manage skills, education, work experience, projects, certifications, social links, reviews, and extra-curricular activities
- Contact Messages β Inbox to view, read, and manage messages from visitors
- Authentication β Login, register, email confirmation, forgot/reset password flows
- Public Portfolio β Public-facing portfolio page and blog with responsive layout, navigation, and footer
- Cloud Image Uploads β Profile photos, resume PDFs, and blog cover images uploaded via Cloudinary
- Resume Preview β Embedded PDF viewer using Google Docs Viewer for reliable rendering
- Settings β Change password, SMTP email configuration
- π JWT Authentication with secure refresh token rotation
- π AES Encryption for sensitive data fields
- π€ Role-based authorization with a seeded admin user
- π Account Management β change password, update name, and delete account
- π Portfolio & Project management via a structured REST API
- πΌοΈ Cloud file uploads β profile images and resumes uploaded directly to Cloudinary CDN
- π Portfolio privacy toggle β portfolios are private by default; owners explicitly publish when ready
- π¨ Contact message email notifications β portfolio owners are notified by email when someone submits a contact form
- π§± Clean Architecture β Domain, Application, Infrastructure, and API layers strictly separated
- ποΈ Entity Framework Core with SQL Server and full migration support
- π Structured logging via Serilog
β οΈ Global exception handling middleware for consistent API error responses
| Layer | Technology |
|---|---|
| Frontend | Next.js 16, React 19, TypeScript 5, Tailwind CSS 4 |
| State Management | TanStack React Query, Zustand |
| UI Components | Radix UI, Lucide React Icons, Shadcn |
| Forms & Validation | React Hook Form, Zod |
| Backend | ASP.NET Core 10, C# |
| ORM | Entity Framework Core 10 |
| Database | SQL Server |
| Authentication | JWT + Refresh Tokens |
| File Storage | Cloudinary CDN |
| Logging | Serilog |
Dependencies flow strictly inward β outer layers depend on inner layers, never the reverse.
PortfolioCMS.Server/
βββ PortfolioCMS.Server.Api/ # HTTP layer: Controllers, Middleware, DI setup
βββ PortfolioCMS.Server.Application/ # Use cases: DTOs, Interfaces, Mappings
βββ PortfolioCMS.Server.Domain/ # Core: Entities, Domain Exceptions, Shared Models
βββ PortfolioCMS.Server.Infrastructure/ # External: DbContext, Migrations, Auth, Email, Cloudinary
[ Api ] β [ Application ] β [ Domain ]
β
[ Infrastructure ]
Domainhas zero external dependencies.Applicationdepends only onDomain.InfrastructureimplementsApplicationinterfaces.Apiwires everything together.
portfoliocms.client/
βββ app/
β βββ (admin)/dashboard/ # Admin dashboard (protected)
β β βββ page.tsx # Dashboard home with stats & charts
β β βββ blog/ # Blog posts & categories management
β β β βββ new/ # New blog post editor (markdown)
β β β βββ [id]/edit/ # Edit existing blog post
β β βββ profile/ # Profile editor with live preview
β β βββ skills/ # Skills management
β β βββ education/ # Education management
β β βββ work-experience/ # Work experience management
β β βββ projects/ # Projects management
β β βββ messages/ # Contact messages inbox
β β βββ settings/ # Password & SMTP settings
β βββ (auth)/ # Authentication pages
β β βββ login/ # Login
β β βββ register/ # Registration
β β βββ forgot-password/ # Forgot password
β β βββ reset-password/ # Reset password
β β βββ confirm-email/ # Email confirmation
β βββ (public)/ # Public-facing portfolio
β βββ page.tsx # Public portfolio page
βββ components/ # Shared UI components
βββ lib/ # API utilities, auth helpers
| Tool | Version |
|---|---|
| .NET SDK | 10.0 or later |
| Node.js | 18.0 or later |
| SQL Server | LocalDB or Docker |
| EF Core CLI | dotnet tool install --global dotnet-ef |
| Cloudinary Account | Free tier is sufficient |
git clone https://github.com/dabananda/portfoliocms.git
cd portfoliocmsNavigate to the PortfolioCMS.Server.Api directory and set up the required secrets:
cd PortfolioCMS.Server/PortfolioCMS.Server.Api
dotnet user-secrets init
dotnet user-secrets set "JwtSettings:Secret" "YourSuperSecretKeyThatIsAtLeast32CharactersLong!"
dotnet user-secrets set "EncryptionSettings:Key" "12345678901234567890123456789012"
dotnet user-secrets set "AdminUser:Email" "admin@example.com"
dotnet user-secrets set "AdminUser:Password" "Admin@123!"
# Cloudinary (required for image and resume uploads)
dotnet user-secrets set "Cloudinary:CloudName" "your_cloud_name"
dotnet user-secrets set "Cloudinary:ApiKey" "your_api_key"
dotnet user-secrets set "Cloudinary:ApiSecret" "your_api_secret"
β οΈ EncryptionSettings:Keymust be exactly 32 characters. Your Cloudinary credentials can be found in the Cloudinary Console. Never commit secrets to source control.
dotnet ef database update --project PortfolioCMS.Server.Infrastructure --startup-project PortfolioCMS.Server.Apidotnet run --project PortfolioCMS.Server.ApiThe API will be available at https://localhost:{port}.
Swagger UI is accessible at https://localhost:{port}/swagger in development mode.
In a new terminal, navigate to the frontend directory:
cd portfoliocms.client
npm installCreate a .env.local file:
NEXT_PUBLIC_API_URL=https://localhost:{port}/api/v1Replace
{port}with your backend's actual port number.
npm run devThe frontend will be available at http://localhost:3000.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/api/v1/auth/register |
β | Register a new user |
POST |
/api/v1/auth/login |
β | Login and receive tokens |
POST |
/api/v1/auth/refresh |
β | Refresh access token |
POST |
/api/v1/auth/forgot-password |
β | Request password reset email |
POST |
/api/v1/auth/reset-password |
β | Reset password using email token |
GET |
/api/v1/auth/confirm-email |
β | Confirm email address |
GET |
/api/v1/auth/check-username |
β | Check username availability |
POST |
/api/v1/account/change-password |
β | Change authenticated user's password |
PUT |
/api/v1/account/update-name |
β | Update first and last name |
DELETE |
/api/v1/account |
β | Permanently delete account and all data |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/api/v1/upload/image |
β | Upload profile image to Cloudinary (max 5 MB) |
POST |
/api/v1/upload/resume |
β | Upload resume to Cloudinary (max 10 MB) |
| Method | Endpoint | Description |
|---|---|---|
GET/POST/PUT |
/api/v1/userprofile |
Manage your profile β includes isPublic privacy toggle |
GET/POST/PUT/DELETE |
/api/v1/skill |
Skills |
GET/POST/PUT/DELETE |
/api/v1/education |
Education history |
GET/POST/PUT/DELETE |
/api/v1/workexperience |
Work experience |
GET/POST/PUT/DELETE |
/api/v1/project |
Projects |
GET/POST/PUT/DELETE |
/api/v1/certification |
Certifications |
GET/POST/PUT/DELETE |
/api/v1/sociallink |
Social links |
GET/POST/PUT/DELETE |
/api/v1/review |
Reviews / testimonials |
GET/POST/PUT/DELETE |
/api/v1/extracurricularactivity |
Extra-curricular activities |
GET/POST/PUT/DELETE |
/api/v1/problemsolving |
Competitive programming stats |
GET/POST/PUT/DELETE |
/api/v1/blogpostcategory |
Blog categories |
GET/POST/PUT/DELETE/PATCH |
/api/v1/blogpost |
Blog posts (with publish/unpublish) |
GET/PATCH/DELETE |
/api/v1/contactmessage |
Inbox β messages sent by visitors |
| Method | Endpoint | Description |
|---|---|---|
GET/PUT |
/api/v1/adminsettings/system |
SMTP email configuration |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/v1/portfolio |
Full public portfolio (default user, only if isPublic = true) |
GET |
/api/v1/portfolio/{username} |
Full public portfolio by username |
GET |
/api/v1/portfolio/{username}/blog |
Published blog posts |
GET |
/api/v1/portfolio/{username}/blog/{slug} |
Single published blog post |
POST |
/api/v1/portfolio/{username}/contact-message |
Send a contact message (triggers owner email) |
| Page | Route | Description |
|---|---|---|
| Dashboard | /dashboard |
Overview with stats cards, blog/message counts, and quick actions |
| Blog | /dashboard/blog |
Posts table with search, category filter, status filter. Categories tab for CRUD |
| New Post | /dashboard/blog/new |
Markdown editor with toolbar, live preview, cover images, and keyboard shortcuts |
| Edit Post | /dashboard/blog/[id]/edit |
Edit existing posts with all the same editor features |
| Profile | /dashboard/profile |
Two-column editor with live preview card and profile completeness tracker |
| Skills | /dashboard/skills |
Manage skills with name and proficiency |
| Education | /dashboard/education |
Manage education history |
| Work Experience | /dashboard/work-experience |
Manage work experience entries |
| Projects | /dashboard/projects |
Manage portfolio projects |
| Messages | /dashboard/messages |
Contact messages inbox with read/unread status |
| Settings | /dashboard/settings |
Change password and configure SMTP email settings |
Contributions are welcome and appreciated! Please read the Contributing Guide to get started, and review our Code of Conduct before participating.
To report a bug, use the Bug Report template. To suggest a feature, use the Feature Request template.
If you discover a security vulnerability, please do not open a public issue. Review our Security Policy and report it privately to dabananda.dev@gmail.com.
This project is licensed under the MIT License.