Skip to content

Projects / Systems

Systems in various states of evolution.

I don't see my work as isolated projects — I see them as evolving systems. None of these are “finished” in the way a portfolio usually means it, and that's the honest part. Some are running, some are half-built, some were quietly set down the moment the interesting problem was solved. What they share isn't polish — it's a way of looking at the world: see something repetitive and real, and build the thing that makes the manual version disappear.

01

Canadeal — Automated Deal Aggregation

Live

The problem

Finding good deals for Canada's Chinese-speaking community meant manually searching fragmented sources, rewriting product descriptions, and posting across multiple platforms by hand — every single day.

What I built

Built the platform end-to-end: FastAPI + Next.js 14 + MongoDB + Meilisearch, containerized with Docker Compose. An automated Playwright crawler bypasses Amazon bot-detection via Chrome DevTools Protocol. A pluggable AI-provider layer (OpenAI / Claude / Deepseek, hot-swappable via one env variable) auto-generates Traditional Chinese titles, categories, and feature summaries. Facebook Graph, Instagram Stories, and Threads APIs handle scheduled auto-posting.

Manual work it replaces

Manual deal hunting, manual description rewriting, manual social posting — the entire content pipeline now runs on its own.

What I learned

Proactive security work matters: I audited the system myself and patched 3 critical vulnerabilities including hardcoded DB credentials, an X-Forwarded-For rate-limit bypass, and an XSS-readable admin key. The pluggable AI layer taught me that swapping cost/quality trade-offs should never require touching business logic.

Built with

  • Python
  • FastAPI
  • Next.js
  • MongoDB
  • Meilisearch
  • Playwright
  • Docker
  • OpenAI
  • LLM

Status

Live at canadeal.ca.

02

T&T Supermarket Price Tracker

Live

The problem

Grocery pricing has almost no transparency. ~12,500 T&T products across 2 stores and 3 languages with no way to track how prices actually move over time.

What I built

A Node.js crawler on a daily crontab + Docker Compose schedule. Two-phase crawl pipeline (bulk category listing, then detail fetch only for items with options) reduces unnecessary requests by ~95%. Polite rate-limiting with p-limit, random jitter, and exponential back-off keeps full runs under 5 minutes per combination. MongoDB price-history sub-documents append only on price change, cutting write volume by ~80%. Replaced Elasticsearch with Meilisearch to eliminate hardcoded-credential risk while keeping multi-language full-text search.

Manual work it replaces

Manual price-checking and the guesswork of "is this actually a good price?" — replaced with a history that accumulates on its own, every day.

What I learned

A race condition was serving empty API responses because of a delete-then-insert pattern; fixed with idempotent bulk upserts. Even a fairly simple system becomes powerful once data accumulates continuously — the value isn't in any single reading, it's in the history.

Built with

  • Next.js
  • Node.js
  • MongoDB
  • Meilisearch
  • Docker
  • Nginx

Status

Live at tnt.canadeal.ca.

03

GoLink — Link Shortener & Affiliate Engine

Live

The problem

Sharing affiliate links at scale means managing custom aliases, tracking clicks, injecting affiliate tags consistently, and splitting traffic for A/B tests — none of which basic link shorteners support.

What I built

Full-stack FastAPI + MongoDB platform with custom alias support, QR code generation, and deep link routing. A/B testing for redirect targets with configurable weight distribution, scoped by subscription plan tier (Free / Pro / Business). Affiliate tag injection with per-domain rules and an ethical no-overwrite policy — existing affiliate parameters are never replaced. Click analytics pipeline tracking device type, geo-location, referrer, and time-series data with EPC metrics via REST API. JWT authentication with role-based access control.

Manual work it replaces

Manual link management, manual affiliate tag insertion, and guesswork about which redirect targets actually convert.

What I learned

The ethical no-overwrite policy on affiliate tags was a deliberate design decision, not an afterthought. Building plan-gated feature enforcement consistently across every endpoint is the kind of unglamorous work that makes a real product feel solid.

Built with

  • Python
  • FastAPI
  • MongoDB
  • Docker
  • JWT

Status

Live at go.canadeal.ca.

04

Flashfood Deal Finder

Live

The problem

Flashfood has real discounted-grocery opportunities, but discovery is fragmented across cities, stores, and categories — with no good way to filter or track what's available.

What I built

Live deal feed using Firestore onSnapshot (no polling) with composable region/store/category/keyword filters and multiple sort modes. Infinite scroll via IntersectionObserver in 32-item batches. Per-city filter persistence in sessionStorage. Multi-language UI (EN / ZH / FR / KR / JP). Dynamic Open Graph images generated at the Edge for rich social-sharing previews.

Manual work it replaces

The manual hunt-and-sort of finding and tracking discounts across a noisy, constantly changing source.

What I learned

A filter-restore race condition on city switch required a ref-based guard to fix cleanly. Deriving context-aware category pills through a two-pass filtering strategy turned out to be the right call — it eliminated a whole class of inconsistency.

Built with

  • Next.js
  • Firebase
  • Firestore
  • Vercel

Status

Live at flashfood.canadeal.ca.

05

AI Resume Builder

Live

The problem

Tailoring a resume to a specific job description is repetitive, time-consuming, and easy to get wrong — especially when you have a large knowledge base of skills, projects, and experience to draw from.

What I built

Full-stack resume builder: Next.js 15 (App Router, TypeScript, TailwindCSS) frontend + FastAPI backend, deployed on Vercel + Docker. OpenAI API analyzes job descriptions and recommends the most relevant skills, projects, and experience entries from a structured knowledge base, with heuristic fallback for offline usage. Personal profile database with full CRUD for skills, work experience, and projects — supporting both manual entry and AI-assisted bulk import from raw text. Resume export in PDF, DOCX, Markdown, and plain text. A privacy-conscious AI rewrite feature polishes bullet points without fabricating facts, inflating responsibilities, or inventing metrics.

Manual work it replaces

The manual process of reading a job description, cross-referencing your own experience, and deciding what to include — replaced with a system that does the matching and drafting automatically.

What I learned

JWT authentication with per-user OpenAI API key management — masking stored keys on retrieval — is the kind of detail that separates a real product from a demo. The privacy constraint on the AI rewrite (no fabrication, no inflation) was a harder design problem than the AI integration itself.

Built with

  • Next.js
  • TypeScript
  • FastAPI
  • MongoDB
  • Docker
  • OpenAI API
  • Vercel

Status

Live at resume.canadeal.ca.