## Summary
- **Base64 cover image uploads broken for books, shows, manga, and music** — a premature `validateStringLength` check ran before the data URL detection, rejecting all base64 images with a 2,048-char URL limit error. Also fixed the size calculation to extract only the base64 portion after the comma (matching the correct pattern already in `game.service.ts`).
- **Audit log flooded with expected 401s on `/api/auth/me`** — these occur during normal token refresh flow and are not genuine security events. Excluded this URL from the global 401/403 audit log handler.
- **ChunkLoadError spam after deployments** — when Angular lazy-loaded chunks are missing (stale cache after a redeploy), the global error handler now detects `ChunkLoadError` and silently reloads the page instead of logging the error and sending it to the API/Discord.
## Test plan
- [ ] Upload a base64 cover image for a book, show, manga, and music item — should succeed
- [ ] Verify `/api/auth/me` 401s no longer appear in the audit log
- [ ] Deploy a new build and confirm stale-chunk users are silently reloaded
✨ This PR was created with help from Hikari~ 🌸
Reviewed-on: #69
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
This PR includes multiple feature additions and fixes to improve the library application:
### 🎨 Base64 Image Upload Support
- Fixed Fastify body limit (1MB → 10MB) to accommodate base64-encoded images
- Corrected base64 size calculation and validation logic
- Improved error handling with proper 400 status codes and helpful messages
- Removed duplicate validation that was blocking uploads
- Users can now upload cover images up to 5MB (decoded size)
### 📝 Reusable Form Components
- Created 6 form components: `GameForm`, `BookForm`, `MusicForm`, `ShowForm`, `MangaForm`, `ArtForm`
- All forms support both 'add' and 'edit' modes with pre-population
- Integrated inline editing into all detail views (edit/delete buttons)
- Enhanced admin suggestions workflow with full forms instead of basic modals
- Added scroll-to-top when clicking edit in list views for better UX
### 🖼️ Default Cover Image
- Added beautiful library reading image as default cover for all media types
- Fixed static asset serving to use correct MIME types
- Updated all 12 components (6 list views + 6 detail views) to always show images
### 🔒 Tiered Rate Limiting
- Unauthenticated users: 100 requests/minute
- Authenticated users: 500 requests/minute (5x more lenient)
- Admin users: No rate limits (complete bypass via allowList)
### 🎮 Discord Integration
- Auto-assign library member role to users in NHCarrigan Discord server
- Checks server membership on every login
- Only assigns role if user is in server and doesn't have it yet
- Graceful error handling without blocking login
- Similar pattern to badge refresh flow
### 📚 Documentation
- Added comprehensive CLAUDE.md with:
- Project structure and tech stack
- Development workflow and commands
- Database schema documentation
- Authentication flow details
- Security features
- Code style conventions
- Common gotchas and solutions
## Test Plan
- [x] Base64 image uploads work for cover images up to 5MB
- [x] Helpful error messages appear for validation failures
- [x] Edit/delete buttons appear on all detail views for admin users
- [x] Inline edit forms display and save correctly
- [x] Admin suggestions workflow uses full forms for all media types
- [x] Scroll-to-top works when editing from list views
- [x] Default cover image displays when no cover is provided
- [x] Static assets serve with correct MIME types
- [x] Rate limiting works correctly for different user types
- [x] Discord role assignment works on login
- [x] All builds pass without errors
- [x] No TypeScript errors
## Related Issues
Closes#65 - Base64 image upload issue
✨ This pull request was created with help from Hikari~ 🌸
Reviewed-on: #66
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
### Explanation
_No response_
### Issue
_No response_
### Attestations
- [ ] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)
- [ ] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
- [ ] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/).
### Dependencies
- [ ] I have pinned the dependencies to a specific patch version.
### Style
- [ ] I have run the linter and resolved any errors.
- [ ] My pull request uses an appropriate title, matching the conventional commit standards.
- [ ] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request.
### Tests
- [ ] My contribution adds new code, and I have added tests to cover it.
- [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes.
- [ ] All new and existing tests pass locally with my changes.
- [ ] Code coverage remains at or above the configured threshold.
### Documentation
_No response_
### Versioning
_No response_
Reviewed-on: #64
## Summary
This PR implements comprehensive user profile enhancements including:
- User profile pages showing stats, badges, social links, and bio
- Achievement system with 62 achievements across 5 categories
- Primary badge selection allowing users to display their preferred badge
- Admin profile editing capabilities
## Changes
### User Profiles (#45)
- **Frontend**: User profile pages with stats display
- Profile cards showing avatar, display name, username, and bio
- Social links section (Website, GitHub, Bluesky, LinkedIn, Twitch, YouTube, Discord)
- Stats display (suggestions, accepted suggestions, likes, comments)
- Recent achievements section
- Badge display
- Report button for other users' profiles
- **Backend**: Profile API endpoints
- Get user profile by username or ID
- Profile includes stats, badges, and achievement points
### Achievement System (#48)
- **Database**: UserAchievement model for tracking progress
- **62 Total Achievements** across 5 categories:
- **Suggestions (15)**: First suggestion through ultimate curator
- **Likes (12)**: First like through legendary fan
- **Comments (12)**: First comment through review legend
- **Engagement (15)**: Login streaks and activity milestones
- **Reports (8)**: Valid reports and accuracy tracking
- **Backend**: AchievementService with real-time checking
- Integrated into all user interaction points
- API endpoints for achievement data
- Progress tracking to avoid recalculation
- **Frontend**: Achievements page and profile integration
- Full achievements page with category filtering
- Tier-based styling (Bronze, Silver, Gold, Platinum, Diamond)
- Progress indicators for in-progress achievements
- Recent achievements on profile pages
### Primary Badge System (#49)
- **Database**: Add primaryBadge field to User model
- **Backend**: Update profile endpoints to include primary badge
- **Frontend**: Primary badge selection in settings
- Only shows badges the user has earned
- Displayed on profile page
- Displayed in comments (next to username)
- Falls back to no badge if selection is invalid
- **Admin Features**: Admin can edit any user's primary badge
### Admin Enhancements
- Comprehensive profile editing modal for admins
- Edit display name, bio, slug, social links
- Set primary badge for users
- Visual feedback for save/error states
- Admin action buttons in report review modals
- Ban user, delete comment, edit profile
- Integrated with report workflow
### Quality Improvements
- Improved dropdown option contrast for readability
- Hide all badges when no primary badge is selected
- "View All" achievements link only shown on own profile
- Improved achievement text readability
## Testing
- ✅ User profiles display correctly with stats and badges
- ✅ Achievement checking works for all interaction types
- ✅ Primary badge selection persists and displays correctly
- ✅ Admin profile editing saves successfully
- ✅ Report workflow integrated with admin actions
- ✅ Achievements page shows all 62 achievements with filtering
- ✅ Text readability improved across components
Closes#45Closes#48Closes#49
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Reviewed-on: #58
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
## Summary
This PR implements several improvements to the library application:
- Added start and finish date tracking for media items
- Added "Retired" category for abandoned media
- Implemented avatar-based user menu with dropdown navigation
- Added automatic background token refresh to prevent session expiry
- Created centralised logging system with frontend-to-API log forwarding
- Added toast notifications for error handling
## Changes
### Media Tracking (#41)
- Added `dateStarted` and `dateFinished` fields to Books, Games, Manga, Music, and Shows
- Updated TypeScript types, Prisma schema, and API services
- Added manual date input fields to frontend forms
- Properly converts HTML date strings to Date objects before API submission
### Retired Category (#43)
- Added `RETIRED` status to all media type enums
- Updated Prisma schema, frontend dropdowns, and filter buttons
- Added status label handling for retired items
### User Menu (#46)
- Replaced username text with avatar image in header
- Created dropdown menu with navigation items (Users, Audit, Suggestions)
- Added logout button to menu
- Implemented keyboard accessibility (tabindex, role, keyup handlers)
### Token Refresh (#44)
- Implemented automatic token refresh every 13 minutes in background
- Added proactive refresh to prevent token expiry during form filling
- Prevents users from losing form data due to expired sessions
### Centralised Logging (#1)
- Created `/log` endpoint on API to receive frontend logs
- Replaced API console.log calls with @nhcarrigan/logger
- Created ConsoleLoggerService to intercept all console methods on frontend
- Added global error handlers (window.error, unhandledrejection) on frontend
- Added process error handlers (uncaughtException, unhandledRejection, SIGTERM, SIGINT) on API
- All frontend console activity now forwarded to centralised logging
### Error Handling
- Created ToastService and ToastComponent for displaying errors
- Integrated with GlobalErrorHandler and HTTP interceptor
- Added accessibility features (keyboard navigation, ARIA attributes)
- Set toast opacity to 40% for optimal readability
### Testing & Build
- Fixed pre-existing test failure for GET / route (now returns version info)
- Added ESM module mocking (jsdom, marked, dompurify, @nhcarrigan/logger)
- Configured Jest with isolatedModules to handle TypeScript errors
- Excluded test-setup.ts from production build
- All tests passing (123 total)
- Build passing with no errors
## Test Plan
- [x] All tests pass (123 tests)
- [x] Build passes without errors
- [x] Lint passes (only pre-existing warnings)
- [x] Date fields work correctly on all media types
- [x] Retired status displays and filters properly
- [x] Avatar menu opens/closes correctly with keyboard and mouse
- [x] Token refresh prevents session expiry
- [x] Toast notifications appear for errors
- [x] Frontend logs forward to API successfully
- [x] Root route returns version information
Closes#41Closes#43Closes#44Closes#46Closes#1🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Reviewed-on: #50
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>