v2.1.0 — Real-time collaboration, performance & security overhaul
Real-Time Collaboration (WebSocket): - WebSocket server with JWT auth and trip-based rooms - Live sync for all CRUD operations (places, assignments, days, notes, budget, packing, reservations, files) - Socket-based exclusion to prevent duplicate updates - Auto-reconnect with exponential backoff - Assignment move sync between days Performance: - 16 database indexes on all foreign key columns - N+1 query fix in places, assignments and days endpoints - Marker clustering (react-leaflet-cluster) with configurable radius - List virtualization (react-window) for places sidebar - useMemo for filtered places - SQLite WAL mode + busy_timeout for concurrent writes - Weather API: server-side cache (1h forecast, 15min current) + client sessionStorage - Google Places photos: persisted to DB after first fetch - Google Details: 3-tier cache (memory → sessionStorage → API) Security: - CORS auto-configuration (production: same-origin, dev: open) - API keys removed from /auth/me response - Admin-only endpoint for reading API keys - Path traversal prevention in cover image deletion - JWT secret persisted to file (survives restarts) - Avatar upload file extension whitelist - API key fallback: normal users use admin's key without exposure - Case-insensitive email login Dark Mode: - Fixed hardcoded colors across PackingList, Budget, ReservationModal, ReservationsPanel - Mobile map buttons and sidebar sheets respect dark mode - Cluster markers always dark UI/UX: - Redesigned login page with animated planes, stars and feature cards - Admin: create user functionality with CustomSelect - Mobile: day-picker popup for assigning places to days - Mobile: touch-friendly reorder buttons (32px targets) - Mobile: responsive text (shorter labels on small screens) - Packing list: index-based category colors - i18n: translated date picker placeholder, fixed German labels - Default map tile: CartoDB Light
This commit is contained in:
@@ -21,6 +21,7 @@ function initDb() {
|
||||
|
||||
_db = new DatabaseSync(dbPath);
|
||||
_db.exec('PRAGMA journal_mode = WAL');
|
||||
_db.exec('PRAGMA busy_timeout = 5000');
|
||||
_db.exec('PRAGMA foreign_keys = ON');
|
||||
|
||||
// Create all tables
|
||||
@@ -212,6 +213,26 @@ function initDb() {
|
||||
);
|
||||
`);
|
||||
|
||||
// Create indexes for performance
|
||||
_db.exec(`
|
||||
CREATE INDEX IF NOT EXISTS idx_places_trip_id ON places(trip_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_places_category_id ON places(category_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_days_trip_id ON days(trip_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_day_assignments_day_id ON day_assignments(day_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_day_assignments_place_id ON day_assignments(place_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_place_tags_place_id ON place_tags(place_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_place_tags_tag_id ON place_tags(tag_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_trip_members_trip_id ON trip_members(trip_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_trip_members_user_id ON trip_members(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_packing_items_trip_id ON packing_items(trip_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_budget_items_trip_id ON budget_items(trip_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_reservations_trip_id ON reservations(trip_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_trip_files_trip_id ON trip_files(trip_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_day_notes_day_id ON day_notes(day_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_photos_trip_id ON photos(trip_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
||||
`);
|
||||
|
||||
// Migrations
|
||||
const migrations = [
|
||||
`ALTER TABLE users ADD COLUMN unsplash_api_key TEXT`,
|
||||
|
||||
Reference in New Issue
Block a user