Most used items
Never checked out
Recent activity
Scan an item
Click in the field (or just start scanning) — USB/Bluetooth scanners type the barcode and press Enter automatically.
Check out
Scan or enter a barcode, then fill in the borrower.
Select multiple available items. Condition carries over from each item's last check-in.
Check in
Scan or enter the barcode of a checked-out item.
Select multiple checked-out items to return at once.
Inventory
Repair queue
Items checked in with a "Needs repair" condition land here automatically.
Barcode labels
Code 39 labels, generated in your browser. Use print to send them to a label printer.
Activity log
Replace soon
Items at or past their end-of-life (purchase date + lifespan, default 10 years), or due within 90 days.
Reservations
Reserve gear for an upcoming date. You'll be notified if it's already booked.
My reservations
Kits
Pre-built gear bundles. Tap one to check out everything available in it.
Checked out
Everything currently out, grouped by borrower.
Settings
Documentation
Documentation
What this is
Asset management for Missouri Baptist University Special Events – Production. Track equipment with barcode asset tags: add items, print Code 39 labels, scan to check items in and out, manage reservations and kits, and keep a full custody history.
How it's built
The entire app — backend API and frontend — runs as a single Cloudflare Worker (free tier).
A build script (build-single.js) inlines the frontend files (index.html, styles.css,
app.js, sw.js) into the Worker bundle as string constants, so one file serves everything.
No build framework, no npm dependencies at runtime.
Why this stack
- Cloudflare Workers (free tier) — no server to maintain, no hosting cost, automatic HTTPS, global edge deployment. The free tier is more than enough for department-scale use.
- Cloudflare D1 (SQLite) — serverless relational database. SQLite means the data model is simple, portable, and queryable with standard SQL. D1 handles backups and replication.
- Vanilla JS + CSS, no framework — the app is a single-page CRUD interface. A framework would add build complexity and bundle size for no real benefit. Vanilla JS keeps it fast and debuggable.
- Service Worker + IndexedDB for offline — the app shell is cached so it loads with no signal; transactions queue in IndexedDB and sync when connectivity returns. Critical for event venues with spotty wifi.
- Resend for email — password resets and welcome emails via a simple HTTP API. No SMTP server to run.
- Code 39 barcodes — the simplest barcode symbology that encodes alphanumerics. Readable by cheap USB scanners and phone cameras alike. JsBarcode (CDN) draws labels client-side; html5-qrcode (CDN) decodes from phone cameras with all processing on-device.
Data model
items— id, name, barcode (unique), category, location, notes, condition, status (available/checked_out/in_repair), current_borrower, due_date, purchase_date, warranty_info, serial_number, brand, lifespan_years, timestamps.transactions— append-only custody log: item_id, type (checkout/checkin), borrower_name, condition, notes, due_date, timestamp. Nothing is ever updated or deleted.users— id, name, email (unique), password_hash (SHA-256), last_login_at, timestamps. Individual team accounts; passwords are never stored in plain text.kits/kit_items— named equipment bundles (e.g. "Chapel PA Kit") with their member items, for one-tap checkout of a whole set.reservations— item_id, reserver name, start/end dates, notes, status. Prevents double-booking.barcode_pool— pre-printed tag numbers an admin can import; new items auto-consume the next available number.reset_tokens— single-use password reset tokens (SHA-256 hashed), 7-day expiry, tied to a user and purpose.settings— key/value store: admin code hash, Resend API key, from-email, rate-limit state.
Authentication
- Team members sign in with individual email + password accounts (admin creates them; temp password
123456). - New users get a welcome email with a 7-day password-reset link. "Forgot password?" sends the same flow.
- Admins sign in with a separate admin code (changeable in Settings; reset via recovery email).
- Sessions are signed, HttpOnly cookies (HMAC-SHA256 with
SESSION_SECRET). "Remember me" = 90 days; unchecked = 24 hours. Logout clears the session. - Borrower identity on checkout is locked server-side to the signed-in user — it can't be spoofed from the client.
- Login rate-limiting: 5 failures trigger a 60-second lockout.
Key workflows
- Scan → Check out: Scan a tag (USB scanner, Bluetooth scanner, or phone camera), confirm the item, set a due date, check out. Borrower is automatically the signed-in user.
- Mass checkout: Scan or add multiple items, check them all out at once to one borrower.
- Check in: Scan, pick condition. "Needs repair" auto-routes the item to the Repair queue.
- Reservations: Reserve items for future dates. Checkout warns if the item is reserved for overlapping dates.
- Kits: Admin builds named bundles; team checks out the whole kit in one tap.
- Labels: Generate printable Code 39 labels for any item.
- Offline: The app shell works with no signal; checkouts queue locally and sync on reconnect.
Version history
All times Central. Deployed to mbu-asset-management.mbuspecialevents.workers.dev.
- 2026-10-07 ~3:38 PM — Initial launch. D1 database, Worker, session auth, barcode inventory, check in/out.
- 2026-10-07 ~3:47 PM — Phone-camera barcode scanning (Code 39) on the Scan tab.
- 2026-10-07 ~4:08 PM — Check Out / Check In pages, Repair queue, Replace (end-of-life) tab, item lifecycle fields (condition, purchase date, warranty, serial, brand, lifespan). Migration 001.
- 2026-10-07 ~4:15 PM — Camera on mass checkout, bulk add by quantity, mass check-in mode.
- 2026-10-07 ~4:30 PM — CSV import/export.
- 2026-10-07 ~4:40 PM — Admin code change flow, password-reset via email (Resend). Migration 002.
- 2026-10-07 ~4:54 PM — "Special Events – Production" branding.
- 2026-10-07 ~6:18 PM — Reservations, equipment kits, individual user accounts, due dates + overdue alerts, offline mode, barcode pool. Migration 003.
- 2026-10-07 ~6:34 PM — Email/password login replaces PINs; borrower identity locked to signed-in user. Migration 004.
- 2026-10-07 ~6:56 PM — Admin reservations calendar, remember-me (90d / 24h).
- 2026-10-07 ~7:23 PM — MBU logo on login, navy theme matched to logo.
- 2026-10-07 ~7:56 PM — Real HTML login form (browser password managers work), team tab reorder.
- 2026-10-07 ~7:59 PM — Last-login tracking per user (Admin → Users). Migration 005.
- 2026-10-07 ~8:20 PM — Checked Out tab (who-has-what), reservation conflict warnings, bulk edit, Activity CSV export, utilization stats, inline item notes, PIN-era cleanup.
- 2026-10-07 ~8:34 PM — Login input font normalized (was oversized PIN-style).
- 2026-10-07 ~8:40 PM — Automatic stale-cache detection: frontend compares build version with server and force-refreshes on mismatch.
- 2026-10-07 ~8:45 PM — Docs moved from Settings to its own admin tab with Guide / Version History sub-tabs.
- 2026-10-07 ~9:05 PM — Fixed password-reset screens rendering as unstyled blocks at the top of the page: added full-screen centered styles matching the login gate, and gate functions now hide reset/forgot/sent screens.
- 2026-10-07 ~9:10 PM — Admin/team view switcher: "Team view" button in the admin header jumps to the team page without re-login; "Admin settings" button in the team header jumps back. Checkouts/reservations made while viewing as admin ask for a name once per session.
- 2026-10-07 ~9:15 PM — Camera scanning for the barcode pool: "📷 Scan tags" in Admin → Settings → Barcode pool scans pre-printed tags straight into the pool back-to-back (repeats ignored, duplicates skipped).
- 2026-10-07 ~10:05 PM — Camera button on the Add/Edit item form: scan a tag to fill the barcode field, then enter the item's info and save — the item goes straight into inventory.
- 2026-10-07 ~10:40 PM — "📷 Scan tags to add items" reworked: scan a stack of tags, tap "Enter item info", and one popup collects the shared item details plus a quantity — each scanned tag becomes its own inventory item. Unused tags are stashed in the pool.
- 2026-10-07 ~10:50 PM — Admin can set the borrower name per checkout/reservation in Team view: the "Checking out as" field is editable (single + mass checkout), the reservation form has a "Reserved for" field, and kit checkout prompts with the last name as default. Regular team users stay locked to their own name (server-enforced). Also fixed: the admin's acting name is now actually sent to the server on checkout.
- 2026-10-07 ~11:00 PM — Stay signed in across reloads: the app now resumes your session on boot (via
/api/me), so a deploy refresh no longer drops you to the login screen. Also, creating an item now marks its barcode as used in the pool. - 2026-10-08 ~7:00 AM — Polish pass: installable app icons (home-screen icon, favicon, Apple touch icon), toast notifications replacing all native alert popups, and loading spinners on checkout/check-in/save buttons.
- 2026-10-08 ~7:10 AM — Team portal reorganized into 5 main tabs: Scan, Checkout (Check Out / Check In / Checked Out / Kits), Inventory (Browse / Labels), Reservations, Maintenance (Repair / Replace).
- 2026-10-08 ~11:30 AM — Header fix: Team view / Admin settings buttons now group with Sign out on the right instead of floating mid-bar.
- 2026-10-08 ~6:35 PM — The app moved to its own domain: mbuspecialevents.com now serves the app, and the old workers.dev address permanently redirects there.
- 2026-10-08 ~6:45 PM — Fix: welcome-email reset links now use the app's actual domain instead of the old workers.dev address (the redirect was stripping the reset token, dumping new users on the login page).
- 2026-10-08 ~7:00 PM — Two improvements: the login page now says when the email/password or admin code is wrong (with attempts left before lockout), and item barcodes are optional — add assets now, tag them later via Edit.
- 2026-10-08 ~7:05 PM — Fix: reset links always use the canonical mbuspecialevents.com domain (the previous fix built the link from the sender's open tab, so resending from the old URL still produced a broken link).
Limits & notes
- Runs on Cloudflare's free tier — plenty for department-scale use.
- Deleting an item also deletes its transaction history.
- Welcome/reset email links expire after 7 days.
- New users start with temp password
123456and must reset via email.
Users
Team members sign in with their email and password. They'll get a welcome email to set their password (temp: 123456). Their name is locked on checkouts.
Kits
Build gear bundles the team can check out in one tap.