Skip to main content

Ristek Link Frontend - Documentation

πŸ”Ž Background

Overview​

The Ristek.Link Frontend is the web application for the RISTEK URL shortening service. It provides a public-facing interface for creating custom short URLs (single and bulk via Excel), generating customizable QR codes, viewing click analytics with charts, and managing user accounts with email/password or Google OAuth authentication. The application also handles short link resolution by rewriting incoming short URLs to the backend redirect API.

Stakeholders​

  • Users: RISTEK members, general public (anonymous shortening)
  • Upstream Dependencies: Ristek.Link Backend API, Google OAuth API, AWS S3 (bulk Excel downloads), Mixpanel (product analytics)
  • Downstream Consumers: None (this is the end-user client application)

Scope & Boundaries​

  • In Scope: URL shortening UI (single and bulk), QR code generation with color/logo customization, click analytics dashboard (visitor, unique visitor, retention charts), user authentication (email/password + Google OAuth), password reset flow, "My URLs" management drawer, short link redirect resolution, maintenance mode, Terms of Service page.
  • Out of Scope: Backend API logic, custom subdomain management (superuser-only backend feature), DNS configuration, mobile applications.

βš™οΈ Architecture & Design

System Diagram​

Architecture Decisions (ADRs)​

  • Language/Framework: JavaScript / Next.js 12.1.6 (Pages Router) / React 18.1.0
  • State Management: React Context API (AuthContext, StateManagementContext, DrawerContext, BackdropContext)
  • Styling: Tailwind CSS 3.0.24 + Chakra UI 2.0.2 + Emotion + styled-components (enabled via Next.js compiler)
  • HTTP Client: Axios for all API calls
  • Authentication: JWT stored in httpOnly cookie (UserAuthToken), verified server-side via Next.js API routes
  • Charts: Chart.js 4.5.0 + react-chartjs-2
  • QR Generation: @cheprasov/qrcode (SVG) + html-to-image (PNG export)
  • Icons: Font Awesome (React) + custom SVG icons
  • Product Analytics: Mixpanel (browser + server)
  • Error Monitoring: Sentry (@sentry/nextjs)
  • Ads Integration: @ristek-kit/ads (RISTEK ad platform)
  • Deployment: Vercel via GitHub Actions
  • Short Link Resolution: Next.js rewrites (/:shortLink* β†’ /api/:shortLink*) handled by a catch-all API route

πŸ’»Technical Specifications

API Documentation​

