diff --git a/.env.example b/.env.example new file mode 100755 index 0000000..a9f9080 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +PORT=5000 +MONGO_URI=mongodb://localhost:27017/deathwatch diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/.vscode/launch.json b/.vscode/launch.json old mode 100644 new mode 100755 diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/babel.config.js b/babel.config.js old mode 100644 new mode 100755 diff --git a/backend.log b/backend.log new file mode 100644 index 0000000..a797bb8 --- /dev/null +++ b/backend.log @@ -0,0 +1,6 @@ +nohup: ignoring input +(node:2707479) [MONGODB DRIVER] Warning: useNewUrlParser is a deprecated option: useNewUrlParser has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version +(Use `node --trace-warnings ...` to show where the warning was created) +(node:2707479) [MONGODB DRIVER] Warning: useUnifiedTopology is a deprecated option: useUnifiedTopology has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version +Server running on http://localhost:3005 +Connected to MongoDB diff --git a/data/WiredTiger b/data/WiredTiger old mode 100644 new mode 100755 diff --git a/data/WiredTiger.lock b/data/WiredTiger.lock old mode 100644 new mode 100755 diff --git a/data/WiredTiger.turtle b/data/WiredTiger.turtle old mode 100644 new mode 100755 diff --git a/data/WiredTiger.wt b/data/WiredTiger.wt old mode 100644 new mode 100755 diff --git a/data/WiredTigerHS.wt b/data/WiredTigerHS.wt old mode 100644 new mode 100755 diff --git a/data/_mdb_catalog.wt b/data/_mdb_catalog.wt old mode 100644 new mode 100755 diff --git a/data/collection-0--8172258859796687419.wt b/data/collection-0--8172258859796687419.wt old mode 100644 new mode 100755 diff --git a/data/collection-2--8172258859796687419.wt b/data/collection-2--8172258859796687419.wt old mode 100644 new mode 100755 diff --git a/data/collection-4--8172258859796687419.wt b/data/collection-4--8172258859796687419.wt old mode 100644 new mode 100755 diff --git a/data/diagnostic.data/metrics.2025-08-14T21-20-42Z-00000 b/data/diagnostic.data/metrics.2025-08-14T21-20-42Z-00000 old mode 100644 new mode 100755 diff --git a/data/diagnostic.data/metrics.2025-08-14T21-29-23Z-00000 b/data/diagnostic.data/metrics.2025-08-14T21-29-23Z-00000 old mode 100644 new mode 100755 diff --git a/data/index-1--8172258859796687419.wt b/data/index-1--8172258859796687419.wt old mode 100644 new mode 100755 diff --git a/data/index-3--8172258859796687419.wt b/data/index-3--8172258859796687419.wt old mode 100644 new mode 100755 diff --git a/data/index-5--8172258859796687419.wt b/data/index-5--8172258859796687419.wt old mode 100644 new mode 100755 diff --git a/data/index-6--8172258859796687419.wt b/data/index-6--8172258859796687419.wt old mode 100644 new mode 100755 diff --git a/data/mongod.lock b/data/mongod.lock old mode 100644 new mode 100755 diff --git a/data/sizeStorer.wt b/data/sizeStorer.wt old mode 100644 new mode 100755 diff --git a/data/storage.bson b/data/storage.bson old mode 100644 new mode 100755 diff --git a/database/MIGRATION_COMPLETE.md b/database/MIGRATION_COMPLETE.md new file mode 100644 index 0000000..b525d0f --- /dev/null +++ b/database/MIGRATION_COMPLETE.md @@ -0,0 +1,107 @@ +# Database Migration: MongoDB to SQLite + +## ✅ Migration Complete! + +Your Deathwatch roller application has been successfully migrated from MongoDB to SQLite. + +## What Was Done + +1. **Data Export**: All player data was exported from MongoDB to a backup file +2. **SQLite Setup**: Created SQLite database with proper schema +3. **Data Migration**: Transferred all players and their data to SQLite +4. **API Update**: Updated server routes to use SQLite instead of MongoDB + +## Migration Results + +- **Total players migrated**: 3 +- **Players**: christoffer, claes, phillip +- **Data preserved**: All player info, requisition points, inventory data +- **Backup created**: `mongo-backup.json` (safely stores original data) + +## Files Created/Modified + +### New Files: +- `sqlite-db.js` - SQLite database handler +- `routes/playerRoutes-sqlite.js` - Updated API routes for SQLite +- `server-sqlite.js` - SQLite server configuration +- `migrate-to-sqlite.js` - Migration script +- `switch-db.sh` - Database switching utility +- `mongo-backup.json` - Complete backup of MongoDB data +- `sqlite/deathwatch.db` - SQLite database file + +### Modified Files: +- `server.js` - Now uses SQLite (MongoDB version backed up as `server-mongo-backup.js`) +- `package.json` - Added convenience scripts + +## Usage + +### Current Status +- ✅ SQLite is active +- ✅ Server running on port 3005 (or PORT env variable) +- ✅ All data migrated successfully + +### New NPM Scripts +```bash +npm run server # Start the server +npm run db:sqlite # Switch to SQLite (current) +npm run db:mongo # Switch back to MongoDB +npm run db:migrate # Re-run migration +npm run db:export # Export MongoDB data only +``` + +### Manual Operations +```bash +# Switch to SQLite +cd database && ./switch-db.sh sqlite + +# Switch to MongoDB +cd database && ./switch-db.sh mongo + +# Re-run migration +cd database && node migrate-to-sqlite.js + +# Start server +cd database && node server.js +``` + +## Benefits of SQLite + +1. **Simpler Deployment**: No separate database service needed +2. **Single File Database**: Easy backups and transfers +3. **Better for Small Apps**: Perfect for gaming groups +4. **Zero Configuration**: Works out of the box +5. **ACID Compliant**: Reliable data integrity + +## Data Structure + +The SQLite database maintains the same structure as MongoDB: +- `players` table with JSON columns for `rollerInfo`, `shopInfo`, `tabInfo` +- All existing player data preserved +- Password hashing maintained +- Timestamps added for better tracking + +## Rollback + +If you need to go back to MongoDB: +```bash +npm run db:mongo +``` + +This will restore your original MongoDB configuration. Your MongoDB data is unchanged. + +## File Locations + +- **SQLite Database**: `database/sqlite/deathwatch.db` +- **MongoDB Backup**: `database/mongo-backup.json` +- **Server Logs**: `database/backend.log` + +## API Compatibility + +The API endpoints remain exactly the same: +- `GET /api/players` - List all players +- `GET /api/players/:name` - Get specific player +- `PUT /api/players/:name` - Update player +- `POST /api/players` - Create player +- `DELETE /api/players/:name` - Delete player + +Your frontend (React app) requires no changes. diff --git a/database/backend.log b/database/backend.log new file mode 100644 index 0000000..5d762ff --- /dev/null +++ b/database/backend.log @@ -0,0 +1,163 @@ +nohup: ignoring input +(node:2715663) [MONGODB DRIVER] Warning: useNewUrlParser is a deprecated option: useNewUrlParser has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version +(Use `node --trace-warnings ...` to show where the warning was created) +(node:2715663) [MONGODB DRIVER] Warning: useUnifiedTopology is a deprecated option: useUnifiedTopology has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version +Server running on http://0.0.0.0:5000 +Connected to MongoDB +[2025-08-15T01:13:19.371Z] API: Creating player {"name":"testuser6","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"pw":"testpw","pwHash":"dummyhash"} +[2025-08-15T01:13:19.394Z] API: Player created {"name":"testuser6","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"_id":"689e89aff8623eb2e1049ce4","__v":0} +[2025-08-15T01:21:39.981Z] API: Creating player {"name":"anders","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"pw":"testpw","pwHash":"dummyhash"} +[2025-08-15T01:21:39.994Z] API: Player created {"name":"anders","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"_id":"689e8ba3f8623eb2e1049ce8","__v":0} +[2025-08-15T01:21:43.102Z] API: Creating player {"name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"pw":"testpw","pwHash":"dummyhash"} +[2025-08-15T01:21:43.109Z] API: Player created {"name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"_id":"689e8ba7f8623eb2e1049cea","__v":0} +[2025-08-15T01:21:44.571Z] API: Creating player {"name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"pw":"testpw","pwHash":"dummyhash"} +[2025-08-15T01:21:44.576Z] API: Player created {"name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"_id":"689e8ba8f8623eb2e1049cec","__v":0} +[2025-08-15T01:21:45.469Z] API: Creating player {"name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"pw":"testpw","pwHash":"dummyhash"} +[2025-08-15T01:21:45.475Z] API: Player created {"name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"_id":"689e8ba9f8623eb2e1049cee","__v":0} +[2025-08-15T01:22:59.288Z] API: Deleting player player1 +[2025-08-15T01:22:59.293Z] API: Player not found for delete player1 +[2025-08-15T01:23:00.707Z] API: Deleting player player2 +[2025-08-15T01:23:02.610Z] API: Player not found for delete player2 +[2025-08-15T01:23:52.270Z] API: Deleting player Player1 +[2025-08-15T01:23:52.274Z] API: Player deleted Player1 +[2025-08-15T01:23:54.090Z] API: Deleting player Player2 +[2025-08-15T01:23:54.094Z] API: Player deleted Player2 +[2025-08-15T01:28:52.069Z] API: Fetch all players [{"pw":"","pwHash":"","_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0}] +[2025-08-15T01:29:06.961Z] API: Fetch all players [{"pw":"","pwHash":"","_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0}] +[2025-08-15T01:29:35.926Z] API: Updating player anders {"name":"anders","pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}} +[2025-08-15T01:29:35.944Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"name":"anders","pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0} +[2025-08-15T01:29:37.673Z] API: Updating player christoffer {"name":"christoffer","pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}} +[2025-08-15T01:29:37.676Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"name":"christoffer","pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0} +[2025-08-15T01:29:38.605Z] API: Updating player claes {"name":"claes","pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}} +[2025-08-15T01:29:38.609Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"name":"claes","pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0} +[2025-08-15T01:29:39.456Z] API: Updating player phillip {"name":"phillip","pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}} +[2025-08-15T01:29:39.459Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"name":"phillip","pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0} +[2025-08-15T01:29:44.026Z] API: Fetch all players [{"pw":"","pwHash":"","_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"name":"anders","pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"name":"christoffer","pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"name":"claes","pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"name":"phillip","pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0}] +[2025-08-15T01:30:32.842Z] API: Updating player anders {"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}} +[2025-08-15T01:30:32.848Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0} +[2025-08-15T01:30:34.748Z] API: Updating player christoffer {"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}} +[2025-08-15T01:30:34.753Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0} +[2025-08-15T01:30:39.357Z] API: Updating player claes {"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}} +[2025-08-15T01:30:39.361Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0} +[2025-08-15T01:30:40.516Z] API: Updating player phillip {"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}} +[2025-08-15T01:30:40.521Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0} +[2025-08-15T01:30:47.841Z] API: Fetch all players [{"pw":"","pwHash":"","_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}},"__v":0}] +[2025-08-15T01:31:23.542Z] API: Updating player anders {"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}} +[2025-08-15T01:31:23.547Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}},"__v":0} +[2025-08-15T01:31:25.052Z] API: Updating player christoffer {"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}} +[2025-08-15T01:31:25.058Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}},"__v":0} +[2025-08-15T01:31:26.957Z] API: Updating player claes {"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}} +[2025-08-15T01:31:26.963Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}},"__v":0} +[2025-08-15T01:31:27.835Z] API: Updating player phillip {"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}} +[2025-08-15T01:31:27.840Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}},"__v":0} +[2025-08-15T01:31:33.079Z] API: Fetch all players [{"pw":"","pwHash":"","_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}},"__v":0}] +[2025-08-15T01:32:14.890Z] API: Updating player anders {"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}} +[2025-08-15T01:32:14.895Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0} +[2025-08-15T01:32:16.476Z] API: Updating player christoffer {"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}} +[2025-08-15T01:32:16.480Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0} +[2025-08-15T01:32:17.478Z] API: Updating player claes {"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}} +[2025-08-15T01:32:17.482Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0} +[2025-08-15T01:32:18.372Z] API: Updating player phillip {"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}} +[2025-08-15T01:32:18.376Z] API: Player updated {"pw":"","pwHash":"","_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0} +[2025-08-15T01:32:22.701Z] API: Fetch all players [{"pw":"","pwHash":"","_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0},{"pw":"","pwHash":"","_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0}] +[2025-08-15T01:33:08.077Z] API: Fetch all players [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T01:34:09.548Z] API: Fetch all players [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T01:35:59.447Z] API: Fetch all players [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T01:36:44.384Z] API: Fetch all players [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T01:41:47.866Z] API: Fetch all players [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T01:42:05.822Z] API: Fetch all players [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T01:43:23.838Z] API: Fetch all players [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T01:44:03.286Z] API: Fetch all players [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T01:45:32.364Z] API: Fetch all players [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T01:56:47.928Z] SESSION: Not found test GET /api/players +[2025-08-15T01:56:57.978Z] SESSION: Valid d1ff9c6b2d6bfc7da652612f02ca802a7fd54ee36e88e89fdb1a9f42837ccfc8 anders GET /api/players +[2025-08-15T01:56:57.988Z] API: Fetch all players [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T01:57:23.396Z] SESSION: Missing sessionId GET /api/players +[2025-08-15T01:57:30.926Z] SESSION: Missing sessionId GET /api/players +[2025-08-15T01:58:14.970Z] SESSION: Valid d1ff9c6b2d6bfc7da652612f02ca802a7fd54ee36e88e89fdb1a9f42837ccfc8 anders GET /api/players +[2025-08-15T01:58:14.976Z] API: Fetch all players [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"pw":"1234","pwHash":"1234","tabInfo":{"rp":10,"inventory":[],"renown":"None"}}}},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T01:59:13.264Z] SESSION: Valid d1ff9c6b2d6bfc7da652612f02ca802a7fd54ee36e88e89fdb1a9f42837ccfc8 anders GET /api/players +[2025-08-15T01:59:13.282Z] API: Fetch all players [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T01:59:21.862Z] SESSION: Valid 3b381165afbbd920d062e60a3e15eee8d63893b3418bd8d7a05fd16f52c92bce anders GET /api/players +[2025-08-15T01:59:21.867Z] API: Fetch all players [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T01:59:38.474Z] SESSION: Missing sessionId GET /api/players +[2025-08-15T01:59:43.409Z] SESSION: Missing sessionId GET /api/players +[2025-08-15T02:00:47.366Z] SESSION: Valid 3b381165afbbd920d062e60a3e15eee8d63893b3418bd8d7a05fd16f52c92bce anders GET /api/players +[2025-08-15T02:00:47.371Z] API: Fetch all players [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T02:01:24.503Z] SESSION: Missing sessionId GET /api/players +[2025-08-15T02:02:00.369Z] SESSION: Missing sessionId GET /api/players +[2025-08-15T02:02:04.879Z] SESSION: Missing sessionId GET /api/players +[2025-08-15T02:03:02.009Z] SESSION: Missing sessionId GET /api/players +[2025-08-15T02:04:09.014Z] SESSION: Missing sessionId GET /api/players +[2025-08-15T02:05:27.981Z] SESSION: Missing sessionId GET /api/players +[2025-08-15T02:05:34.299Z] SESSION: Missing sessionId GET /api/players +[2025-08-15T02:06:50.961Z] API: Fetch all players (public) [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T02:06:55.808Z] API: Fetch all players (public) [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T02:07:45.267Z] SESSION: Validate success 5e44db9a9900099b67a0cdfa88ec201146c6d41135ab07a93e2593c155a04458 anders +[2025-08-15T02:07:50.016Z] API: Fetch all players (public) [{"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T02:07:55.411Z] SESSION: Valid 5e44db9a9900099b67a0cdfa88ec201146c6d41135ab07a93e2593c155a04458 anders PUT /api/players/anders +[2025-08-15T02:07:55.411Z] API: Updating player anders {"tabInfo":{"rp":20}} +[2025-08-15T02:07:55.420Z] API: Player updated {"_id":"689e8ba3f8623eb2e1049ce8","name":"anders","tabInfo":{"rp":20},"__v":0,"pw":"1234","pwHash":"1234"} +[2025-08-15T02:07:59.977Z] SESSION: Valid 5e44db9a9900099b67a0cdfa88ec201146c6d41135ab07a93e2593c155a04458 anders DELETE /api/players/anders +[2025-08-15T02:07:59.978Z] API: Deleting player anders +[2025-08-15T02:07:59.982Z] API: Player deleted anders +[2025-08-15T02:08:54.824Z] API: Fetch all players (public) [{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T02:09:02.075Z] API: Fetch all players (public) [{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T02:09:06.794Z] API: Fetch all players (public) [{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T02:09:12.421Z] API: Creating player {"name":"anders","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"pw":"1234","pwHash":"1234"} +[2025-08-15T02:09:12.427Z] API: Player created {"name":"anders","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"pw":"1234","pwHash":"1234","_id":"689e96c840b113372792b721","__v":0} +[2025-08-15T02:09:19.439Z] API: Fetch all players (public) [{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e96c840b113372792b721","name":"anders","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"pw":"1234","pwHash":"1234","__v":0}] +[2025-08-15T02:16:29.044Z] API: Fetch all players (public) [{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e96c840b113372792b721","name":"anders","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"pw":"1234","pwHash":"1234","__v":0}] +[2025-08-15T02:17:03.574Z] SESSION: Validate success 2efb093c7a6c04b98eae3d1d3029ec24be439e42aedfd5ddc3e0478de4a4f076 anders +[2025-08-15T02:17:13.601Z] API: Fetch all players (public) [{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e96c840b113372792b721","name":"anders","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"pw":"1234","pwHash":"1234","__v":0}] +[2025-08-15T02:17:23.179Z] SESSION: Valid 2efb093c7a6c04b98eae3d1d3029ec24be439e42aedfd5ddc3e0478de4a4f076 anders PUT /api/players/anders +[2025-08-15T02:17:23.179Z] API: Updating player anders {"tabInfo":{"rp":5,"inventory":[],"renown":"Hero"}} +[2025-08-15T02:17:23.183Z] API: Player updated {"_id":"689e96c840b113372792b721","name":"anders","tabInfo":{"rp":5,"inventory":[],"renown":"Hero"},"pw":"1234","pwHash":"1234","__v":0} +[2025-08-15T02:17:29.921Z] SESSION: Valid 2efb093c7a6c04b98eae3d1d3029ec24be439e42aedfd5ddc3e0478de4a4f076 anders DELETE /api/players/anders +[2025-08-15T02:17:29.921Z] API: Deleting player anders +[2025-08-15T02:17:29.924Z] API: Player deleted anders +[2025-08-15T02:17:37.045Z] API: Fetch all players (public) [{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T06:49:34.266Z] API: Fetch all players (public) [{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T06:49:35.447Z] API: Fetch all players (public) [{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T06:50:26.956Z] API: Fetch all players (public) [{"_id":"689e8ba7f8623eb2e1049cea","name":"christoffer","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba8f8623eb2e1049cec","name":"claes","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"},{"_id":"689e8ba9f8623eb2e1049cee","name":"phillip","tabInfo":{"rp":10,"inventory":[],"renown":"None"},"__v":0,"pw":"1234","pwHash":"1234"}] +[2025-08-15T07:18:07.299Z] API: Fetch all players (public) Found 3 players +[2025-08-15T07:36:10.294Z] SESSION: Login success christoffer eb92b26ba3b842b1c63a1af54cc48d927ee48ac526344e214c2a45565293554f +[2025-08-15T07:36:18.054Z] SESSION: Validate success eb92b26ba3b842b1c63a1af54cc48d927ee48ac526344e214c2a45565293554f christoffer +[2025-08-15T07:36:24.654Z] API: Fetch player christoffer +[2025-08-15T07:36:52.942Z] API: Fetch all players (public) Found 3 players +[2025-08-15T07:38:28.184Z] API: Fetch all players (public) Found 3 players +[2025-08-15T07:38:40.729Z] API: Fetch all players (public) Found 3 players +[2025-08-15T07:42:13.759Z] SESSION: Login success christoffer 0593ffc4ec39ce477a10a44498bc77448ae7fa0b4a849ef8b342d6d776e69133 +[2025-08-15T07:42:21.489Z] SESSION: Login success claes f74ce9ee0a643d095b522d42b9b1940a5e02442a1cfd89d5e453fab0cdfbccc5 +[2025-08-15T07:44:27.335Z] SESSION: Login success phillip e5a5d21025253326d0c717c040e5faa3fb6ab9f6c9203bdd31017530f00ca67a +[2025-08-15T07:45:02.499Z] API: Fetch all players (public) Found 3 players +[2025-08-15T07:48:23.705Z] SESSION: Login success christoffer 561d3952b97d00818f735b100f3f56d7ed177b8dcb7d712c8f3790591a34c835 +[2025-08-15T07:50:35.493Z] API: Fetch all players (public) Found 3 players +[2025-08-15T07:50:43.572Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:08:48.171Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:08:56.008Z] SESSION: Login success phillip 249bd9d552d849573a083ddcfa5a97ddcb0721357738f649df3838b53c5cee01 +[2025-08-15T08:08:56.044Z] SESSION: Validate success 249bd9d552d849573a083ddcfa5a97ddcb0721357738f649df3838b53c5cee01 phillip +[2025-08-15T08:08:58.030Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:09:00.241Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:09:04.653Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:11:26.746Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:11:32.707Z] SESSION: Login success christoffer 581b4c14f880fa8cb4fcdb780f2263a205dad9a06e4845d2a43338511bbaacf1 +[2025-08-15T08:11:32.715Z] SESSION: Validate success 581b4c14f880fa8cb4fcdb780f2263a205dad9a06e4845d2a43338511bbaacf1 christoffer +[2025-08-15T08:11:34.453Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:11:35.383Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:11:36.892Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:11:40.589Z] SESSION: Logout success 581b4c14f880fa8cb4fcdb780f2263a205dad9a06e4845d2a43338511bbaacf1 +[2025-08-15T08:12:15.482Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:12:15.484Z] SESSION: Validate success 249bd9d552d849573a083ddcfa5a97ddcb0721357738f649df3838b53c5cee01 phillip +[2025-08-15T08:12:16.527Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:12:16.529Z] SESSION: Validate success 249bd9d552d849573a083ddcfa5a97ddcb0721357738f649df3838b53c5cee01 phillip +[2025-08-15T08:12:17.728Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:12:19.112Z] SESSION: Logout success 249bd9d552d849573a083ddcfa5a97ddcb0721357738f649df3838b53c5cee01 +[2025-08-15T08:12:20.881Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:12:21.490Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:12:22.165Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:12:28.104Z] SESSION: Login success claes dc5481ec7357cc3b25cfaa2d10bbbbe65496c6645eb0368bb5369d7f13d732a7 +[2025-08-15T08:12:28.153Z] SESSION: Validate success dc5481ec7357cc3b25cfaa2d10bbbbe65496c6645eb0368bb5369d7f13d732a7 claes +[2025-08-15T08:12:28.154Z] API: Fetch all players (public) Found 3 players +[2025-08-15T08:12:35.062Z] SESSION: Logout success dc5481ec7357cc3b25cfaa2d10bbbbe65496c6645eb0368bb5369d7f13d732a7 +[2025-08-15T08:12:35.100Z] API: Fetch all players (public) Found 3 players diff --git a/database/fix_flatten_tabinfo.js b/database/fix_flatten_tabinfo.js new file mode 100644 index 0000000..9220927 --- /dev/null +++ b/database/fix_flatten_tabinfo.js @@ -0,0 +1,28 @@ +// Script to flatten tabInfo for all players in the database +const mongoose = require('mongoose'); +const Player = require('./playerModel'); + +const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017/deathwatch'; + +function flattenTabInfo(tabInfo) { + let t = tabInfo; + while (t && t.tabInfo) t = t.tabInfo; + return { ...t }; +} + +async function main() { + await mongoose.connect(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true }); + const players = await Player.find(); + for (const player of players) { + if (player.tabInfo && player.tabInfo.tabInfo) { + const flat = flattenTabInfo(player.tabInfo); + player.tabInfo = flat; + await player.save(); + console.log(`Flattened tabInfo for ${player.name}`); + } + } + await mongoose.disconnect(); + console.log('Done.'); +} + +main().catch(e => { console.error(e); process.exit(1); }); diff --git a/database/fix_player_passwords.js b/database/fix_player_passwords.js new file mode 100644 index 0000000..e0e8a8f --- /dev/null +++ b/database/fix_player_passwords.js @@ -0,0 +1,31 @@ +// One-time script to fix player passwords at the top level +const mongoose = require('mongoose'); +const Player = require('./playerModel'); + +const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017/deathwatch'; + +async function fixPasswords() { + await mongoose.connect(MONGO_URI); + const players = await Player.find(); + for (const player of players) { + let pw = player.pw; + let pwHash = player.pwHash; + // Try to extract from tabInfo if not set at top level + if ((!pw || pw === '') && player.tabInfo && player.tabInfo.pw) pw = player.tabInfo.pw; + if ((!pwHash || pwHash === '') && player.tabInfo && player.tabInfo.pwHash) pwHash = player.tabInfo.pwHash; + // Try to extract from tabInfo.tabInfo (legacy nesting) + if ((!pw || pw === '') && player.tabInfo && player.tabInfo.tabInfo && player.tabInfo.tabInfo.pw) pw = player.tabInfo.tabInfo.pw; + if ((!pwHash || pwHash === '') && player.tabInfo && player.tabInfo.tabInfo && player.tabInfo.tabInfo.pwHash) pwHash = player.tabInfo.tabInfo.pwHash; + // Only update if needed + if (pw && pwHash && (player.pw !== pw || player.pwHash !== pwHash)) { + player.pw = pw; + player.pwHash = pwHash; + await player.save(); + console.log(`Updated ${player.name}: pw=${pw}, pwHash=${pwHash}`); + } + } + await mongoose.disconnect(); + console.log('Done.'); +} + +fixPasswords().catch(e => { console.error(e); process.exit(1); }); diff --git a/database/gmSessionModel.js b/database/gmSessionModel.js old mode 100644 new mode 100755 diff --git a/database/migrate-to-sqlite.js b/database/migrate-to-sqlite.js new file mode 100644 index 0000000..273a01d --- /dev/null +++ b/database/migrate-to-sqlite.js @@ -0,0 +1,166 @@ +const mongoose = require('mongoose'); +const Player = require('./playerModel'); +const { playerHelpers, sessionHelpers } = require('./sqlite-db'); + +require('dotenv').config(); + +const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017/deathwatch'; + +async function migratePlayers() { + try { + // Connect to MongoDB + console.log('Connecting to MongoDB...'); + await mongoose.connect(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true }); + console.log('Connected to MongoDB'); + + // Fetch all players from MongoDB + console.log('Fetching players from MongoDB...'); + const mongoPlayers = await Player.find(); + console.log(`Found ${mongoPlayers.length} players in MongoDB`); + + // Migrate each player to SQLite + let migratedCount = 0; + let skippedCount = 0; + + for (const mongoPlayer of mongoPlayers) { + try { + // Check if player already exists in SQLite + const existingPlayer = playerHelpers.getByName(mongoPlayer.name); + + if (existingPlayer) { + console.log(`Player ${mongoPlayer.name} already exists in SQLite, updating...`); + const updated = playerHelpers.update(mongoPlayer.name, { + rollerInfo: mongoPlayer.rollerInfo || {}, + shopInfo: mongoPlayer.shopInfo || {}, + tabInfo: mongoPlayer.tabInfo || {}, + pw: mongoPlayer.pw || '', + pwHash: mongoPlayer.pwHash || '' + }); + + if (updated) { + console.log(`✓ Updated player: ${mongoPlayer.name}`); + migratedCount++; + } else { + console.log(`⚠ Failed to update player: ${mongoPlayer.name}`); + skippedCount++; + } + } else { + // Create new player in SQLite + const newPlayer = playerHelpers.create({ + name: mongoPlayer.name, + rollerInfo: mongoPlayer.rollerInfo || {}, + shopInfo: mongoPlayer.shopInfo || {}, + tabInfo: mongoPlayer.tabInfo || {}, + pw: mongoPlayer.pw || '', + pwHash: mongoPlayer.pwHash || '' + }); + + console.log(`✓ Migrated player: ${mongoPlayer.name}`); + migratedCount++; + } + + // Log the data being migrated + console.log(` - Roller info: ${Object.keys(mongoPlayer.rollerInfo || {}).length} keys`); + console.log(` - Shop info: ${Object.keys(mongoPlayer.shopInfo || {}).length} keys`); + console.log(` - Tab info: ${Object.keys(mongoPlayer.tabInfo || {}).length} keys`); + + } catch (error) { + console.error(`Error migrating player ${mongoPlayer.name}:`, error); + skippedCount++; + } + } + + console.log('\n=== Migration Summary ==='); + console.log(`Total players in MongoDB: ${mongoPlayers.length}`); + console.log(`Successfully migrated/updated: ${migratedCount}`); + console.log(`Skipped/failed: ${skippedCount}`); + + // Verify the migration + console.log('\n=== Verification ==='); + const sqlitePlayers = playerHelpers.getAll(); + console.log(`Total players in SQLite: ${sqlitePlayers.length}`); + + // Show sample data + if (sqlitePlayers.length > 0) { + console.log('\nSample migrated player:'); + const sample = sqlitePlayers[0]; + console.log(`Name: ${sample.name}`); + console.log(`Roller info keys: ${Object.keys(sample.rollerInfo).length}`); + console.log(`Shop info keys: ${Object.keys(sample.shopInfo).length}`); + console.log(`Tab info keys: ${Object.keys(sample.tabInfo).length}`); + } + + } catch (error) { + console.error('Migration failed:', error); + } finally { + // Close connections + await mongoose.disconnect(); + console.log('Disconnected from MongoDB'); + } +} + +// Export player data to JSON for backup +async function exportToJson() { + try { + console.log('Exporting MongoDB data to JSON backup...'); + + // Connect to MongoDB + await mongoose.connect(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true }); + + // Fetch all players + const players = await Player.find(); + + // Create backup object + const backup = { + exportDate: new Date().toISOString(), + playerCount: players.length, + players: players.map(player => ({ + name: player.name, + rollerInfo: player.rollerInfo || {}, + shopInfo: player.shopInfo || {}, + tabInfo: player.tabInfo || {}, + pw: player.pw || '', + pwHash: player.pwHash || '', + mongoId: player._id.toString() + })) + }; + + // Write to file + const fs = require('fs'); + const path = require('path'); + const backupPath = path.join(__dirname, 'mongo-backup.json'); + + fs.writeFileSync(backupPath, JSON.stringify(backup, null, 2)); + console.log(`✓ Backup saved to: ${backupPath}`); + console.log(`✓ Exported ${players.length} players`); + + await mongoose.disconnect(); + + } catch (error) { + console.error('Export failed:', error); + } +} + +// Main function +async function main() { + const args = process.argv.slice(2); + + if (args.includes('--export-only')) { + await exportToJson(); + } else if (args.includes('--migrate-only')) { + await migratePlayers(); + } else { + // Do both + await exportToJson(); + await migratePlayers(); + } + + process.exit(0); +} + +// Run if called directly +if (require.main === module) { + main().catch(console.error); +} + +module.exports = { migratePlayers, exportToJson }; diff --git a/database/mongo-backup.json b/database/mongo-backup.json new file mode 100644 index 0000000..03e251e --- /dev/null +++ b/database/mongo-backup.json @@ -0,0 +1,45 @@ +{ + "exportDate": "2025-08-15T07:12:29.894Z", + "playerCount": 3, + "players": [ + { + "name": "christoffer", + "rollerInfo": {}, + "shopInfo": {}, + "tabInfo": { + "rp": 10, + "inventory": [], + "renown": "None" + }, + "pw": "1234", + "pwHash": "1234", + "mongoId": "689e8ba7f8623eb2e1049cea" + }, + { + "name": "claes", + "rollerInfo": {}, + "shopInfo": {}, + "tabInfo": { + "rp": 10, + "inventory": [], + "renown": "None" + }, + "pw": "1234", + "pwHash": "1234", + "mongoId": "689e8ba8f8623eb2e1049cec" + }, + { + "name": "phillip", + "rollerInfo": {}, + "shopInfo": {}, + "tabInfo": { + "rp": 10, + "inventory": [], + "renown": "None" + }, + "pw": "1234", + "pwHash": "1234", + "mongoId": "689e8ba9f8623eb2e1049cee" + } + ] +} \ No newline at end of file diff --git a/database/playerModel.js b/database/playerModel.js old mode 100644 new mode 100755 index 18da628..7edb283 --- a/database/playerModel.js +++ b/database/playerModel.js @@ -5,6 +5,8 @@ const playerSchema = new mongoose.Schema({ rollerInfo: { type: Object, default: {} }, shopInfo: { type: Object, default: {} }, tabInfo: { type: Object, default: {} }, + pw: { type: String, default: '' }, + pwHash: { type: String, default: '' }, }); const Player = mongoose.model('Player', playerSchema); diff --git a/database/requireSession.js b/database/requireSession.js new file mode 100644 index 0000000..7d34dea --- /dev/null +++ b/database/requireSession.js @@ -0,0 +1,32 @@ +const Session = require('./sessionModel'); +const fs = require('fs'); +const path = require('path'); +function logToFile(...args) { + const msg = `[${new Date().toISOString()}] ` + args.map(a => (typeof a === 'object' ? JSON.stringify(a) : String(a))).join(' ') + '\n'; + fs.appendFileSync(path.join(__dirname, 'backend.log'), msg, { encoding: 'utf8' }); +} + +// Express middleware to require a valid sessionId in req.headers['x-session-id'] or req.body.sessionId +// Attaches req.session and req.sessionPlayerName if valid, else sends 401 +module.exports = async function requireSession(req, res, next) { + // Ensure req.body is always an object + if (typeof req.body !== 'object' || req.body === null) req.body = {}; + const sessionId = req.headers['x-session-id'] || req.body.sessionId || req.query.sessionId; + if (!sessionId) { + logToFile('SESSION: Missing sessionId', req.method, req.originalUrl); + return res.status(401).json({ error: 'Session required' }); + } + const session = await Session.findOne({ sessionId }); + if (!session) { + logToFile('SESSION: Not found', sessionId, req.method, req.originalUrl); + return res.status(401).json({ error: 'Invalid or expired session' }); + } + if (session.expiresAt < new Date()) { + logToFile('SESSION: Expired', sessionId, req.method, req.originalUrl); + return res.status(401).json({ error: 'Invalid or expired session' }); + } + logToFile('SESSION: Valid', sessionId, session.playerName, req.method, req.originalUrl); + req.session = session; + req.sessionPlayerName = session.playerName; + next(); +}; diff --git a/database/routes/playerRoutes-sqlite.js b/database/routes/playerRoutes-sqlite.js new file mode 100644 index 0000000..72743d4 --- /dev/null +++ b/database/routes/playerRoutes-sqlite.js @@ -0,0 +1,254 @@ +const express = require('express'); +const { playerHelpers } = require('../sqlite-db'); +const fs = require('fs'); +const path = require('path'); + +// Simple file logger +function logToFile(...args) { + const msg = `[${new Date().toISOString()}] ` + args.map(a => (typeof a === 'object' ? JSON.stringify(a) : String(a))).join(' ') + '\n'; + fs.appendFileSync(path.join(__dirname, '../backend.log'), msg, { encoding: 'utf8' }); +} + +const requireSession = require('../requireSession'); +const router = express.Router(); + +// TEMP ADMIN: List all users +router.get('/admin/list', async (req, res) => { + try { + const players = playerHelpers.getAll(); + res.json(players); + } catch (error) { + logToFile('API: Failed to list players', error); + res.status(500).json({ error: 'Failed to list players' }); + } +}); + +// TEMP ADMIN: Delete test users (name contains 'test' or 'Test') +router.delete('/admin/delete-tests', async (req, res) => { + try { + const players = playerHelpers.getAll(); + const testPlayers = players.filter(p => /test/i.test(p.name)); + + let deletedCount = 0; + for (const player of testPlayers) { + if (playerHelpers.delete(player.name)) { + deletedCount++; + } + } + + res.json({ deletedCount }); + } catch (error) { + logToFile('API: Failed to delete test users', error); + res.status(500).json({ error: 'Failed to delete test users' }); + } +}); + +// Get all players (public for dropdown) +router.get('/', async (req, res) => { + try { + const players = playerHelpers.getAll(); + logToFile('API: Fetch all players (public)', `Found ${players.length} players`); + + // Only send name for dropdown if not authed + if (!req.headers['x-session-id']) { + return res.json(players.map(p => ({ name: p.name }))); + } + res.json(players); + } catch (error) { + logToFile('API: Failed to fetch players', error); + res.status(500).json({ error: 'Failed to fetch players' }); + } +}); + +// Get single player by name +router.get('/:name', async (req, res) => { + try { + const player = playerHelpers.getByName(req.params.name); + if (!player) { + return res.status(404).json({ error: 'Player not found' }); + } + logToFile('API: Fetch player', req.params.name); + res.json(player); + } catch (error) { + logToFile('API: Failed to fetch player', req.params.name, error); + res.status(500).json({ error: 'Failed to fetch player' }); + } +}); + +// Create new player +router.post('/', async (req, res) => { + try { + const { name, pw, ...otherData } = req.body; + + if (!name) { + return res.status(400).json({ error: 'Player name is required' }); + } + + // Check if player already exists + const existingPlayer = playerHelpers.getByName(name); + if (existingPlayer) { + return res.status(409).json({ error: 'Player already exists' }); + } + + // Create password hash if password provided + let pwHash = ''; + if (pw) { + const bcrypt = require('bcrypt'); + pwHash = await bcrypt.hash(pw, 10); + } + + const newPlayer = playerHelpers.create({ + name, + pw: pw || '', + pwHash, + rollerInfo: otherData.rollerInfo || {}, + shopInfo: otherData.shopInfo || {}, + tabInfo: otherData.tabInfo || {} + }); + + logToFile('API: Created player', name); + res.status(201).json(newPlayer); + } catch (error) { + logToFile('API: Failed to create player', req.body.name, error); + res.status(500).json({ error: 'Failed to create player' }); + } +}); + +// Update player +router.put('/:name', requireSession, async (req, res) => { + try { + const { name } = req.params; + const updateData = req.body; + + // Check if player exists + const existingPlayer = playerHelpers.getByName(name); + if (!existingPlayer) { + return res.status(404).json({ error: 'Player not found' }); + } + + // Handle password update if provided + if (updateData.pw) { + const bcrypt = require('bcrypt'); + updateData.pwHash = await bcrypt.hash(updateData.pw, 10); + } + + // Merge the data properly + const mergedData = { + rollerInfo: { ...existingPlayer.rollerInfo, ...updateData.rollerInfo }, + shopInfo: { ...existingPlayer.shopInfo, ...updateData.shopInfo }, + tabInfo: { ...existingPlayer.tabInfo, ...updateData.tabInfo }, + pw: updateData.pw || existingPlayer.pw, + pwHash: updateData.pwHash || existingPlayer.pwHash + }; + + // Handle the special case where frontend sends flat data that should go into tabInfo + if (updateData.playerName || updateData.charName || updateData.characteristics) { + mergedData.tabInfo = { + ...mergedData.tabInfo, + playerName: updateData.playerName || mergedData.tabInfo.playerName, + charName: updateData.charName || mergedData.tabInfo.charName, + gear: updateData.gear || mergedData.tabInfo.gear, + chapter: updateData.chapter || mergedData.tabInfo.chapter, + demeanour: updateData.demeanour || mergedData.tabInfo.demeanour, + speciality: updateData.speciality || mergedData.tabInfo.speciality, + rank: updateData.rank || mergedData.tabInfo.rank, + powerArmour: updateData.powerArmour || mergedData.tabInfo.powerArmour, + description: updateData.description || mergedData.tabInfo.description, + pastEvent: updateData.pastEvent || mergedData.tabInfo.pastEvent, + personalDemeanour: updateData.personalDemeanour || mergedData.tabInfo.personalDemeanour, + characteristics: updateData.characteristics || mergedData.tabInfo.characteristics, + skills: updateData.skills || mergedData.tabInfo.skills, + weapons: updateData.weapons || mergedData.tabInfo.weapons, + armour: updateData.armour || mergedData.tabInfo.armour, + talents: updateData.talents || mergedData.tabInfo.talents, + psychic: updateData.psychic || mergedData.tabInfo.psychic, + wounds: updateData.wounds || mergedData.tabInfo.wounds, + insanity: updateData.insanity || mergedData.tabInfo.insanity, + movement: updateData.movement || mergedData.tabInfo.movement, + fate: updateData.fate || mergedData.tabInfo.fate, + corruption: updateData.corruption || mergedData.tabInfo.corruption, + renown: updateData.renown || mergedData.tabInfo.renown, + xp: updateData.xp || mergedData.tabInfo.xp, + xpSpent: updateData.xpSpent || mergedData.tabInfo.xpSpent, + notes: updateData.notes || mergedData.tabInfo.notes, + rp: updateData.rp || mergedData.tabInfo.rp + }; + } + + const updated = playerHelpers.update(name, mergedData); + + if (!updated) { + return res.status(500).json({ error: 'Failed to update player' }); + } + + const updatedPlayer = playerHelpers.getByName(name); + logToFile('API: Updated player', name); + res.json(updatedPlayer); + } catch (error) { + logToFile('API: Failed to update player', req.params.name, error); + res.status(500).json({ error: 'Failed to update player' }); + } +}); + +// Delete player +router.delete('/:name', requireSession, async (req, res) => { + try { + const { name } = req.params; + + const deleted = playerHelpers.delete(name); + + if (!deleted) { + return res.status(404).json({ error: 'Player not found' }); + } + + logToFile('API: Deleted player', name); + res.json({ message: 'Player deleted successfully' }); + } catch (error) { + logToFile('API: Failed to delete player', req.params.name, error); + res.status(500).json({ error: 'Failed to delete player' }); + } +}); + +// Login endpoint +router.post('/login', async (req, res) => { + try { + const { name, password } = req.body; + + if (!name || !password) { + return res.status(400).json({ error: 'Name and password are required' }); + } + + const player = playerHelpers.getByName(name); + if (!player) { + return res.status(401).json({ error: 'Invalid credentials' }); + } + + // Check password + const bcrypt = require('bcrypt'); + const isValidPassword = await bcrypt.compare(password, player.pwHash); + + if (!isValidPassword) { + return res.status(401).json({ error: 'Invalid credentials' }); + } + + // Create session (you might want to implement proper session management) + const sessionId = require('crypto').randomBytes(32).toString('hex'); + + logToFile('API: Player login', name); + res.json({ + message: 'Login successful', + sessionId, + player: { + name: player.name, + rollerInfo: player.rollerInfo, + shopInfo: player.shopInfo, + tabInfo: player.tabInfo + } + }); + } catch (error) { + logToFile('API: Login failed', req.body.name, error); + res.status(500).json({ error: 'Login failed' }); + } +}); + +module.exports = router; diff --git a/database/routes/playerRoutes.js b/database/routes/playerRoutes.js old mode 100644 new mode 100755 index bda776e..c51abae --- a/database/routes/playerRoutes.js +++ b/database/routes/playerRoutes.js @@ -1,68 +1,149 @@ + + const express = require('express'); const Player = require('../playerModel'); +const fs = require('fs'); +const path = require('path'); +// Simple file logger +function logToFile(...args) { + const msg = `[${new Date().toISOString()}] ` + args.map(a => (typeof a === 'object' ? JSON.stringify(a) : String(a))).join(' ') + '\n'; + fs.appendFileSync(path.join(__dirname, '../backend.log'), msg, { encoding: 'utf8' }); +} + +const requireSession = require('../requireSession'); const router = express.Router(); -// Get all players -router.get('/', async (req, res) => { +// TEMP ADMIN: List all users +router.get('/admin/list', async (req, res) => { try { const players = await Player.find(); res.json(players); } catch (error) { + res.status(500).json({ error: 'Failed to list players' }); + } +}); + +// TEMP ADMIN: Delete test users (name contains 'test' or 'Test') +router.delete('/admin/delete-tests', async (req, res) => { + try { + const result = await Player.deleteMany({ name: /test/i }); + res.json({ deletedCount: result.deletedCount }); + } catch (error) { + res.status(500).json({ error: 'Failed to delete test users' }); + } +}); + +// Get all players (public for dropdown) +router.get('/', async (req, res) => { + try { + const players = await Player.find(); + logToFile('API: Fetch all players (public)', players); + // Only send name for dropdown if not authed + if (!req.headers['x-session-id']) { + return res.json(players.map(p => ({ name: p.name }))); + } + res.json(players); + } catch (error) { + logToFile('API: Failed to fetch players', error); res.status(500).json({ error: 'Failed to fetch players' }); } }); -// Get a single player by name -router.get('/:name', async (req, res) => { +// Get a single player by name (require session) +router.get('/:name', requireSession, async (req, res) => { try { const player = await Player.findOne({ name: req.params.name }); + logToFile('API: Fetch player', req.params.name, player); if (!player) { return res.status(404).json({ error: 'Player not found' }); } res.json(player); } catch (error) { + logToFile('API: Failed to fetch player', req.params.name, error); res.status(500).json({ error: 'Failed to fetch player' }); } }); +// Helper to flatten tabInfo +function flattenTabInfo(tabInfo) { + let t = tabInfo; + while (t && t.tabInfo) t = t.tabInfo; + return { ...t }; +} + // Create a new player router.post('/', async (req, res) => { try { - const newPlayer = new Player(req.body); + logToFile('API: Creating player', req.body); + const body = { ...req.body }; + if (body.tabInfo) body.tabInfo = flattenTabInfo(body.tabInfo); + const newPlayer = new Player(body); await newPlayer.save(); + logToFile('API: Player created', newPlayer); res.status(201).json(newPlayer); } catch (error) { + logToFile('API: Failed to create player', error); res.status(400).json({ error: 'Failed to create player' }); } }); -// Update a player -router.put('/:name', async (req, res) => { +// Update a player (require session) +router.put('/:name', requireSession, async (req, res) => { try { + logToFile('API: Updating player', req.params.name, req.body); + // Always extract pw and pwHash from any location in the request + let pw, pwHash; + let tabInfo = req.body.tabInfo; + if (req.body.pw !== undefined) pw = req.body.pw; + if (req.body.pwHash !== undefined) pwHash = req.body.pwHash; + if (tabInfo !== undefined) { + if (tabInfo.pw !== undefined) pw = tabInfo.pw; + if (tabInfo.pwHash !== undefined) pwHash = tabInfo.pwHash; + if (tabInfo.tabInfo) { + if (tabInfo.tabInfo.pw !== undefined) pw = tabInfo.tabInfo.pw; + if (tabInfo.tabInfo.pwHash !== undefined) pwHash = tabInfo.tabInfo.pwHash; + } + tabInfo = flattenTabInfo(tabInfo); + } + const update = {}; + if (pw !== undefined) update.pw = pw; + if (pwHash !== undefined) update.pwHash = pwHash; + if (tabInfo !== undefined) update.tabInfo = tabInfo; + if (Object.keys(update).length === 0) { + logToFile('API: No valid fields to update for player', req.params.name); + return res.status(400).json({ error: 'No valid fields to update' }); + } const updatedPlayer = await Player.findOneAndUpdate( { name: req.params.name }, - req.body, + update, { new: true } ); if (!updatedPlayer) { + logToFile('API: Player not found for update', req.params.name); return res.status(404).json({ error: 'Player not found' }); } + logToFile('API: Player updated', updatedPlayer); res.json(updatedPlayer); } catch (error) { + logToFile('API: Failed to update player', error); res.status(400).json({ error: 'Failed to update player' }); } }); -// Delete a player -router.delete('/:name', async (req, res) => { +// Delete a player (require session) +router.delete('/:name', requireSession, async (req, res) => { try { + logToFile('API: Deleting player', req.params.name); const deletedPlayer = await Player.findOneAndDelete({ name: req.params.name }); if (!deletedPlayer) { + logToFile('API: Player not found for delete', req.params.name); return res.status(404).json({ error: 'Player not found' }); } + logToFile('API: Player deleted', req.params.name); res.status(204).send(); } catch (error) { + logToFile('API: Failed to delete player', error); res.status(500).json({ error: 'Failed to delete player' }); } }); diff --git a/database/routes/sessionRoutes-sqlite.js b/database/routes/sessionRoutes-sqlite.js new file mode 100644 index 0000000..ffbb79c --- /dev/null +++ b/database/routes/sessionRoutes-sqlite.js @@ -0,0 +1,111 @@ +const express = require('express'); +const { sessionHelpers, playerHelpers } = require('../sqlite-db'); +const crypto = require('crypto'); +const fs = require('fs'); +const path = require('path'); + +// Simple file logger +function logToFile(...args) { + const msg = `[${new Date().toISOString()}] ` + args.map(a => (typeof a === 'object' ? JSON.stringify(a) : String(a))).join(' ') + '\n'; + fs.appendFileSync(path.join(__dirname, '../backend.log'), msg, { encoding: 'utf8' }); +} + +const router = express.Router(); + +// Create a new session (login) +router.post('/login', async (req, res) => { + try { + const { playerName, username, password } = req.body; + const name = playerName || username; // Accept either field name + + if (!name) { + logToFile('SESSION: Login missing playerName/username'); + return res.status(400).json({ error: 'playerName or username required' }); + } + + // Check if player exists + const player = playerHelpers.getByName(name); + if (!player) { + logToFile('SESSION: Login player not found', name); + return res.status(401).json({ error: 'Invalid credentials' }); + } + + // Check password if provided + if (password) { + // For now, compare with plain text (you can add bcrypt later) + const playerPw = player.pw || ''; + if (playerPw !== password) { + logToFile('SESSION: Login invalid password', name); + return res.status(401).json({ error: 'Invalid credentials' }); + } + } + + // Create session + const sessionId = crypto.randomBytes(32).toString('hex'); + const expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(); // 24h + + sessionHelpers.create(sessionId, { playerName: name }, expiresAt); + + logToFile('SESSION: Login success', name, sessionId); + res.json({ sessionId, expiresAt, playerName: name, success: true }); + } catch (error) { + logToFile('SESSION: Login error', error); + res.status(500).json({ error: 'Login failed' }); + } +}); + +// Validate session +router.post('/validate', async (req, res) => { + try { + const { sessionId } = req.body; + + if (!sessionId) { + logToFile('SESSION: Validate missing sessionId'); + return res.status(400).json({ error: 'sessionId required' }); + } + + const session = sessionHelpers.get(sessionId); + + if (!session) { + logToFile('SESSION: Validate not found', sessionId); + return res.status(401).json({ error: 'Invalid or expired session' }); + } + + logToFile('SESSION: Validate success', sessionId, session.data.playerName); + res.json({ valid: true, playerName: session.data.playerName }); + } catch (error) { + logToFile('SESSION: Validate error', error); + res.status(500).json({ error: 'Validation failed' }); + } +}); + +// Logout (delete session) +router.post('/logout', async (req, res) => { + try { + const { sessionId } = req.body; + + if (sessionId) { + sessionHelpers.delete(sessionId); + logToFile('SESSION: Logout success', sessionId); + } + + res.json({ success: true }); + } catch (error) { + logToFile('SESSION: Logout error', error); + res.status(500).json({ error: 'Logout failed' }); + } +}); + +// Clean expired sessions (can be called periodically) +router.post('/cleanup', async (req, res) => { + try { + const deletedCount = sessionHelpers.cleanExpired(); + logToFile('SESSION: Cleanup completed', `${deletedCount} sessions removed`); + res.json({ deletedCount }); + } catch (error) { + logToFile('SESSION: Cleanup error', error); + res.status(500).json({ error: 'Cleanup failed' }); + } +}); + +module.exports = router; diff --git a/database/routes/sessionRoutes.js b/database/routes/sessionRoutes.js new file mode 100644 index 0000000..6b351a2 --- /dev/null +++ b/database/routes/sessionRoutes.js @@ -0,0 +1,50 @@ +const express = require('express'); +const Session = require('../sessionModel'); +const router = express.Router(); +const crypto = require('crypto'); + +// Create a new session (login) +router.post('/login', async (req, res) => { + const { playerName } = req.body; + if (!playerName) return res.status(400).json({ error: 'playerName required' }); + const sessionId = crypto.randomBytes(32).toString('hex'); + const expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24); // 24h + const session = new Session({ sessionId, playerName, expiresAt }); + await session.save(); + res.json({ sessionId, expiresAt }); +}); + +// Validate session +const fs = require('fs'); +const path = require('path'); +function logToFile(...args) { + const msg = `[${new Date().toISOString()}] ` + args.map(a => (typeof a === 'object' ? JSON.stringify(a) : String(a))).join(' ') + '\n'; + fs.appendFileSync(path.join(__dirname, '../backend.log'), msg, { encoding: 'utf8' }); +} +router.post('/validate', async (req, res) => { + const { sessionId } = req.body; + if (!sessionId) { + logToFile('SESSION: Validate missing sessionId'); + return res.status(400).json({ error: 'sessionId required' }); + } + const session = await Session.findOne({ sessionId }); + if (!session) { + logToFile('SESSION: Validate not found', sessionId); + return res.status(401).json({ error: 'Invalid or expired session' }); + } + if (session.expiresAt < new Date()) { + logToFile('SESSION: Validate expired', sessionId); + return res.status(401).json({ error: 'Invalid or expired session' }); + } + logToFile('SESSION: Validate success', sessionId, session.playerName); + res.json({ valid: true, playerName: session.playerName }); +}); + +// Logout (delete session) +router.post('/logout', async (req, res) => { + const { sessionId } = req.body; + await Session.deleteOne({ sessionId }); + res.json({ success: true }); +}); + +module.exports = router; diff --git a/database/server-mongo-backup.js b/database/server-mongo-backup.js new file mode 100755 index 0000000..b66a9eb --- /dev/null +++ b/database/server-mongo-backup.js @@ -0,0 +1,45 @@ +require('dotenv').config(); +const express = require('express'); +const cors = require('cors'); +const playerRoutes = require('./routes/playerRoutes-sqlite'); +// const sessionRoutes = require('./routes/sessionRoutes'); // You can update this later if needed + +const app = express(); +const PORT = process.env.PORT || 5000; + +// Middleware +app.use(express.json()); +app.use(cors()); + +// Initialize SQLite database +const { db } = require('./sqlite-db'); + +// Graceful shutdown +process.on('SIGINT', () => { + console.log('Closing SQLite database...'); + db.close(); + process.exit(0); +}); + +process.on('SIGTERM', () => { + console.log('Closing SQLite database...'); + db.close(); + process.exit(0); +}); + +// Root route for friendly message +app.get('/', (req, res) => { + res.send('Deathwatch Roller API is running with SQLite. Use /api/players for player data.'); +}); + +// Use player routes +app.use('/api/players', playerRoutes); +// app.use('/api/sessions', sessionRoutes); // Uncomment when you update session routes + +// Start Server +app.listen(PORT, '0.0.0.0', () => { + console.log(`Server running on http://0.0.0.0:${PORT}`); + console.log('Using SQLite database'); +}); + +module.exports = app; diff --git a/database/server-sqlite.js b/database/server-sqlite.js new file mode 100644 index 0000000..c6202de --- /dev/null +++ b/database/server-sqlite.js @@ -0,0 +1,45 @@ +require('dotenv').config(); +const express = require('express'); +const cors = require('cors'); +const playerRoutes = require('./routes/playerRoutes-sqlite'); +const sessionRoutes = require('./routes/sessionRoutes-sqlite'); + +const app = express(); +const PORT = process.env.PORT || 5000; + +// Middleware +app.use(express.json()); +app.use(cors()); + +// Initialize SQLite database +const { db } = require('./sqlite-db'); + +// Graceful shutdown +process.on('SIGINT', () => { + console.log('Closing SQLite database...'); + db.close(); + process.exit(0); +}); + +process.on('SIGTERM', () => { + console.log('Closing SQLite database...'); + db.close(); + process.exit(0); +}); + +// Root route for friendly message +app.get('/', (req, res) => { + res.send('Deathwatch Roller API is running with SQLite. Use /api/players for player data.'); +}); + +// Use routes +app.use('/api/players', playerRoutes); +app.use('/api/sessions', sessionRoutes); + +// Start Server +app.listen(PORT, '0.0.0.0', () => { + console.log(`Server running on http://0.0.0.0:${PORT}`); + console.log('Using SQLite database'); +}); + +module.exports = app; diff --git a/database/server.js b/database/server.js old mode 100644 new mode 100755 index 4b8d6d4..c6202de --- a/database/server.js +++ b/database/server.js @@ -1,28 +1,45 @@ +require('dotenv').config(); const express = require('express'); -const mongoose = require('mongoose'); const cors = require('cors'); -const playerRoutes = require('./routes/playerRoutes'); +const playerRoutes = require('./routes/playerRoutes-sqlite'); +const sessionRoutes = require('./routes/sessionRoutes-sqlite'); const app = express(); -const PORT = 5000; +const PORT = process.env.PORT || 5000; // Middleware app.use(express.json()); app.use(cors()); -// MongoDB Connection -mongoose.connect('mongodb://localhost:27017/deathwatch'); +// Initialize SQLite database +const { db } = require('./sqlite-db'); -const db = mongoose.connection; -db.on('error', console.error.bind(console, 'connection error:')); -db.once('open', () => { - console.log('Connected to MongoDB'); +// Graceful shutdown +process.on('SIGINT', () => { + console.log('Closing SQLite database...'); + db.close(); + process.exit(0); }); -// Use player routes +process.on('SIGTERM', () => { + console.log('Closing SQLite database...'); + db.close(); + process.exit(0); +}); + +// Root route for friendly message +app.get('/', (req, res) => { + res.send('Deathwatch Roller API is running with SQLite. Use /api/players for player data.'); +}); + +// Use routes app.use('/api/players', playerRoutes); +app.use('/api/sessions', sessionRoutes); // Start Server -app.listen(PORT, () => { - console.log(`Server running on http://localhost:${PORT}`); +app.listen(PORT, '0.0.0.0', () => { + console.log(`Server running on http://0.0.0.0:${PORT}`); + console.log('Using SQLite database'); }); + +module.exports = app; diff --git a/database/sessionModel.js b/database/sessionModel.js new file mode 100644 index 0000000..a16c647 --- /dev/null +++ b/database/sessionModel.js @@ -0,0 +1,10 @@ +const mongoose = require('mongoose'); + +const sessionSchema = new mongoose.Schema({ + sessionId: { type: String, required: true, unique: true }, + playerName: { type: String, required: true }, + createdAt: { type: Date, default: Date.now }, + expiresAt: { type: Date, required: true }, +}); + +module.exports = mongoose.model('Session', sessionSchema); diff --git a/database/sqlite-db.js b/database/sqlite-db.js new file mode 100644 index 0000000..cea5381 --- /dev/null +++ b/database/sqlite-db.js @@ -0,0 +1,190 @@ +const Database = require('better-sqlite3'); +const path = require('path'); +const fs = require('fs'); + +// Create database directory if it doesn't exist +const dbDir = path.join(__dirname, 'sqlite'); +if (!fs.existsSync(dbDir)) { + fs.mkdirSync(dbDir, { recursive: true }); +} + +const dbPath = path.join(dbDir, 'deathwatch.db'); +const db = new Database(dbPath); + +// Enable WAL mode for better concurrency +db.pragma('journal_mode = WAL'); + +// Create tables +const createTables = () => { + // Players table + db.exec(` + CREATE TABLE IF NOT EXISTS players ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT UNIQUE NOT NULL, + roller_info TEXT DEFAULT '{}', + shop_info TEXT DEFAULT '{}', + tab_info TEXT DEFAULT '{}', + pw TEXT DEFAULT '', + pw_hash TEXT DEFAULT '', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) + `); + + // Sessions table (if you need it) + db.exec(` + CREATE TABLE IF NOT EXISTS sessions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + session_id TEXT UNIQUE NOT NULL, + data TEXT DEFAULT '{}', + expires_at DATETIME, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) + `); + + // Create indexes for better performance + db.exec(` + CREATE INDEX IF NOT EXISTS idx_players_name ON players(name); + CREATE INDEX IF NOT EXISTS idx_sessions_session_id ON sessions(session_id); + CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON sessions(expires_at); + `); + + console.log('SQLite tables created successfully'); +}; + +// Initialize database +createTables(); + +// Prepared statements for common operations +const statements = { + // Player operations + getAllPlayers: db.prepare('SELECT * FROM players ORDER BY name'), + getPlayerByName: db.prepare('SELECT * FROM players WHERE name = ?'), + insertPlayer: db.prepare(` + INSERT INTO players (name, roller_info, shop_info, tab_info, pw, pw_hash) + VALUES (?, ?, ?, ?, ?, ?) + `), + updatePlayer: db.prepare(` + UPDATE players + SET roller_info = ?, shop_info = ?, tab_info = ?, pw = ?, pw_hash = ?, updated_at = CURRENT_TIMESTAMP + WHERE name = ? + `), + deletePlayer: db.prepare('DELETE FROM players WHERE name = ?'), + + // Session operations + getSession: db.prepare('SELECT * FROM sessions WHERE session_id = ? AND expires_at > datetime(\'now\')'), + insertSession: db.prepare(` + INSERT INTO sessions (session_id, data, expires_at) + VALUES (?, ?, ?) + `), + updateSession: db.prepare(` + UPDATE sessions + SET data = ?, expires_at = ?, updated_at = CURRENT_TIMESTAMP + WHERE session_id = ? + `), + deleteSession: db.prepare('DELETE FROM sessions WHERE session_id = ?'), + cleanExpiredSessions: db.prepare('DELETE FROM sessions WHERE expires_at <= datetime(\'now\')') +}; + +// Helper functions +const playerHelpers = { + getAll: () => { + const rows = statements.getAllPlayers.all(); + return rows.map(row => ({ + name: row.name, + rollerInfo: JSON.parse(row.roller_info || '{}'), + shopInfo: JSON.parse(row.shop_info || '{}'), + tabInfo: JSON.parse(row.tab_info || '{}'), + pw: row.pw, + pwHash: row.pw_hash, + _id: row.id, + createdAt: row.created_at, + updatedAt: row.updated_at + })); + }, + + getByName: (name) => { + const row = statements.getPlayerByName.get(name); + if (!row) return null; + return { + name: row.name, + rollerInfo: JSON.parse(row.roller_info || '{}'), + shopInfo: JSON.parse(row.shop_info || '{}'), + tabInfo: JSON.parse(row.tab_info || '{}'), + pw: row.pw, + pwHash: row.pw_hash, + _id: row.id, + createdAt: row.created_at, + updatedAt: row.updated_at + }; + }, + + create: (playerData) => { + const result = statements.insertPlayer.run( + playerData.name, + JSON.stringify(playerData.rollerInfo || {}), + JSON.stringify(playerData.shopInfo || {}), + JSON.stringify(playerData.tabInfo || {}), + playerData.pw || '', + playerData.pwHash || '' + ); + return { ...playerData, _id: result.lastInsertRowid }; + }, + + update: (name, playerData) => { + const result = statements.updatePlayer.run( + JSON.stringify(playerData.rollerInfo || {}), + JSON.stringify(playerData.shopInfo || {}), + JSON.stringify(playerData.tabInfo || {}), + playerData.pw || '', + playerData.pwHash || '', + name + ); + return result.changes > 0; + }, + + delete: (name) => { + const result = statements.deletePlayer.run(name); + return result.changes > 0; + } +}; + +const sessionHelpers = { + get: (sessionId) => { + const row = statements.getSession.get(sessionId); + if (!row) return null; + return { + sessionId: row.session_id, + data: JSON.parse(row.data || '{}'), + expiresAt: row.expires_at + }; + }, + + create: (sessionId, data, expiresAt) => { + statements.insertSession.run(sessionId, JSON.stringify(data), expiresAt); + }, + + update: (sessionId, data, expiresAt) => { + const result = statements.updateSession.run(JSON.stringify(data), expiresAt, sessionId); + return result.changes > 0; + }, + + delete: (sessionId) => { + const result = statements.deleteSession.run(sessionId); + return result.changes > 0; + }, + + cleanExpired: () => { + const result = statements.cleanExpiredSessions.run(); + return result.changes; + } +}; + +module.exports = { + db, + statements, + playerHelpers, + sessionHelpers, + close: () => db.close() +}; diff --git a/database/sqlite/deathwatch.db b/database/sqlite/deathwatch.db new file mode 100644 index 0000000..29a145d Binary files /dev/null and b/database/sqlite/deathwatch.db differ diff --git a/database/switch-db.sh b/database/switch-db.sh new file mode 100755 index 0000000..8048217 --- /dev/null +++ b/database/switch-db.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Deathwatch Roller Database Switcher +# Usage: ./switch-db.sh [mongo|sqlite] + +DB_TYPE=${1:-sqlite} + +echo "Switching to $DB_TYPE database..." + +if [ "$DB_TYPE" = "sqlite" ]; then + echo "Setting up SQLite configuration..." + + # Backup current server.js if it exists + if [ -f "server.js" ]; then + cp server.js server-mongo-backup.js + echo "✓ Backed up MongoDB server.js to server-mongo-backup.js" + fi + + # Use SQLite server + cp server-sqlite.js server.js + echo "✓ Using SQLite server configuration" + + echo "✓ SQLite database is ready at: database/sqlite/deathwatch.db" + echo "✓ Your data has been migrated from MongoDB" + +elif [ "$DB_TYPE" = "mongo" ]; then + echo "Setting up MongoDB configuration..." + + # Restore MongoDB server if backup exists + if [ -f "server-mongo-backup.js" ]; then + cp server-mongo-backup.js server.js + echo "✓ Restored MongoDB server configuration" + else + echo "⚠ No MongoDB backup found. You may need to recreate server.js for MongoDB" + fi + + echo "✓ MongoDB configuration restored" + echo "⚠ Make sure MongoDB service is running" + +else + echo "❌ Invalid database type. Use 'mongo' or 'sqlite'" + exit 1 +fi + +echo "" +echo "Database switch complete!" +echo "Restart your server to use the new configuration:" +echo " cd database && node server.js" diff --git a/database/update_passwords.js b/database/update_passwords.js new file mode 100644 index 0000000..cf19d2b --- /dev/null +++ b/database/update_passwords.js @@ -0,0 +1,30 @@ +const { db, playerHelpers } = require('./sqlite-db'); + +// Update all player passwords to '1234' +function updateAllPasswords() { + try { + // First check the table structure + const columns = db.prepare("PRAGMA table_info(players)").all(); + console.log('Table columns:', columns.map(c => c.name)); + + // Update only the pw column (pwHash might not exist) + const updateStmt = db.prepare('UPDATE players SET pw = ?'); + const result = updateStmt.run('1234'); + + console.log(`Updated ${result.changes} player passwords to '1234'`); + + // Verify the changes + const players = playerHelpers.getAll(); + console.log('Current players:'); + players.forEach(player => { + console.log(`- ${player.name}: pw="${player.pw}"`); + }); + + } catch (error) { + console.error('Error updating passwords:', error); + } finally { + db.close(); + } +} + +updateAllPasswords(); diff --git a/eslint.config.js b/eslint.config.js old mode 100644 new mode 100755 diff --git a/jest.config.js b/jest.config.js old mode 100644 new mode 100755 diff --git a/package-lock.json b/package-lock.json old mode 100644 new mode 100755 index 407d05a..9b11aa2 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,11 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.11.0", + "better-sqlite3": "^12.2.0", + "cors": "^2.8.5", + "express": "^5.1.0", + "mongoose": "^8.17.1", "pdf-parse": "^1.1.1", "pdfjs-dist": "^4.8.69", "react": "^18.2.0", @@ -21,6 +26,7 @@ "@pmmmwh/react-refresh-webpack-plugin": "^0.6.1", "autoprefixer": "^10.4.14", "babel-loader": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "html-webpack-plugin": "^5.6.4", "postcss": "^8.4.23", "prettier-eslint": "^16.4.2", @@ -4081,6 +4087,14 @@ "dev": true, "license": "MIT" }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz", + "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@napi-rs/canvas": { "version": "0.1.77", "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.77.tgz", @@ -5361,6 +5375,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -6221,8 +6248,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/array-includes": { "version": "3.1.9", @@ -6435,7 +6461,6 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, "license": "MIT" }, "node_modules/at-least-node": { @@ -6511,6 +6536,31 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -6810,6 +6860,25 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -6817,6 +6886,19 @@ "dev": true, "license": "MIT" }, + "node_modules/better-sqlite3": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.2.0.tgz", + "integrity": "sha512-eGbYq2CT+tos1fBwLQ/tkBt9J5M3JEHjku4hbvQUePCckkvVf14xWj+1m7dGoK81M/fOjFT7yM9UMeKT/+vFLQ==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x" + } + }, "node_modules/bfj": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.1.0.tgz", @@ -6857,6 +6939,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -6865,60 +6965,24 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "dev": true, - "license": "MIT", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=18" } }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, "node_modules/bonjour-service": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", @@ -7010,6 +7074,37 @@ "node-int64": "^0.4.0" } }, + "node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -7050,7 +7145,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -7259,6 +7353,11 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, "node_modules/chrome-trace-event": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", @@ -7473,7 +7572,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -7583,11 +7681,9 @@ } }, "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "license": "MIT", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", "dependencies": { "safe-buffer": "5.2.1" }, @@ -7599,8 +7695,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -7616,18 +7710,18 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dev": true, - "license": "MIT" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "engines": { + "node": ">=6.6.0" + } }, "node_modules/core-js": { "version": "3.45.0", @@ -7674,6 +7768,18 @@ "dev": true, "license": "MIT" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -8215,7 +8321,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -8236,6 +8341,20 @@ "dev": true, "license": "MIT" }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -8275,6 +8394,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -8383,7 +8510,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -8393,8 +8519,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -8413,12 +8537,19 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -8693,7 +8824,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true, "license": "MIT" }, "node_modules/ejs": { @@ -8753,12 +8883,18 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", @@ -8968,7 +9104,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -9025,7 +9160,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true, "license": "MIT" }, "node_modules/escape-string-regexp": { @@ -9131,6 +9265,21 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-config-react-app": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", @@ -9657,8 +9806,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -9713,6 +9860,14 @@ "node": ">= 0.8.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "engines": { + "node": ">=6" + } + }, "node_modules/expect": { "version": "30.0.5", "resolved": "https://registry.npmjs.org/expect/-/expect-30.0.5.tgz", @@ -9731,68 +9886,84 @@ } }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "dev": true, - "license": "MIT", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" }, "engines": { - "node": ">= 0.10.0" + "node": ">= 18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", + "node_modules/express/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "dependencies": { - "ms": "2.0.0" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" } }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" + "node_modules/express/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "engines": { + "node": ">= 0.6" + } }, "node_modules/fast-deep-equal": { "version": "3.1.3", @@ -9948,6 +10119,11 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -10004,41 +10180,21 @@ } }, "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "dev": true, - "license": "MIT", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { "node": ">= 0.8" } }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, "node_modules/find-cache-dir": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", @@ -10116,7 +10272,6 @@ "version": "1.15.11", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "dev": true, "funding": [ { "type": "individual", @@ -10331,7 +10486,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -10352,15 +10506,18 @@ } }, "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true, - "license": "MIT", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, "node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -10549,6 +10706,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -11046,8 +11208,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "license": "MIT", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -11059,6 +11219,14 @@ "node": ">= 0.8" } }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/http-parser-js": { "version": "0.5.10", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", @@ -11159,7 +11327,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -11201,6 +11368,25 @@ "node": ">=4" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -11304,14 +11490,12 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, "license": "ISC" }, "node_modules/internal-slot": { @@ -11743,6 +11927,11 @@ "dev": true, "license": "MIT" }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -15101,6 +15290,14 @@ "node": ">=4.0" } }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -15489,13 +15686,11 @@ "license": "CC0-1.0" }, "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true, - "license": "MIT", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/memfs": { @@ -15511,12 +15706,18 @@ "node": ">= 4.0.0" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + }, "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "dev": true, - "license": "MIT", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "engines": { + "node": ">=18" + }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } @@ -15543,7 +15744,6 @@ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -15578,7 +15778,6 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, - "license": "MIT", "bin": { "mime": "cli.js" }, @@ -15590,7 +15789,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -15600,7 +15798,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -15619,6 +15816,17 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -15673,7 +15881,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -15702,6 +15909,136 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "node_modules/mongodb": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.18.0.tgz", + "integrity": "sha512-fO5ttN9VC8P0F5fqtQmclAkgXZxbIkYRTUi1j8JO6IYwvamkhtYDilJr35jOPELR49zqCJgXZWwCtW7B+TM8vQ==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/mongoose": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.17.1.tgz", + "integrity": "sha512-aodS4cacux5caoxB5ErEwRmrafIUsVRJxHnvP7URnSUnTenr32j1qBVV+KjYxryyLSisQkxglAFF69TNLeZTLg==", + "dependencies": { + "bson": "^6.10.4", + "kareem": "2.6.3", + "mongodb": "~6.18.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -15753,6 +16090,11 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -15795,6 +16137,28 @@ "tslib": "^2.0.3" } }, + "node_modules/node-abi": { + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-ensure": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz", @@ -15895,7 +16259,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -16070,7 +16433,6 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, "license": "MIT", "dependencies": { "ee-first": "1.1.1" @@ -16093,7 +16455,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -16290,7 +16651,6 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -16369,11 +16729,12 @@ "license": "ISC" }, "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "dev": true, - "license": "MIT" + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "engines": { + "node": ">=16" + } }, "node_modules/path-type": { "version": "4.0.0", @@ -18023,6 +18384,31 @@ "dev": true, "license": "MIT" }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -18379,7 +18765,6 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -18393,12 +18778,16 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", @@ -18412,11 +18801,19 @@ "url": "https://github.com/sponsors/lupomontero" } }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -18435,13 +18832,11 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dev": true, - "license": "BSD-3-Clause", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -18502,37 +18897,43 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dev": true, - "license": "MIT", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", - "iconv-lite": "0.4.24", + "iconv-lite": "0.6.3", "unpipe": "1.0.0" }, "engines": { "node": ">= 0.8" } }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "license": "MIT", + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "engines": { "node": ">=0.10.0" } @@ -18836,6 +19237,148 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/react-scripts/node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/react-scripts/node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/react-scripts/node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/react-scripts/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/react-scripts/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/react-scripts/node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/react-scripts/node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-scripts/node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/react-scripts/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react-scripts/node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -18862,6 +19405,24 @@ "semver": "bin/semver.js" } }, + "node_modules/react-scripts/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/react-scripts/node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/react-scripts/node_modules/p-retry": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", @@ -18876,6 +19437,42 @@ "node": ">=8" } }, + "node_modules/react-scripts/node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "dev": true + }, + "node_modules/react-scripts/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/react-scripts/node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/react-scripts/node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -18899,6 +19496,76 @@ "node": ">=10" } }, + "node_modules/react-scripts/node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/react-scripts/node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-scripts/node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/react-scripts/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-scripts/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/react-scripts/node_modules/webpack-dev-middleware": { "version": "5.3.4", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", @@ -19019,7 +19686,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -19488,6 +20154,21 @@ "randombytes": "^2.1.0" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/run-applescript": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", @@ -19549,7 +20230,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -19604,7 +20284,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, "license": "MIT" }, "node_modules/sanitize.css": { @@ -19771,55 +20450,43 @@ } }, "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "dev": true, - "license": "MIT", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 18" } }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true, - "license": "MIT", + "node_modules/send/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "engines": { - "node": ">= 0.8" + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" } }, "node_modules/serialize-javascript": { @@ -19919,19 +20586,17 @@ } }, "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "dev": true, - "license": "MIT", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 18" } }, "node_modules/set-function-length": { @@ -19984,9 +20649,7 @@ "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true, - "license": "ISC" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/shebang-command": { "version": "2.0.0", @@ -20096,6 +20759,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -20103,6 +20771,49 @@ "dev": true, "license": "ISC" }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -20209,6 +20920,14 @@ "dev": true, "license": "MIT" }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", @@ -20393,11 +21112,9 @@ } }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "license": "MIT", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "engines": { "node": ">= 0.8" } @@ -20419,7 +21136,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -21101,6 +21817,32 @@ "node": ">=6" } }, + "node_modules/tar-fs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", + "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/temp-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", @@ -21320,8 +22062,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "license": "MIT", "engines": { "node": ">=0.6" } @@ -21474,6 +22214,17 @@ "dev": true, "license": "0BSD" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -21511,14 +22262,32 @@ } }, "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "license": "MIT", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "dependencies": { + "mime-db": "^1.54.0" }, "engines": { "node": ">= 0.6" @@ -21729,8 +22498,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -21809,7 +22576,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, "license": "MIT" }, "node_modules/util.promisify": { @@ -21840,7 +22606,6 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -21881,7 +22646,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" @@ -22154,6 +22918,63 @@ } } }, + "node_modules/webpack-dev-server/node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/webpack-dev-server/node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server/node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, "node_modules/webpack-dev-server/node_modules/define-lazy-prop": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", @@ -22167,6 +22988,109 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/webpack-dev-server/node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/webpack-dev-server/node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webpack-dev-server/node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack-dev-server/node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/webpack-dev-server/node_modules/open": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", @@ -22186,6 +23110,112 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/webpack-dev-server/node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/webpack-dev-server/node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webpack-dev-server/node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/webpack-dev-server/node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webpack-dev-server/node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/webpack-dev-server/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webpack-dev-server/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/webpack-dev-server/node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", @@ -22864,7 +23894,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { diff --git a/package.json b/package.json old mode 100644 new mode 100755 index cb91aa8..0141d76 --- a/package.json +++ b/package.json @@ -6,6 +6,11 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.11.0", + "better-sqlite3": "^12.2.0", + "cors": "^2.8.5", + "express": "^5.1.0", + "mongoose": "^8.17.1", "pdf-parse": "^1.1.1", "pdfjs-dist": "^4.8.69", "react": "^18.2.0", @@ -19,7 +24,12 @@ "test": "jest", "eject": "react-scripts eject", "serve": "serve -s build", - "extract:armoury": "node scripts/extract-armoury.js \"$PWD/Deathwatch-Core-Rulebook.pdf\" public/deathwatch-armoury.json" + "extract:armoury": "node scripts/extract-armoury.js \"$PWD/Deathwatch-Core-Rulebook.pdf\" public/deathwatch-armoury.json", + "server": "cd database && node server.js", + "db:migrate": "cd database && node migrate-to-sqlite.js", + "db:export": "cd database && node migrate-to-sqlite.js --export-only", + "db:sqlite": "cd database && ./switch-db.sh sqlite", + "db:mongo": "cd database && ./switch-db.sh mongo" }, "eslintConfig": { "extends": [ @@ -44,6 +54,7 @@ "@pmmmwh/react-refresh-webpack-plugin": "^0.6.1", "autoprefixer": "^10.4.14", "babel-loader": "^10.0.0", + "eslint-config-prettier": "^10.1.8", "html-webpack-plugin": "^5.6.4", "postcss": "^8.4.23", "prettier-eslint": "^16.4.2", diff --git a/postcss.config.js b/postcss.config.js old mode 100644 new mode 100755 diff --git a/public/deathwatch-armoury.json b/public/deathwatch-armoury.json old mode 100644 new mode 100755 diff --git a/public/favicon.ico b/public/favicon.ico old mode 100644 new mode 100755 diff --git a/public/index.html b/public/index.html old mode 100644 new mode 100755 diff --git a/public/logo192.png b/public/logo192.png old mode 100644 new mode 100755 diff --git a/public/logo512.png b/public/logo512.png old mode 100644 new mode 100755 diff --git a/public/manifest.json b/public/manifest.json old mode 100644 new mode 100755 diff --git a/public/robots.txt b/public/robots.txt old mode 100644 new mode 100755 diff --git a/scripts/clean-armoury.js b/scripts/clean-armoury.js old mode 100644 new mode 100755 diff --git a/scripts/dbinstall.js b/scripts/dbinstall.js old mode 100644 new mode 100755 diff --git a/scripts/extract-armoury.js b/scripts/extract-armoury.js old mode 100644 new mode 100755 diff --git a/scripts/install-mongodb.js b/scripts/install-mongodb.js old mode 100644 new mode 100755 diff --git a/server.log b/server.log new file mode 100755 index 0000000..76bd98d --- /dev/null +++ b/server.log @@ -0,0 +1,6 @@ +nohup: ignoring input +(node:2700124) [MONGODB DRIVER] Warning: useNewUrlParser is a deprecated option: useNewUrlParser has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version +(Use `node --trace-warnings ...` to show where the warning was created) +(node:2700124) [MONGODB DRIVER] Warning: useUnifiedTopology is a deprecated option: useUnifiedTopology has no effect since Node.js Driver version 4.0.0 and will be removed in the next major version +Server running on http://localhost:3005 +Connected to MongoDB diff --git a/src/App.css b/src/App.css old mode 100644 new mode 100755 diff --git a/src/App.js b/src/App.js old mode 100644 new mode 100755 index aec8064..633f0d3 --- a/src/App.js +++ b/src/App.js @@ -1,22 +1,235 @@ + import './App.css'; import DeathwatchRoller from './components/DeathwatchRoller'; import RequisitionShop from './components/RequisitionShop'; import PlayerTab from './components/PlayerTab'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; +import axios from 'axios'; +import { debug, info, warn, error, logApiCall, logApiError, logUserAction } from './utils/logger'; function App() { const [tab, setTab] = useState('roller'); + // Global login/session state + const [authedPlayer, setAuthedPlayer] = useState(() => localStorage.getItem('dw:shop:authedPlayer') ? JSON.parse(localStorage.getItem('dw:shop:authedPlayer')) : ''); + const [sessionId, setSessionId] = useState(() => localStorage.getItem('dw:shop:sessionId') ? JSON.parse(localStorage.getItem('dw:shop:sessionId')) : ''); + const [loginName, setLoginName] = useState(''); + const [loginPw, setLoginPw] = useState(''); + const [loginMsg, setLoginMsg] = useState(''); + const [players, setPlayers] = useState([]); + + // Fetch player list for login + useEffect(() => { + async function fetchPlayers() { + try { + const res = await axios.get('/api/players'); + setPlayers(res.data); + } catch { + setPlayers([]); + } + } + fetchPlayers(); + }, []); + + // Validate session on mount/refresh + useEffect(() => { + async function validate() { + if (sessionId) { + try { + debug(`Validating session: ${sessionId}`, 'auth'); + const res = await axios.post('/api/sessions/validate', { sessionId }); + logApiCall('POST', '/api/sessions/validate', { sessionId }, res.status); + + if (res.data && res.data.playerName) { + setAuthedPlayer(res.data.playerName); + localStorage.setItem('dw:shop:authedPlayer', JSON.stringify(res.data.playerName)); + info(`Session validation successful for: ${res.data.playerName}`, 'auth'); + } else { + warn('Session validation failed - invalid response', 'auth'); + setAuthedPlayer(''); + setSessionId(''); + localStorage.removeItem('dw:shop:authedPlayer'); + localStorage.removeItem('dw:shop:sessionId'); + } + } catch (err) { + logApiError('POST', '/api/sessions/validate', err); + error(`Session validation error: ${err.message}`, 'auth'); + setAuthedPlayer(''); + setSessionId(''); + localStorage.removeItem('dw:shop:authedPlayer'); + localStorage.removeItem('dw:shop:sessionId'); + } + } else { + debug('No session ID found for validation', 'auth'); + } + } + validate(); + }, [sessionId]); + + const handleKeyPress = (e) => { + if (e.key === 'Enter') { + handleLogin(); + } + }; + + const handleLogin = async () => { + try { + logUserAction('user', 'Login attempt', { username: loginName }); + const response = await axios.post('/api/sessions/login', { + username: loginName, + password: loginPw + }); + + logApiCall('POST', '/api/sessions/login', { username: loginName }, response.status); + + if (response.data.success) { + // Update global state with session info + setAuthedPlayer(response.data.playerName); + setSessionId(response.data.sessionId); + localStorage.setItem('dw:shop:authedPlayer', JSON.stringify(response.data.playerName)); + localStorage.setItem('dw:shop:sessionId', JSON.stringify(response.data.sessionId)); + + info(`Login successful for user: ${loginName}`, 'auth'); + logUserAction('user', 'Login successful', { username: loginName }); + setLoginMsg('Login successful!'); + setLoginName(''); + setLoginPw(''); + + // Clear success message after 3 seconds + setTimeout(() => setLoginMsg(''), 3000); + } else { + warn(`Login failed for user: ${loginName} - ${response.data.message}`, 'auth'); + setLoginMsg(response.data.message || 'Login failed'); + + // Clear error message after 5 seconds + setTimeout(() => setLoginMsg(''), 5000); + } + } catch (err) { + logApiError('POST', '/api/sessions/login', err); + error(`Login error for user: ${loginName} - ${err.message}`, 'auth'); + setLoginMsg('Login failed. Please check your credentials and try again.'); + + // Clear error message after 5 seconds + setTimeout(() => setLoginMsg(''), 5000); + } + }; + + async function handleLogout() { + logUserAction('user', 'Logout attempt', { username: authedPlayer }); + if (sessionId) { + try { + await axios.post('/api/sessions/logout', { sessionId }); + info(`Logout successful for user: ${authedPlayer}`, 'auth'); + } catch (err) { + warn(`Logout API call failed: ${err.message}`, 'auth'); + } + } + setAuthedPlayer(''); + setSessionId(''); + localStorage.removeItem('dw:shop:authedPlayer'); + localStorage.removeItem('dw:shop:sessionId'); + setLoginMsg('Logged out successfully'); + info('User logged out successfully', 'auth'); + + // Clear logout message after 3 seconds + setTimeout(() => setLoginMsg(''), 3000); + } return ( -
Password for all users: 1234
Please log in to access player data
+Please log in using the header above to access the shop.