back

How this site works

A map of how every piece of the project connects — hover for details.

edits contentcontent read (cached)user searchesfull-text + semanticreads/writes per permissionimages/videossends messagemessages receivedflag turns on/offlatency/volume
1Request arrivesmiddleware.ts reads the path, detects the locale (the "locale" cookie or Accept-Language via detectLocaleFromAcceptLanguage) and rewrites /pt/sobre -> /sobre with the x-locale header; /admin and /api pass straight through (isPassthrough).
2Theme/animation are already correctapp/layout.tsx reads the "theme" and "reduce-motion" cookies before rendering -- with no cookie, the default is dark and animated. It doesn't depend on client-side JS, so there's no wrong-theme flash.
3Server Component runsThe page (e.g. app/(site)/sobre/page.tsx) is async and fetches data straight on the server, with no extra client-side JS.
4Content comes cachedgetSiteContent()/queries-cached.ts uses Next's unstable_cache -- it doesn't hit Supabase on every request.
5Bilingual text is resolvedresolveText(pt, en, locale) picks the right version of the text based on the detected locale, using the dictionary from lib/i18n/dictionaries.ts.
6Ready-made HTML is sentThe already-rendered page reaches the browser; React hydration only wires up the interactive parts (animations, forms).
1Person typesSearchPanel waits 300ms of no typing (debounce) before firing the search, so it doesn't hit the API on every keystroke.
2POST /api/searchapp/api/search/route.ts receives { query } and first checks the rate limit (countRecentSearchesFromIp, default 20/min).
3Two searches in parallelsearchFullText (Postgres, tsvector) and searchSemantic (embeddings) run at the same time; the semantic one has a 4s timeout so it never blocks the response.
4Reciprocal Rank FusionreciprocalRankFusion (lib/search/rank.ts) merges both result lists into a single ranking, without either method dominating the other.
5Log and responselogSearchRequest records the search (for /status metrics) and the combined result goes back to SearchPanel.
1Every new tableA migration creates the table + an RLS policy + an explicit grant -- RLS alone doesn't unlock Supabase's JS client, it needs the grant too. Main tables: projects, articles, languages, authors, companies, messages, resume, resume_links, contact_links, site_content, search_index, search_requests, mcp_connections (+ project_authors/project_languages as join tables).
2Public readslib/supabase/queries.ts uses the anon key, read-only, filtered by RLS policies (e.g. only visible content).
3Authenticated mutationlib/supabase/admin-queries.ts uses the admin session's authenticated client -- RLS policies require a valid auth.uid().
4Access via MCPlib/supabase/service.ts builds a dedicated client per MCP connection, respecting that specific token's permissions.
1Tries to access /adminmiddleware.ts intercepts it and checks the Supabase Auth session via cookie.
2No session -> /admin/loginshouldRedirectToLogin decides the redirect; with a valid session, the request just goes through.
3Email/password loginapp/admin/login/actions.ts calls supabase.auth.signInWithPassword({ email, password }); the session becomes a cookie via @supabase/ssr, with no separate token to manage on the client.
4Edits in a tabEach tab (Projects, Articles, Technologies, Authors, Companies, Messages, Contact, Customization, Resume, Images, MCP, Preview) calls a function from admin-queries.ts, e.g. upsertProjeto.
5Writes to PostgresThe mutation goes through RLS policies that require an authenticated user.
6Reindexes for searchreindexProject/reindexArticle (lib/supabase/search-index.ts) generate a fresh embedding of the text and write it to the search_index table -- that's what semantic search reads afterward.
7The site's cache needs to updateSince the public reads through a cache (unstable_cache), the mutation revalidates the right tag so the change shows up on the site.
1Folder configured in the admindrive_folder_url in the site content stores which Drive folder is the source of the images.
2Markdown references it by nameA piece of text writes ![photo](file-name.jpg); remark-drive-images resolves that into a real URL.
3Looked up in the Drive APIlib/drive.ts looks for the file by name inside the configured folder -- the Drive token never leaves the server.
4Proxied to the browser/api/drive-image/[fileId] and /api/drive-video/[fileId] serve the file as a proxy, without exposing the Drive API directly to the client.
1Connection created in the adminThe MCP tab generates a new token and sets which permissions that specific connection has.
2AI client calls the APIPOST /api/mcp with the token (Authorization header, or X-Auth-Token for clients that don't allow setting Authorization).
3Token gets validatedlib/mcp/auth.ts checks the token and loads that specific connection's permissions.
4MCP server is assembledlib/mcp/server.ts + registerMcpTools only register the tools/resources that connection has permission to use.
5Tool runs against SupabaseEach tool reads or writes via lib/supabase/service.ts, within what that token was granted. Covers full CRUD per permission: projects, articles, technologies, authors, companies (upsert/delete/list of each); messages (list/mark read/delete); contact and resume links; resume (read/update); and site content (read/set/delete of each key).
1Client-side formcontact-form.tsx validates name/email/message before sending.
2Writes to SupabaseThe message is saved to its own table, no authentication needed (RLS only allows the insert).
3Shows up in the adminThe Messages tab lists the received messages, with an option to mark them as read.
4Also reachable via MCPAn MCP connection with the right permission can read the contact messages as a resource.
1General flag in the admineaster_eggs_ativo turns all of them on/off at once, with no new deploy needed.
2Each one is independentSudoEasterEgg, SpinEasterEgg and Mascote are separate client components, each listening for its own trigger.
3Example: the mascotMascote counts clicks within a short time window; once it hits the right number, it opens the video via /api/drive-video/[fileId].
1Page fetches liveEvery time /status is loaded, the server measures the metrics on the spot -- nothing is pre-computed. It also shows the current deploy and branch, read for real, not hardcoded.
2Database latencyA simple query is timed against Supabase to show the real response time.
3Last republish/reindexTimestamps stored in the site content itself, updated whenever the content changes or search gets reindexed.
drag to move · scroll to zoom
How this site works — Zanoni