The Complete Resource Hub for DSMNRU Students — PYQs, Notes, Syllabus & Tools
Browse 500+ Previous Year Question Papers with instant preview, locked downloads, and verified contributions. Built for speed, SEO, and offline-first.
DSMNRU PYQs only. Not affiliated with DSMNRU University. Papers are contributed by students for educational use.
📖 Table of Contents — Click to expand
- ✨ Why DSMNRU Archive?
- 🌟 Features
- 🆕 What's New
- 🛠️ Tech Stack
- 📂 Project Structure
- 🚀 Quick Start
- ☁️ Cloudflare Worker API (new architecture)
- 🔧 Environment & Firebase Setup
- 📡 Data Model & Collections
- 🔐 Firestore Security Rules
- 📤 Student Upload Flow
- 👨💼 Admin Guide
- 🎨 Styling & Theme
- 📱 PWA & Offline
- 🔍 SEO & Performance
- ⚡ Caching & Quota Saving (50K Reads)
- 🛡️ Auth & Verification
- 🧪 Development Workflow
- 🚨 Common Issues
- 🎯 Roadmap
- 🤝 Contributing
- 👥 Contributors
- 📞 Support
- 📄 License
| Before (Modal-only) | After (Resource Hub) |
|---|---|
| PYQs in a list, preview in modal, no SEO | 1 URL = 1 Paper (paper.html?id=xxx) — Google indexes every paper, breadcrumb, JSON-LD |
| Search loads all docs every keystroke → hits 50K quota | Session + 15m cache → 0 reads on refresh/search, 12 reads for homepage vs N |
| Anon can scrape all PDFs | Login gate for Search / Filters / Load More / Preview — drives sign-ups, saves quota |
| Uploads via temporary hosts | gofile.io + admin review queue, image→PDF auto-merge (≤10 MB) |
| No feedback loop | Report Broken Link / Request PYQ → admin Feedback inbox |
- 🔐 Auth — Email/Password + Google, email verification enforced (blocking overlay, no bypass)
- 👤 Profiles — Name, course, phone, avatar
- 📚 Browse PYQs — 20 free on load, View Details →
paper.html(locked preview + inline same-site viewer forarchive.org/catbox.moe, never jumps out) - 🔍 Search & Filters — Course / Semester / Session + Sort (Newest, Most Viewed, A-Z) — gated, 0 reads when cached
- 📖 Paper Detail Page — Breadcrumb, pills (course/sem/session/branch/views), inline preview, Server 1/2 same-site, Report, Related (6, cache-first), Discussion (comments)
- 📤 Upload — Single PDF or multiple images → auto-merged PDF (jsPDF, 6 quality attempts) →
pendingUploads - 🧰 Student Tools — SGPA Calculator, Study Planner (reminders), Attendance Tracker (per-date)
- 📝 Feedback — Report Broken Link / Request PYQ →
feedbackcollection - 🌗 PWA — Installable, offline cache (
sw.js v5), works onlocalhost+ HTTPS - 💬 Live Chat — Floating chat widget →
realtime-agent
- 🔑 Secure Login — Admin check via
pendingUploadsread permission (no email exposed) - ➕ Quick Create — Course / Sem / Session / Subject / Branch → auto
title→pyqswithfile/file2 - 📥 Bulk CSV Import —
pyqs/contributors(id→ update, else create ) - 📚 Content Library — Card list, local 0-read search (
#adminPyqSearch), Copy Server 1/2, Edit/Delete by doc id (fixes filtered delete bug) - ⏳ Review Queue —
pendingUploadswherestatus=='pending'→ Download / Delete - 👥 Contributors — Add/edit/delete (
PYQs Provideretc.) - 👤 Users — Registered profiles, edit role/course/phone, delete
- 🚩 Feedback Inbox — NEW
Broken Reports & PYQ Requests→ filtersAll / Broken / Requests / New,Mark Resolved/Delete/Clear resolved, realtimenewpulse, counts in hero - 📊 Lazy Admin — 0 reads on login — each section fetches only when expanded (saves 50K quota), hero note: "Expand any card to fetch"
- 🧹 No Analytics — Intentionally removed (non-mandate) to save reads
paper.html?id=xxx— breadcrumb, view count, same-site preview (noarchive.orgjump), related, comments, share, report, JSON-LDfirestore.rules—isVerified()forviewsincrement +comments/feedback/pendingUploadswrites;pyqsread public, write admin-only- Cache —
sessionStorage+localStorage TTL 15m+ Firestore persistence → homepage 12 reads vs N, search 0 reads when cached (signed-in only) - Gates — Search/Filters/Load More/paper preview now require login + verified email (blocking overlay,
staticmodal, re-show if dismissed) - Admin — Feedback inbox, lazy 0-read, local search, id-based delete/edit fix
- Homepage — Simplified to single View Details button → paper page (no Open/Backup/Share/Bookmark clutter), Trending/Recently/Bookmarks tab removed
| Layer | Tech |
|---|---|
| Frontend | HTML5, CSS3 (Manrope + FKGroteskNeue, glassmorphism), vanilla JS, Bootstrap 5.3, Font Awesome 6, SweetAlert2 |
| Backend (public data) | Cloudflare Worker API (worker/) — Firestore REST via service account, Cloudflare Cache API + KV, rate limiting |
| Backend (user-scoped) | Firebase Auth (compat 9.22.1), Firestore, Storage (compat), enablePersistence({synchronizeTabs:true}) |
| PWA | sw.js (cache-first, API bypass), manifest.json, sitemap.xml, robots.txt |
| Tools | jsPDF 2.5.1 (image→PDF), PapaParse 5.4.1 (CSV), Normalize.css |
| Hosting | Netlify (dsmnru-pyq.netlify.app) + Cloudflare Workers (free) + Firebase |
| PDF storage | Archive.org / Catbox (unchanged) |
.
├── index.html # Homepage — search, filters, 20 free PYQs → View Details
├── paper.html # Paper Detail — breadcrumb, preview, related, comments (id=gated)
├── paper.js # Paper logic — cache-first related, verification block, same-site preview
├── script.js # Main — auth, cache (session+15m), gated search, upload, tools, feedback
├── admin.html # Admin — lazy sections, Feedback inbox, local search
├── admin.js # Admin — CRUD, lazy, CSV, Feedback, id-based delete fix
├── styles.css # Dark glass theme, design tokens, responsive
├── sw.js # Service Worker v5 — caches /, /index.html, /paper.html, /styles.css, /script.js, /paper.js
├── manifest.json # PWA manifest (standalone, theme #0f172a)
├── sitemap.xml # Includes /, /index.html, /paper.html, /contributors.html, /tools.html, /links.html
├── firestore.rules # NEW — isVerified() + isAdminByEmail() + pendingUploads/comments/feedback rules
├── cors.json # CORS for local + Netlify
├── courses.json # Course catalog for filters
├── tools.html # SGPA / Attendance / Planner
├── links.html # University portals
├── contributors.html # Contributors grid
├── _redirects # Netlify → Worker /api/* proxy (optional)
├── ARCHITECTURE.md # New Worker architecture, API docs, migration/rollback
├── worker/ # Cloudflare Worker API (free tier)
│ ├── wrangler.toml # Worker config (KV binding, vars)
│ ├── src/ # index.js (routes), firestore.js, cache.js, search.js,
│ │ # auth.js, rateLimit.js, validation.js, cors.js
│ └── test/ # worker.test.js + frontend smoke tests + read simulation
└── img/Logo.png
# 1. Clone
git clone https://github.com/Lav-developer/PYQ.git
cd PYQ
# 2. Run (no build needed)
python -m http.server 8000
# or
npx serve .
# Visit http://localhost:8000
# Admin: http://localhost:8000/admin.htmlPWA install needs HTTPS — works on
localhostand Netlify, not onfile://.
Public data no longer flows directly from Firestore to the browser. All public reads (PYQ list, search, filters, paper detail, contributors, homepage, stats) go through a Cloudflare Worker that serves from the Cloudflare Cache API and Cloudflare KV, with Firestore as the source of truth. This keeps Firestore reads near zero under traffic.
Netlify Frontend → Cloudflare Worker → Edge Cache / KV → Firestore (only on cache miss)
- Worker code:
worker/(wrangler.toml,src/*, tests) - Deployment & secrets:
worker/README.md - Full design, API reference, migration & rollback:
ARCHITECTURE.md - Firestore read simulation (OLD vs NEW):
worker/test/performance-simulation.md - The browser still uses the Firebase SDK only for user-scoped data (auth, profile, comments, feedback, uploads, view increments) and admin writes. PDFs remain on Archive.org / Catbox — untouched.
Run the Worker test suite:
cd worker
npm install
node test/worker.test.js # 69 assertions, mocked FirestoreFrontend smoke tests (need jsdom):
cd worker
npm install --no-save jsdom
node test/frontend-smoke-test.cjs
node test/paper-smoke-test.cjs1. Create Firebase project → Enable Auth (Email/Password + Google) → Firestore (test mode then apply rules below).
2. Copy config into script.js, admin.js, paper.js, admin.html:
const firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_AUTH_DOMAIN",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_STORAGE_BUCKET",
messagingSenderId: "YOUR_SENDER_ID",
appId: "YOUR_APP_ID",
measurementId: "YOUR_MEASUREMENT_ID"
};3. Create collections: pyqs, users, contributors, pendingUploads, feedback, comments, meta (optional).
4. Create admin user in Auth, then in Firestore users/{uid} set role: "admin" and in Rules replace [email protected] with your admin email.
5. Deploy rules:
firebase deploy --only firestore:rules
# or paste firestore.rules in Console → Firestore → Rules → Publishpyqs — title (auto-generated Course Branch Sem Subject {Session}), file (Server1), file2 (Server2), course, semester, session, subject, branch, description, views (int), createdAt (timestamp)
users — uid, email/signupEmail, name/signupName, course/signupCourse, phone, role, emailVerified, createdAt, makeSubscriberSynced etc.
contributors — name, avatar (initials), role (PYQs Provider)
pendingUploads — title, course, semester, studentName, studentCourse, studentEmail, userId, fileName, downloadUrl (gofile), fileSize, uploadedAt, status: 'pending'
feedback — type (broken_link | pyq_request), status: 'new'|'resolved', title/course/details or course/subject/semester/session, email, userId, userEmail, createdAt
comments — paperId, text (3-600), userId, userName, userEmail, createdAt (also supports pyqs/{id}/comments subcollection fallback)
Local Storage: dsmnruStudyPlanner, dsmnruAttendance, dsmnruCgpaLast, profileCompletionDismissed, dsmnru_pyqs_full_v1 + dsmnru_pyqs_full_time_v1 (cache), dsmnru_pyqs_session_v1
See firestore.rules — key ideas:
function isAdminByEmail() { return auth.token.email == "[email protected]"; }
function isVerified() { return auth.token.email_verified == true; }
// pyqs: public read, admin full write, verified can +1 views only
match /pyqs/{doc} {
allow read: if true;
allow create, delete: if isAdminByEmail();
allow update: if isAdminByEmail() || (isVerified() && diff.hasOnly(['views']) && views+1);
}
// comments/feedback/pendingUploads: create requires isVerified()
match /comments/{id} { allow read: true; allow create: if isVerified() && text 3-600 && userId==auth.uid; }
match /feedback/{id} { allow read: if isAdminByEmail(); allow create: if isVerified() && type in [...] && status=='new'; }Deploy after editing [email protected] → your email.
- Fill Help us grow → Your Name, Title, Course/Sem, 1 PDF or N images (≤10 MB final)
- Images →
jsPDF6 attempts (2000px→900px, 0.9→0.58 quality) → single PDF - PDF →
https://api.gofile.io/servers→https://{server}.gofile.io/uploadFile→downloadPageURL - Metadata →
pendingUploads(status: pending) - Admin → Review Queue → Download → verify → Quick Create →
pyqs→ credited
Login: admin.html → admin email → dashboard. Non-admin auto sign-out.
Hero: Lazy — 0 reads on login → expand a card to fetch. Counts update live.
Quick Create: Course / Sem / Session / Subject / Branch → auto-title → Server 1 (+ Server 2) → Add PYQ
Bulk CSV: Target pyqs / contributors → choose .csv (collection,id,title,Server 1,Server 2,course,semester,session) → Import (id → update, else add)
Content Library: Manage PYQs → local 0-read search → Edit (id-based) / Delete (id-based, fixes filtered delete bug) / Copy Server 1/2
Review Queue: Pending pyqs to upload → Copy URL / Download / Delete
Contributors: People → Add (name → auto avatar) / Edit / Delete
Users: Registered profiles → Edit (name/course/phone/role) / Delete
Feedback (NEW): Broken reports & PYQ requests → filters All / Broken / Requests / New, Mark Resolved / Delete / Clear resolved + Refresh (real-time new pulse)
CSV Backup: Floating file-csv button → database-backup.csv — all collections (pyqs, contributors, users, pendingUploads, feedback, comments) with every field, fresh from Firestore (not lazy cache)
Glassmorphism dark theme (styles.css tokens: --color-primary teal, --color-background charcoal, --color-surface card). All pages share Manrope + FKGroteskNeue, backdrop-filter: blur(14px), border-radius: 18-24px. Edit tokens in :root for theming.
sw.js v5caches/,/index.html,/paper.html,/styles.css,/script.js,/paper.js,courses.json,manifest.jsonmanifest.json→standalone,theme #0f172a, emoji icons- Install via Chrome address bar → offline after first load (Firestore persistence + Cache API)
- 1 URL = 1 Paper (
paper.html?id=xxx) withcanonical,og:title/description/url/image,twitter:card,JSON-LD Article(headline, publisher) sitemap.xml(auto includes/paper.html) +robots.txt(Allow/, Disallow/admin.html)paper.jssets title/meta on load for crawler preview- Lighthouse: inline skeletons,
content-visibilityready,loading="lazy"on iframes
Problem (OLD): pyqs.get() from the browser on every search = N reads × searches → hits 50K/day.
Fix (NEW): the browser calls the Cloudflare Worker API instead of reading Firestore. The Worker serves public data from the Cloudflare Cache API (edge, whole responses) and KV (compact search index, per-item docs, contributors, courses, homepage) with Firestore only touched on cache miss.
- Browse/Search/Filters: served from the Worker's KV search index — 0
Firestore reads for every user once the index is warm. The index is
refreshed by admin invalidation (
POST /api/invalidate) — a 7-day hard-TTL acts only as a safety fallback. There is no aggressive short-cycle rebuild. - Paper detail: KV-cached per item (1 h); 1 read only on first-ever view of a paper, then 0 for everyone else
- Contributors: KV-cached (1 h); 1 read per hour
- Homepage (recent/trending/course counts/stats): KV-cached (5 min)
- Admin invalidation:
POST /api/invalidate(headerX-Api-Key) bumps the invalidation timestamp; the next request serves the stale index while a single-flight background rebuild (ctx.waitUntil) runs via the Worker. If the key is not set, admin changes propagate at the next read after the cache hard-TTL (safe fallback). - View increments / comments / feedback / uploads / auth: still direct Firestore (user-scoped writes or ≤30-doc scoped reads — not full collections)
Pagination correctness uses a composite cursor ([primaryFieldValue, __name__]) so duplicate primary orderBy values (e.g. views) never
skip or duplicate rows across pages.
See worker/test/performance-simulation.md
for the read-count table (311 → 10,000 PYQs, 100 users).
isGoogleUser()→ Google considered verifiedrequiresEmailVerification(user) = !isGoogleUser && !emailVerified- Enforced: Search/Filters/Load More/paper preview/comment/upload/report all check
isVerifiedOrPrompt()→ shows blocking overlay + static modal (backdrop:'static', keyboard:false, re-shows if dismissed) withResend/I verified — Check/Use different email(deletes unverified account) - Server:
isVerified()infirestore.rulesfor all writes
# Add feature
# 1. UI → index.html / paper.html / admin.html
# 2. Logic → script.js / paper.js / admin.js (keep id-based, not index)
# 3. Style → styles.css (tokens)
# 4. Test locally + check Firestore rules
# 5. Bump sw.js CACHE_NAME if caching changed- Admin can't delete →
permission-denied→ checkfirestore.rulesadmin email + ensure logged in as admin (pendingUploads read test) - Verification bypassed → ensure
firestore.rulespublished and hard-refresh (old rules cached) - Homepage blank → was bookmark tab
nullerror → fixed in5b38bd3(guarded), hard-refresh - Comment prompt always visible → was
d-flex !important+ duplicatestyle→ fixed in50347a8 - Paper jumps to archive.org → fixed in
d7d63be→ Server 1/2 now load inline on same site - Counts 0 in admin → intentional lazy — expand a section to fetch
- Paper Detail SEO + same-site preview
- Feedback inbox
- Cache + gates
- Lazy admin
- Rating & reviews
- Solution uploads
- Discussion forum
- Mobile app (TWA)
- i18n
PRs welcome! For large features, open an issue first.
git checkout -b feat/my-feature
# ... make changes, test locally
git commit -m "feat: my feature"
git push origin feat/my-feature
# Open PR to mainPlease keep id-based edits/deletes and 0-read local searches.
See /contributors.html and the contributors collection. Thank you to all student contributors! Add yourself via Help us grow → admin will credit you.
- 💬 Live Chat — floating chat button →
realtime-agent - 📝 Report / Request —
Request PYQ/Report broken linkmodals (card after PYQs + paper page) → admin Feedback - 📧 Email — via profile / feedback
emailfield
MIT — for DSMNRU students and contributors.
DSMNRU PYQs only • Not affiliated with DSMNRU University