table of contents
01

Architecture overview

The site is fully static with zero build steps or server-side frameworks. Dynamic capabilities (guestbook, reactions, visitor counter, contact messages, real-time status) are powered by serverless cloud integrations.

system topology
1
Client (GitHub Pages): Static HTML/CSS/JS served directly from edge locations.
2
Proxy Layer (Cloudflare Worker): Strips /proxy, injects secret Supabase API key, handles CORS headers, and routes notifications to Telegram.
3
Database Layer (Supabase Postgres): Handles REST/RPC endpoints, Row Level Security (RLS) evaluation, and scheduled maintenance.
4
Alert Layer (Telegram Bot API): Real-time push notifications delivered directly to the site owner for messages, guestbook entries, and reactions.
Design Choice: Routing API requests through a Worker prevents exposing the secret Supabase key in the frontend client code while enabling custom CORS rules and serverless webhook dispatching.
02

Cloudflare Worker proxy

The serverless worker at <worker-domain>.workers.dev serves three distinct routing responsibilities:

Route Method Functionality
/proxy/* GET, POST, OPTIONS Rewrites path to Supabase REST/RPC API with secret SUPABASE_ANON_KEY.
/notify POST Formats and pushes incoming user submissions to Telegram Bot API.
/webhook POST Receives incoming Telegram updates (e.g., bot /about command handling).
cloudflare-worker/worker.js — core request router export default { async fetch(request, env) { const url = new URL(request.url); if (url.pathname === '/notify') return handleNotify(request, env, corsHeaders); if (url.pathname === '/webhook') return handleWebhook(request, env, corsHeaders); // Proxy to Supabase const targetURL = SUPABASE_URL + url.pathname.replace('/proxy', '') + url.search; const subReq = new Request(targetURL, { method: request.method, headers: { 'apikey': env.SUPABASE_ANON_KEY, 'Authorization': 'Bearer ' + env.SUPABASE_ANON_KEY, } }); return fetch(subReq); } };
03

Telegram notification engine

To eliminate polling or manual database checks, the Cloudflare Worker contains a dedicated notification dispatcher (/notify) connected to a private Telegram bot channel.

worker.js — handleNotify dispatcher if (body.type === 'contact') { text = `📩 New message\nName: ${clip(body.name, 60)}\nEmail: ${clip(body.email, 100)}\n\n${clip(body.message, 1000)}`; } else if (body.type === 'guestbook') { text = `📖 New guestbook entry\nName: ${clip(body.name, 40)}\n\n${clip(body.message, 300)}`; } else if (body.type === 'reply') { text = `💬 New reply\nName: ${clip(body.name, 40)}\n\n${clip(body.message, 300)}`; } else if (body.type === 'reaction') { text = `👍 New reaction: ${clip(body.reaction, 10)} on ${clip(body.target_type, 20)}:${clip(body.target_id, 40)}`; }

Input strings are clipped to safe buffer lengths before sending to Telegram's sendMessage API, preventing payload overflow attacks while enforcing origin verification checks (Origin: https://iamovi.github.io).

04

Supabase & RLS security

Data persistence is managed via Supabase PostgreSQL tables. Security is strictly maintained using PostgreSQL Row Level Security (RLS) policies.

Table Description Read Policy Insert Policy Update/Delete
guestbook Messages & threaded reply nodes Public (anon) Public (anon) Authenticated (admin)
reactions Emoji reactions across site assets Public (anon) Public (anon) Blocked
status Single-row header status message Public (anon) Blocked Authenticated (admin)
visits Daily visitor session log Public (anon) Public (anon) Blocked
05

Guestbook & threaded tree

Threaded conversations use a self-referential foreign key inside a single database table:

guestbook_replies_migration.sql ALTER TABLE guestbook ADD COLUMN parent_id bigint REFERENCES guestbook(id) ON DELETE CASCADE;

The frontend retrieves all rows in a single query ordered by created_at ASC, then constructs a nested tree in JavaScript:

tree rendering pipeline
1
buildTree(all, null) isolates top-level roots (where parent_id === null), sorted newest first.
2
Recursively fetches child nodes matching parent_id === parent.id, sorted chronologically.
3
Sanitizes user text through escapeHtml() before embedding into nested indent structures.
4
Deleting a top-level parent automatically triggers PostgreSQL ON DELETE CASCADE to purge all sub-replies cleanly.
06

Reactions & optimistic UI

Emoji reactions (💀 🔥 👾 [lol]) support projects, blog posts, and guestbook comments.

Batched Data Fetching

Instead of dispatching one network request per card, loadReactionCounts() combines all visible items into a single batched OR REST query, minimizing database connections.

Deduplication & Optimistic Feedback

Reaction states are stored locally per device using keys formatted as r:target_type:target_id:reaction in localStorage. Clicking a reaction immediately increments UI tallies; if the POST call fails, the state automatically reverts.

07

Status bar & visit counter

The banner status message ("ovi says —") relies on an automated PostgreSQL trigger to keep timestamp metadata updated on edit.

Timezone-Aware Visitor Counting

The visitor counter relies on a PostgreSQL Remote Procedure Call (RPC) bound explicitly to Bangladesh Time (Asia/Dhaka, UTC+6):

get_today_visit_count() RPC SELECT count(*) FROM visits WHERE (visited_at AT TIME ZONE 'Asia/Dhaka')::date = (now() AT TIME ZONE 'Asia/Dhaka')::date;

Sessions are guarded using sessionStorage so tab reloads do not trigger duplicate counts during an active session.

08

Database maintenance cron

To keep the free-tier database under size quotas, the PostgreSQL pg_cron extension runs a scheduled deletion job daily:

visits_corn.sql — daily midnight purge SELECT cron.schedule( 'delete-old-visits', '0 18 * * *', -- 18:00 UTC = 00:00 Asia/Dhaka $$ DELETE FROM visits WHERE (visited_at AT TIME ZONE 'Asia/Dhaka')::date < (now() AT TIME ZONE 'Asia/Dhaka')::date; $$ );
09

Admin panel & JWT auth

Located at /admin/ (marked with noindex meta tags), the control panel provides site administration via Supabase Auth email/password login.

Upon successful authentication, the generated JWT token is sent with all mutating requests via the Authorization: Bearer <JWT> header, enabling RLS policies for database mutation and comment moderation.

10

Web Audio SFX synthesizer

All UI interaction sounds (hover taps, click clacks, menu opens, page loads) are synthesized procedurally in real-time via standard browser Web Audio API nodes in sounds.js — zero external sound files required.

Sound Action Audio Graph Components
Page load Sawtooth sweep + noise buffer burst + sine thud layered
Menu open/close Bandpass noise + frequency ramp oscillator
Button click High-frequency bandpass noise + typewriter square wave decay
Hover tap Low-gain (vol 0.04) sine rise with soft noise tap
11

Framework-free frontend

The main portfolio operates as a lightweight single page with pure CSS display switching:

  • Navigation: showSection(name) toggles CSS classes without full page reloads, persisting navigation state to sessionStorage.
  • Skeleton Screen: Pure JS shimmer overlay rendered prior to API data resolution.
  • Pagination & Search: Client-side dynamic search filter over projects.json paginated at 3 items per view.
12

Zero-cost hosting matrix

Service Role Tier / Cost
GitHub Pages Static site hosting (HTML/CSS/JS) Free ($0)
Supabase PostgreSQL database, Auth, REST/RPC API Free Tier ($0)
Cloudflare Workers Security proxy, CORS headers, Telegram dispatch Free Tier ($0)
GitHub Actions Automated workflow pinging Supabase to prevent project pausing Free ($0)
Keep-Alive Automation: A GitHub Actions cron workflow (.github/workflows/keep-supabase-alive.yml) pings Supabase periodically to prevent the free tier database from pausing due to inactivity.
Note: Some updated details may be missing because the creator updates the site frequently. This page was last edited on 09.22.2026.