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 · 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 |
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.
| 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 |
| 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/ 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.
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.
- Keep an Anime or Donghua entry Active.
- Add any streaming/site link in Watch URL. Chronicle opens it but never scrapes it.
- 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.
# 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
# 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-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 buildAfter 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.pyNo CI workflow is tracked in this checkout. Run 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 |
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.
After upgrading an existing database to the duplicate-protected media model, run:
npm run media:dedupe:backfillThe command fills normalized title keys for existing media rows where safe. Existing duplicate groups are reported and left for manual review.
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| 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 |
MIT © VortexDevX