Skip to content
abusayed0206Public

About

View basic information about CUET Students

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Repository files navigation

CUET Students Directory

An independent index of Chittagong University of Engineering & Technology students: search by name or ID, browse every batch and department, or use the public JSON API.

Site https://cuet.sayed.app · API docs https://cuet.sayed.app/api

Stack

  • Astro 7 (server-rendered) on Cloudflare Workers via @astrojs/cloudflare
  • Cloudflare D1 (CUET_students): the single source of truth for student data; no data files live in this repo
  • Tailwind CSS 4, almost no client JavaScript
  • Edge cache per deploy (src/middleware.ts), API rate limit via the Workers rate-limiting binding

Project layout

src/
  lib/departments.ts   department table, keyed by the ID's department digits
  lib/db.ts            all D1 queries
  lib/api.ts           JSON helpers, rate limiting, API response shapes
  pages/               routes (pages + /api endpoints)
  components/, layouts/
migrations/            D1 schema and data fixes (data imports are gitignored)
scripts/csv-to-sql.mjs CSV → D1 migration for importing a new batch
scripts/refresh-derived.sql  rebuilds class_counts / name_words (see below)

D1 bills every row a query reads, and students has ~8.6K rows (a full scan counts as ~17K reads), so no request path may scan it. Counts come from class_counts (one row per class) and name search goes through the indexed name_words table. After changing students by hand, run npm run db:refresh (imports made with csv-to-sql do it already).

Student IDs

IDs are YY DD RRR: batch, department code, class roll. The site and API take the department from DD, not from the department text column. Code 12 shows as MSE for batches ≤ 22 and MME after that.

Routes

Page URL
Home /
Search /search?q=…&batch=25&dept=cse (a full ID redirects to the profile)
Batches /batch, /batch/25, /batch/25/cse
Departments /department, /department/cse
Student /2504001
API docs /api

Old URLs (/batch?department=…&batch=…, /search?name=…, sitemap_N.xml) redirect.

API

Read-only, no key, CORS *, 60 requests/minute per IP, responses cached up to 24 h.

Endpoint Returns
GET /api/v1/students/{id} one student
GET /api/v1/search?q=&batch=&dept=&limit= name search, or ID prefix if q is digits
GET /api/v1/batches batches with per-department counts
GET /api/v1/batches/{batch}/{dept} full class list
GET /api/v1/departments departments with their batches

The legacy /api/student/{id} and /api/student/search?query= endpoints still answer in the old flat format. Full reference with a live console: /api.

Development

Node 22.12+ (.nvmrc pins 24).

npm install
npm run dev       # http://localhost:4321 with a local D1
npm run check     # wrangler types + astro check
npm run preview   # build and run the real Worker locally

The local D1 starts empty. Copy production into it:

npx wrangler d1 export CUET_students --remote --output prod.sql
npx wrangler d1 execute CUET_students --local --file prod.sql

Deployment

Cloudflare Workers Builds watches this repo:

  • push to main → npm run build → npx wrangler deploy (production)
  • push to any other branch (except redirect) → preview version only

npm run deploy deploys manually. Every deploy also clears the edge cache.

Search engines

  • /sitemap.xml is a sitemap index: /sitemap-pages.xml (home, listings, batch/class/department pages) plus one /sitemap-batch-YY.xml per batch with every profile. All entries carry <lastmod> from D1's updated_at.
  • robots.txt allows everything except /search?… (search result pages are also noindex).
  • Pages have canonical URLs, Open Graph tags and JSON-LD (WebSite + search action, Person, BreadcrumbList).
  • Google: the sitemap is submitted in Search Console (property cuet.sayed.app).
  • Bing, Yandex, Seznam, Naver: node scripts/indexnow.mjs pushes every sitemap URL via IndexNow; --since YYYY-MM-DD sends only changed pages. Key file: public/1144afef155385c08e4aca5b16ec8642.txt.

Adding a new batch

  1. Prepare a CSV with headers studentid,name,admission_roll,admission_merit.
  2. node scripts/csv-to-sql.mjs batch26.csv migrations/0006_import_batch_26.sql (use the next free migration number; the script appends scripts/refresh-derived.sql, so class_counts and name_words update in the same migration)
  3. npm run db:migrate (applies new migrations to production D1)
  4. Check the derived tables match; all three numbers must be equal:
    npx wrangler d1 execute CUET_students --remote --command "SELECT (SELECT COUNT(*) FROM students), (SELECT SUM(count) FROM class_counts), (SELECT COUNT(DISTINCT studentid) FROM name_words)"
    If they differ, run npm run db:refresh.
  5. Push to main so the edge cache and the in-memory overview refresh, then node scripts/indexnow.mjs --since <today>.

Import migrations contain personal data and are gitignored; keep the CSV outside the repo.

Editing existing students

Any hand-made change to students (a fix migration, d1 execute, the dashboard) must be followed by npm run db:refresh, or counts and search go stale:

  • Added/removed students or changed batch/session: db:refresh is enough.
  • Renamed a student: the refresh only indexes students it hasn't seen, so delete their old words first: DELETE FROM name_words WHERE studentid = '2504001'; then db:refresh.
  • Deleted a student: also DELETE FROM name_words WHERE studentid = ... (stale rows are harmless for results, but waste reads).

Don't rebuild name_words from scratch (DELETE FROM name_words + refresh): that is ~50K row writes, half the free daily limit.

D1 budget (free plan)

D1 bills every row a query reads: 5M/day, then all queries fail until 00:00 UTC. A query without an index on its WHERE reads all ~8.6K students (plus index rows). Before adding a query, check its plan in wrangler d1 execute --local with EXPLAIN QUERY PLAN: it must say SEARCH ... USING INDEX, never SCAN students. Usage per database: dashboard → D1 → CUET_students → Metrics.

Avoid node scripts/indexnow.mjs without --since: it makes crawlers fetch every profile at once (that, plus full-table scans, caused the Nov 2026 outage).

Backups

D1 keeps 30 days of point-in-time history (Time Travel). For an offline copy:

npx wrangler d1 export CUET_students --remote --output cuet-backup.sql

Branches

  • main: this site
  • redirect: the cuet.pages.dev Pages project, which redirects to cuet.sayed.app
  • archive/* tags: the old Next.js versions

Data and terms

Data was compiled from publicly available sources and may contain errors. Personal use is free; commercial use or integration into another system requires acknowledgment. See privacy and terms. Contact: hello@sayed.page.

Built by Sayed.

About

View basic information about CUET Students

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Used by

Contributors

Languages