🌐 Full‑Stack Professional Portfolio
This repository contains Wesley Correia’s (wmakeouthill) professional portfolio, composed of:
- Backend in Java 17 + Spring Boot 3.2.3 that:
- exposes REST APIs for AI chat, contact, and projects;
- serves the frontend build as a SPA;
- fetches portfolio markdowns dynamically via the GitHub API (
certificados-wesleyrepo).
- Frontend in Angular 20 + TypeScript that:
- presents the portfolio in a modern, responsive, accessible UI;
- integrates with the backend and GitHub API;
- has an AI chat trained on the portfolio’s own content.
🧱 General Architecture
- Backend
- Java 17
- Spring Boot 3.2.3
- Lombok
- Liquibase 4.25.0 (already configured as a dependency)
- Integrations:
- OpenAI (AI chat + model fallback)
- GitHub API (projects, languages, and portfolio content)
- SMTP (contact email)
- Optimizations:
- TokenBudgetService to manage AI token budget
- In-memory cache (5min TTL) for GitHub content
- Frontend
- Angular 20.3.0 (standalone components, Signals, RxJS 7.8.0)
- TypeScript 5.9.2
- Modern, responsive CSS
- Infra / Build
- Maven (with
frontend-maven-pluginconfigured) - Node 20.19.0 (auto-downloaded by Maven on backend build)
- Deploy targets:
- GitHub Pages (via
docs/) - Oracle Cloud Always Free (VPS — Docker backend serving the SPA, replacing the old Google Cloud Run)
- GitHub Pages (via
- Maven (with
Architecture Diagram (Mermaid)
%%{title: "General Architecture of the Professional Portfolio"}%%
flowchart LR
subgraph Browser
A[SPA Angular 20]
end
subgraph Backend[Backend Spring Boot 3.2.3]
B1[ChatController]
B2[ContactController]
B3[ProjectsController]
B4[SpaController]
UC_CHAT[ChatUseCase]
UC_CONTACT[EnviarEmailContatoUseCase]
UC_PROJECTS[ListarProjetosGithubUseCase]
B1 --> UC_CHAT
B2 --> UC_CONTACT
B3 --> UC_PROJECTS
end
subgraph Domain[Domain]
D1[PortfolioPromptService]
D2[ContextSearchService]
end
subgraph Infra[Infrastructure]
AI[OpenAIAdapter]
GH[GithubApiAdapter]
GH_CONTENT[GithubPortfolioContentAdapter]
CACHE[GithubContentCache]
MAIL[GmailAdapter]
BUDGET[TokenBudgetService]
end
subgraph Cloud[Oracle Cloud / External]
SM[(Secrets / Env)]
VPS[(Oracle VPS Always Free)]
OA[(OpenAI API)]
GITHUB[(GitHub API)]
end
A <-- HTTP --> B1
A <-- HTTP --> B2
A <-- HTTP --> B3
A <-- HTTP --> B4
UC_CHAT --> D1
UC_CHAT --> D2
UC_CHAT --> BUDGET
D1 --> GH_CONTENT
D2 --> GH_CONTENT
BUDGET --> AI
UC_CONTACT --> MAIL
UC_PROJECTS --> GH
AI --> OA
GH --> GITHUB
GH_CONTENT --> CACHE
GH_CONTENT --> GITHUB
MAIL --> SM
Backend --> VPS
VPS --> Browser
📁 Folder Structure (Overview)
.
├── backend/ # Spring Boot API + SPA server
│ ├── src/main/java/com/wmakeouthill/portfolio
│ │ ├── application/ # DTOs, ports, use cases (application layer)
│ │ ├── domain/ # Domain entities, models, services
│ │ └── infrastructure/ # Web, OpenAI, GitHub, Email adapters
│ ├── src/main/resources/
│ │ ├── application.properties # Main config
│ │ └── static/ # Angular build (copied on build)
│ │ # Note: Markdowns are fetched dynamically from GitHub (repo: certificados-wesley)
│ └── pom.xml # Build + frontend integration
│
├── frontend/ # Angular 20 SPA
│ ├── src/app/
│ │ ├── components/ # Portfolio sections (standalone)
│ │ │ ├── header/
│ │ │ ├── hero/
│ │ │ ├── about/
│ │ │ ├── skills/
│ │ │ ├── experience/
│ │ │ ├── education/
│ │ │ ├── projects/
│ │ │ ├── certifications/
│ │ │ ├── contact/
│ │ │ ├── pdf-viewer/
│ │ │ ├── cv-modal/
│ │ │ ├── readme-modal/
│ │ │ ├── chat-widget/ # AI chat + composables
│ │ │ └── footer/
│ │ ├── services/ # GitHub, Email, Markdown, AI chat
│ │ ├── models/ # TypeScript interfaces
│ │ └── utils/ # Utils (API URL, session-storage, etc.)
│ ├── public/ # Assets, icons, resumes, etc.
│ └── package.json # Scripts and dependencies (Angular 20)
│
├── docs/ # Static artifacts for GitHub Pages
├── deploy.sh / Dockerfile.* # Build and deploy scripts
└── README.md # (this file)
🔌 Backend – API, AI & Integrations
The backend follows a clean architecture (application / domain / infrastructure) and exposes these main APIs:
-
AI Chat
POST /api/chat- Request:
ChatRequest(user message + metadata) - Response:
ChatResponse(AI reply) - Uses
X-Session-IDto keep chat context per session.
- Request:
POST /api/chat/clear- Clears chat history for the given
X-Session-ID.
- Clears chat history for the given
-
Contact
POST /api/contact- Request:
ContactRequest - Sends email using
EnviarEmailContatoUseCase+ email adapter (Gmail/SMTP).
- Request:
-
Projects
GET /api/projects- Returns list of GitHub repositories (
GithubRepositoryDto) via GitHub API.
- Returns list of GitHub repositories (
GET /api/projects/{projectName}/markdown- Fetches markdown dynamically from GitHub repo
certificados-wesley. - Path:
portfolio-content/projects/{projectName}.mdorportfolio-content/trabalhos/{projectName}.md. - Example:
lol-matchmaking-fazenda→certificados-wesley/portfolio-content/projects/lol-matchmaking-fazenda.md.
- Fetches markdown dynamically from GitHub repo
-
AI Chat (OpenAI + fallback + budget)
- Implemented in
OpenAIAdapter(AIChatPort). - API key read from:
- Spring property
openai.api.key, or - env var
OPENAI_API_KEY.
- Spring property
- Supports model list with automatic fallback:
openai.model– primary model (default:gpt-5-mini);openai.models.fallback– comma list (gpt-4o-mini,gpt-3.5-turbo);openai.max-tokens– output token limit (default:4000).
- The adapter:
- builds a list
[primary + fallbacks]; - tries each model in order;
- treats rate limit and transient errors (429, 502, 503, 504) as recoverable;
- logs estimated token usage via
TokenCounterand structured logs.
- builds a list
- TokenBudgetService (budget optimization):
- monitors estimated tokens before sending to the AI;
- trims chat history (keeps the most recent messages);
- trims documentation contexts when needed;
- truncates system prompt only as a last resort;
- ensures requests stay within model limits.
- Implemented in
-
Serve the SPA (Angular)
SpaControllerintercepts non-API routes:- Static assets (JS/CSS/images) in
static/ - Fallback to
static/index.htmlfor client-side routes (/,/projects, etc.).
- Static assets (JS/CSS/images) in
Portfolio Content (Markdown via GitHub API)
The backend no longer uses static files in portfolio-content/. All content is fetched dynamically from the GitHub repo certificados-wesley:
- GithubPortfolioMarkdownAdapter (
@Primary) replaces the deprecatedClasspathPortfolioContentAdapter. - GithubPortfolioContentAdapter pulls markdown via GitHub API:
- General markdowns:
portfolio-content/*.md(root) - Projects:
portfolio-content/projects/*.md - Work/Experience:
portfolio-content/trabalhos/*.md
- General markdowns:
- GithubContentCache: in-memory cache with 5-minute TTL to reduce API calls.
- Benefits:
- Update content without rebuilding the backend
- Versioning via Git
- Smart cache for performance
- Separation of repos (code vs. content)
💻 Frontend – Angular 20 SPA
The Angular app is a modern, responsive SPA focused on portfolio reading experience, with:
- Main sections:
hero,about,skills,experience,education,projects,certifications,contact,footer.
- Advanced features:
- AI Chat Widget (
chat-widget+ composablesuse-...). - Resume PDF viewer (
pdf-viewer+cv-modal). - Project README/markdown viewer (
readme-modal+markdown.service). - GitHub API integration (
github.service) to list repositories.
- AI Chat Widget (
The frontend is bundled into dist/portfolio/browser and then:
- copied to
backend/src/main/resources/staticduring Maven build; and - copied to
backend/target/classes/staticto run directly from the JAR.
🧩 Stacks & Technologies
This project uses only a subset of the full stack described in backend/src/main/resources/portfolio-content/STACKS.md. At a high level:
-
Backend
- Language: Java 17
- Framework: Spring Boot 3.2.3 (Spring Web, Validation, Mail)
- Data infra: Liquibase 4.25.0 for schema versioning
- Good practices: Lombok, SLF4J/Logback, layered architecture (application, domain, infrastructure)
- Integrations:
- OpenAI API (chat with model fallback)
- Gmail SMTP (contact emails)
- GitHub API (projects and languages)
-
Frontend
- Framework: Angular 20.3.0 (standalone components, DI with
inject, RxJS 7.8.0) - Language: TypeScript 5.9.2
- Libraries:
pdfjs-dist,marked,mermaid,prismjs,lottie-web - Practices: Responsive SPA, decoupled components, services for HTTP/integrations, utils for API config.
- Framework: Angular 20.3.0 (standalone components, DI with
-
DevOps / Deploy
- Build: Maven (integration with
frontend-maven-plugin) - Containerization: Docker
- Deploy: Oracle Cloud Always Free (VPS — Docker backend serving the SPA)
- Secrets: environment variables on the server (or Google Secret Manager, depending on setup).
- Build: Maven (integration with
For a much more detailed description of technologies, proficiency levels, and project context, see STACKS.md.
🛠️ How to Run the Project Locally
1. Prerequisites
- Java 17
- Maven 3.8+
- (Optional) Node 20+ / npm if you want to run the frontend alone
2. Run everything via backend (automatic Angular build)
In the backend/ directory:
cd backend
mvn clean package
# Run the application
mvn spring-boot:run
Maven will:
- install Node and npm (via
frontend-maven-plugin); - run
npm installinsidefrontend/; - run
npm run build -- --configuration=production; - copy the build to
src/main/resources/staticandtarget/classes/static.
After that, access:
- Web app:
http://localhost:8080 - APIs:
http://localhost:8080/api/...
3. Run frontend in development mode (optional)
In the frontend/ directory:
cd frontend
npm install
npm run start:local # or: npm start
# Frontend: http://localhost:4200
If you want the frontend to hit a local backend, ensure services use the right URL in api-url.util.ts (default: http://localhost:8080).
🌐 Deploy & Secrets Management
GitHub Pages (docs/)
The repo includes the docs/ folder for GitHub Pages. Typical flow:
-
Build the frontend:
cd frontend npm install npm run build -
Copy
dist/portfolio/browsertodocs/(as described inDEPLOY-GOOGLE-CLOUD-RUN.mdand deploy scripts). -
Commit and push to the configured GitHub Pages branch (usually
main).
Google Cloud Run (backend + SPA) — legacy
The repo still includes documentation and scripts for optional Google Cloud Run deploy:
Dockerfile.cloud-run.projeto-wesleydeploy.shanddeploy-completo-projeto-wesley.ps1DEPLOY-GOOGLE-CLOUD-RUN.md
Current deploy: the backend + SPA are hosted on Oracle Cloud Always Free (VPS), with the Docker backend image serving the SPA. Secrets are configured as environment variables on the server.
Google Secret Manager (optional)
If using Cloud Run or another GCP-integrated environment, secrets can be managed in Google Secret Manager. For Oracle VPS deploy, secrets are configured as environment variables on the server.
📚 Portfolio Content (Markdown via GitHub)
Portfolio markdowns are stored in the certificados-wesley GitHub repo and fetched dynamically via API:
- GitHub structure:
portfolio-content/README.md– overviewportfolio-content/README_GITHUB_PROFILE.md– GitHub profile READMEportfolio-content/STACKS.md– detailed tech documentationportfolio-content/CURRICULO.md– resume in markdownportfolio-content/projects/*.md– projects:lol-matchmaking-fazenda.mdexperimenta-ai---soneca.mdmercearia-r-v.mdaa_space.mdtraffic_manager.mdinvestment_calculator.mdpintarapp.mdpinta-como-eu-pinto.mdlobby-pedidos.mdobaid-with-bro.md
portfolio-content/trabalhos/*.md– professional experiences
These files are the source of truth that feed:
- the AI chat (base context from root files, with smart search via
ContextSearchService), and - the project pages/modals in the frontend (via
/api/projects/{projectName}/markdown).
Cache: Content is cached in memory for 5 minutes to improve performance and reduce GitHub calls.
🧪 Demo Flow (User Experience)
-
1. Access the portfolio
- Open the published URL (GitHub Pages or Oracle VPS).
- The landing (
hero) already shows profile summary and key links.
-
2. Browse the sections
- Scroll through:
about,skills,experience,education,certifications,projects,contact. - Each section is a standalone Angular component, reflecting the contents of
portfolio-content/.
- Scroll through:
-
3. Use the AI Chat
- Click the floating/chat widget (
chat-widget). - Ask about:
- stack/technologies (based on
STACKS.mdin GitHub); - specific projects (based on
projects/*.mdin GitHub); - profile summary (based on
README_GITHUB_PROFILE.md).
- stack/technologies (based on
- The backend:
- fetches relevant markdowns from
certificados-wesley(with cache); ContextSearchServicefinds the most relevant excerpts;TokenBudgetServiceoptimizes tokens (reduces history/contexts if needed);PortfolioPromptServicebuilds the system prompt with the selected contexts;OpenAIAdapterpicks the best available model with automatic fallback;- returns the response for the frontend to render in chat format.
- fetches relevant markdowns from
- Click the floating/chat widget (
-
4. Explore projects
- In
projects, click a project to open the modal/README. - The frontend calls
/api/projects/{projectName}/markdown. - The backend fetches markdown from GitHub (
certificados-wesley/portfolio-content/projects/{projectName}.md) and returns it.
- In
-
5. Send a contact message
- Fill out the form in
contactand send. - The frontend triggers
POST /api/contact, and the backend sends email using Gmail + secrets from the server environment.
- Fill out the form in
AI Chat Flow (Mermaid)
%%{title: "AI Chat Flow with GitHub Integration"}%%
sequenceDiagram
participant U as User
participant FW as Angular Frontend
participant C as ChatController
participant UC as ChatUseCase
participant TB as TokenBudgetService
participant PS as PortfolioPromptService
participant GH_MD as GithubMarkdownAdapter
participant CACHE as GithubContentCache
participant GH_API as GitHub API
participant AI as OpenAIAdapter
participant OA as OpenAI API
U->>FW: Types message in chat
FW->>C: POST /api/chat
C->>UC: execute request
UC->>PS: buildSystemPrompt
PS->>GH_MD: load markdowns
alt Cache hit
GH_MD->>CACHE: fetch cache
CACHE-->>GH_MD: cached content
else Cache miss
GH_MD->>GH_API: GET contents
GH_API-->>GH_MD: file list
GH_MD->>GH_API: GET raw content
GH_API-->>GH_MD: markdown content
GH_MD->>CACHE: store in cache
end
GH_MD-->>PS: relevant content
PS-->>UC: final system prompt
UC->>TB: optimize tokens
alt Tokens above threshold
TB->>TB: trim history
TB->>TB: trim contexts
end
TB-->>UC: TokenBudgetResult
UC->>AI: chat with optimized prompt
AI->>OA: call primary model
alt Rate limit
OA-->>AI: 429 error
AI->>OA: try fallback
OA-->>AI: response
else Success
OA-->>AI: response
end
AI-->>UC: ChatResponse
UC-->>C: ChatResponse
C-->>FW: 200 OK
FW-->>U: Render response
👨💻 Author & Contact
- Name: Wesley de Carvalho Augusto Correia
- GitHub: github.com/wmakeouthill
- LinkedIn: linkedin.com/in/wcacorreia
- Email: wcacorreia1995@gmail.com
If this project helped you, consider leaving a star on the repo. 🙂