Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/santiagodc8/tu_perfil.net/llms.txt

Use this file to discover all available pages before exploring further.

TuPerfil.net is a monorepo Next.js 14 application with two distinct surfaces — a public news portal and a protected admin dashboard — backed by a single Supabase project.

Two main surfaces

Public site

Server-rendered with Next.js App Router. Readers browse articles, categories, and search results without logging in. All data is fetched from Supabase on the server, so pages are SEO-friendly and fast.

Admin panel

Protected by Next.js middleware. Every request to /admin/* (except /admin/login) is intercepted and checked for an active Supabase session before the page renders.

Technology layers

LayerTechnologyRole
FrameworkNext.js 14 (App Router, TypeScript)Routing, SSR, API routes
DatabaseSupabase PostgreSQLAll persistent data
AuthSupabase AuthSession management, JWT
StorageSupabase StorageArticle images (article-images bucket)
StylingTailwind CSS + Typography pluginUI and article body rendering
Rich textTipTapArticle content editor
EmailResendNewsletter delivery
Datesdate-fns (Spanish locale)Date formatting across the UI
DeploymentVercelHosting and CI/CD

Folder structure

src/
├── app/
│   ├── (public)/          # Public-facing routes
│   │   ├── page.tsx       # Home page
│   │   ├── [category]/    # Category listing
│   │   ├── noticia/[slug] # Article detail
│   │   ├── buscar/        # Search
│   │   ├── contacto/      # Contact form
│   │   └── acerca-de/     # Static about page
│   ├── admin/
│   │   ├── login/         # Login page
│   │   └── (dashboard)/   # Protected dashboard
│   │       ├── page.tsx   # Dashboard home
│   │       ├── noticias/  # Article CRUD
│   │       ├── categorias/# Category CRUD
│   │       └── mensajes/  # Contact messages
│   ├── api/               # Route handlers
│   ├── sitemap.ts
│   └── robots.ts
├── components/
│   ├── public/            # Header, Footer, ArticleCard, HeroCarousel, etc.
│   └── admin/             # Sidebar, ArticleForm, TipTap editor, etc.
├── lib/
│   ├── supabase/          # Supabase clients (client, server, admin, middleware)
│   ├── auth.ts            # getCurrentUserRole, getCurrentProfile
│   └── utils.ts           # Slug generation, date formatting
└── types/
    └── index.ts           # TypeScript interfaces

How server components fetch data

The public site uses React Server Components by default. Each page imports the server-side Supabase client from src/lib/supabase/server.ts and queries the database directly — no intermediate API call is needed for read operations:
src/lib/supabase/server.ts
import { createServerClient, type CookieOptions } from "@supabase/ssr";
import { cookies } from "next/headers";

export function createClient() {
  const cookieStore = cookies();

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return cookieStore.getAll();
        },
        setAll(cookiesToSet: { name: string; value: string; options: CookieOptions }[]) {
          try {
            cookiesToSet.forEach(({ name, value, options }) =>
              cookieStore.set(name, value, options)
            );
          } catch {
            // Safe to ignore: setAll called from a Server Component.
          }
        },
      },
    }
  );
}
Because this client runs on the server, it reads session cookies automatically and respects RLS policies based on the authenticated user’s role. Client components that need Supabase (for example, the article editor) use the browser client from src/lib/supabase/client.ts instead:
src/lib/supabase/client.ts
"use client";

import { createBrowserClient } from "@supabase/ssr";

export function createClient() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  );
}

How middleware protects /admin routes

The file src/middleware.ts applies to every request matching /admin/:path*:
src/middleware.ts
import { type NextRequest } from "next/server";
import { updateSession } from "@/lib/supabase/middleware";

export async function middleware(request: NextRequest) {
  return await updateSession(request);
}

export const config = {
  matcher: ["/admin/:path*"],
};
updateSession (in src/lib/supabase/middleware.ts) creates a short-lived Supabase client, refreshes the session cookie if needed, and applies two redirect rules:
  • Unauthenticated user → /admin/*: redirect to /admin/login.
  • Authenticated user → /admin/login: redirect to /admin (already logged in).
See the Authentication page for the full middleware implementation.

API route handlers

Write operations from the public site (and some admin actions) go through Next.js API routes under src/app/api/. These routes use the server-side Supabase client or the admin client (which uses the SUPABASE_SERVICE_ROLE_KEY and bypasses RLS):
src/lib/supabase/admin.ts
import { createClient } from "@supabase/supabase-js";

export function createAdminClient() {
  return createClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.SUPABASE_SERVICE_ROLE_KEY!,
    {
      auth: {
        autoRefreshToken: false,
        persistSession: false,
      },
    }
  );
}
Use createAdminClient() only in server-side code (API routes or Server Actions). It bypasses RLS and should never be exposed to the browser.
Key API route areas include:
Route prefixPurpose
/api/viewsIncrement article view counts and record page-view events
/api/contactAccept and store contact form submissions
/api/newsletterSubscribe or unsubscribe email addresses
/api/commentsSubmit reader comments (pending moderation)
/api/adsRecord ad impression and click events

Row Level Security

Every table in the database has RLS enabled. Supabase enforces these policies at the PostgreSQL level — even if application code has a bug, unauthenticated users cannot read unpublished articles, access contact messages, or modify any data they are not allowed to touch. The general pattern across all tables:
ActionWho can perform it
Read published contentAnonymous and authenticated users
Read unpublished / sensitive dataAuthenticated users only
Insert, update, deleteAuthenticated users only
See Database for per-table RLS policies and Authentication for how sessions and roles interact with RLS.

Build docs developers (and LLMs) love