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.
| Category | Highlights |
|---|---|
| Command Center Home | Dynamic focus hero banner with quick log · Real-time "Today" orientation metrics (unread, continue, rhythm) · Activity feed · Rhythm heatmap |
| Media Tracking | Add, edit, single delete & bulk delete · Track progress (chapters/episodes) · Quick +1 log · One-click catchup · Ratings (0–10) · Notes |
| Tracker Scraping | Automated cron checks for Manhwa and Donghua · Direct Cheerio scraping with Cloudflare reader fallback proxy · Zero external calendar dependencies |
| Updates Inbox | Dedicated unread updates feed with exact chapter/episode deltas · Bulk "Mark all caught up" action · Detailed tracker error diagnostics |
| 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 |
| Statistics | Analytics dashboard with type/status breakdowns, ratings, progress totals, and recent progress activity timeline |
| Import / Export | Full library export as JSON · Bulk JSON import to easily backup, restore, or migrate libraries |
| Discovery & Covers | Omnisearch discovery via AniList and MangaDex · AniList & MyAnimeList cover art · Cached cover proxy with custom URL overrides |
| Droppedyard | Dedicated "Graveyard" for dropped entries with a "Maybe Revisit" queue to filter out titles you might want to try again |
| Notifications | Android push notifications (Firebase FCM) & Telegram bot alerts delivered when new chapters or episodes are detected |
| Auth & Security | JWT cookie auth · bcrypt password hashing · Branded HTML account verification & password reset emails via Resend |
| Design & Experience | Pure dark geometric aesthetic · Responsive mobile-first layouts · Accessible badging · Skeleton loading states · PWA install support |
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.
| 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 verification tokens |
| Resend Transactional API | |
| Scraper Engine | Cheerio · fetch retry/timeout helpers · Cloudflare proxy fallback |
| Metadata APIs | AniList GraphQL (discovery/covers) · MangaDex · MyAnimeList (fallback) |
| Notifications | Firebase Cloud Messaging (Android) · Telegram Bot API |
| Testing | Vitest · TypeScript · ESLint |
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 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.
Chronicle relies strictly on direct source scraping for update checks — no external release calendar dumps, third-party schedules, or artificial season offsets:
- Supported Formats:
- Manhwa: Scrapes aggregator and scanlation sources (Asura, FlameComics, MangaDex, ManhuaFast, and generic chapter selectors).
- Donghua: Scrapes supported video aggregators (such as AnimeXin) and generic episode lists.
- Anime & Light Novels: Purely user-managed in your library with manual logging and watch/read bookmarks.
- Cloudflare Resilience:
- Direct HTTP scraping is attempted first with modern browser headers.
- If a target site blocks serverless datacenter IPs with HTTP 403 / Cloudflare challenges, Chronicle automatically routes the request through a reader proxy to extract the latest chapter or episode number without failing.
- Safe Progress Contracts:
- Chronicle updates
latest_remote_progressand leavesprogress_currententirely under user control. It never auto-advances your personal reading position. - Fractional chapters (e.g.
10.5) are fully preserved.
- Chronicle updates
- Bounded Cron Budget:
- The cron endpoint
/api/cron/checkChaptersoperates within a bounded time budget (default 24s) to fit within standard serverless timeouts. Unfinished scans rotate safely and defer to the next run without data loss.
- The cron endpoint
# 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.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
# Optional MyAnimeList API client ID for anime cover fallback when AniList is unavailable.
MAL_CLIENT_ID=your-myanimelist-client-id
# Transactional emails (verification & password reset)
RESEND_API_KEY=your-resend-api-key
SENDER_EMAIL=noreply@your-domain.com
SENDER_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 (recommended for serverless)
UPSTASH_REDIS_REST_URL=https://your-redis.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-upstash-tokenGenerate a JWT secret:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"# Start the Next.js development server
npm run devAccess the application at http://localhost:3000.
npm run typecheck # TypeScript type checking
npm run lint # ESLint checks
npm run test # Run Vitest suite
npm run build # Production Next.js buildRun the checks above before pushing or opening a pull request.
- Set
CRON_SECRETin production so/api/cron/checkChaptersrejects unauthenticated requests. - Tune
CRON_CHECK_CONCURRENCYif tracker notifications arrive late. Default is4; values above8are capped to avoid hammering tracker sites. - Cron runs stop taking new tracker work before their HTTP deadline. A
200response withpartial: trueanddeferred > 0is healthy: deferred entries rotate bylast_attempted_atand are retried on the next run. - Keep
CRON_TIME_BUDGET_MSbelow the scheduler timeout. Chronicle clamps it to10000–25000ms and reserves the final eight seconds for Android push and Telegram delivery. CRON_SCRAPE_RETRIES=2preserves full retry behavior. If the HTTP budget ends first, unfinished attempts rotate safely and continue on a later run.- Configure
APP_ORIGINto 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.jsonpinspostcssthroughoverridesso transitive tooling uses the patched 8.5.x line consistently.
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 |
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 tracker requests (Manhwa and Donghua) when its scan budget ends, returns partial
progress instead of timing out, and retries deferred trackers on the next run.
Chronicle/
├── app/
│ ├── (dashboard)/ # Main app (Home, Library, Updates, Shelves, Droppedyard, Analytics, Cron History)
│ ├── api/ # Auth, Media, Profile, Covers, Cron, Tracker test routes
│ ├── login/ # Login, registration, and email verification
│ ├── reset-password/ # Password reset page
│ ├── globals.css # Global CSS variables, design tokens, 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, icons, etc.)
└── scripts/ # Local operator utilities, including cron and data checks| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/auth |
No | Login, register (sends verification email), or logout |
| GET | /api/auth |
Yes | Current session |
| POST | /api/auth/forgot-password |
No | Send Resend password reset email |
| POST | /api/auth/reset-password |
No | Reset password with one-time token |
| POST | /api/auth/verify-email |
Yes | Resend account verification link |
| GET | /api/auth/verify-email |
No | Verify email token and redirect to dashboard |
| 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 media entries |
| POST | /api/media/link |
Yes | Link or unlink related entries |
| POST | /api/media/test-tracker |
Yes | Test a Manhwa or Donghua tracker URL |
| GET | /api/search/unified |
Yes | Omnisearch discovery for Anime, Donghua, and Manhwa |
| GET | /api/cron/checkChapters |
Bearer CRON_SECRET |
Sync tracker progress and deliver push/Telegram notifications |
| GET | /api/cron/history |
Yes | Inspect recent cron check runs and delivery status |
| GET | /api/shelves |
Yes | Fetch user shelves and custom collections |
| POST | /api/shelves |
Yes | Create or update shelves |
| GET | /api/manga-cover |
No | Fetch MangaDex cover URL |
| GET | /api/anime-cover |
No | Fetch Anime/Donghua covers from AniList (with MAL fallback) |
| GET | /api/image-proxy |
No | Cache/proxy external cover images |
MIT © VortexDevX