A full-featured, self-hosted portfolio content management system with an AI-powered resume generator.
Built with Laravel 12, Vue 3, Inertia.js, and Tailwind CSS 4.
Features β’ Tech Stack β’ Architecture β’ Getting Started β’ Docker β’ API β’ Structure β’ Contributing
- Dynamic Landing Page β Hero section with live availability status badge, animated tech stack showcase, featured projects carousel, and interactive stats counter
- About Page β Profile sections (bio, specialties, philosophy), skills with proficiency bars and category grouping, certification badges with expiration tracking
- Experience Timeline β Chronological work history with achievement highlights, technology tags, and current-role indicators
- Project Gallery β Filterable project cards with descriptions, tech stacks, tags, live/GitHub links, and featured project pinning
- Contact Page β Social links (LinkedIn, GitHub, Twitter, etc.), email, phone, and location info managed from the admin panel
- SEO Optimized β Dynamic meta tags (Open Graph, description, title) per page via Inertia
<Head>, auto-generatedsitemap.xml - Page View Tracking β Anonymous analytics tracking visitor counts per route
- Job Description Analysis β Paste a job posting and the system extracts keywords, required skills, and role context to tailor your resume
- Smart Content Selection β
ContentSelectorServicescores and ranks your skills, experiences, projects, education, and certifications for relevance to each specific job - Multiple Templates β Choose from professional resume templates (e.g., Professional, Modern Minimal) rendered as PDF via DomPDF
- Shareable Links β Each generated resume gets a unique token-based URL with optional expiration
- Download as PDF β One-click PDF download with path-traversal protection
- Full CRUD Management β Create, read, update, and delete: skills, experiences, projects, education, certifications, profile sections, contact info, and resume templates
- Site Settings β Configure site name, tagline, social links, profile image, and meta defaults from a centralized settings page
- Theme Customization β Live-preview color theme editor (primary, secondary, accent, background colors) with one-click reset to defaults
- Database Manager β In-browser database backup/restore, migration runner, seeder execution, cache clearing, and table inspection
- File Uploads β Drag-and-drop image uploads for projects, profile photos, and certifications (JPEG, PNG, WebP, SVG β max 2MB)
- Flash Notifications β Success/error toast messages on all admin actions
- Sanctum Authentication β Token-based API auth with 24-hour expiration, session-based web auth
- Protected Registration β Admin-only user creation (no public registration)
- Rate Limiting β Tiered throttling: 5/min for auth, 10/min for resume generation, 30/min for public resume viewing, 60/min for portfolio API
- Input Validation β Comprehensive request validation on all endpoints with
sometimesmodifier for updates - Password Reset Flow β Email-based password recovery with signed URLs
- Email Verification β Required email verification for new admin accounts
- Path Traversal Protection β Sanitized PDF download paths
- CSRF Protection β Automatic on all web routes via Laravel middleware
| Layer | Technology | Version |
|---|---|---|
| Backend Framework | Laravel | 12.x |
| Frontend Framework | Vue.js | 3.5 |
| SPA Bridge | Inertia.js | 2.x |
| CSS Framework | Tailwind CSS | 4.x |
| Build Tool | Vite | 7.x |
| Database | MySQL | 8.0 |
| Auth | Laravel Sanctum | 4.3 |
| PDF Generation | barryvdh/laravel-dompdf | 3.1 |
| URL Generation | Ziggy (tightenco/ziggy) | 2.6 |
| UI Components | Headless UI + Heroicons | 1.7 / 2.2 |
| Progress Bar | NProgress | 0.2 |
| Testing | PHPUnit | 11.5 |
| Code Style | Laravel Pint | 1.24 |
| Containerization | Docker + Docker Compose | 3.8 spec |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Browser β
β βββββββββββββββ ββββββββββββββββ ββββββββββββββββββββββ β
β β Portfolio β β Admin Panel β β Resume Builder β β
β β (Vue + TW) β β (Vue + TW) β β (Vue + TW) β β
β ββββββββ¬βββββββ ββββββββ¬ββββββββ ββββββββββ¬ββββββββββββ β
β β β β β
β ββββββββββββββββββΌβββββββββββββββββββββ β
β β Inertia.js (no API needed) β
ββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββ
β Laravel 12 Backend β
β βββββββββββββββββ ββββββ΄βββββββββ ββββββββββββββββββββββ β
β β Web Routes β β API Routes β β Services β β
β β (Inertia) β β (Sanctum) β β β’ ContentSelector β β
β β β’ Portfolio β β β’ Auth β β β’ PdfGenerator β β
β β β’ Admin CRUD β β β’ Admin CRUDβ β β β
β β β’ Auth β β β’ Portfolio β ββββββββββββββββββββββ β
β β β’ Resume β β β’ Resume β β
β βββββββββββββββββ βββββββββββββββ β
β β β
β βββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββ β
β β Eloquent ORM + Migrations β β
β β Models: User, Skill, Experience, Project, Education, β β
β β Certification, ProfileSection, ContactInfo, β β
β β ResumeTemplate, ResumeGeneration, SiteSetting, β β
β β PageView β β
β βββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββ
β
ββββββββ΄βββββββ
β MySQL 8.0 β
βββββββββββββββ
| Context | Layout | Auth | Description |
|---|---|---|---|
| Portfolio | PortfolioLayout.vue |
Public | Dark theme (#0a0a0a), green accent (#00ff88), animated nav, footer |
| Admin | AdminLayout.vue |
Required | Collapsible sidebar, DB-driven theme colors, breadcrumbs, flash messages |
| Resume | ResumeLayout.vue |
Public | Light theme, step wizard indicator, minimal UI |
- PHP >= 8.2 with extensions:
pdo_mysql,mbstring,exif,pcntl,bcmath,gd,zip - Composer >= 2.x
- Node.js >= 18.x with npm
- MySQL >= 8.0 (or MariaDB 10.6+)
# 1. Clone the repository
git clone https://github.com/Jay-kod/portfolio-cms.git
cd portfolio-cms/backend
# 2. Install all dependencies
composer install
npm install
# 3. Configure environment
cp .env.example .env
php artisan key:generateEdit backend/.env with your MySQL credentials:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=portfolio_cms
DB_USERNAME=root
DB_PASSWORD=your_passwordThen run migrations and seed demo data:
php artisan migrate
php artisan db:seedOption A: Concurrent dev server (recommended)
composer devThis starts all four services simultaneously:
| Service | URL | Description |
|---|---|---|
| Laravel Server | http://localhost:8000 |
PHP backend |
| Vite Dev Server | http://localhost:5173 |
Hot-reload frontend |
| Queue Worker | β | Background job processing |
| Pail Log Viewer | β | Real-time log tailing |
Option B: Manual startup
# Terminal 1: Backend
php artisan serve
# Terminal 2: Frontend (hot reload)
npm run devnpm run build
php artisan config:cache
php artisan route:cache
php artisan view:cacheThe project includes a full Docker Compose stack:
# Build and start all containers
docker compose up -d --build
# Run migrations inside the container
docker compose exec app php artisan migrate --seed| Container | Image | Port | Role |
|---|---|---|---|
portfolio-app |
PHP 8.2-FPM | 9000 (internal) | Laravel backend |
portfolio-web |
Nginx | 80 | Reverse proxy + static files |
portfolio-db |
MySQL 8.0 | 3306 | Database |
DB_CONNECTION: mysql
DB_HOST: db
DB_PORT: 3306
DB_DATABASE: portfolio_cms
DB_USERNAME: root
DB_PASSWORD: rootpassword
APP_URL: http://localhostAll API routes are prefixed with /api and return JSON. Protected routes require a Bearer token via Laravel Sanctum.
| Method | Endpoint | Auth | Rate Limit | Description |
|---|---|---|---|---|
POST |
/api/auth/login |
β | 5/min | Login, returns Sanctum token |
POST |
/api/auth/logout |
Bearer | β | Revoke current token |
GET |
/api/auth/user |
Bearer | β | Get authenticated user |
POST |
/api/admin/auth/register |
Bearer + Admin | β | Create new admin user |
| Resource | Endpoints | Description |
|---|---|---|
| Skills | GET/POST/PUT/DELETE /api/admin/skills |
Programming skills & proficiency |
| Experiences | GET/POST/PUT/DELETE /api/admin/experiences |
Work history entries |
| Projects | GET/POST/PUT/DELETE /api/admin/projects |
Portfolio projects |
| Education | GET/POST/PUT/DELETE /api/admin/education |
Degrees & coursework |
| Certifications | GET/POST/PUT/DELETE /api/admin/certifications |
Professional certs |
| Profile Sections | GET/POST/PUT/DELETE /api/admin/profile-sections |
Bio, about me, etc. |
| Contact Info | GET/POST/PUT/DELETE /api/admin/contact-info |
Social links, email, phone |
| Templates | GET/POST/PUT/DELETE /api/admin/templates |
Resume templates |
| Method | Endpoint | Auth | Rate Limit | Description |
|---|---|---|---|---|
POST |
/api/resumes/analyze |
Bearer | 10/min | Analyze job description & score content |
POST |
/api/resumes/generate |
Bearer | 10/min | Generate tailored PDF resume |
GET |
/api/resumes/{token} |
β | 30/min | View generated resume by token |
GET |
/api/resumes/{token}/download |
β | 30/min | Download resume PDF |
| Method | Endpoint | Rate Limit | Description |
|---|---|---|---|
GET |
/api/portfolio/full |
60/min | Complete portfolio data |
GET |
/api/portfolio/profile |
60/min | Profile sections only |
GET |
/api/portfolio/skills |
60/min | Skills list |
GET |
/api/portfolio/experiences |
60/min | Work experiences |
GET |
/api/portfolio/projects |
60/min | Projects |
GET |
/api/portfolio/education |
60/min | Education entries |
GET |
/api/portfolio/certifications |
60/min | Certifications |
GET |
/api/portfolio/contact |
60/min | Contact information |
portfolio-cms/
βββ docker-compose.yml # Docker orchestration (app + web + db)
βββ nginx/
β βββ default.conf # Nginx reverse proxy config
βββ backend/
β βββ app/
β β βββ Http/
β β β βββ Controllers/
β β β β βββ Admin/ # Web admin controllers (Inertia)
β β β β β βββ AuthController.php
β β β β β βββ DashboardController.php
β β β β β βββ SkillController.php
β β β β β βββ ExperienceController.php
β β β β β βββ ProjectController.php
β β β β β βββ EducationController.php
β β β β β βββ CertificationController.php
β β β β β βββ ProfileSectionController.php
β β β β β βββ ContactInfoController.php
β β β β β βββ ResumeTemplateController.php
β β β β β βββ SiteSettingsController.php
β β β β β βββ ThemeController.php
β β β β β βββ DatabaseController.php
β β β β β βββ FileUploadController.php
β β β β β βββ PasswordResetController.php
β β β β β βββ EmailVerificationController.php
β β β β βββ API/ # JSON API controllers (Sanctum)
β β β β β βββ Admin/ # Protected admin API
β β β β β βββ AuthController.php
β β β β β βββ PortfolioController.php
β β β β β βββ ResumeController.php
β β β β βββ PortfolioPageController.php # Public Inertia pages
β β β β βββ ResumePageController.php # Resume builder pages
β β β β βββ SitemapController.php # sitemap.xml
β β β βββ Middleware/
β β β βββ HandleInertiaRequests.php # Shared Inertia data
β β β βββ EnsureUserHasRole.php # Role-based access
β β β βββ TrackPageView.php # Analytics tracking
β β βββ Models/
β β β βββ User.php # Admin user (MustVerifyEmail)
β β β βββ Skill.php # Skills with proficiency & categories
β β β βββ Experience.php # Work history
β β β βββ Project.php # Portfolio projects
β β β βββ Education.php # Degrees & education
β β β βββ Certification.php # Professional certifications
β β β βββ ProfileSection.php # Bio, about, philosophy sections
β β β βββ ContactInfo.php # Social links & contact details
β β β βββ ResumeTemplate.php # PDF template definitions
β β β βββ ResumeGeneration.php # Generated resume records
β β β βββ SiteSetting.php # Key-value site configuration
β β β βββ PageView.php # Visitor tracking
β β βββ Services/
β β βββ ContentSelectorService.php # AI content scoring for resumes
β β βββ PdfGeneratorService.php # DomPDF resume generation
β βββ database/
β β βββ migrations/ # 18 migrations for all tables
β β βββ seeders/ # Demo data seeders
β βββ resources/
β β βββ css/app.css # Tailwind CSS entry point
β β βββ js/
β β β βββ app.js # Inertia + Vue app bootstrap
β β β βββ Layouts/
β β β β βββ PortfolioLayout.vue # Public site layout
β β β β βββ AdminLayout.vue # Admin panel layout
β β β β βββ ResumeLayout.vue # Resume builder layout
β β β βββ Pages/
β β β βββ Portfolio/ # 6 public pages (Index, About, Experience, Projects, Contact, + legacy)
β β β βββ Admin/ # 12 admin pages (Dashboard, CRUD, Settings, Theme, Database)
β β β βββ Auth/ # 4 auth pages (Login, ForgotPassword, ResetPassword, VerifyEmail)
β β β βββ Resume/ # 2 resume pages (Create wizard, View)
β β β βββ Errors/ # Error pages (404, 403, 500, 503)
β β βββ views/
β β βββ app.blade.php # Root Blade template (Inertia entry)
β β βββ resumes/ # PDF Blade templates
β βββ routes/
β β βββ web.php # Inertia routes (portfolio, admin, auth)
β β βββ api.php # JSON API routes (Sanctum-protected)
β β βββ console.php # Artisan command scheduling
β βββ config/ # Laravel config (sanctum, database, etc.)
β βββ storage/ # File uploads, logs, cache, generated PDFs
β βββ composer.json # PHP dependencies
β βββ package.json # Node.js dependencies
βββ TASKS.md # Development task tracker (40 tasks, 6 phases)
| Model | Table | Key Fields |
|---|---|---|
| User | users |
name, email, password, role, email_verified_at |
| Skill | skills |
name, category, proficiency_level, is_active, display_order |
| Experience | experiences |
company, position, start_date, end_date, is_current, description, achievements[], technologies_used[] |
| Project | projects |
title, description, technologies_used[], tags[], live_url, github_url, is_featured, display_order |
| Education | education |
institution, degree, field_of_study, start_date, end_date, is_current, description, achievements[] |
| Certification | certifications |
name, issuing_organization, issue_date, expiry_date, credential_id, credential_url |
| ProfileSection | profile_sections |
title, key, content, type, metadata, display_order |
| ContactInfo | contact_info |
type, value, label, icon, display_order, is_primary |
| ResumeTemplate | resume_templates |
name, slug, description, template_path, is_active |
| ResumeGeneration | resume_generations |
token, template_id, job_title, company_name, job_description, selected_skills/experiences/projects/education/certifications, generated_pdf_path, expires_at |
| SiteSetting | site_settings |
key, value (longtext) |
| PageView | page_views |
path, ip_address, user_agent, referrer |
| Variable | Default | Description |
|---|---|---|
APP_NAME |
Laravel |
Site name displayed in nav and title |
APP_ENV |
local |
Environment (local, production) |
APP_DEBUG |
true |
Show debug errors (disable in production!) |
APP_URL |
http://localhost |
Base URL for the application |
DB_CONNECTION |
sqlite |
Database driver (mysql for production) |
DB_HOST |
127.0.0.1 |
Database host |
DB_DATABASE |
portfolio_cms |
Database name |
SANCTUM_TOKEN_EXPIRATION |
1440 |
API token lifetime in minutes (24h) |
MAIL_MAILER |
log |
Mail driver for password resets & verification |
LOG_CHANNEL |
stack |
Logging channel (daily recommended for prod) |
MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=your@email.com
MAIL_PASSWORD=your_app_password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@jaykod.io
MAIL_FROM_NAME="jaykod.io"cd backend
# Run all tests
php artisan test
# Run with coverage
php artisan test --coverage
# Run specific test suite
php artisan test --testsuite=Feature
php artisan test --testsuite=Unit
# Code style check
./vendor/bin/pint --test
# Auto-fix code style
./vendor/bin/pint# Development
composer dev # Start all dev services (server + vite + queue + logs)
php artisan serve # Start PHP dev server only
npm run dev # Start Vite HMR only
npm run build # Build frontend for production
# Database
php artisan migrate # Run pending migrations
php artisan migrate:fresh --seed # Reset DB and seed demo data
php artisan db:seed # Seed demo data
# Maintenance
php artisan config:cache # Cache configuration
php artisan route:cache # Cache routes
php artisan view:cache # Cache Blade views
php artisan cache:clear # Clear application cache
php artisan storage:link # Create public storage symlink
# Code Quality
./vendor/bin/pint # Fix code style (PSR-12)
php artisan test # Run test suiteSee TASKS.md for the complete 40-task development roadmap across 6 phases:
- β Phase 1 β Critical Security Fixes (T-01 β T-05)
- β Phase 2 β High Priority Fixes (T-06 β T-14)
- β Phase 3 β Validation & Data Integrity (T-15 β T-22)
- π² Phase 4 β Code Quality & Architecture (T-23 β T-30)
- π² Phase 5 β Performance & DevOps (T-31 β T-36)
- π² Phase 6 β Feature Enhancements (T-37 β T-40)
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes and add tests
- Run the linter:
./vendor/bin/pint - Run tests:
php artisan test - Commit:
git commit -m "feat: add my feature" - Push:
git push origin feature/my-feature - Open a Pull Request
| Prefix | Usage |
|---|---|
feat: |
New feature |
fix: |
Bug fix |
docs: |
Documentation only |
style: |
Code formatting (no logic change) |
refactor: |
Code restructuring |
test: |
Adding/updating tests |
chore: |
Build, config, dependency changes |
This project is open-sourced under the MIT License.
Built with β€οΈ by Jay-kod