wekan/docs/Security/PerUserDataAudit2025-12-23/QUICK_REFERENCE.md
2025-12-23 08:01:30 +02:00

339 lines
8 KiB
Markdown

# Wekan Persistence Improvements - Quick Reference
## What Was Changed?
### ❌ Removed
- Board-level `collapsed` field from Swimlanes
- Board-level `collapsed` field from Lists
- REST API endpoint for updating list `collapsed` status
- `collapse()` mutation from Swimlanes
### ✅ Added
- Per-user position history with undo/redo
- LocalStorage validation and cleanup
- SwimlaneId validation migration
- Checkpoint/savepoint system for position history
- Enhanced data validation for all UI preferences
---
## How It Works
### Per-User Settings (Your Preferences)
These are NOW per-user and persisted:
- ✅ Swimlane collapse state
- ✅ List collapse state
- ✅ List width
- ✅ Swimlane height
**Where it's stored:**
- Logged-in users: `user.profile`
- Non-logged-in users: Browser localStorage
- Validated & cleaned automatically
### Position History (Card Movements)
Every time you move a card:
- Automatically tracked in `userPositionHistory` collection
- Stored with previous and new position
- Can be undone with `Meteor.call('userPositionHistory.undo', historyId)`
- Checkpoints can be created with `Meteor.call('userPositionHistory.createCheckpoint', boardId, name)`
### Data Validation
All UI preference data is validated:
- List widths: 100-1000 pixels
- Swimlane heights: -1 (auto) or 50-2000 pixels
- Corrupted data: automatically removed
- Invalid data: rejected on write
---
## For Users
### What Changed?
- Your collapse preferences are now **private to you** (not shared with others)
- They persist across page reloads
- They work even if not logged in (saved in browser)
- Invalid data is automatically cleaned up
### What You Can Do (Coming Soon)
- Undo/redo card movements
- Create savepoints of board state
- Restore to previous savepoints
- Use Ctrl+Z to undo
---
## For Developers
### New Collections
**UserPositionHistory**
```javascript
{
userId: String,
boardId: String,
entityType: 'card' | 'list' | 'swimlane' | 'checklist' | 'checklistItem',
entityId: String,
actionType: 'move' | 'create' | 'delete',
previousState: Object,
newState: Object,
isCheckpoint: Boolean,
checkpointName: String,
createdAt: Date
}
```
### New Meteor Methods
```javascript
// Create a checkpoint
Meteor.call('userPositionHistory.createCheckpoint', boardId, 'name');
// Undo a change
Meteor.call('userPositionHistory.undo', historyId);
// Get recent history
Meteor.call('userPositionHistory.getRecent', boardId, 50, (err, result) => {
// result is array of history entries
});
// Get checkpoints
Meteor.call('userPositionHistory.getCheckpoints', boardId, (err, checkpoints) => {
// result is array of checkpoints
});
// Restore to checkpoint
Meteor.call('userPositionHistory.restoreToCheckpoint', checkpointId);
```
### Updated Models
**cards.js**
- `move()` now automatically tracks changes
- Uses `UserPositionHistory.trackChange()`
**swimlanes.js**
- `collapsed` field removed (use profile.collapsedSwimlanes)
- `collapse()` mutation removed
**lists.js**
- `collapsed` field removed (use profile.collapsedLists)
- Removed from REST API
**users.js**
- Enhanced `getListWidthFromStorage()` with validation
- Enhanced `setSwimlaneHeightToStorage()` with validation
- Added automatic cleanup of invalid data
### New Files
```
client/lib/localStorageValidator.js
- validateAndCleanLocalStorage()
- shouldRunCleanup()
- getValidatedLocalStorageData()
- setValidatedLocalStorageData()
- validators object with all validation functions
models/lib/userStorageHelpers.js
- getValidatedNumber()
- setValidatedNumber()
- getValidatedBoolean()
- setValidatedBoolean()
models/userPositionHistory.js
- UserPositionHistory collection
- Helpers: getDescription(), canUndo(), undo()
- Meteor methods for interaction
server/migrations/ensureValidSwimlaneIds.js
- Runs automatically on startup
- Fixes cards/lists without swimlaneId
- Rescues orphaned data
```
---
## Migration Details
### Automatic Migration: ensureValidSwimlaneIds
Runs on server startup:
1. **Finds cards without swimlaneId**
- Assigns them to default swimlane
2. **Finds orphaned cards**
- SwimlaneId points to deleted swimlane
- Moves them to "Rescued Data" swimlane
3. **Adds validation hooks**
- Prevents swimlaneId removal
- Auto-assigns on card creation
**Tracking:**
```javascript
Migrations.findOne({ name: 'ensure-valid-swimlane-ids' })
// Shows results of migration
```
---
## Data Examples
### Before (Broken)
```javascript
// Swimlane with board-level collapse
{
_id: 'swim123',
title: 'Development',
collapsed: true // ❌ Shared with all users!
}
// Card without swimlaneId
{
_id: 'card456',
title: 'Fix bug',
swimlaneId: undefined // ❌ No swimlane!
}
```
### After (Fixed)
```javascript
// Swimlane - no collapsed field
{
_id: 'swim123',
title: 'Development',
// collapsed: removed ✅
}
// User's profile - has per-user settings
{
_id: 'user789',
profile: {
collapsedSwimlanes: {
'board123': {
'swim123': true // ✅ Per-user!
}
},
listWidths: {
'board123': {
'list456': 300 // ✅ Per-user!
}
}
}
}
// Card with swimlaneId
{
_id: 'card456',
title: 'Fix bug',
swimlaneId: 'swim123' // ✅ Always set!
}
// Position history entry
{
_id: 'hist789',
userId: 'user789',
boardId: 'board123',
entityType: 'card',
entityId: 'card456',
actionType: 'move',
previousState: { swimlaneId: 'swim123', listId: 'list456', sort: 1 },
newState: { swimlaneId: 'swim123', listId: 'list789', sort: 2 },
createdAt: ISODate('2025-12-23T07:00:00Z')
}
```
---
## Troubleshooting
### Q: My collapse state isn't persisting
**A:** Make sure you're using the new per-user settings methods:
```javascript
user.setCollapsedSwimlane(boardId, swimlaneId, true);
user.getCollapsedSwimlaneFromStorage(boardId, swimlaneId);
```
### Q: I see "Rescued Data" swimlane with orphaned cards
**A:** Migration found cards pointing to deleted swimlanes. They're safe in the rescue swimlane. You can move them to proper swimlanes.
### Q: localStorage is being cleared
**A:** That's intentional - we only keep valid data. Invalid/corrupted data is removed automatically during daily cleanup.
### Q: How do I create a checkpoint?
**A:** Use the Meteor method:
```javascript
Meteor.call('userPositionHistory.createCheckpoint', boardId, 'Before big changes');
```
### Q: How do I undo a card move?
**A:** Use the Meteor method:
```javascript
Meteor.call('userPositionHistory.undo', historyEntryId);
```
---
## Performance Notes
### Storage
- localStorage: Max 50 boards, max 100 items per board
- UserPositionHistory: Max 1000 entries per user per board
- Auto-cleanup: Runs daily
### Queries
- Limited to 100 results per query
- Indexed by userId, boardId, createdAt
- Fast checkpoint retrieval
### Validation
- Runs on startup (once per day)
- Only validates if needed
- Removes excess data automatically
---
## What's Next?
### Coming Soon
- [ ] Undo/redo buttons in UI
- [ ] History sidebar
- [ ] Keyboard shortcuts (Ctrl+Z)
- [ ] Checkpoint UI
### Future
- [ ] Field-level history (description, comments)
- [ ] Search across historical values
- [ ] Visual timeline
- [ ] Collaborative undo
---
## Files to Know
| File | Purpose |
|------|---------|
| [models/userPositionHistory.js](models/userPositionHistory.js) | Position history collection |
| [client/lib/localStorageValidator.js](client/lib/localStorageValidator.js) | Data validation |
| [server/migrations/ensureValidSwimlaneIds.js](server/migrations/ensureValidSwimlaneIds.js) | Automatic migration |
| [models/swimlanes.js](models/swimlanes.js) | Swimlane model |
| [models/lists.js](models/lists.js) | List model |
| [models/cards.js](models/cards.js) | Card model with tracking |
---
## Questions?
See detailed documentation:
- [ARCHITECTURE_IMPROVEMENTS.md](ARCHITECTURE_IMPROVEMENTS.md) - Complete guide
- [PERSISTENCE_AUDIT.md](PERSISTENCE_AUDIT.md) - System audit
- [IMPLEMENTATION_SUMMARY.md](IMPLEMENTATION_SUMMARY.md) - Implementation details
- [FIXES_CHECKLIST.md](FIXES_CHECKLIST.md) - What was fixed
---
**Status**: ✅ Ready for use
**Last Updated**: 2025-12-23