This repository contains the source code for my personal portfolio website.
The application uses Nuxt, Nuxt UI, Tailwind CSS, Nuxt Content to manage content, and a custom read-only Git repository viewer.
You can check it out here.
The site is statically generated; however, the Git viewer is purely client-side.
Beyond standard portfolio features, this repository serves as a serverless, read-only Git infrastructure. It includes a custom-built Git repository viewer that parses bare repositories natively in the browser. Instead of exposing repository contents through an API, the application downloads Git packfiles via the Git Dumb HTTP protocol and reconstructs the repository client-side using isomorphic-git. Trees, blobs, commits, history, and diffs are derived directly from Git objects in the browser.
The Git repository viewer uses a custom Nitro API proxy (
/api/git-proxy) to fetch static Git objects (.pack,info/refs) from a private Cloudflare R2 bucket via the Dumb HTTP protocol, rendering them entirely client-side.
Cloudflare is optional. You can point the proxy to a local path where you store all your repositories and serve them directly from there.
The Git features will not work properly on massive repositories (QEMU, the Linux kernel, etc.). For this scale, consider looking into cgit or similar.
-
Clones and reads bare repositories directly in the browser using isomorphic-git.
-
Calculates character-level diffs on the client using the
difflibrary, supports both split and unified views, and weaves them into server-rendered Shiki HTML tokens. -
Virtual File System: Uses lightning-fs backed by IndexedDB to cache repository objects, reducing bandwidth usage and load times for repeat visits.
-
Has file views with code highlighting, tree views, commit trees, and filtering functionality.
-
Uses aggressive caching.
- Write and manage blog posts and content with Nuxt Content.
- Integrates with Resend for contact forms.
- Integrates with Cal.com to schedule meetings.
- Integrates with Cloudflare Turnstile for the contact form.
If you decide to use this repository for your own portfolio, you will need to modify scripts/getPublicRepos.mjs to point to your account, create a GitHub App with configured webhooks pointing to https://your-domain.com/webhooks/repos-sync, configure an appropriate secret, and provide it as the NUXT_WEBHOOK_SECRET environment variable. Then simply install the GitHub App on all your repositories.
Your webhook needs to fire on Create, Push, Repository, Delete, and Release events.
Once you have a GitHub App, create a GitHub Personal Access Token with Read access to code and metadata, and Read & Write access to Actions, allowing the application to trigger the sync-repos.yml workflow.
At this point, every time you make a change to one of your repositories, GitHub will fire a webhook to /webhooks/repos-sync. This handler validates that the request is coming from GitHub and triggers the workflow at:
https://api.github.com/repos/${owner}/${portfolioRepo}/actions/workflows/sync-repos.yml/dispatches
where ${portfolioRepo} is portfolio and ${owner} is pushPayload.repository.owner.login, for public repositories.
The workflow uploads the bare repositories generated by scripts/getPublicRepos.mjs to the configured Cloudflare R2 bucket using the credentials configured in your repository secrets:
CLOUDFLARE_R2_ACCOUNT_ID: secrets.CLOUDFLARE_R2_ACCOUNT_ID
CLOUDFLARE_R2_ACCESS_KEY_ID: secrets.CLOUDFLARE_R2_ACCESS_KEY_ID
CLOUDFLARE_R2_SECRET_ACCESS_KEY: secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY
CLOUDFLARE_R2_BUCKET: secrets.CLOUDFLARE_R2_BUCKETThen go to your Cloudflare account and supply the following environment variables:
NUXT_RESEND_API_KEY=your_resend_api_key
NUXT_TURNSTILE_SECRET_KEY=your_turnstile_secret_key
NUXT_PUBLIC_TURNSTILE_SITE_KEY=your_turnstile_site_key
NUXT_WEBHOOK_SECRET=the_webhook_secret
NUXT_GITHUB_TOKEN=the_pat_you_generatedFinally, bind the R2 bucket you want to use to the R2_BUCKET environment variable.
If you do not want this behavior, supply the NUXT_REPOS_DIR environment variable, or simply make sure all repositories you want to expose are located in cwd/repos.
This option does not require GitHub webhooks, buckets, or any additional infrastructure. It also does not include any automation except the scripts/getPublicRepos.mjs script.
To modify the site name, URL, and related settings, edit nuxt.config.ts and app/app.config.ts with your data.
To modify the site's content, edit the files in the content/ directory before building or deploying the site.
NUXT_RESEND_API_KEY- Used for receiving emails from leads. Emails will be sent using this key to your configured email address (Required). Change the receiving email inserver/api/contact.post.ts.NUXT_TURNSTILE_SECRET_KEY- Secret key for the Cloudflare Turnstile CAPTCHA used by the contact form (Required).NUXT_PUBLIC_TURNSTILE_SITE_KEY- Public site key for the Cloudflare Turnstile CAPTCHA (Required).NUXT_WEBHOOK_SECRET- Secret used to verify that webhook requests originate from your GitHub App (Required when using Cloudflare).NUXT_GITHUB_TOKEN- GitHub Personal Access Token used to trigger the synchronization workflow (Required when using Cloudflare).NUXT_REPOS_DIR- Directory containing your repositories. Defaults tocwd/reposwhen omitted and when an R2 bucket binding is not configured.
You will need Node.js and npm installed to run the project locally.
Clone the repository:
git clone https://github.com/Sergo706/portfolio.git
cd portfolioOr:
git clone https://riavzon.com/api/git-proxy/portfolio.gitInstall dependencies:
npm installCreate a .env file in the root directory. You will need to provide the required keys for the contact form and Git proxy to function fully during local development.
NUXT_RESEND_API_KEY=your_resend_api_key
NUXT_TURNSTILE_SECRET_KEY=your_turnstile_secret_key
NUXT_PUBLIC_TURNSTILE_SITE_KEY=your_turnstile_site_key
NUXT_WEBHOOK_SECRET=the_webhook_secret
NUXT_GITHUB_TOKEN=the_pat_you_generated
NUXT_REPOS_DIR=local_development_dirStart the Nuxt development server:
npm run devThe application will be available at http://localhost:3000.
This application is built for the Cloudflare Pages environment. Deployment is fully automated via GitHub Actions (.github/workflows/deploy.yml).
To build the application manually for production:
npm run buildThis generates the .output directory containing the Nitro server and static public assets, ready to be deployed.