The frontend uses Next.js API routes (pages/api/) as a BFF (Backend-for-Frontend) proxy layer. Client-side code calls internal /api/* endpoints, which then forward requests to the Ristek.Link Backend with proper authentication headers. Backend base URL resolution (config/apiTarget.js):

EnvironmentURL
productionAPI_URL env var
OtherDEV_API_URL env var

Internal API Routes (pages/api/)​

EndpointMethodBackend Proxy TargetDescription
/api/authGETβ€”Validate JWT from UserAuthToken cookie, return token or status
/api/authPOSTPOST /auth/loginEmail/password login, sets httpOnly cookie
/api/googlePOSTPOST /auth/googleGoogle OAuth login, sets httpOnly cookie
/api/logoutPOSTβ€”Clears UserAuthToken cookie
/api/registerPOSTPOST /auth/registerUser registration
/api/resetPasswordPOSTPOST /auth/reset-passwordReset password with token
/api/shortenPOSTPOST /shortenCreate short URL
/api/shortenPUTPUT /shortenEdit existing short URL (auth required)
/api/urlsGETGET /shortenList user's URLs (auth required)
/api/analyticGETGET /analyticsGet analytics for a short URL (auth required)
/api/generateQRPATCHPATCH /shorten/generate-qrGenerate custom QR code
/api/bulkShortenerPOSTPOST /shorten/bulkBulk URL shortening from Excel
/api/[...shortLink]GETPOST /shorten/redirectResolve short link β†’ redirect to target URL
Direct backend call (not proxied):
POST /auth/forgot-password​

Called directly from pages/login/forgot.js.

Pages & Routes​

RouteFileAuth RequiredDescription
/pages/index.jsNoLanding page with URL shortener, QR generator, analytics (state-driven)
/loginpages/login/index.jsNoLogin (email/password + Google OAuth)
/login/forgotpages/login/forgot.jsNoForgot password (sends reset email)
/login/resetpages/login/reset.jsNoReset password (with JWT token from email)
/registerpages/register/index.jsNoUser registration
/bulk-shortenerpages/bulk-shortener/index.jsNoBulk URL shortening with Excel upload
/terms-of-servicepages/terms-of-service.jsNoTerms of Service (13 content sections)
/maintenancepages/maintenance.jsNoMaintenance mode page (shown when MAINTENANCE_MODE=true)
/404pages/404.jsNoCustom 404 page
/:shortLink*Rewrite β†’ /api/[...shortLink]NoShort link resolution and redirect

State Management (React Context)​

ContextFileStatePurpose
AuthContextcontext/AuthContext/AuthContext.jsuser, loggingIn, authStatusAuthentication state, login/logout/authenticate functions
StateManagementContextcontext/StateManagementContext/StateManagementContext.jsstate, dataLink, analyticState, rangeState, file, bulkShortenerState, processingLanding page view state (shortener/QR/analytics), active link data, chart range
DrawerContextcontext/DrawerContext/DrawerContext.jsdrawerOpened, urls, loadingUrls, featureDrawerOpened"My URLs" side drawer state, URL list, refresh function
BackdropContextcontext/BackdropContext/BackdropContext.jsbackdropActiveOverlay backdrop visibility
Auth status enum:
AUTHENTICATED (1) β€” valid JWT in cookie
NOT_AUTHENTICATED (2) β€” no token, no prior session
SESSION_EXPIRED (3) β€” had token but it's gone (shows toast)
TOKEN_UNVERIFIED (4) β€” token exists but failed verification (shows toast)

Authentication Flow​

  • Email/Password Login: form submit β†’ POST /api/auth β†’ API route proxies to backend POST /auth/login β†’ sets UserAuthToken as an httpOnly, sameSite strict cookie β†’ AuthContext.login() stores the user in state and localStorage.
  • Google OAuth Login: @react-oauth/google provides token β†’ POST /api/google β†’ API route proxies to backend POST /auth/google β†’ uses the same cookie flow.
  • Session Validation: on page load, AuthContext.authenticate() calls GET /api/auth β†’ API route reads UserAuthToken cookie β†’ verifies JWT with jsonwebtoken using JWT_SECRET β†’ returns token or status code (TOKEN_UNVERIFIED, TOKEN_DOES_NOT_EXIST).
  • Logout: POST /api/logout β†’ clears cookie β†’ clears localStorage while preserving hasOpen and lastBulk.
  • Short Link Device Tracking: DeviceId cookie (UUID) is set on first short-link redirect for unique click analytics.

Short URLs (e.g. ristek.link/my-link) are resolved via:

  • Rewrite: /:shortLink* β†’ /api/:shortLink*
  • Resolver: pages/api/[...shortLink].js calls backend POST /shorten/redirect with the short code and DeviceId cookie.
  • Success: 302 redirect to the target URL.
  • Failure: redirect to the Ristek Link homepage.

Project Structure​

ristek.link-frontend/
β”œβ”€β”€ .github/
β”‚ └── workflows/
β”‚ β”œβ”€β”€ deploy.yml # Production: push to main β†’ Vercel prod
β”‚ └── preview.yml # Staging: push to staging β†’ Vercel preview
β”œβ”€β”€ components/
β”‚ β”œβ”€β”€ common/
β”‚ β”‚ β”œβ”€β”€ Backdrop/Backdrop.js # Overlay backdrop component
β”‚ β”‚ β”œβ”€β”€ Banner/ # Announcement banners
β”‚ β”‚ β”‚ β”œβ”€β”€ Banner.js # Banner UI
β”‚ β”‚ β”‚ └── BannerContainer.js # Banner state management
β”‚ β”‚ β”œβ”€β”€ Button/index.js # Reusable button component
β”‚ β”‚ β”œβ”€β”€ FeatureDrawer/ # Mobile drawer for QR/Analytics
β”‚ β”‚ β”‚ β”œβ”€β”€ FeatureDrawer.js # Drawer container
β”‚ β”‚ β”‚ └── components/ # Drawer sub-components
β”‚ β”‚ β”œβ”€β”€ Icon/ # CopyIcon, PngDownloadIcon, WarningIcon, etc.
β”‚ β”‚ β”œβ”€β”€ Input/Input.js # Reusable input component
β”‚ β”‚ └── URLDrawer/ # "My URLs" side drawer
β”‚ β”‚ β”œβ”€β”€ URLDrawer.js # Drawer container with URL list
β”‚ β”‚ β”œβ”€β”€ URLCard.js # Single URL card (copy, edit, QR, analytics)
β”‚ β”‚ β”œβ”€β”€ OpenDrawerlogo.js # Drawer toggle button
β”‚ β”‚ β”œβ”€β”€ Logos.js # Drawer logo assets
β”‚ β”‚ └── components/ # URLDrawerComponents/ sub-components
β”‚ β”œβ”€β”€ layout/
β”‚ β”‚ β”œβ”€β”€ Layout.js # Page wrapper (Navbar, Footer, Backdrop, FeatureDrawer)
β”‚ β”‚ β”œβ”€β”€ Footer/
β”‚ β”‚ β”‚ β”œβ”€β”€ Footer.js # Site footer
β”‚ β”‚ β”‚ └── SocialContainer.js # Social media links
β”‚ β”‚ └── Navbar/
β”‚ β”‚ β”œβ”€β”€ Navbar.js # Top nav with auth state, login/logout
β”‚ β”‚ └── components/ # Navbar sub-components
β”‚ β”œβ”€β”€ pages/
β”‚ β”‚ β”œβ”€β”€ Analytic/ # Click analytics view
β”‚ β”‚ β”‚ β”œβ”€β”€ Analytic.js # Main analytics component with Chart.js
β”‚ β”‚ β”‚ └── components/ # Chart, stats sub-components
β”‚ β”‚ β”œβ”€β”€ AuthPage/ # Login/register form components
β”‚ β”‚ β”‚ β”œβ”€β”€ FormHeader.js # Auth form header
β”‚ β”‚ β”‚ β”œβ”€β”€ FormFields.js # Auth form fields
β”‚ β”‚ β”‚ └── components/ # Google button, form sub-components
β”‚ β”‚ β”œβ”€β”€ BulkShortener/ # Bulk URL shortening
β”‚ β”‚ β”‚ β”œβ”€β”€ BulkShortener.js # Multi-step bulk flow with Excel upload
β”‚ β”‚ β”‚ └── component/ # Upload, progress, result sub-components
β”‚ β”‚ β”œβ”€β”€ GenerateQR/ # QR code generation
β”‚ β”‚ β”‚ β”œβ”€β”€ GenerateQR.js # QR generation with color picker
β”‚ β”‚ β”‚ β”œβ”€β”€ Preview.js # QR preview
β”‚ β”‚ β”‚ β”œβ”€β”€ QROption.js # QR customization options
β”‚ β”‚ β”‚ └── ... # Download, option sub-components
β”‚ β”‚ β”œβ”€β”€ Landing/ # Main landing page
β”‚ β”‚ β”‚ β”œβ”€β”€ Landing.js # State-driven view (shortener/QR/analytics)
β”‚ β”‚ β”‚ β”œβ”€β”€ Container.js # Page container
β”‚ β”‚ β”‚ β”œβ”€β”€ ShortenerInput.js # Landing shortener input
β”‚ β”‚ β”‚ └── ... # Success modal, input sub-components
β”‚ β”‚ β”œβ”€β”€ TermsOfService/ # Terms of Service
β”‚ β”‚ β”‚ β”œβ”€β”€ TermsOfService.js # ToS wrapper
β”‚ β”‚ β”‚ └── (13 section components) # Individual ToS sections
β”‚ β”‚ └── URLShotener/ # URL shortener feature
β”‚ β”‚ β”œβ”€β”€ URLShortener.js # Shortener container
β”‚ β”‚ β”œβ”€β”€ ShortenerInput.js # Form logic, validation, API call
β”‚ β”‚ └── Logo.js # Shortener branding
β”‚ └── utils/
β”‚ β”œβ”€β”€ useCustomToast.js # Chakra useToast wrapper
β”‚ β”œβ”€β”€ chart.js # Chart.js registration (scales, elements)
β”‚ └── downloadFromS3.js # Download Excel results from S3
β”œβ”€β”€ config/
β”‚ β”œβ”€β”€ apiTarget.js # Backend API URL resolver (prod/dev)
β”‚ └── awsConfig.js # AWS S3 config (bucket, keys)
β”œβ”€β”€ context/
β”‚ β”œβ”€β”€ AuthContext/AuthContext.js # Auth state, login/logout/authenticate
β”‚ β”œβ”€β”€ StateManagementContext/StateManagementContext.js # Landing page view state
β”‚ β”œβ”€β”€ DrawerContext/DrawerContext.js # URL drawer state
β”‚ └── BackdropContext/BackdropContext.js # Backdrop overlay state
β”œβ”€β”€ hooks/
β”‚ β”œβ”€β”€ useWindowSize.js # Window dimension hook
β”‚ β”œβ”€β”€ useMixpanelClient.js # Mixpanel client-side tracking
β”‚ └── useMixpanelServer.js # Mixpanel server-side tracking
β”œβ”€β”€ pages/
β”‚ β”œβ”€β”€ _app.js # App root (providers: Auth, Google OAuth, Chakra, Contexts, Ads, Layout)
β”‚ β”œβ”€β”€ _error.js # Error page with Sentry
β”‚ β”œβ”€β”€ 404.js # Custom 404
β”‚ β”œβ”€β”€ index.js # Landing page
β”‚ β”œβ”€β”€ maintenance.js # Maintenance mode page
β”‚ β”œβ”€β”€ terms-of-service.js # Terms of Service page
β”‚ β”œβ”€β”€ bulk-shortener/index.js # Bulk shortener page
β”‚ β”œβ”€β”€ login/
β”‚ β”‚ β”œβ”€β”€ index.js # Login page
β”‚ β”‚ β”œβ”€β”€ forgot.js # Forgot password
β”‚ β”‚ └── reset.js # Reset password
β”‚ β”œβ”€β”€ register/index.js # Registration page
β”‚ └── api/ # Next.js API routes (BFF proxy)
β”‚ β”œβ”€β”€ auth.js # Auth validation + login proxy
β”‚ β”œβ”€β”€ google.js # Google OAuth proxy
β”‚ β”œβ”€β”€ logout.js # Cookie clear
β”‚ β”œβ”€β”€ register.js # Registration proxy
β”‚ β”œβ”€β”€ resetPassword.js # Password reset proxy
β”‚ β”œβ”€β”€ shorten.js # Create/edit short URL proxy
β”‚ β”œβ”€β”€ urls.js # List user URLs proxy
β”‚ β”œβ”€β”€ analytic.js # Analytics proxy
β”‚ β”œβ”€β”€ generateQR.js # QR generation proxy
β”‚ β”œβ”€β”€ bulkShortener.js # Bulk shorten proxy
β”‚ └── [...shortLink].js # Catch-all short link redirect
β”œβ”€β”€ public/
β”‚ β”œβ”€β”€ images/ # SVG assets (logos, icons, illustrations)
β”‚ └── vercel.svg
β”œβ”€β”€ styles/
β”‚ β”œβ”€β”€ globals.css # Global styles (Poppins font, base resets)
β”‚ └── theme.js # Chakra UI custom theme
β”œβ”€β”€ next.config.js # Next.js config (Sentry, env vars, rewrites, headers)
β”œβ”€β”€ tailwind.config.js # Tailwind config (custom colors, animations, JIT)
β”œβ”€β”€ postcss.config.js # PostCSS (Tailwind + autoprefixer)
β”œβ”€β”€ sentry.client.config.js # Sentry client initialization
β”œβ”€β”€ sentry.server.config.js # Sentry server initialization
β”œβ”€β”€ sentry.properties # Sentry org/project config
β”œβ”€β”€ .eslintrc.json # ESLint (next/core-web-vitals)
β”œβ”€β”€ .env.example # Environment variable template
└── package.json # Dependencies and scripts

Components​

LayerPathsResponsibility
Landing Pagecomponents/pages/Landing/Landing.jsThe main view at /, driven by StateManagementContext.state. Switches between three sub-views: URL Shortener (default), QR Code Generator, and Analytics. Includes a success modal after shortening and integrates with the URL drawer for authenticated users.
URL Shortenercomponents/pages/URLShotener/Two-input form (long URL + custom alias). Validates URL format, checks for duplicate aliases, and calls POST /api/shorten. Supports both anonymous and authenticated shortening. Displays the resulting short link with copy-to-clipboard functionality.
Bulk Shortenercomponents/pages/BulkShortener/BulkShortener.jsMulti-step flow: (1) Excel file upload with template download, (2) processing indicator, (3) results summary (success/fail counts) with downloadable result Excel from S3. Max 100 links per batch.
QR Code Generatorcomponents/pages/GenerateQR/Customizable QR code generation for existing short URLs. Features a color picker (react-colorful), optional RISTEK logo toggle, SVG preview, and PNG/SVG download via html-to-image and downloadjs.
Analytics Dashboardcomponents/pages/Analytic/Bar chart visualization using Chart.js showing visitor and unique visitor data. Supports three time ranges (week, month, six months) with pagination. Displays aggregate stats (total, average, max, min, percentage change).
URL Drawercomponents/common/URLDrawer/Slide-in side panel showing authenticated user's URLs. Each URL card displays click counts, creation date, and provides actions: copy link, edit URL/alias, generate QR, view analytics. Includes search filtering.
Feature Drawercomponents/common/FeatureDrawer/Mobile-optimized bottom drawer for accessing QR generation and analytics features on smaller screens.
Layoutcomponents/layout/Layout.jsPage wrapper providing Navbar, Footer, Backdrop overlay, and the Feature/URL drawers. Applied globally via _app.js.
Navbarcomponents/layout/Navbar/Navbar.jsTop navigation with RISTEK logo, navigation links (Bulk Shortener, Terms of Service), and auth-aware UI (Login/Register buttons or user dropdown with logout).
App Rootpages/_app.jsProvider hierarchy: AuthContextProvider β†’ GoogleOAuthProvider β†’ ChakraProvider β†’ StateManagementContextProvider β†’ BackdropContextProvider β†’ DrawerContextProvider β†’ AdsProvider β†’ Layout. Shows maintenance page when MAINTENANCE_MODE=true.

☁️ Operational Playbook

Infrastructure​

  • Cloud Provider: Vercel
  • Production Deployment: Push to main branch β†’ GitHub Actions (deploy.yml) β†’ Vercel production deploy
  • Staging Deployment: Push to staging branch β†’ GitHub Actions (preview.yml) β†’ Vercel preview deploy
  • Link: https://ristek.link

Environment Variables​

(example)

KeyDescriptionDefault (Dev)Sensitive?
API_URLBackend API URL (production)β€”No
DEV_API_URLBackend API URL (development)β€”No
NODE_ENV_TARGETEnvironment selector (production / other)β€”No
JWT_SECRETJWT verification secret (must match backend)β€”Yes
GOOGLE_CLIENT_IDGoogle OAuth Client IDβ€”Yes
SENTRY_DSNSentry DSN for error trackingβ€”Yes
SENTRY_AUTH_TOKENSentry auth token for source mapsβ€”Yes
NEXT_PUBLIC_AWS_BUCKET_NAMES3 bucket for bulk Excel downloadsβ€”No
NEXT_PUBLIC_AWS_ACCESS_KEY_IDAWS access key ID (client-side)β€”Yes
NEXT_PUBLIC_AWS_ACCESS_KEY_VALUEAWS secret key (client-side)β€”Yes
NEXT_PUBLIC_MIXPANEL_PROJECT_TOKENMixpanel project token for product analyticsβ€”No
MAINTENANCE_MODEEnable maintenance page ("true" / "false")"false"No
WEBDEV_VERCEL_TOKENVercel deploy token (GitHub Actions secret)β€”Yes
ORG_IDVercel organization ID (GitHub Actions secret)β€”Yes
PROJECT_IDVercel project ID (GitHub Actions secret)β€”Yes

πŸ™‹Questions

πŸ—’ List of frequently asked questions or question that need to be answered that is related to this initiative ..