feat: enhance CV with project title links, experience durations, certifications, and improved navigation

## Project Title Links
- Add projectName and projectDesc fields to Project struct
- Split project titles to make only project name clickable
- Update template logic for conditional title rendering
- Apply changes to both English and Spanish versions

## Experience Duration Display
- Restore duration calculation display in experience section
- Move duration from date line to after company name
- Style duration in light gray (#999) for subtle appearance
- Calculate durations dynamically (e.g., "4 years 10 months")

## Certifications Section Enhancement
- Add Codecademy Certifications (2022-2024) with AI Transformers and React courses
- Add LinkedIn Learning Certifications (2019-2020) with 5 professional courses
- Implement colored icon system with brand colors (purple, cyan, green, etc.)
- Use responsibilities format matching Third Party Contributions layout
- Reorder courses chronologically (most recent first)

## localStorage Improvements
- Save CV length preference (short/long)
- Save logos visibility preference (show/hide)
- Save theme preference (default/clean)
- Restore all preferences on page load

## Navigation UX Enhancements
- Fix scroll positioning to show sections below action bar
- Add keepHeaderVisible flag to maintain header visibility after navigation
- Ensure smooth scrolling with proper offset calculations
- Reset flag on scroll up to restore normal hide/show behavior

## Files Modified
- internal/models/cv.go: Add ProjectName, ProjectDesc fields
- templates/cv-content.html: Update project and experience rendering
- static/css/main.css: Add duration-text styling
- templates/index.html: Enhance scroll behavior and localStorage
- data/cv-*.json: Add certifications, split project titles, reorder courses
- static/images/courses/: Add codecademy.png, linkedin.png
This commit is contained in:
juanatsap
2025-11-09 11:42:52 +00:00
parent e64e63de98
commit 2ce13481d0
13 changed files with 975 additions and 33 deletions
+133
View File
@@ -0,0 +1,133 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official email address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement via GitHub issues
or by contacting the project maintainer directly.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
+296
View File
@@ -0,0 +1,296 @@
# Contributing to CV Site
First off, thank you for considering contributing to this project! This CV site is a personal project, but contributions are welcome to improve the template, fix bugs, or add features that others might find useful.
## Table of Contents
- [Code of Conduct](#code-of-conduct)
- [How Can I Contribute?](#how-can-i-contribute)
- [Reporting Bugs](#reporting-bugs)
- [Suggesting Enhancements](#suggesting-enhancements)
- [Pull Requests](#pull-requests)
- [Development Setup](#development-setup)
- [Style Guidelines](#style-guidelines)
- [Go Code Style](#go-code-style)
- [HTMX Patterns](#htmx-patterns)
- [CSS Style](#css-style)
- [Testing](#testing)
- [Commit Messages](#commit-messages)
## Code of Conduct
This project and everyone participating in it is governed by our [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to the project maintainer.
## How Can I Contribute?
### Reporting Bugs
Before creating bug reports, please check existing issues to avoid duplicates. When creating a bug report, include as many details as possible:
**Bug Report Template:**
```markdown
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '...'
3. See error
**Expected behavior**
What you expected to happen.
**Environment:**
- Go version: [e.g., 1.21.5]
- OS: [e.g., macOS 14.0, Ubuntu 22.04]
- Browser (if applicable): [e.g., Chrome 120, Firefox 121]
**Additional context**
Add any other context about the problem here.
```
### Suggesting Enhancements
Enhancement suggestions are welcome! Please create an issue with:
- **Clear title** describing the enhancement
- **Detailed description** of the proposed functionality
- **Use case** explaining why this would be useful
- **Implementation ideas** (optional) if you have thoughts on how to implement it
### Pull Requests
1. **Fork the repository** and create your branch from `main`
2. **Branch naming convention:**
- `feature/description` - New features (e.g., `feature/add-dark-mode`)
- `fix/description` - Bug fixes (e.g., `fix/pdf-export-fonts`)
- `docs/description` - Documentation updates (e.g., `docs/update-readme`)
- `refactor/description` - Code refactoring (e.g., `refactor/simplify-handlers`)
3. **Make your changes:**
- Follow the [style guidelines](#style-guidelines)
- Add tests if adding new functionality (see [Testing](#testing))
- Update documentation as needed
4. **Test your changes:**
- Run `make dev` to test locally
- Test PDF export functionality
- Test both English and Spanish versions
- Test responsive design on different screen sizes
5. **Commit your changes** with clear commit messages (see [Commit Messages](#commit-messages))
6. **Push to your fork** and submit a pull request to the `main` branch
7. **Pull Request Template:**
```markdown
**Description**
Brief description of what this PR does.
**Type of change**
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
**How Has This Been Tested?**
Describe the tests you ran and how to reproduce them.
**Checklist:**
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have tested the changes locally
```
## Development Setup
### Prerequisites
- **Go 1.21+** installed
- **Git** for version control
- **Make** (optional, but recommended)
- **Chrome/Chromium** for PDF generation testing
### Setup Steps
1. **Clone your fork:**
```bash
git clone https://github.com/YOUR-USERNAME/cv.git
cd cv
```
2. **Install dependencies:**
```bash
go mod download
```
3. **Set up environment:**
```bash
cp .env.example .env
# Edit .env if needed
```
4. **Run development server:**
```bash
make dev
# Or: GO_ENV=development go run main.go
```
5. **Open browser:**
```
http://localhost:1999
```
### Useful Make Commands
- `make dev` - Run in development mode (hot-reload enabled)
- `make build` - Build production binary
- `make test` - Test endpoints (requires server running)
- `make clean` - Remove build artifacts
- `make help` - Show all available commands
## Style Guidelines
### Go Code Style
- **Follow standard Go conventions:**
- Use `gofmt` to format code (runs automatically with most editors)
- Run `go vet` to catch common mistakes
- Use meaningful variable and function names
- Add comments for exported functions and complex logic
- **Code organization:**
- Keep handlers in `main.go` or separate handler files
- Use the `internal/models` package for data structures
- Keep utilities in appropriate packages
- **Error handling:**
```go
// Good
if err != nil {
log.Printf("Error loading CV data: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
// Avoid silent failures
// Bad: ignoring errors without logging
data, _ := loadCV()
```
### HTMX Patterns
- **Use semantic HTML:**
```html
<!-- Good -->
<button hx-get="/cv?lang=en" hx-target="#cv-content" hx-swap="innerHTML">
English
</button>
<!-- Avoid inline styles when possible -->
```
- **Keep HTMX attributes organized:**
- `hx-get/post` first
- `hx-target` second
- `hx-swap` third
- Other attributes follow
- **Progressive enhancement:**
- Ensure basic functionality works without JavaScript
- HTMX should enhance, not be required
### CSS Style
- **Organization:**
- Group related styles together
- Use comments to separate sections
- Keep selectors specific but not overly complex
- **Naming:**
- Use semantic class names
- Prefer descriptive names over abbreviations
- **Responsive design:**
- Mobile-first approach
- Use media queries for larger screens
- Test on multiple screen sizes
## Testing
**Current State:** This project does not yet have automated tests. This is a known gap.
**When adding tests (future):**
- Write unit tests for new utility functions
- Add integration tests for HTTP handlers
- Test PDF generation functionality
- Ensure tests pass before submitting PR:
```bash
go test ./...
```
**Manual testing requirements:**
- Test both language versions (English/Spanish)
- Test PDF export (both server-side and browser print)
- Test responsive design (mobile, tablet, desktop)
- Test in multiple browsers (Chrome, Firefox, Safari)
- Verify console has no errors
- Check network tab for failed requests
## Commit Messages
Use clear, descriptive commit messages following this format:
```
type: brief description
Optional longer description explaining what and why (not how).
Fixes #123
```
**Types:**
- `feat:` - New feature
- `fix:` - Bug fix
- `docs:` - Documentation changes
- `style:` - Code style changes (formatting, no logic change)
- `refactor:` - Code refactoring
- `perf:` - Performance improvements
- `test:` - Adding or updating tests
- `chore:` - Maintenance tasks, dependency updates
**Examples:**
```
feat: add dark mode toggle
Add user preference for dark mode with localStorage persistence.
Respects system preference on first visit.
Fixes #42
```
```
fix: correct PDF font rendering in headless Chrome
The custom Quicksand font wasn't loading properly in chromedp.
Updated to wait for fonts to load before PDF generation.
Fixes #38
```
## Questions?
Feel free to open an issue with the `question` label if you need help or clarification on anything!
## Thank You!
Your contributions help make this project better for everyone. Thank you for taking the time to contribute! 🎉
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Juan Andrés Moreno Rubio
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+167 -10
View File
@@ -1,7 +1,29 @@
# CV Site - Go + HTMX # CV Site - Go + HTMX
[![Go Version](https://img.shields.io/badge/Go-1.21%2B-00ADD8?logo=go)](https://go.dev/)
[![HTMX](https://img.shields.io/badge/HTMX-1.9.10-3366CC)](https://htmx.org/)
[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
**Modern, minimal curriculum vitae website** for Juan Andrés Moreno Rubio built with **Go** and **HTMX**. **Modern, minimal curriculum vitae website** for Juan Andrés Moreno Rubio built with **Go** and **HTMX**.
A professional, bilingual CV site with server-side PDF generation, HTMX interactivity, and a clean paper design aesthetic. Perfect template for developers looking to create their own CV website with modern tech and minimal JavaScript.
## 📑 Table of Contents
- [Features](#-features)
- [Demo](#-demo)
- [Quick Start](#-quick-start)
- [Updating Your CV](#-updating-your-cv)
- [Export to PDF](#-export-to-pdf)
- [Key Technologies](#-key-technologies)
- [Documentation](#-documentation)
- [Deployment](#-deployment)
- [Customization](#-customization)
- [Contributing](#-contributing)
- [License](#-license)
- [Support](#-support)
## 🚀 Features ## 🚀 Features
-**Bilingual Support** - Spanish and English with instant switching (no page reload) -**Bilingual Support** - Spanish and English with instant switching (no page reload)
@@ -13,24 +35,55 @@
-**JSON-Based Content** - Easy to update without touching code -**JSON-Based Content** - Easy to update without touching code
-**AI Development Section** - Showcases modern AI-assisted development skills -**AI Development Section** - Showcases modern AI-assisted development skills
-**Fast & Lightweight** - Go backend with chromedp for PDF generation -**Fast & Lightweight** - Go backend with chromedp for PDF generation
-**Security Hardened** - CSP headers, XSS protection, secure defaults
-**Production Ready** - Docker support, systemd service, CI/CD workflows
-**Developer Friendly** - Hot reload, clear code structure, comprehensive Makefile
## 📸 Demo
**Live Features:**
- Single-page application with no page reloads
- Instant language switching (English ↔ Spanish)
- Professional PDF export with perfect font rendering
- Responsive design from mobile to desktop
- Clean paper aesthetic on gray background
- Print-friendly layouts
**Note:** This is a personal CV site template. Fork it and customize the JSON files with your own information!
## 📋 Quick Start ## 📋 Quick Start
### Prerequisites ### Prerequisites
- **Go 1.21+** installed - **Go 1.21+** installed
- **Chrome/Chromium** (for PDF generation)
- **Make** (optional, recommended for easier development)
### Run ### Installation & Run
\`\`\`bash \`\`\`bash
# Build and run # Clone the repository
git clone https://github.com/yourusername/cv.git
cd cv
# Option 1: Using Make (recommended)
make dev
# Option 2: Using Go directly
go run main.go
# Option 3: Build and run binary
go build -o cv-server && ./cv-server go build -o cv-server && ./cv-server
\`\`\` \`\`\`
Open **http://localhost:1999** ### Access the Site
- 🇬🇧 English: http://localhost:1999/?lang=en Open **http://localhost:1999** in your browser
- 🇪🇸 Spanish: http://localhost:1999/?lang=es
- 🇬🇧 **English version:** http://localhost:1999/?lang=en
- 🇪🇸 **Spanish version:** http://localhost:1999/?lang=es
**Language switching** is instant via HTMX - no page reload required!
## 📄 Updating Your CV ## 📄 Updating Your CV
@@ -66,11 +119,115 @@ No code changes needed - just refresh browser!
## 🎯 Key Technologies ## 🎯 Key Technologies
- Backend: **Go** (stdlib net/http) - **Backend:** Go 1.21+ (stdlib `net/http`, graceful shutdown)
- PDF Generation: **chromedp** (headless Chrome automation) - **PDF Generation:** chromedp (headless Chrome automation)
- Frontend: **HTMX** 1.9.10 - **Frontend:** HTMX 1.9.10 (hypermedia-driven interactions)
- Styling: Custom **CSS** with Quicksand font - **Styling:** Custom CSS with Quicksand font from Google Fonts
- Data: **JSON** files - **Data:** JSON files for easy content management
- **Deployment:** Docker, systemd service, GitHub Actions CI/CD
## 📚 Documentation
- **[ARCHITECTURE.md](ARCHITECTURE.md)** - System design, data flow, and technical decisions
- **[CONTRIBUTING.md](CONTRIBUTING.md)** - How to contribute (issues, PRs, code style)
- **[SECURITY.md](SECURITY.md)** - Security policy, vulnerability reporting, deployment considerations
- **[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)** - Community guidelines and standards
- **[LICENSE](LICENSE)** - MIT License
## 🚀 Deployment
This project is production-ready with multiple deployment options:
### Docker Deployment
\`\`\`bash
# Build Docker image
make docker-build
# Run container
make docker-run
\`\`\`
### Systemd Service
\`\`\`bash
# Install as systemd service
make install-service
# Update running service
make update-service
\`\`\`
### Manual Deployment
\`\`\`bash
# Build optimized binary
make build
# Run in production mode
GO_ENV=production ./cv-server
\`\`\`
**Environment Configuration:** Copy `.env.example` to `.env` and customize:
- `PORT` - Server port (default: 1999)
- `GO_ENV` - Environment (development/production)
- `TEMPLATE_HOT_RELOAD` - Enable template hot-reload in development
**Security:** See [SECURITY.md](SECURITY.md) for production deployment best practices.
## 🎨 Customization
### Update Your CV Content
1. Edit `data/cv-en.json` and `data/cv-es.json` with your information
2. No code changes required - just refresh the browser!
### Customize Styling
- **Main styles:** `static/css/main.css`
- **Colors:** Modify CSS variables in `:root` selector
- **Fonts:** Update Google Fonts import in HTML templates
- **Layout:** Edit templates in `templates/` directory
### Add New Sections
1. Update the `CV` struct in `internal/models/cv.go`
2. Add content to JSON files in `data/`
3. Update templates in `templates/` to display new sections
## 🤝 Contributing
Contributions are welcome! Whether it's:
- 🐛 Bug reports
- 💡 Feature suggestions
- 📝 Documentation improvements
- 🔧 Code contributions
Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on:
- Submitting issues
- Creating pull requests
- Code style and testing requirements
- Development workflow
## 📄 License
This project is licensed under the **MIT License** - see the [LICENSE](LICENSE) file for details.
**TL;DR:** You can use this template for your own CV site, modify it, and distribute it. Just keep the original copyright notice.
## 💬 Support
- **Issues:** [GitHub Issues](https://github.com/yourusername/cv/issues) for bug reports and feature requests
- **Discussions:** [GitHub Discussions](https://github.com/yourusername/cv/discussions) for questions and ideas
- **Security:** See [SECURITY.md](SECURITY.md) for reporting security vulnerabilities
## 🙏 Acknowledgments
- **HTMX** - For making hypermedia-driven applications enjoyable
- **chromedp** - For reliable headless Chrome automation
- **Go Community** - For excellent standard library and tooling
- **AI Assistance** - For accelerating development and documentation
--- ---
+205
View File
@@ -0,0 +1,205 @@
# Security Policy
## Supported Versions
This project is actively maintained. Security updates will be provided for the latest release on the `main` branch.
| Version | Supported |
| ------- | ------------------ |
| main | :white_check_mark: |
| < main | :x: |
## Reporting a Vulnerability
We take the security of this CV site seriously. If you discover a security vulnerability, please help us protect users by following responsible disclosure practices.
### How to Report
**DO NOT** open a public issue for security vulnerabilities.
Instead, please report security vulnerabilities by:
1. **GitHub Security Advisories** (Preferred):
- Go to the repository's Security tab
- Click "Report a vulnerability"
- Fill out the form with details
2. **Direct Contact**:
- Open a private issue or contact the maintainer directly
- Use encrypted communication if the vulnerability is severe
### What to Include
Please provide the following information in your report:
- **Description** of the vulnerability
- **Steps to reproduce** the issue
- **Potential impact** (e.g., data exposure, XSS, CSRF)
- **Affected versions** (if known)
- **Suggested fix** (if you have one)
- **Your contact information** for follow-up questions
### Response Timeline
- **Initial Response**: Within 48 hours
- **Status Update**: Within 7 days
- **Fix Timeline**: Depends on severity
- Critical: Within 7 days
- High: Within 14 days
- Medium: Within 30 days
- Low: Next release cycle
### Disclosure Policy
- We ask that you give us reasonable time to fix the vulnerability before public disclosure
- We will credit you in the security advisory (unless you prefer to remain anonymous)
- Once the fix is deployed, we will publish a security advisory with details
## Security Considerations for Deployments
If you're deploying this CV site, please be aware of these security considerations:
### 1. PDF Generation Security
The server uses headless Chrome (via chromedp) to generate PDFs:
- **Risk**: Chromedp executes JavaScript and renders HTML, which could be exploited if user input is not sanitized
- **Mitigation**:
- The CV data comes from trusted JSON files, not user input
- If you modify the application to accept user input, ensure proper sanitization
- Consider running chromedp in a sandboxed environment
### 2. Content Security Policy
The application includes CSP headers:
```go
Content-Security-Policy: default-src 'self';
script-src 'self' 'unsafe-inline' https://unpkg.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data:
```
- Review and adjust CSP headers based on your deployment needs
- Remove `'unsafe-inline'` if possible by moving inline scripts/styles to separate files
### 3. Environment Variables
Sensitive configuration is managed via environment variables:
- **Never commit** `.env` file to version control
- Use `.env.example` as a template
- In production, use secure secret management (e.g., HashiCorp Vault, AWS Secrets Manager)
### 4. HTTPS in Production
- **Always use HTTPS** in production
- Configure TLS certificates (Let's Encrypt recommended)
- Consider using a reverse proxy (nginx, Caddy) for TLS termination
### 5. Rate Limiting
The application does not include built-in rate limiting:
- **Recommendation**: Use a reverse proxy (nginx, Caddy) to implement rate limiting
- Protect the `/export/pdf` endpoint to prevent PDF generation abuse
### 6. Input Validation
While this application primarily serves static CV data:
- If you extend it to accept user input, implement strict validation
- Sanitize all inputs before rendering in templates
- Use Go's `html/template` package (which auto-escapes) for HTML rendering
### 7. Dependency Management
Keep dependencies up to date:
```bash
# Check for outdated dependencies
go list -u -m all
# Update dependencies
go get -u ./...
go mod tidy
```
### 8. Security Headers
The application sets security headers:
- `X-Content-Type-Options: nosniff`
- `X-Frame-Options: DENY`
- `X-XSS-Protection: 1; mode=block`
- `Content-Security-Policy: ...`
Review and enhance these headers based on your deployment needs.
### 9. Logging and Monitoring
- Enable structured logging in production
- Monitor for unusual patterns (e.g., excessive PDF generation requests)
- Set up alerts for errors and anomalies
### 10. Docker Security
If deploying via Docker:
- Use official, minimal base images
- Run container as non-root user
- Scan images for vulnerabilities (e.g., `docker scan`, Trivy)
- Keep base images updated
## Known Security Considerations
### PDF Generation Resource Usage
- **Issue**: PDF generation uses headless Chrome, which consumes significant CPU/memory
- **Impact**: Potential DoS via excessive PDF generation requests
- **Mitigation**:
- Implement rate limiting on `/export/pdf` endpoint
- Consider caching generated PDFs
- Monitor resource usage
### Third-Party Dependencies
External dependencies loaded from CDNs:
- HTMX from `https://unpkg.com`
- Google Fonts from `https://fonts.googleapis.com`
**Recommendation**: For production, consider:
- Self-hosting these dependencies for better control
- Using Subresource Integrity (SRI) hashes
- Implementing a Content Security Policy
## Security Best Practices
If you're forking this project for your own CV:
1. **Review all code** before deploying
2. **Update personal information** in JSON files
3. **Configure security headers** appropriate for your use case
4. **Enable HTTPS** with valid certificates
5. **Keep dependencies updated** regularly
6. **Monitor application logs** for suspicious activity
7. **Backup your data** regularly
8. **Test security** before going live
## Security Updates
Security updates will be announced via:
- GitHub Security Advisories
- Release notes on GitHub
- Git commit messages tagged with `[SECURITY]`
## Contact
For security concerns, please contact the project maintainer via GitHub.
## Acknowledgments
We appreciate the security research community's efforts in responsibly disclosing vulnerabilities. Thank you for helping keep this project secure!
+37
View File
@@ -664,6 +664,35 @@
} }
], ],
"courses": [ "courses": [
{
"title": "Codecademy Certifications",
"institution": "Codecademy",
"courseLogo": "codecademy.png",
"location": "Online",
"date": "2022-2024",
"duration": "Various",
"shortDescription": "Professional development courses in AI and modern web technologies through Codecademy's interactive learning platform.",
"responsibilities": [
"<iconify-icon icon='mdi:robot' width='60' height='60' class='default-company-icon' style='color: #9333EA;'></iconify-icon><div><strong>Intro to AI Transformers Course</strong> <em>April 2024</em>: Comprehensive introduction to transformer architecture and AI models, covering attention mechanisms, encoder-decoder structures, and practical applications in natural language processing</div>",
"<iconify-icon icon='mdi:react' width='60' height='60' class='default-company-icon' style='color: #61DAFB;'></iconify-icon><div><strong>Learn React Course</strong> <em>March 2022</em>: Complete React framework training covering components, state management, hooks, lifecycle methods, and modern React development practices</div>"
]
},
{
"title": "LinkedIn Learning Certifications",
"institution": "LinkedIn Learning",
"courseLogo": "linkedin.png",
"location": "Online",
"date": "2019-2020",
"duration": "Various",
"shortDescription": "Professional development courses in SAP technologies, UX design, security, and data analytics through LinkedIn Learning's comprehensive training platform.",
"responsibilities": [
"<iconify-icon icon='mdi:book-open-page-variant' width='60' height='60' class='default-company-icon' style='color: #D97706;'></iconify-icon><div><strong>Aprende lectura rápida</strong> <em>April 2020</em>: Speed reading techniques and comprehension strategies for professional development and efficient information processing</div>",
"<iconify-icon icon='mdi:cloud' width='60' height='60' class='default-company-icon' style='color: #0FAAFF;'></iconify-icon><div><strong>A Tour of the SAP Cloud Platform</strong> <em>February 2020</em>: Comprehensive overview of SAP Cloud Platform services, architecture, and integration capabilities for enterprise cloud solutions</div>",
"<iconify-icon icon='mdi:android' width='60' height='60' class='default-company-icon' style='color: #3DDC84;'></iconify-icon><div><strong>Learning Android Security</strong> <em>February 2020</em>: Android security best practices, encryption methods, secure coding practices, and mobile application security fundamentals</div>",
"<iconify-icon icon='mdi:account-group' width='60' height='60' class='default-company-icon' style='color: #EC4899;'></iconify-icon><div><strong>Persuasive UX: Creating Credibility</strong> <em>January 2020</em>: User experience design principles focused on building trust, credibility, and persuasive design patterns for web applications</div>",
"<iconify-icon icon='mdi:database' width='60' height='60' class='default-company-icon' style='color: #3B82F6;'></iconify-icon><div><strong>Big Data Foundations: Techniques and Concepts</strong> <em>December 2019</em>: Fundamentals of big data technologies, distributed computing, data processing frameworks, and analytics techniques</div>"
]
},
{ {
"title": "Servoy World 2011", "title": "Servoy World 2011",
"institution": "Servoy", "institution": "Servoy",
@@ -738,6 +767,8 @@
"projects": [ "projects": [
{ {
"title": "Somos Una Ola - Beach Cleaning Initiative", "title": "Somos Una Ola - Beach Cleaning Initiative",
"projectName": "Somos Una Ola",
"projectDesc": "Beach Cleaning Initiative",
"url": "https://somosunaola.org", "url": "https://somosunaola.org",
"projectLogo": "somosunaola.png", "projectLogo": "somosunaola.png",
"location": "La Palma, Canary Islands", "location": "La Palma, Canary Islands",
@@ -753,6 +784,8 @@
}, },
{ {
"title": "Herrumbre Vivo Arte - Artist Portfolio Website", "title": "Herrumbre Vivo Arte - Artist Portfolio Website",
"projectName": "Herrumbre Vivo Arte",
"projectDesc": "Artist Portfolio Website",
"url": "https://herrumbrevivoarte.com", "url": "https://herrumbrevivoarte.com",
"projectLogo": "herrumbre-vivo.png", "projectLogo": "herrumbre-vivo.png",
"location": "Fuencaliente, La Palma", "location": "Fuencaliente, La Palma",
@@ -768,6 +801,8 @@
}, },
{ {
"title": "La Porra.club - Football Prediction Platform", "title": "La Porra.club - Football Prediction Platform",
"projectName": "La Porra.club",
"projectDesc": "Football Prediction Platform",
"url": "https://laporra.club", "url": "https://laporra.club",
"projectLogo": "laporra.png", "projectLogo": "laporra.png",
"gitRepoUrl": "/Users/txeo/laporra", "gitRepoUrl": "/Users/txeo/laporra",
@@ -784,6 +819,8 @@
}, },
{ {
"title": "CDC Starter Kit - SAP Customer Data Cloud Demo", "title": "CDC Starter Kit - SAP Customer Data Cloud Demo",
"projectName": "CDC Starter Kit",
"projectDesc": "SAP Customer Data Cloud Demo",
"url": "https://gigyademo.com/cdc-starter-kit/", "url": "https://gigyademo.com/cdc-starter-kit/",
"projectLogo": "sap.png", "projectLogo": "sap.png",
"location": "Online", "location": "Online",
+37
View File
@@ -669,6 +669,35 @@
} }
], ],
"courses": [ "courses": [
{
"title": "Certificaciones Codecademy",
"institution": "Codecademy",
"courseLogo": "codecademy.png",
"location": "Online",
"date": "2022-2024",
"duration": "Varios",
"shortDescription": "Cursos de desarrollo profesional en IA y tecnologías web modernas a través de la plataforma de aprendizaje interactivo de Codecademy.",
"responsibilities": [
"<iconify-icon icon='mdi:robot' width='60' height='60' class='default-company-icon' style='color: #9333EA;'></iconify-icon><div><strong>Intro to AI Transformers Course</strong> <em>Abril 2024</em>: Introducción completa a la arquitectura de transformers y modelos de IA, cubriendo mecanismos de atención, estructuras encoder-decoder y aplicaciones prácticas en procesamiento de lenguaje natural</div>",
"<iconify-icon icon='mdi:react' width='60' height='60' class='default-company-icon' style='color: #61DAFB;'></iconify-icon><div><strong>Learn React Course</strong> <em>Marzo 2022</em>: Formación completa en React framework cubriendo componentes, gestión de estado, hooks, métodos de ciclo de vida y prácticas modernas de desarrollo con React</div>"
]
},
{
"title": "Certificaciones LinkedIn Learning",
"institution": "LinkedIn Learning",
"courseLogo": "linkedin.png",
"location": "Online",
"date": "2019-2020",
"duration": "Varios",
"shortDescription": "Cursos de desarrollo profesional en tecnologías SAP, diseño UX, seguridad y análisis de datos a través de la plataforma de formación integral de LinkedIn Learning.",
"responsibilities": [
"<iconify-icon icon='mdi:book-open-page-variant' width='60' height='60' class='default-company-icon' style='color: #D97706;'></iconify-icon><div><strong>Aprende lectura rápida</strong> <em>Abril 2020</em>: Técnicas de lectura rápida y estrategias de comprensión para desarrollo profesional y procesamiento eficiente de información</div>",
"<iconify-icon icon='mdi:cloud' width='60' height='60' class='default-company-icon' style='color: #0FAAFF;'></iconify-icon><div><strong>A Tour of the SAP Cloud Platform</strong> <em>Febrero 2020</em>: Visión general completa de servicios de SAP Cloud Platform, arquitectura y capacidades de integración para soluciones empresariales en la nube</div>",
"<iconify-icon icon='mdi:android' width='60' height='60' class='default-company-icon' style='color: #3DDC84;'></iconify-icon><div><strong>Learning Android Security</strong> <em>Febrero 2020</em>: Mejores prácticas de seguridad Android, métodos de encriptación, prácticas de codificación segura y fundamentos de seguridad de aplicaciones móviles</div>",
"<iconify-icon icon='mdi:account-group' width='60' height='60' class='default-company-icon' style='color: #EC4899;'></iconify-icon><div><strong>Persuasive UX: Creating Credibility</strong> <em>Enero 2020</em>: Principios de diseño de experiencia de usuario enfocados en generar confianza, credibilidad y patrones de diseño persuasivo para aplicaciones web</div>",
"<iconify-icon icon='mdi:database' width='60' height='60' class='default-company-icon' style='color: #3B82F6;'></iconify-icon><div><strong>Big Data Foundations: Techniques and Concepts</strong> <em>Diciembre 2019</em>: Fundamentos de tecnologías big data, computación distribuida, frameworks de procesamiento de datos y técnicas de análisis</div>"
]
},
{ {
"title": "Servoy World 2011", "title": "Servoy World 2011",
"institution": "Servoy", "institution": "Servoy",
@@ -743,6 +772,8 @@
"projects": [ "projects": [
{ {
"title": "Somos Una Ola - Iniciativa de Limpieza de Playas", "title": "Somos Una Ola - Iniciativa de Limpieza de Playas",
"projectName": "Somos Una Ola",
"projectDesc": "Iniciativa de Limpieza de Playas",
"url": "https://somosunaola.org", "url": "https://somosunaola.org",
"projectLogo": "somosunaola.png", "projectLogo": "somosunaola.png",
"location": "La Palma, Islas Canarias", "location": "La Palma, Islas Canarias",
@@ -758,6 +789,8 @@
}, },
{ {
"title": "Herrumbre Vivo Arte - Sitio Web Portfolio de Artista", "title": "Herrumbre Vivo Arte - Sitio Web Portfolio de Artista",
"projectName": "Herrumbre Vivo Arte",
"projectDesc": "Sitio Web Portfolio de Artista",
"url": "https://herrumbrevivoarte.com", "url": "https://herrumbrevivoarte.com",
"projectLogo": "herrumbre-vivo.png", "projectLogo": "herrumbre-vivo.png",
"location": "Fuencaliente, La Palma", "location": "Fuencaliente, La Palma",
@@ -773,6 +806,8 @@
}, },
{ {
"title": "La Porra.club - Plataforma de Predicción de Fútbol", "title": "La Porra.club - Plataforma de Predicción de Fútbol",
"projectName": "La Porra.club",
"projectDesc": "Plataforma de Predicción de Fútbol",
"url": "https://laporra.club", "url": "https://laporra.club",
"projectLogo": "laporra.png", "projectLogo": "laporra.png",
"gitRepoUrl": "/Users/txeo/laporra", "gitRepoUrl": "/Users/txeo/laporra",
@@ -789,6 +824,8 @@
}, },
{ {
"title": "CDC Starter Kit - Demo de SAP Customer Data Cloud", "title": "CDC Starter Kit - Demo de SAP Customer Data Cloud",
"projectName": "CDC Starter Kit",
"projectDesc": "Demo de SAP Customer Data Cloud",
"url": "https://gigyademo.com/cdc-starter-kit/", "url": "https://gigyademo.com/cdc-starter-kit/",
"projectLogo": "sap.png", "projectLogo": "sap.png",
"location": "Online", "location": "Online",
+6 -4
View File
@@ -101,13 +101,15 @@ type Language struct {
type Project struct { type Project struct {
Title string `json:"title"` Title string `json:"title"`
ProjectName string `json:"projectName,omitempty"` // Optional: linkable part of title
ProjectDesc string `json:"projectDesc,omitempty"` // Optional: non-linkable description part
URL string `json:"url"` URL string `json:"url"`
ProjectLogo string `json:"projectLogo,omitempty"` // Optional logo filename ProjectLogo string `json:"projectLogo,omitempty"` // Optional logo filename
GitRepoUrl string `json:"gitRepoUrl,omitempty"` // Optional git repository URL for dynamic dates GitRepoUrl string `json:"gitRepoUrl,omitempty"` // Optional git repository URL for dynamic dates
Location string `json:"location"` Location string `json:"location"`
StartDate string `json:"startDate,omitempty"` // Optional static start date StartDate string `json:"startDate,omitempty"` // Optional static start date
Current bool `json:"current"` Current bool `json:"current"`
MaintainedBy string `json:"maintainedBy,omitempty"` // Optional maintainer name (e.g., "SAP") MaintainedBy string `json:"maintainedBy,omitempty"` // Optional maintainer name (e.g., "SAP")
Technologies []string `json:"technologies"` Technologies []string `json:"technologies"`
ShortDescription string `json:"shortDescription"` ShortDescription string `json:"shortDescription"`
Responsibilities []string `json:"responsibilities"` Responsibilities []string `json:"responsibilities"`
+5
View File
@@ -779,6 +779,11 @@ iconify-icon {
margin-top: 0.5rem; margin-top: 0.5rem;
} }
.duration-text {
color: #999;
font-weight: normal;
}
.responsibilities { .responsibilities {
list-style: none; list-style: none;
margin-top: 1rem; margin-top: 1rem;
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

+8 -2
View File
@@ -91,7 +91,7 @@
{{end}} {{end}}
</div> </div>
<div class="experience-content"> <div class="experience-content">
<strong>{{.Position}}{{if .Company}} - {{if .CompanyURL}}<a href="{{.CompanyURL}}" target="_blank" rel="noopener noreferrer">{{.Company}}</a>{{else}}{{.Company}}{{end}}{{end}}</strong><br> <strong>{{.Position}}{{if .Company}} - {{if .CompanyURL}}<a href="{{.CompanyURL}}" target="_blank" rel="noopener noreferrer">{{.Company}}</a>{{else}}{{.Company}}{{end}}{{if .Duration}} <span class="duration-text">{{.Duration}}</span>{{end}}{{end}}</strong><br>
<small>{{.StartDate}} / {{if .Current}}{{if eq $.Lang "es"}}presente{{else}}now{{end}}{{else}}{{.EndDate}}{{end}} - ({{.Location}})</small> <small>{{.StartDate}} / {{if .Current}}{{if eq $.Lang "es"}}presente{{else}}now{{end}}{{else}}{{.EndDate}}{{end}} - ({{.Location}})</small>
{{if .ShortDescription}} {{if .ShortDescription}}
@@ -186,7 +186,13 @@
</div> </div>
{{end}} {{end}}
<div class="project-content"> <div class="project-content">
<strong>{{if .URL}}<a href="{{.URL}}" target="_blank" rel="noopener noreferrer">{{.Title}}</a>{{else}}{{.Title}}{{end}}</strong><br> <strong>
{{if .ProjectName}}
{{if .URL}}<a href="{{.URL}}" target="_blank" rel="noopener noreferrer">{{.ProjectName}}</a>{{else}}{{.ProjectName}}{{end}}{{if .ProjectDesc}} - {{.ProjectDesc}}{{end}}
{{else}}
{{if .URL}}<a href="{{.URL}}" target="_blank" rel="noopener noreferrer">{{.Title}}</a>{{else}}{{.Title}}{{end}}
{{end}}
</strong><br>
<small>{{if .StartDate}}{{.StartDate}}{{if .Current}}{{if .DynamicDate}} / {{.DynamicDate}}{{else}} / {{if eq $.Lang "es"}}presente{{else}}now{{end}}{{end}}{{end}}{{end}} - ({{.Location}})</small> <small>{{if .StartDate}}{{.StartDate}}{{if .Current}}{{if .DynamicDate}} / {{.DynamicDate}}{{else}} / {{if eq $.Lang "es"}}presente{{else}}now{{end}}{{end}}{{end}}{{end}} - ({{.Location}})</small>
{{if .ShortDescription}} {{if .ShortDescription}}
+60 -17
View File
@@ -277,28 +277,41 @@
} }
} }
// Flag to keep header visible after navigation
let keepHeaderVisible = false;
// Scroll to section smoothly // Scroll to section smoothly
function scrollToSection(sectionId) { function scrollToSection(sectionId) {
event.preventDefault(); // Prevent default anchor behavior event.preventDefault(); // Prevent default anchor behavior
const section = document.getElementById(sectionId); const section = document.getElementById(sectionId);
if (section) { if (section) {
const actionBarHeight = document.querySelector('.action-bar').offsetHeight; // Ensure header is visible before scrolling
const menuHeight = document.querySelector('.navigation-menu').offsetHeight; const actionBar = document.querySelector('.action-bar');
const offset = actionBarHeight + (menuHeight || 0) + 20; // Add 20px padding const navMenu = document.querySelector('.navigation-menu');
actionBar.classList.remove('header-hidden');
navMenu.classList.remove('header-hidden');
const elementPosition = section.getBoundingClientRect().top; // Set flag to keep header visible
const offsetPosition = elementPosition + window.pageYOffset - offset; keepHeaderVisible = true;
window.scrollTo({
top: offsetPosition,
behavior: 'smooth'
});
// Close menu after clicking // Close menu after clicking
const menu = document.getElementById('navigation-menu'); navMenu.classList.remove('menu-open');
menu.classList.remove('menu-open');
document.querySelector('.hamburger-btn').setAttribute('aria-expanded', 'false'); document.querySelector('.hamburger-btn').setAttribute('aria-expanded', 'false');
// Wait a bit for header to be visible, then calculate offset
setTimeout(() => {
const actionBarHeight = actionBar.offsetHeight;
const offset = actionBarHeight + 20; // Add 20px padding
const elementPosition = section.getBoundingClientRect().top;
const offsetPosition = elementPosition + window.pageYOffset - offset;
window.scrollTo({
top: offsetPosition,
behavior: 'smooth'
});
}, 100);
} }
} }
@@ -348,9 +361,11 @@
if (toggle.checked) { if (toggle.checked) {
paper.classList.add('cv-long'); paper.classList.add('cv-long');
paper.classList.remove('cv-short'); paper.classList.remove('cv-short');
localStorage.setItem('cv-length', 'long');
} else { } else {
paper.classList.add('cv-short'); paper.classList.add('cv-short');
paper.classList.remove('cv-long'); paper.classList.remove('cv-long');
localStorage.setItem('cv-length', 'short');
} }
// Restore scroll position after DOM updates // Restore scroll position after DOM updates
@@ -368,8 +383,10 @@
if (toggle.checked) { if (toggle.checked) {
paper.classList.add('show-logos'); paper.classList.add('show-logos');
localStorage.setItem('cv-logos', 'show');
} else { } else {
paper.classList.remove('show-logos'); paper.classList.remove('show-logos');
localStorage.setItem('cv-logos', 'hide');
} }
// Restore scroll position after DOM updates // Restore scroll position after DOM updates
@@ -412,10 +429,31 @@
}, 100); }, 100);
} }
// Initialize with short version, logos enabled, and saved theme // Initialize with saved preferences or defaults
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
document.querySelector('.cv-paper').classList.add('cv-short'); const paper = document.querySelector('.cv-paper');
document.querySelector('.cv-paper').classList.add('show-logos');
// Restore CV length preference
const savedLength = localStorage.getItem('cv-length') || 'short';
if (savedLength === 'long') {
paper.classList.add('cv-long');
paper.classList.remove('cv-short');
document.getElementById('lengthToggle').checked = true;
} else {
paper.classList.add('cv-short');
paper.classList.remove('cv-long');
document.getElementById('lengthToggle').checked = false;
}
// Restore logos preference
const savedLogos = localStorage.getItem('cv-logos') || 'show';
if (savedLogos === 'show') {
paper.classList.add('show-logos');
document.getElementById('logoToggle').checked = true;
} else {
paper.classList.remove('show-logos');
document.getElementById('logoToggle').checked = false;
}
// Restore theme preference // Restore theme preference
const savedTheme = localStorage.getItem('cv-theme') || 'default'; const savedTheme = localStorage.getItem('cv-theme') || 'default';
@@ -436,10 +474,15 @@
const currentScroll = window.pageYOffset || document.documentElement.scrollTop; const currentScroll = window.pageYOffset || document.documentElement.scrollTop;
const isMenuOpen = navMenu.classList.contains('menu-open'); const isMenuOpen = navMenu.classList.contains('menu-open');
// If scrolling up, reset the keepHeaderVisible flag
if (currentScroll < lastScrollTop) {
keepHeaderVisible = false;
}
// Hide/show header based on scroll direction // Hide/show header based on scroll direction
if (currentScroll > scrollThreshold) { if (currentScroll > scrollThreshold) {
if (currentScroll > lastScrollTop) { if (currentScroll > lastScrollTop && !keepHeaderVisible) {
// Scrolling down - hide header // Scrolling down - hide header (only if keepHeaderVisible is false)
actionBar.classList.add('header-hidden'); actionBar.classList.add('header-hidden');
// Only hide menu if it's open // Only hide menu if it's open
if (isMenuOpen) { if (isMenuOpen) {