Skip to content

Repository files navigation

Chronicle

A sleek, self-hosted media tracker for Anime, Manhwa, Donghua, and Light Novels — built with Next.js App Router, React, Zustand, MongoDB, and serverless API routes.

chronicle-preview license vercel

✨ Features

Category Highlights
Command Center Home Dynamic focus hero banner · Real-time "Today" orientation metrics · "Next Up" scheduled release cards · Weekly rhythm progress heatmap
Media Tracking Add, edit, delete entries · Track progress (episodes/chapters) · Quick +1 increment · One-click catchup · Ratings (0–10) · Notes
3D Curated Shelves 3D fanned poster stack gallery cards with depth animations · Quick library entry manager modal · Media type drilldowns · Custom shelf tags
Modern Navigation Context-aware TopBar with dynamic route badges & search shortcut (⌘K//) · Collapsible sectioned Sidebar · Clean 5-slot mobile dock
Updates Inbox Dedicated unread tracker updates feed · Bulk "Mark all caught up" actions · Tracker error diagnostics panel with inline retry controls
Release Radar Automated Anime/Donghua release schedules powered by SIMKL calendar · Live release countdowns · Interactive manual match dialog
Statistics Analytics dashboard with type/status breakdowns, ratings, progress totals, and recent activity
Import / Export Full library export as JSON · Bulk import from JSON to easily restore or migrate libraries
Metadata & Covers AniList primary + Jikan fallback lookup for Anime/Donghua · MangaDex IDs for Manhwa · Cached proxy cover pipeline with custom URL overrides
Droppedyard Dedicated "Graveyard" for dropped entries with a "Maybe Revisit" queue to filter out shows you might want to try again
Auth & Security JWT cookie auth · bcrypt password hashing · Brevo email recovery/verification · Session invalidation after password reset
Design & Experience Pure dark geometric aesthetic · Responsive mobile-first layouts · Accessible badging · Skeleton loading states · PWA install support

🔎 Public SEO Page

Chronicle keeps account data private while exposing one concise, server-rendered product page to search engines and prospective users:

Route Search intent
/ Combined anime, manhwa, donghua, and novel tracker

The page summarizes all four formats without separate tracker landing pages. Technical discovery files are generated at /robots.txt, /sitemap.xml, /manifest.webmanifest, and /llms.txt. Private dashboard, login, and password-reset routes emit noindex metadata. The optional browser smoke command below verifies the public metadata, crawler files, private-route noindex, mobile overflow, and browser errors.

🛠️ Tech Stack

Layer Technology
Frontend Next.js App Router · React · Zustand · CSS
Backend Next.js API Routes · Next.js Proxy middleware
Database MongoDB (Atlas or local) via Mongoose
Auth JWT httpOnly cookies · bcryptjs · hashed one-time reset tokens
Email Brevo Transactional API
Scraping Cheerio · SIMKL calendar data · fetch retry/timeout helpers · host-specific rules
APIs AniList GraphQL · Jikan v4 · MangaDex · Telegram Bot API
Testing Vitest · TypeScript · ESLint

Android app

android-app/ contains a native Android shell that reuses this deployment and UI. It adds a hardened WebView, persistent account session, native offline handling, and Firebase push alerts generated by the existing chapter-check cron. It does not require another application server.

Because Android uses the responsive Chronicle UI, Cron history and MangaDex URL normalization work in both the browser and Android app without a second mobile UI.

See ANDROID_APP.md for APK builds, Firebase setup, and release notes.

Episode schedules and Release Radar

Release Radar uses the shared SIMKL calendar for Anime and Donghua. It finds each title through its stable AniList ID, not by guessing from a translated title. Calendar data is refreshed automatically; no source links, test buttons, or setup fields needed.

  1. Keep an Anime or Donghua entry Active.
  2. Add any streaming/site link in Watch URL. Chronicle opens it but never scrapes it.
  3. Open Release Radar. It shows announced episodes across the current and next three calendar months in your local time.

Release Radar never changes progress_current. Manhwa tracker checks remain separate.

🚀 Getting Started

Prerequisites

Setup

# Clone
git clone https://github.com/VortexDevX/Chronicle.git
cd Chronicle

# Install dependencies
npm install

# Configure environment
cp .env.example .env.local
# Edit .env.local with your MongoDB URI, JWT secret, etc.

Environment Variables

Create a .env.local file:

# Required
MONGODB_URI=mongodb://localhost:27017/chronicle
JWT_SECRET=your-32-char-secret-here

# App origins (comma-separated). First origin is used in password reset emails.
APP_ORIGIN=http://localhost:3000,https://chroniclex.vercel.app

# Optional public URL fallback for metadata when no request host is available.
NEXT_PUBLIC_APP_URL=https://chroniclex.vercel.app

# Password reset email
BREVO_API_KEY=your-brevo-api-key
BREVO_FROM_EMAIL=no-reply@your-domain.com
BREVO_FROM_NAME=Chronicle

# Cron protection (required in production)
CRON_SECRET=your-cron-secret

# Optional cron tuning (default 4, max 8)
CRON_CHECK_CONCURRENCY=4

# Keep response below cron-job.org's 30-second request limit (default 24000, max 25000)
CRON_TIME_BUDGET_MS=24000

# Preserve full tracker retry behavior (default 2, max 2)
CRON_SCRAPE_RETRIES=2

# Telegram notifications
TELEGRAM_BOT_TOKEN=your-telegram-bot-token
TELEGRAM_CHAT_ID=your-telegram-chat-id

# Optional rate limiting
UPSTASH_REDIS_REST_URL=https://your-redis.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-upstash-token

Generate a JWT secret:

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

Run Locally

# Start the Next.js development server
npm run dev

Access the application at http://localhost:3000.

Quality Checks

npm run typecheck    # TypeScript type checking
npm run lint         # ESLint checks
npm run test         # Run Vitest suite
npm run build        # Production Next.js build

After a production build is running on port 3100, the optional browser SEO smoke test checks public titles, canonicals, JSON-LD, private-route noindex, sitemap coverage, crawler rules, mobile overflow, and browser errors:

npm run start -- -p 3100
python scripts/verifySeoUi.py

No CI workflow is tracked in this checkout. Run the checks above before pushing or opening a pull request.

Production Notes

  • Set CRON_SECRET in production so /api/cron/checkChapters rejects unauthenticated requests.
  • Tune CRON_CHECK_CONCURRENCY if tracker notifications arrive late. Default is 4; values above 8 are capped to avoid hammering tracker sites.
  • Cron runs stop taking new tracker work before their HTTP deadline. A 200 response with partial: true and deferred > 0 is healthy: deferred entries rotate by last_attempted_at and are retried on the next run.
  • Keep CRON_TIME_BUDGET_MS below the scheduler timeout. Chronicle clamps it to 1000025000 ms and reserves the final eight seconds for Android push and Telegram delivery.
  • CRON_SCRAPE_RETRIES=2 preserves full retry behavior. If the HTTP budget ends first, unfinished attempts rotate safely and continue on a later run.
  • Configure APP_ORIGIN to the exact deployed origins that may call the API.
  • Upstash Redis rate limiting is optional for local/single-instance installs, but recommended for serverless or horizontally scaled production. Without it, rate limits use process-local memory.
  • package.json pins postcss through overrides so transitive tooling uses the patched 8.5.x line consistently.

cron-job.org Chapter Check

Vercel Cron is not configured for this project. Create one job in the cron-job.org console with these settings:

Setting Value
Title Chronicle 4-hour release check
URL https://chroniclex.vercel.app/api/cron/checkChapters
Enabled Yes
Schedule Every 4 hours, at minute 0 (00:00, 04:00, 08:00, 12:00, 16:00, 20:00)
Time zone Asia/Kolkata
Request method GET
Request body Empty
HTTP authentication Disabled
Custom header name Authorization
Custom header value Bearer <CRON_SECRET>
Save response Disabled after setup
Failure notification Enabled

If https://chroniclex.vercel.app is the active deployment, use https://chroniclex.vercel.app/api/cron/checkChapters instead. Replace <CRON_SECRET> with the same production secret configured in the deployment; never commit the real value. Run one manual test from the cron-job.org dashboard and confirm an HTTP 200 JSON response before enabling the four-hour schedule. cron-job.org stops requests after 30 seconds. Chronicle therefore uses a bounded 24-second run, aborts in-flight scrapes when its scan budget ends, returns partial progress instead of timing out, and retries deferred trackers on the next run.

Maintenance

After upgrading an existing database to the duplicate-protected media model, run:

npm run media:dedupe:backfill

The command fills normalized title keys for existing media rows where safe. Existing duplicate groups are reported and left for manual review.

📁 Project Structure

Chronicle/
├── app/
│   ├── (dashboard)/        # Main app (Home, Library, Updates, Release Radar, Shelves, Droppedyard, Analytics, Cron History)
│   ├── api/                # Auth, Media, Profile, Covers, Cron, Tracker test routes
│   ├── login/              # Login, registration, and forgot-password entry
│   ├── reset-password/     # Password reset page
│   ├── globals.css         # Global CSS variables, resets, and utility classes
│   ├── robots.ts           # Search and AI crawler policy
│   ├── sitemap.ts          # Public canonical URL inventory
│   └── layout.tsx          # Root metadata and Next.js layout
├── components/             # Reusable UI components (Sidebar, TopBar, MediaCard, Modals)
├── hooks/                  # Custom React hooks (e.g., useAuth)
├── lib/                    # Shared utilities (DB, Auth, HTTP, Rate Limiting, Models, source adapters)
├── store/                  # Zustand state management and Cover caching
├── types/                  # TypeScript interface definitions
├── proxy.ts                # API CORS proxy using APP_ORIGIN allowlist
├── public/                 # Static assets (Favicon, etc.)
└── scripts/                # Local operator utilities, including cron and data checks

📋 API Reference

Method Endpoint Auth Description
POST /api/auth No Login, register, or logout
GET /api/auth Yes Current session
POST /api/auth/forgot-password No Send Brevo password reset email
POST /api/auth/reset-password No Reset password with one-time token
POST /api/auth/verify-email Yes Send email verification link
GET /api/auth/verify-email No Verify email token and redirect to login
GET /api/profile Yes Profile, recovery email, notification settings
PUT /api/profile Yes Update profile settings
GET /api/analytics Yes Aggregated library analytics
GET /api/media Yes List media with search/filter/sort/pagination
POST /api/media Yes Create entry (single or ?bulk=1)
PUT /api/media?id= Yes Update entry
DELETE /api/media?id= Yes Delete entry
POST /api/media?bulk_delete=1 Yes Bulk delete
POST /api/media/link Yes Link or unlink related entries
POST /api/media/test-tracker Yes Test a Manhwa tracker
GET /api/release-radar Yes Upcoming Anime and Donghua episodes from SIMKL
GET /api/cron/checkChapters Bearer CRON_SECRET Sync tracker progress/release schedules and send updates
GET /api/manga-cover No Fetch MangaDex cover URL
GET /api/anime-cover No Fetch AniList/Jikan cover URL
GET /api/image-proxy No Cache/proxy external cover images

📄 License

MIT © VortexDevX

About

A sleek, self-hosted media tracker for Anime, Manhwa, Donghua, and Light Novels - built with Next.js App Router, React, Zustand, MongoDB, and serverless API routes.

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages