From 671865d1004904623b2f9df1ec986e87af89edfc Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 16 Aug 2025 15:14:28 +0200 Subject: [PATCH] feat: enhance build process and testing workflow - Updated package.json scripts to include separate unit and integration tests before building. - Added a new script `test-build-test` to run tests before and after the build process. - Introduced a new local CI/CD script for streamlined local development and testing. - Refactored PlayerTab component to normalize skills data structure and added tooltips for Space Marine and Power Armour abilities. - Added named export for Tooltip in DeathwatchRoller for reuse in other components. --- database/backend.log | 1246 ++++++++++++++++++++++++ database/routes/playerRoutes-sqlite.js | 9 +- database/scripts/pm2_smoke_test.sh | 0 database/server.js | 72 +- database/sqlite/deathwatch.db | Bin 131072 -> 192512 bytes database/sqlite/deathwatch.db-shm | Bin 32768 -> 32768 bytes database/sqlite/deathwatch.db-wal | Bin 3427872 -> 4120032 bytes package-lock.json | 1016 ++++++++++++++++++- package.json | 14 +- scripts/local-ci.sh | 36 + src/components/DeathwatchRoller.jsx | 3 + src/components/PlayerTab.jsx | 101 +- 12 files changed, 2462 insertions(+), 35 deletions(-) mode change 100644 => 100755 database/scripts/pm2_smoke_test.sh create mode 100755 scripts/local-ci.sh diff --git a/database/backend.log b/database/backend.log index 6aea169..967d558 100644 --- a/database/backend.log +++ b/database/backend.log @@ -6968,3 +6968,1249 @@ Connected to MongoDB [2025-08-16T10:59:27.227Z] API: Deleted player shoptestplayer [2025-08-16T10:59:27.726Z] DB: getAllPlayers - start [2025-08-16T10:59:27.728Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T11:01:45.300Z] SESSION: Missing sessionId GET /api/players +[2025-08-16T11:04:44.846Z] DB: getAllPlayers - start +[2025-08-16T11:04:44.848Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T11:05:41.191Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:05:41.193Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:05:41.193Z] DB: deletePlayer - start gmtest +[2025-08-16T11:05:41.193Z] DB: deletePlayer - changes 0 gmtest +[2025-08-16T11:05:41.242Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T11:05:41.243Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:05:41.243Z] DB: getPlayerByName - not found gmtest +[2025-08-16T11:05:41.425Z] DB: createPlayer - start gmtest +[2025-08-16T11:05:41.426Z] DB: createPlayer - done gmtest rowid 93 +[2025-08-16T11:05:41.426Z] API: Created player gmtest +[2025-08-16T11:05:41.445Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:05:41.445Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:05:41.446Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:05:41.446Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:05:41.447Z] DB: updatePlayer - start gmtest +[2025-08-16T11:05:41.447Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T11:05:41.447Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:05:41.448Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:05:41.448Z] API: Updated player gmtest +[2025-08-16T11:05:41.464Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:05:41.464Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:05:41.464Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:05:41.464Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:05:41.547Z] DB: updatePlayer - start gmtest +[2025-08-16T11:05:41.548Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T11:05:41.548Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:05:41.548Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:05:41.548Z] API: Updated player gmtest +[2025-08-16T11:05:41.564Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:05:41.564Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:05:41.644Z] DB: createSession - start b60955848b75efd424cff5aae6cfd22bc7a62f67850669fa507e60060dc7bb8e expiresAt 2025-08-17T11:05:41.644Z +[2025-08-16T11:05:41.645Z] DB: createSession - done b60955848b75efd424cff5aae6cfd22bc7a62f67850669fa507e60060dc7bb8e +[2025-08-16T11:05:41.645Z] API: Player login gmtest b60955848b75efd424cff5aae6cfd22bc7a62f67850669fa507e60060dc7bb8e +[2025-08-16T11:05:41.661Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:05:41.662Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:05:41.662Z] DB: deletePlayer - start gmtest +[2025-08-16T11:05:41.662Z] DB: deletePlayer - changes 1 gmtest +[2025-08-16T11:05:41.662Z] API: Deleted player gmtest +[2025-08-16T11:05:41.677Z] SESSION: GM bypass accepted GET /api/players +[2025-08-16T11:05:41.678Z] DB: getAllPlayers - start +[2025-08-16T11:05:41.679Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T11:05:41.679Z] API: Fetch all players (public) Found 7 players +[2025-08-16T11:05:42.118Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:05:42.119Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:05:42.119Z] DB: deletePlayer - start testplayer +[2025-08-16T11:05:42.119Z] DB: deletePlayer - changes 0 testplayer +[2025-08-16T11:05:42.136Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T11:05:42.136Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:05:42.136Z] DB: getPlayerByName - not found testplayer +[2025-08-16T11:05:42.217Z] DB: createPlayer - start testplayer +[2025-08-16T11:05:42.217Z] DB: createPlayer - done testplayer rowid 94 +[2025-08-16T11:05:42.217Z] API: Created player testplayer +[2025-08-16T11:05:42.235Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:05:42.236Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:05:42.316Z] DB: createSession - start f0da084fc561c480ddec76d8c668d623da717b55305abef2dcfefabee9596efe expiresAt 2025-08-17T11:05:42.316Z +[2025-08-16T11:05:42.317Z] DB: createSession - done f0da084fc561c480ddec76d8c668d623da717b55305abef2dcfefabee9596efe +[2025-08-16T11:05:42.317Z] API: Player login testplayer f0da084fc561c480ddec76d8c668d623da717b55305abef2dcfefabee9596efe +[2025-08-16T11:05:42.334Z] DB: getSession - start f0da084fc561c480ddec76d8c668d623da717b55305abef2dcfefabee9596efe +[2025-08-16T11:05:42.335Z] DB: getSession - ok f0da084fc561c480ddec76d8c668d623da717b55305abef2dcfefabee9596efe +[2025-08-16T11:05:42.335Z] SESSION: Valid f0da084fc561c480ddec76d8c668d623da717b55305abef2dcfefabee9596efe testplayer PUT /api/players/testplayer +[2025-08-16T11:05:42.335Z] DB: getSession - start f0da084fc561c480ddec76d8c668d623da717b55305abef2dcfefabee9596efe +[2025-08-16T11:05:42.335Z] DB: getSession - ok f0da084fc561c480ddec76d8c668d623da717b55305abef2dcfefabee9596efe +[2025-08-16T11:05:42.335Z] SESSION: Valid f0da084fc561c480ddec76d8c668d623da717b55305abef2dcfefabee9596efe testplayer PUT /api/players/testplayer +[2025-08-16T11:05:42.335Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:05:42.335Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:05:42.335Z] DB: updatePlayer - start testplayer +[2025-08-16T11:05:42.336Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T11:05:42.336Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:05:42.336Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:05:42.336Z] API: Updated player testplayer +[2025-08-16T11:05:42.352Z] DB: getSession - start f0da084fc561c480ddec76d8c668d623da717b55305abef2dcfefabee9596efe +[2025-08-16T11:05:42.353Z] DB: getSession - ok f0da084fc561c480ddec76d8c668d623da717b55305abef2dcfefabee9596efe +[2025-08-16T11:05:42.353Z] SESSION: Valid f0da084fc561c480ddec76d8c668d623da717b55305abef2dcfefabee9596efe testplayer PUT /api/players/testplayer +[2025-08-16T11:05:42.353Z] DB: getSession - start f0da084fc561c480ddec76d8c668d623da717b55305abef2dcfefabee9596efe +[2025-08-16T11:05:42.353Z] DB: getSession - ok f0da084fc561c480ddec76d8c668d623da717b55305abef2dcfefabee9596efe +[2025-08-16T11:05:42.353Z] SESSION: Valid f0da084fc561c480ddec76d8c668d623da717b55305abef2dcfefabee9596efe testplayer PUT /api/players/testplayer +[2025-08-16T11:05:42.353Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:05:42.353Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:05:42.353Z] DB: updatePlayer - start testplayer +[2025-08-16T11:05:42.354Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T11:05:42.354Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:05:42.354Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:05:42.354Z] API: Updated player testplayer +[2025-08-16T11:05:42.370Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:05:42.371Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:05:42.371Z] DB: deletePlayer - start testplayer +[2025-08-16T11:05:42.371Z] DB: deletePlayer - changes 1 testplayer +[2025-08-16T11:05:42.371Z] API: Deleted player testplayer +[2025-08-16T11:05:43.192Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:05:43.192Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:05:43.192Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T11:05:43.192Z] DB: deletePlayer - changes 0 shoptestplayer +[2025-08-16T11:05:43.206Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T11:05:43.206Z] DB: getPlayerByName - start shoptestplayer +[2025-08-16T11:05:43.206Z] DB: getPlayerByName - not found shoptestplayer +[2025-08-16T11:05:43.286Z] DB: createPlayer - start shoptestplayer +[2025-08-16T11:05:43.286Z] DB: createPlayer - done shoptestplayer rowid 95 +[2025-08-16T11:05:43.286Z] API: Created player shoptestplayer +[2025-08-16T11:05:43.299Z] SESSION: Missing sessionId GET /api/shop/inventory/shoptestplayer +[2025-08-16T11:05:43.312Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:05:43.312Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:05:43.312Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T11:05:43.313Z] DB: deletePlayer - changes 1 shoptestplayer +[2025-08-16T11:05:43.313Z] API: Deleted player shoptestplayer +[2025-08-16T11:05:45.065Z] DB: getAllPlayers - start +[2025-08-16T11:05:45.067Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T11:05:58.509Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:05:58.511Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:05:58.511Z] DB: deletePlayer - start gmtest +[2025-08-16T11:05:58.511Z] DB: deletePlayer - changes 0 gmtest +[2025-08-16T11:05:58.546Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T11:05:58.547Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:05:58.547Z] DB: getPlayerByName - not found gmtest +[2025-08-16T11:05:58.719Z] DB: createPlayer - start gmtest +[2025-08-16T11:05:58.720Z] DB: createPlayer - done gmtest rowid 96 +[2025-08-16T11:05:58.720Z] API: Created player gmtest +[2025-08-16T11:05:58.739Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:05:58.739Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:05:58.739Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:05:58.739Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:05:58.741Z] DB: updatePlayer - start gmtest +[2025-08-16T11:05:58.741Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T11:05:58.741Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:05:58.741Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:05:58.741Z] API: Updated player gmtest +[2025-08-16T11:05:58.757Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:05:58.757Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:05:58.757Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:05:58.757Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:05:58.839Z] DB: updatePlayer - start gmtest +[2025-08-16T11:05:58.839Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T11:05:58.840Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:05:58.840Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:05:58.840Z] API: Updated player gmtest +[2025-08-16T11:05:58.856Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:05:58.856Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:05:58.940Z] DB: createSession - start 2e78a050d627baab5e9472b93ca12ee6fe486ba73637c081129743e10a3c6b9b expiresAt 2025-08-17T11:05:58.939Z +[2025-08-16T11:05:58.940Z] DB: createSession - done 2e78a050d627baab5e9472b93ca12ee6fe486ba73637c081129743e10a3c6b9b +[2025-08-16T11:05:58.940Z] API: Player login gmtest 2e78a050d627baab5e9472b93ca12ee6fe486ba73637c081129743e10a3c6b9b +[2025-08-16T11:05:58.965Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:05:58.965Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:05:58.965Z] DB: deletePlayer - start gmtest +[2025-08-16T11:05:58.965Z] DB: deletePlayer - changes 1 gmtest +[2025-08-16T11:05:58.966Z] API: Deleted player gmtest +[2025-08-16T11:05:58.981Z] SESSION: GM bypass accepted GET /api/players +[2025-08-16T11:05:58.981Z] DB: getAllPlayers - start +[2025-08-16T11:05:58.983Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T11:05:58.983Z] API: Fetch all players (public) Found 7 players +[2025-08-16T11:05:59.324Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:05:59.325Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:05:59.325Z] DB: deletePlayer - start testplayer +[2025-08-16T11:05:59.325Z] DB: deletePlayer - changes 0 testplayer +[2025-08-16T11:05:59.341Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T11:05:59.341Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:05:59.342Z] DB: getPlayerByName - not found testplayer +[2025-08-16T11:05:59.422Z] DB: createPlayer - start testplayer +[2025-08-16T11:05:59.422Z] DB: createPlayer - done testplayer rowid 97 +[2025-08-16T11:05:59.423Z] API: Created player testplayer +[2025-08-16T11:05:59.441Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:05:59.441Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:05:59.522Z] DB: createSession - start 21d478a4969ad8c935ceab83c3ce74bc386d3b15afa945ab86198b866e6d2e5b expiresAt 2025-08-17T11:05:59.522Z +[2025-08-16T11:05:59.522Z] DB: createSession - done 21d478a4969ad8c935ceab83c3ce74bc386d3b15afa945ab86198b866e6d2e5b +[2025-08-16T11:05:59.522Z] API: Player login testplayer 21d478a4969ad8c935ceab83c3ce74bc386d3b15afa945ab86198b866e6d2e5b +[2025-08-16T11:05:59.539Z] DB: getSession - start 21d478a4969ad8c935ceab83c3ce74bc386d3b15afa945ab86198b866e6d2e5b +[2025-08-16T11:05:59.540Z] DB: getSession - ok 21d478a4969ad8c935ceab83c3ce74bc386d3b15afa945ab86198b866e6d2e5b +[2025-08-16T11:05:59.540Z] SESSION: Valid 21d478a4969ad8c935ceab83c3ce74bc386d3b15afa945ab86198b866e6d2e5b testplayer PUT /api/players/testplayer +[2025-08-16T11:05:59.540Z] DB: getSession - start 21d478a4969ad8c935ceab83c3ce74bc386d3b15afa945ab86198b866e6d2e5b +[2025-08-16T11:05:59.540Z] DB: getSession - ok 21d478a4969ad8c935ceab83c3ce74bc386d3b15afa945ab86198b866e6d2e5b +[2025-08-16T11:05:59.540Z] SESSION: Valid 21d478a4969ad8c935ceab83c3ce74bc386d3b15afa945ab86198b866e6d2e5b testplayer PUT /api/players/testplayer +[2025-08-16T11:05:59.540Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:05:59.540Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:05:59.540Z] DB: updatePlayer - start testplayer +[2025-08-16T11:05:59.541Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T11:05:59.541Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:05:59.541Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:05:59.541Z] API: Updated player testplayer +[2025-08-16T11:05:59.557Z] DB: getSession - start 21d478a4969ad8c935ceab83c3ce74bc386d3b15afa945ab86198b866e6d2e5b +[2025-08-16T11:05:59.558Z] DB: getSession - ok 21d478a4969ad8c935ceab83c3ce74bc386d3b15afa945ab86198b866e6d2e5b +[2025-08-16T11:05:59.558Z] SESSION: Valid 21d478a4969ad8c935ceab83c3ce74bc386d3b15afa945ab86198b866e6d2e5b testplayer PUT /api/players/testplayer +[2025-08-16T11:05:59.558Z] DB: getSession - start 21d478a4969ad8c935ceab83c3ce74bc386d3b15afa945ab86198b866e6d2e5b +[2025-08-16T11:05:59.558Z] DB: getSession - ok 21d478a4969ad8c935ceab83c3ce74bc386d3b15afa945ab86198b866e6d2e5b +[2025-08-16T11:05:59.558Z] SESSION: Valid 21d478a4969ad8c935ceab83c3ce74bc386d3b15afa945ab86198b866e6d2e5b testplayer PUT /api/players/testplayer +[2025-08-16T11:05:59.558Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:05:59.558Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:05:59.558Z] DB: updatePlayer - start testplayer +[2025-08-16T11:05:59.558Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T11:05:59.558Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:05:59.559Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:05:59.559Z] API: Updated player testplayer +[2025-08-16T11:05:59.575Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:05:59.575Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:05:59.575Z] DB: deletePlayer - start testplayer +[2025-08-16T11:05:59.576Z] DB: deletePlayer - changes 1 testplayer +[2025-08-16T11:05:59.576Z] API: Deleted player testplayer +[2025-08-16T11:06:00.079Z] DB: getAllPlayers - start +[2025-08-16T11:06:00.081Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T11:06:00.483Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:06:00.483Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:06:00.483Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T11:06:00.483Z] DB: deletePlayer - changes 0 shoptestplayer +[2025-08-16T11:06:00.497Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T11:06:00.497Z] DB: getPlayerByName - start shoptestplayer +[2025-08-16T11:06:00.497Z] DB: getPlayerByName - not found shoptestplayer +[2025-08-16T11:06:00.576Z] DB: createPlayer - start shoptestplayer +[2025-08-16T11:06:00.577Z] DB: createPlayer - done shoptestplayer rowid 98 +[2025-08-16T11:06:00.577Z] API: Created player shoptestplayer +[2025-08-16T11:06:00.590Z] SESSION: Missing sessionId GET /api/shop/inventory/shoptestplayer +[2025-08-16T11:06:00.603Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:06:00.604Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:06:00.604Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T11:06:00.604Z] DB: deletePlayer - changes 1 shoptestplayer +[2025-08-16T11:06:00.604Z] API: Deleted player shoptestplayer +[2025-08-16T11:13:49.819Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:13:49.821Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:13:49.821Z] DB: deletePlayer - start gmtest +[2025-08-16T11:13:49.822Z] DB: deletePlayer - changes 0 gmtest +[2025-08-16T11:13:49.870Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T11:13:49.871Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:13:49.871Z] DB: getPlayerByName - not found gmtest +[2025-08-16T11:13:50.040Z] DB: createPlayer - start gmtest +[2025-08-16T11:13:50.040Z] DB: createPlayer - done gmtest rowid 99 +[2025-08-16T11:13:50.040Z] API: Created player gmtest +[2025-08-16T11:13:50.059Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:13:50.060Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:13:50.060Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:13:50.060Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:13:50.062Z] DB: updatePlayer - start gmtest +[2025-08-16T11:13:50.062Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T11:13:50.062Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:13:50.062Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:13:50.063Z] API: Updated player gmtest +[2025-08-16T11:13:50.079Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:13:50.079Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:13:50.079Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:13:50.079Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:13:50.160Z] DB: updatePlayer - start gmtest +[2025-08-16T11:13:50.160Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T11:13:50.160Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:13:50.160Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:13:50.160Z] API: Updated player gmtest +[2025-08-16T11:13:50.179Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:13:50.180Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:13:50.260Z] DB: createSession - start 6513e68620cde0ea4e1b3b006ae3ce5a854a5c8ccd802566c90b0bb2e194575e expiresAt 2025-08-17T11:13:50.260Z +[2025-08-16T11:13:50.261Z] DB: createSession - done 6513e68620cde0ea4e1b3b006ae3ce5a854a5c8ccd802566c90b0bb2e194575e +[2025-08-16T11:13:50.261Z] API: Player login gmtest 6513e68620cde0ea4e1b3b006ae3ce5a854a5c8ccd802566c90b0bb2e194575e +[2025-08-16T11:13:50.278Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:13:50.278Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:13:50.278Z] DB: deletePlayer - start gmtest +[2025-08-16T11:13:50.278Z] DB: deletePlayer - changes 1 gmtest +[2025-08-16T11:13:50.278Z] API: Deleted player gmtest +[2025-08-16T11:13:50.295Z] SESSION: GM bypass accepted GET /api/players +[2025-08-16T11:13:50.295Z] DB: getAllPlayers - start +[2025-08-16T11:13:50.297Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T11:13:50.297Z] API: Fetch all players (public) Found 7 players +[2025-08-16T11:13:50.645Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:13:50.646Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:13:50.646Z] DB: deletePlayer - start testplayer +[2025-08-16T11:13:50.646Z] DB: deletePlayer - changes 0 testplayer +[2025-08-16T11:13:50.663Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T11:13:50.663Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:13:50.664Z] DB: getPlayerByName - not found testplayer +[2025-08-16T11:13:50.745Z] DB: createPlayer - start testplayer +[2025-08-16T11:13:50.745Z] DB: createPlayer - done testplayer rowid 100 +[2025-08-16T11:13:50.745Z] API: Created player testplayer +[2025-08-16T11:13:50.773Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:13:50.773Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:13:50.855Z] DB: createSession - start 3bce64bb61037c9ed953f1d83629bee501f908979a688d30d83a30ee9f3e080d expiresAt 2025-08-17T11:13:50.855Z +[2025-08-16T11:13:50.856Z] DB: createSession - done 3bce64bb61037c9ed953f1d83629bee501f908979a688d30d83a30ee9f3e080d +[2025-08-16T11:13:50.856Z] API: Player login testplayer 3bce64bb61037c9ed953f1d83629bee501f908979a688d30d83a30ee9f3e080d +[2025-08-16T11:13:50.873Z] DB: getSession - start 3bce64bb61037c9ed953f1d83629bee501f908979a688d30d83a30ee9f3e080d +[2025-08-16T11:13:50.873Z] DB: getSession - ok 3bce64bb61037c9ed953f1d83629bee501f908979a688d30d83a30ee9f3e080d +[2025-08-16T11:13:50.873Z] SESSION: Valid 3bce64bb61037c9ed953f1d83629bee501f908979a688d30d83a30ee9f3e080d testplayer PUT /api/players/testplayer +[2025-08-16T11:13:50.874Z] DB: getSession - start 3bce64bb61037c9ed953f1d83629bee501f908979a688d30d83a30ee9f3e080d +[2025-08-16T11:13:50.874Z] DB: getSession - ok 3bce64bb61037c9ed953f1d83629bee501f908979a688d30d83a30ee9f3e080d +[2025-08-16T11:13:50.874Z] SESSION: Valid 3bce64bb61037c9ed953f1d83629bee501f908979a688d30d83a30ee9f3e080d testplayer PUT /api/players/testplayer +[2025-08-16T11:13:50.874Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:13:50.874Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:13:50.874Z] DB: updatePlayer - start testplayer +[2025-08-16T11:13:50.874Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T11:13:50.874Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:13:50.874Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:13:50.874Z] API: Updated player testplayer +[2025-08-16T11:13:50.891Z] DB: getSession - start 3bce64bb61037c9ed953f1d83629bee501f908979a688d30d83a30ee9f3e080d +[2025-08-16T11:13:50.891Z] DB: getSession - ok 3bce64bb61037c9ed953f1d83629bee501f908979a688d30d83a30ee9f3e080d +[2025-08-16T11:13:50.891Z] SESSION: Valid 3bce64bb61037c9ed953f1d83629bee501f908979a688d30d83a30ee9f3e080d testplayer PUT /api/players/testplayer +[2025-08-16T11:13:50.891Z] DB: getSession - start 3bce64bb61037c9ed953f1d83629bee501f908979a688d30d83a30ee9f3e080d +[2025-08-16T11:13:50.891Z] DB: getSession - ok 3bce64bb61037c9ed953f1d83629bee501f908979a688d30d83a30ee9f3e080d +[2025-08-16T11:13:50.891Z] SESSION: Valid 3bce64bb61037c9ed953f1d83629bee501f908979a688d30d83a30ee9f3e080d testplayer PUT /api/players/testplayer +[2025-08-16T11:13:50.891Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:13:50.891Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:13:50.891Z] DB: updatePlayer - start testplayer +[2025-08-16T11:13:50.892Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T11:13:50.892Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:13:50.892Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:13:50.892Z] API: Updated player testplayer +[2025-08-16T11:13:50.909Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:13:50.909Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:13:50.909Z] DB: deletePlayer - start testplayer +[2025-08-16T11:13:50.909Z] DB: deletePlayer - changes 1 testplayer +[2025-08-16T11:13:50.909Z] API: Deleted player testplayer +[2025-08-16T11:13:51.293Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:13:51.293Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:13:51.293Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T11:13:51.293Z] DB: deletePlayer - changes 0 shoptestplayer +[2025-08-16T11:13:51.308Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T11:13:51.309Z] DB: getPlayerByName - start shoptestplayer +[2025-08-16T11:13:51.309Z] DB: getPlayerByName - not found shoptestplayer +[2025-08-16T11:13:51.389Z] DB: createPlayer - start shoptestplayer +[2025-08-16T11:13:51.390Z] DB: createPlayer - done shoptestplayer rowid 101 +[2025-08-16T11:13:51.390Z] API: Created player shoptestplayer +[2025-08-16T11:13:51.403Z] SESSION: Missing sessionId GET /api/shop/inventory/shoptestplayer +[2025-08-16T11:13:51.416Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:13:51.416Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:13:51.417Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T11:13:51.417Z] DB: deletePlayer - changes 1 shoptestplayer +[2025-08-16T11:13:51.417Z] API: Deleted player shoptestplayer +[2025-08-16T11:13:51.932Z] DB: getAllPlayers - start +[2025-08-16T11:13:51.935Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T11:14:06.041Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:14:06.043Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:14:06.043Z] DB: deletePlayer - start gmtest +[2025-08-16T11:14:06.043Z] DB: deletePlayer - changes 0 gmtest +[2025-08-16T11:14:06.080Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T11:14:06.081Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:14:06.081Z] DB: getPlayerByName - not found gmtest +[2025-08-16T11:14:06.249Z] DB: createPlayer - start gmtest +[2025-08-16T11:14:06.280Z] DB: createPlayer - done gmtest rowid 102 +[2025-08-16T11:14:06.281Z] API: Created player gmtest +[2025-08-16T11:14:06.299Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:14:06.300Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:14:06.300Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:14:06.301Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:14:06.302Z] DB: updatePlayer - start gmtest +[2025-08-16T11:14:06.316Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T11:14:06.316Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:14:06.317Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:14:06.317Z] API: Updated player gmtest +[2025-08-16T11:14:06.334Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:14:06.334Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:14:06.334Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:14:06.334Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:14:06.416Z] DB: updatePlayer - start gmtest +[2025-08-16T11:14:06.417Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T11:14:06.417Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:14:06.417Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:14:06.417Z] API: Updated player gmtest +[2025-08-16T11:14:06.444Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:14:06.444Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:14:06.524Z] DB: createSession - start d417e0750c0991c43176847586e187a9ac69ff0eb0442f8e2774be1fd6dcf75c expiresAt 2025-08-17T11:14:06.524Z +[2025-08-16T11:14:06.524Z] DB: createSession - done d417e0750c0991c43176847586e187a9ac69ff0eb0442f8e2774be1fd6dcf75c +[2025-08-16T11:14:06.524Z] API: Player login gmtest d417e0750c0991c43176847586e187a9ac69ff0eb0442f8e2774be1fd6dcf75c +[2025-08-16T11:14:06.541Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:14:06.541Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:14:06.541Z] DB: deletePlayer - start gmtest +[2025-08-16T11:14:06.542Z] DB: deletePlayer - changes 1 gmtest +[2025-08-16T11:14:06.542Z] API: Deleted player gmtest +[2025-08-16T11:14:06.557Z] SESSION: GM bypass accepted GET /api/players +[2025-08-16T11:14:06.558Z] DB: getAllPlayers - start +[2025-08-16T11:14:06.559Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T11:14:06.559Z] API: Fetch all players (public) Found 7 players +[2025-08-16T11:14:06.890Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:14:06.890Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:14:06.890Z] DB: deletePlayer - start testplayer +[2025-08-16T11:14:06.890Z] DB: deletePlayer - changes 0 testplayer +[2025-08-16T11:14:06.907Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T11:14:06.908Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:14:06.908Z] DB: getPlayerByName - not found testplayer +[2025-08-16T11:14:06.992Z] DB: createPlayer - start testplayer +[2025-08-16T11:14:06.992Z] DB: createPlayer - done testplayer rowid 103 +[2025-08-16T11:14:06.992Z] API: Created player testplayer +[2025-08-16T11:14:07.011Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:14:07.012Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:14:07.093Z] DB: createSession - start 129b199277c66b461abc644092e47ecf3c0266a971db00f3f24cc7b7c5c73482 expiresAt 2025-08-17T11:14:07.093Z +[2025-08-16T11:14:07.094Z] DB: createSession - done 129b199277c66b461abc644092e47ecf3c0266a971db00f3f24cc7b7c5c73482 +[2025-08-16T11:14:07.094Z] API: Player login testplayer 129b199277c66b461abc644092e47ecf3c0266a971db00f3f24cc7b7c5c73482 +[2025-08-16T11:14:07.111Z] DB: getSession - start 129b199277c66b461abc644092e47ecf3c0266a971db00f3f24cc7b7c5c73482 +[2025-08-16T11:14:07.111Z] DB: getSession - ok 129b199277c66b461abc644092e47ecf3c0266a971db00f3f24cc7b7c5c73482 +[2025-08-16T11:14:07.111Z] SESSION: Valid 129b199277c66b461abc644092e47ecf3c0266a971db00f3f24cc7b7c5c73482 testplayer PUT /api/players/testplayer +[2025-08-16T11:14:07.111Z] DB: getSession - start 129b199277c66b461abc644092e47ecf3c0266a971db00f3f24cc7b7c5c73482 +[2025-08-16T11:14:07.111Z] DB: getSession - ok 129b199277c66b461abc644092e47ecf3c0266a971db00f3f24cc7b7c5c73482 +[2025-08-16T11:14:07.111Z] SESSION: Valid 129b199277c66b461abc644092e47ecf3c0266a971db00f3f24cc7b7c5c73482 testplayer PUT /api/players/testplayer +[2025-08-16T11:14:07.111Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:14:07.111Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:14:07.111Z] DB: updatePlayer - start testplayer +[2025-08-16T11:14:07.112Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T11:14:07.112Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:14:07.112Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:14:07.112Z] API: Updated player testplayer +[2025-08-16T11:14:07.130Z] DB: getSession - start 129b199277c66b461abc644092e47ecf3c0266a971db00f3f24cc7b7c5c73482 +[2025-08-16T11:14:07.130Z] DB: getSession - ok 129b199277c66b461abc644092e47ecf3c0266a971db00f3f24cc7b7c5c73482 +[2025-08-16T11:14:07.130Z] SESSION: Valid 129b199277c66b461abc644092e47ecf3c0266a971db00f3f24cc7b7c5c73482 testplayer PUT /api/players/testplayer +[2025-08-16T11:14:07.130Z] DB: getSession - start 129b199277c66b461abc644092e47ecf3c0266a971db00f3f24cc7b7c5c73482 +[2025-08-16T11:14:07.130Z] DB: getSession - ok 129b199277c66b461abc644092e47ecf3c0266a971db00f3f24cc7b7c5c73482 +[2025-08-16T11:14:07.130Z] SESSION: Valid 129b199277c66b461abc644092e47ecf3c0266a971db00f3f24cc7b7c5c73482 testplayer PUT /api/players/testplayer +[2025-08-16T11:14:07.130Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:14:07.131Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:14:07.131Z] DB: updatePlayer - start testplayer +[2025-08-16T11:14:07.131Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T11:14:07.131Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:14:07.131Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:14:07.131Z] API: Updated player testplayer +[2025-08-16T11:14:07.147Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:14:07.147Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:14:07.147Z] DB: deletePlayer - start testplayer +[2025-08-16T11:14:07.148Z] DB: deletePlayer - changes 1 testplayer +[2025-08-16T11:14:07.148Z] API: Deleted player testplayer +[2025-08-16T11:14:07.648Z] DB: getAllPlayers - start +[2025-08-16T11:14:07.651Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T11:14:08.036Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:14:08.036Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:14:08.036Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T11:14:08.036Z] DB: deletePlayer - changes 0 shoptestplayer +[2025-08-16T11:14:08.050Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T11:14:08.050Z] DB: getPlayerByName - start shoptestplayer +[2025-08-16T11:14:08.050Z] DB: getPlayerByName - not found shoptestplayer +[2025-08-16T11:14:08.130Z] DB: createPlayer - start shoptestplayer +[2025-08-16T11:14:08.131Z] DB: createPlayer - done shoptestplayer rowid 104 +[2025-08-16T11:14:08.131Z] API: Created player shoptestplayer +[2025-08-16T11:14:08.147Z] SESSION: Missing sessionId GET /api/shop/inventory/shoptestplayer +[2025-08-16T11:14:08.161Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:14:08.162Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:14:08.162Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T11:14:08.162Z] DB: deletePlayer - changes 1 shoptestplayer +[2025-08-16T11:14:08.162Z] API: Deleted player shoptestplayer +[2025-08-16T11:48:36.029Z] DB: getAllPlayers - start +[2025-08-16T11:48:36.032Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T11:48:39.449Z] DB: getPlayerByName - start chris +[2025-08-16T11:48:39.449Z] DB: getPlayerByName - found chris +[2025-08-16T11:48:39.621Z] DB: createSession - start 136fa7e048654da0ae9bee0b988af8621cff2290857d3babb1e925c756f2fba0 expiresAt 2025-08-17T11:48:39.621Z +[2025-08-16T11:48:39.622Z] DB: createSession - done 136fa7e048654da0ae9bee0b988af8621cff2290857d3babb1e925c756f2fba0 +[2025-08-16T11:48:39.623Z] API: Player login chris 136fa7e048654da0ae9bee0b988af8621cff2290857d3babb1e925c756f2fba0 +[2025-08-16T11:48:39.635Z] DB: getSession - start 136fa7e048654da0ae9bee0b988af8621cff2290857d3babb1e925c756f2fba0 +[2025-08-16T11:48:39.635Z] DB: getSession - ok 136fa7e048654da0ae9bee0b988af8621cff2290857d3babb1e925c756f2fba0 +[2025-08-16T11:48:39.636Z] SESSION: Validate success 136fa7e048654da0ae9bee0b988af8621cff2290857d3babb1e925c756f2fba0 chris +[2025-08-16T11:52:29.510Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:52:29.512Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:52:29.512Z] DB: deletePlayer - start gmtest +[2025-08-16T11:52:29.512Z] DB: deletePlayer - changes 0 gmtest +[2025-08-16T11:52:29.548Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T11:52:29.549Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:52:29.549Z] DB: getPlayerByName - not found gmtest +[2025-08-16T11:52:29.726Z] DB: createPlayer - start gmtest +[2025-08-16T11:52:29.727Z] DB: createPlayer - done gmtest rowid 105 +[2025-08-16T11:52:29.727Z] API: Created player gmtest +[2025-08-16T11:52:29.746Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:52:29.746Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:52:29.746Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:52:29.747Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:52:29.747Z] DB: updatePlayer - start gmtest +[2025-08-16T11:52:29.748Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T11:52:29.748Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:52:29.748Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:52:29.748Z] API: Updated player gmtest +[2025-08-16T11:52:29.764Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:52:29.765Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T11:52:29.765Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:52:29.765Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:52:29.845Z] DB: updatePlayer - start gmtest +[2025-08-16T11:52:29.845Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T11:52:29.845Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:52:29.846Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:52:29.846Z] API: Updated player gmtest +[2025-08-16T11:52:29.863Z] DB: getPlayerByName - start gmtest +[2025-08-16T11:52:29.863Z] DB: getPlayerByName - found gmtest +[2025-08-16T11:52:29.943Z] DB: createSession - start bdd63b0e56ae612a327ea66d64b9569d391e45ebe76615a0ab0e41754d0061bf expiresAt 2025-08-17T11:52:29.943Z +[2025-08-16T11:52:29.944Z] DB: createSession - done bdd63b0e56ae612a327ea66d64b9569d391e45ebe76615a0ab0e41754d0061bf +[2025-08-16T11:52:29.944Z] API: Player login gmtest bdd63b0e56ae612a327ea66d64b9569d391e45ebe76615a0ab0e41754d0061bf +[2025-08-16T11:52:29.960Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:52:29.961Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T11:52:29.962Z] DB: deletePlayer - start gmtest +[2025-08-16T11:52:29.962Z] DB: deletePlayer - changes 1 gmtest +[2025-08-16T11:52:29.962Z] API: Deleted player gmtest +[2025-08-16T11:52:29.988Z] SESSION: GM bypass accepted GET /api/players +[2025-08-16T11:52:29.989Z] DB: getAllPlayers - start +[2025-08-16T11:52:29.990Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T11:52:29.990Z] API: Fetch all players (public) Found 7 players +[2025-08-16T11:52:30.331Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:52:30.332Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:52:30.332Z] DB: deletePlayer - start testplayer +[2025-08-16T11:52:30.332Z] DB: deletePlayer - changes 0 testplayer +[2025-08-16T11:52:30.349Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T11:52:30.349Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:52:30.349Z] DB: getPlayerByName - not found testplayer +[2025-08-16T11:52:30.429Z] DB: createPlayer - start testplayer +[2025-08-16T11:52:30.429Z] DB: createPlayer - done testplayer rowid 106 +[2025-08-16T11:52:30.429Z] API: Created player testplayer +[2025-08-16T11:52:30.447Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:52:30.447Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:52:30.527Z] DB: createSession - start 104c2215dd9790ccf9c2af2049cb62c4577efb91a23aa403e55331591219f843 expiresAt 2025-08-17T11:52:30.527Z +[2025-08-16T11:52:30.528Z] DB: createSession - done 104c2215dd9790ccf9c2af2049cb62c4577efb91a23aa403e55331591219f843 +[2025-08-16T11:52:30.528Z] API: Player login testplayer 104c2215dd9790ccf9c2af2049cb62c4577efb91a23aa403e55331591219f843 +[2025-08-16T11:52:30.545Z] DB: getSession - start 104c2215dd9790ccf9c2af2049cb62c4577efb91a23aa403e55331591219f843 +[2025-08-16T11:52:30.545Z] DB: getSession - ok 104c2215dd9790ccf9c2af2049cb62c4577efb91a23aa403e55331591219f843 +[2025-08-16T11:52:30.545Z] SESSION: Valid 104c2215dd9790ccf9c2af2049cb62c4577efb91a23aa403e55331591219f843 testplayer PUT /api/players/testplayer +[2025-08-16T11:52:30.545Z] DB: getSession - start 104c2215dd9790ccf9c2af2049cb62c4577efb91a23aa403e55331591219f843 +[2025-08-16T11:52:30.545Z] DB: getSession - ok 104c2215dd9790ccf9c2af2049cb62c4577efb91a23aa403e55331591219f843 +[2025-08-16T11:52:30.545Z] SESSION: Valid 104c2215dd9790ccf9c2af2049cb62c4577efb91a23aa403e55331591219f843 testplayer PUT /api/players/testplayer +[2025-08-16T11:52:30.545Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:52:30.545Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:52:30.546Z] DB: updatePlayer - start testplayer +[2025-08-16T11:52:30.546Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T11:52:30.546Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:52:30.546Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:52:30.547Z] API: Updated player testplayer +[2025-08-16T11:52:30.563Z] DB: getSession - start 104c2215dd9790ccf9c2af2049cb62c4577efb91a23aa403e55331591219f843 +[2025-08-16T11:52:30.563Z] DB: getSession - ok 104c2215dd9790ccf9c2af2049cb62c4577efb91a23aa403e55331591219f843 +[2025-08-16T11:52:30.563Z] SESSION: Valid 104c2215dd9790ccf9c2af2049cb62c4577efb91a23aa403e55331591219f843 testplayer PUT /api/players/testplayer +[2025-08-16T11:52:30.563Z] DB: getSession - start 104c2215dd9790ccf9c2af2049cb62c4577efb91a23aa403e55331591219f843 +[2025-08-16T11:52:30.563Z] DB: getSession - ok 104c2215dd9790ccf9c2af2049cb62c4577efb91a23aa403e55331591219f843 +[2025-08-16T11:52:30.563Z] SESSION: Valid 104c2215dd9790ccf9c2af2049cb62c4577efb91a23aa403e55331591219f843 testplayer PUT /api/players/testplayer +[2025-08-16T11:52:30.563Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:52:30.563Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:52:30.564Z] DB: updatePlayer - start testplayer +[2025-08-16T11:52:30.564Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T11:52:30.564Z] DB: getPlayerByName - start testplayer +[2025-08-16T11:52:30.564Z] DB: getPlayerByName - found testplayer +[2025-08-16T11:52:30.564Z] API: Updated player testplayer +[2025-08-16T11:52:30.581Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:52:30.581Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T11:52:30.581Z] DB: deletePlayer - start testplayer +[2025-08-16T11:52:30.582Z] DB: deletePlayer - changes 1 testplayer +[2025-08-16T11:52:30.582Z] API: Deleted player testplayer +[2025-08-16T11:52:30.957Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:52:30.957Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:52:30.957Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T11:52:30.957Z] DB: deletePlayer - changes 0 shoptestplayer +[2025-08-16T11:52:30.971Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T11:52:30.971Z] DB: getPlayerByName - start shoptestplayer +[2025-08-16T11:52:30.971Z] DB: getPlayerByName - not found shoptestplayer +[2025-08-16T11:52:31.050Z] DB: createPlayer - start shoptestplayer +[2025-08-16T11:52:31.051Z] DB: createPlayer - done shoptestplayer rowid 107 +[2025-08-16T11:52:31.051Z] API: Created player shoptestplayer +[2025-08-16T11:52:31.064Z] SESSION: Missing sessionId GET /api/shop/inventory/shoptestplayer +[2025-08-16T11:52:31.078Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:52:31.078Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T11:52:31.078Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T11:52:31.078Z] DB: deletePlayer - changes 1 shoptestplayer +[2025-08-16T11:52:31.078Z] API: Deleted player shoptestplayer +[2025-08-16T11:52:31.555Z] DB: getAllPlayers - start +[2025-08-16T11:52:31.557Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T11:54:39.307Z] SESSION: Missing sessionId GET /api/players +[2025-08-16T12:14:07.561Z] DB: getAllPlayers - start +[2025-08-16T12:14:07.563Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T12:14:10.433Z] DB: getPlayerByName - start chris +[2025-08-16T12:14:10.433Z] DB: getPlayerByName - found chris +[2025-08-16T12:14:10.613Z] DB: createSession - start c36d64f88e4f0aa7226f51b1810c3b686a936e42c419252a98c3869c757f1b2f expiresAt 2025-08-17T12:14:10.613Z +[2025-08-16T12:14:10.614Z] DB: createSession - done c36d64f88e4f0aa7226f51b1810c3b686a936e42c419252a98c3869c757f1b2f +[2025-08-16T12:14:10.614Z] API: Player login chris c36d64f88e4f0aa7226f51b1810c3b686a936e42c419252a98c3869c757f1b2f +[2025-08-16T12:14:10.668Z] DB: getSession - start c36d64f88e4f0aa7226f51b1810c3b686a936e42c419252a98c3869c757f1b2f +[2025-08-16T12:14:10.668Z] DB: getSession - ok c36d64f88e4f0aa7226f51b1810c3b686a936e42c419252a98c3869c757f1b2f +[2025-08-16T12:14:10.668Z] SESSION: Validate success c36d64f88e4f0aa7226f51b1810c3b686a936e42c419252a98c3869c757f1b2f chris +[2025-08-16T12:15:34.212Z] SESSION: Missing sessionId GET /api/players +[2025-08-16T12:15:34.228Z] DB: getAllPlayers - start +[2025-08-16T12:15:34.229Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T12:16:57.570Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T12:16:57.572Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T12:16:57.572Z] DB: deletePlayer - start gmtest +[2025-08-16T12:16:57.572Z] DB: deletePlayer - changes 0 gmtest +[2025-08-16T12:16:57.619Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:16:57.620Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:16:57.621Z] DB: getPlayerByName - not found gmtest +[2025-08-16T12:16:57.791Z] DB: createPlayer - start gmtest +[2025-08-16T12:16:57.792Z] DB: createPlayer - done gmtest rowid 108 +[2025-08-16T12:16:57.792Z] API: Created player gmtest +[2025-08-16T12:16:57.811Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T12:16:57.811Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T12:16:57.812Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:16:57.812Z] DB: getPlayerByName - found gmtest +[2025-08-16T12:16:57.813Z] DB: updatePlayer - start gmtest +[2025-08-16T12:16:57.814Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T12:16:57.814Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:16:57.814Z] DB: getPlayerByName - found gmtest +[2025-08-16T12:16:57.814Z] API: Updated player gmtest +[2025-08-16T12:16:57.830Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T12:16:57.830Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T12:16:57.830Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:16:57.830Z] DB: getPlayerByName - found gmtest +[2025-08-16T12:16:57.911Z] DB: updatePlayer - start gmtest +[2025-08-16T12:16:57.911Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T12:16:57.911Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:16:57.911Z] DB: getPlayerByName - found gmtest +[2025-08-16T12:16:57.911Z] API: Updated player gmtest +[2025-08-16T12:16:57.927Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:16:57.927Z] DB: getPlayerByName - found gmtest +[2025-08-16T12:16:58.007Z] DB: createSession - start a31c4eebf90ecd0554c153186047fc61796ccb98c5cd399e86fe9623654350ea expiresAt 2025-08-17T12:16:58.007Z +[2025-08-16T12:16:58.008Z] DB: createSession - done a31c4eebf90ecd0554c153186047fc61796ccb98c5cd399e86fe9623654350ea +[2025-08-16T12:16:58.008Z] API: Player login gmtest a31c4eebf90ecd0554c153186047fc61796ccb98c5cd399e86fe9623654350ea +[2025-08-16T12:16:58.025Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T12:16:58.025Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T12:16:58.025Z] DB: deletePlayer - start gmtest +[2025-08-16T12:16:58.026Z] DB: deletePlayer - changes 1 gmtest +[2025-08-16T12:16:58.026Z] API: Deleted player gmtest +[2025-08-16T12:16:58.041Z] SESSION: GM bypass accepted GET /api/players +[2025-08-16T12:16:58.042Z] DB: getAllPlayers - start +[2025-08-16T12:16:58.043Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T12:16:58.043Z] API: Fetch all players (public) Found 7 players +[2025-08-16T12:16:58.406Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T12:16:58.406Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T12:16:58.406Z] DB: deletePlayer - start testplayer +[2025-08-16T12:16:58.406Z] DB: deletePlayer - changes 0 testplayer +[2025-08-16T12:16:58.423Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:16:58.424Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:16:58.424Z] DB: getPlayerByName - not found testplayer +[2025-08-16T12:16:58.504Z] DB: createPlayer - start testplayer +[2025-08-16T12:16:58.505Z] DB: createPlayer - done testplayer rowid 109 +[2025-08-16T12:16:58.506Z] API: Created player testplayer +[2025-08-16T12:16:58.534Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:16:58.535Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:16:58.616Z] DB: createSession - start f1bf14dab2c9d0a135e3ec7ee0ba28ebdf1d02299b5c74483a60f5cf35e2e282 expiresAt 2025-08-17T12:16:58.616Z +[2025-08-16T12:16:58.616Z] DB: createSession - done f1bf14dab2c9d0a135e3ec7ee0ba28ebdf1d02299b5c74483a60f5cf35e2e282 +[2025-08-16T12:16:58.616Z] API: Player login testplayer f1bf14dab2c9d0a135e3ec7ee0ba28ebdf1d02299b5c74483a60f5cf35e2e282 +[2025-08-16T12:16:58.634Z] DB: getSession - start f1bf14dab2c9d0a135e3ec7ee0ba28ebdf1d02299b5c74483a60f5cf35e2e282 +[2025-08-16T12:16:58.634Z] DB: getSession - ok f1bf14dab2c9d0a135e3ec7ee0ba28ebdf1d02299b5c74483a60f5cf35e2e282 +[2025-08-16T12:16:58.634Z] SESSION: Valid f1bf14dab2c9d0a135e3ec7ee0ba28ebdf1d02299b5c74483a60f5cf35e2e282 testplayer PUT /api/players/testplayer +[2025-08-16T12:16:58.634Z] DB: getSession - start f1bf14dab2c9d0a135e3ec7ee0ba28ebdf1d02299b5c74483a60f5cf35e2e282 +[2025-08-16T12:16:58.634Z] DB: getSession - ok f1bf14dab2c9d0a135e3ec7ee0ba28ebdf1d02299b5c74483a60f5cf35e2e282 +[2025-08-16T12:16:58.634Z] SESSION: Valid f1bf14dab2c9d0a135e3ec7ee0ba28ebdf1d02299b5c74483a60f5cf35e2e282 testplayer PUT /api/players/testplayer +[2025-08-16T12:16:58.634Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:16:58.634Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:16:58.635Z] DB: updatePlayer - start testplayer +[2025-08-16T12:16:58.635Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T12:16:58.635Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:16:58.635Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:16:58.635Z] API: Updated player testplayer +[2025-08-16T12:16:58.652Z] DB: getSession - start f1bf14dab2c9d0a135e3ec7ee0ba28ebdf1d02299b5c74483a60f5cf35e2e282 +[2025-08-16T12:16:58.653Z] DB: getSession - ok f1bf14dab2c9d0a135e3ec7ee0ba28ebdf1d02299b5c74483a60f5cf35e2e282 +[2025-08-16T12:16:58.653Z] SESSION: Valid f1bf14dab2c9d0a135e3ec7ee0ba28ebdf1d02299b5c74483a60f5cf35e2e282 testplayer PUT /api/players/testplayer +[2025-08-16T12:16:58.653Z] DB: getSession - start f1bf14dab2c9d0a135e3ec7ee0ba28ebdf1d02299b5c74483a60f5cf35e2e282 +[2025-08-16T12:16:58.653Z] DB: getSession - ok f1bf14dab2c9d0a135e3ec7ee0ba28ebdf1d02299b5c74483a60f5cf35e2e282 +[2025-08-16T12:16:58.653Z] SESSION: Valid f1bf14dab2c9d0a135e3ec7ee0ba28ebdf1d02299b5c74483a60f5cf35e2e282 testplayer PUT /api/players/testplayer +[2025-08-16T12:16:58.653Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:16:58.653Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:16:58.653Z] DB: updatePlayer - start testplayer +[2025-08-16T12:16:58.654Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T12:16:58.654Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:16:58.654Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:16:58.654Z] API: Updated player testplayer +[2025-08-16T12:16:58.670Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T12:16:58.670Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T12:16:58.670Z] DB: deletePlayer - start testplayer +[2025-08-16T12:16:58.672Z] DB: deletePlayer - changes 1 testplayer +[2025-08-16T12:16:58.672Z] API: Deleted player testplayer +[2025-08-16T12:16:59.168Z] DB: getAllPlayers - start +[2025-08-16T12:16:59.170Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T12:16:59.557Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T12:16:59.558Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T12:16:59.558Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T12:16:59.558Z] DB: deletePlayer - changes 0 shoptestplayer +[2025-08-16T12:16:59.572Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:16:59.572Z] DB: getPlayerByName - start shoptestplayer +[2025-08-16T12:16:59.572Z] DB: getPlayerByName - not found shoptestplayer +[2025-08-16T12:16:59.652Z] DB: createPlayer - start shoptestplayer +[2025-08-16T12:16:59.652Z] DB: createPlayer - done shoptestplayer rowid 110 +[2025-08-16T12:16:59.652Z] API: Created player shoptestplayer +[2025-08-16T12:16:59.667Z] SESSION: Missing sessionId GET /api/shop/inventory/shoptestplayer +[2025-08-16T12:16:59.679Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T12:16:59.680Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T12:16:59.680Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T12:16:59.680Z] DB: deletePlayer - changes 1 shoptestplayer +[2025-08-16T12:16:59.680Z] API: Deleted player shoptestplayer +[2025-08-16T12:20:28.034Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T12:20:28.035Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T12:20:28.036Z] DB: deletePlayer - start gmtest +[2025-08-16T12:20:28.036Z] DB: deletePlayer - changes 0 gmtest +[2025-08-16T12:20:28.072Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:20:28.072Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:20:28.073Z] DB: getPlayerByName - not found gmtest +[2025-08-16T12:20:28.238Z] DB: createPlayer - start gmtest +[2025-08-16T12:20:28.238Z] DB: createPlayer - done gmtest rowid 111 +[2025-08-16T12:20:28.239Z] API: Created player gmtest +[2025-08-16T12:20:28.260Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T12:20:28.261Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T12:20:28.261Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:20:28.261Z] DB: getPlayerByName - found gmtest +[2025-08-16T12:20:28.262Z] DB: updatePlayer - start gmtest +[2025-08-16T12:20:28.262Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T12:20:28.263Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:20:28.263Z] DB: getPlayerByName - found gmtest +[2025-08-16T12:20:28.263Z] API: Updated player gmtest +[2025-08-16T12:20:28.279Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T12:20:28.279Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T12:20:28.280Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:20:28.280Z] DB: getPlayerByName - found gmtest +[2025-08-16T12:20:28.360Z] DB: updatePlayer - start gmtest +[2025-08-16T12:20:28.360Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T12:20:28.360Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:20:28.360Z] DB: getPlayerByName - found gmtest +[2025-08-16T12:20:28.360Z] API: Updated player gmtest +[2025-08-16T12:20:28.377Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:20:28.377Z] DB: getPlayerByName - found gmtest +[2025-08-16T12:20:28.457Z] DB: createSession - start a05f3b20f65da484ec880a27a92622061b0b3a0d9c8064d3e6d8a5f8ce0ff84f expiresAt 2025-08-17T12:20:28.457Z +[2025-08-16T12:20:28.458Z] DB: createSession - done a05f3b20f65da484ec880a27a92622061b0b3a0d9c8064d3e6d8a5f8ce0ff84f +[2025-08-16T12:20:28.458Z] API: Player login gmtest a05f3b20f65da484ec880a27a92622061b0b3a0d9c8064d3e6d8a5f8ce0ff84f +[2025-08-16T12:20:28.475Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T12:20:28.475Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T12:20:28.475Z] DB: deletePlayer - start gmtest +[2025-08-16T12:20:28.476Z] DB: deletePlayer - changes 1 gmtest +[2025-08-16T12:20:28.476Z] API: Deleted player gmtest +[2025-08-16T12:20:28.490Z] SESSION: GM bypass accepted GET /api/players +[2025-08-16T12:20:28.491Z] DB: getAllPlayers - start +[2025-08-16T12:20:28.492Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T12:20:28.492Z] API: Fetch all players (public) Found 7 players +[2025-08-16T12:20:28.845Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T12:20:28.846Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T12:20:28.846Z] DB: deletePlayer - start testplayer +[2025-08-16T12:20:28.846Z] DB: deletePlayer - changes 0 testplayer +[2025-08-16T12:20:28.865Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:20:28.865Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:20:28.865Z] DB: getPlayerByName - not found testplayer +[2025-08-16T12:20:28.948Z] DB: createPlayer - start testplayer +[2025-08-16T12:20:28.949Z] DB: createPlayer - done testplayer rowid 112 +[2025-08-16T12:20:28.949Z] API: Created player testplayer +[2025-08-16T12:20:28.968Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:20:28.968Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:20:29.049Z] DB: createSession - start 091d5e839225c114b01b728452a37cf0adecb20ca5d001eef74301c1171b68fb expiresAt 2025-08-17T12:20:29.049Z +[2025-08-16T12:20:29.087Z] DB: createSession - done 091d5e839225c114b01b728452a37cf0adecb20ca5d001eef74301c1171b68fb +[2025-08-16T12:20:29.087Z] API: Player login testplayer 091d5e839225c114b01b728452a37cf0adecb20ca5d001eef74301c1171b68fb +[2025-08-16T12:20:29.105Z] DB: getSession - start 091d5e839225c114b01b728452a37cf0adecb20ca5d001eef74301c1171b68fb +[2025-08-16T12:20:29.105Z] DB: getSession - ok 091d5e839225c114b01b728452a37cf0adecb20ca5d001eef74301c1171b68fb +[2025-08-16T12:20:29.106Z] SESSION: Valid 091d5e839225c114b01b728452a37cf0adecb20ca5d001eef74301c1171b68fb testplayer PUT /api/players/testplayer +[2025-08-16T12:20:29.106Z] DB: getSession - start 091d5e839225c114b01b728452a37cf0adecb20ca5d001eef74301c1171b68fb +[2025-08-16T12:20:29.106Z] DB: getSession - ok 091d5e839225c114b01b728452a37cf0adecb20ca5d001eef74301c1171b68fb +[2025-08-16T12:20:29.106Z] SESSION: Valid 091d5e839225c114b01b728452a37cf0adecb20ca5d001eef74301c1171b68fb testplayer PUT /api/players/testplayer +[2025-08-16T12:20:29.106Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:20:29.106Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:20:29.107Z] DB: updatePlayer - start testplayer +[2025-08-16T12:20:29.107Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T12:20:29.107Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:20:29.107Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:20:29.107Z] API: Updated player testplayer +[2025-08-16T12:20:29.124Z] DB: getSession - start 091d5e839225c114b01b728452a37cf0adecb20ca5d001eef74301c1171b68fb +[2025-08-16T12:20:29.124Z] DB: getSession - ok 091d5e839225c114b01b728452a37cf0adecb20ca5d001eef74301c1171b68fb +[2025-08-16T12:20:29.124Z] SESSION: Valid 091d5e839225c114b01b728452a37cf0adecb20ca5d001eef74301c1171b68fb testplayer PUT /api/players/testplayer +[2025-08-16T12:20:29.124Z] DB: getSession - start 091d5e839225c114b01b728452a37cf0adecb20ca5d001eef74301c1171b68fb +[2025-08-16T12:20:29.124Z] DB: getSession - ok 091d5e839225c114b01b728452a37cf0adecb20ca5d001eef74301c1171b68fb +[2025-08-16T12:20:29.124Z] SESSION: Valid 091d5e839225c114b01b728452a37cf0adecb20ca5d001eef74301c1171b68fb testplayer PUT /api/players/testplayer +[2025-08-16T12:20:29.124Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:20:29.124Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:20:29.125Z] DB: updatePlayer - start testplayer +[2025-08-16T12:20:29.125Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T12:20:29.125Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:20:29.125Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:20:29.125Z] API: Updated player testplayer +[2025-08-16T12:20:29.142Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T12:20:29.142Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T12:20:29.142Z] DB: deletePlayer - start testplayer +[2025-08-16T12:20:29.143Z] DB: deletePlayer - changes 1 testplayer +[2025-08-16T12:20:29.143Z] API: Deleted player testplayer +[2025-08-16T12:20:29.658Z] DB: getAllPlayers - start +[2025-08-16T12:20:29.660Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T12:20:30.052Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T12:20:30.052Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T12:20:30.052Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T12:20:30.052Z] DB: deletePlayer - changes 0 shoptestplayer +[2025-08-16T12:20:30.067Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:20:30.067Z] DB: getPlayerByName - start shoptestplayer +[2025-08-16T12:20:30.067Z] DB: getPlayerByName - not found shoptestplayer +[2025-08-16T12:20:30.148Z] DB: createPlayer - start shoptestplayer +[2025-08-16T12:20:30.148Z] DB: createPlayer - done shoptestplayer rowid 113 +[2025-08-16T12:20:30.148Z] API: Created player shoptestplayer +[2025-08-16T12:20:30.162Z] SESSION: Missing sessionId GET /api/shop/inventory/shoptestplayer +[2025-08-16T12:20:30.175Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T12:20:30.175Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T12:20:30.175Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T12:20:30.176Z] DB: deletePlayer - changes 1 shoptestplayer +[2025-08-16T12:20:30.176Z] API: Deleted player shoptestplayer +[2025-08-16T12:23:25.786Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:23:25.786Z] DB: getPlayerByName - not found testplayer +[2025-08-16T12:23:42.594Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:23:42.595Z] DB: getPlayerByName - not found testplayer +[2025-08-16T12:23:53.086Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:23:53.086Z] DB: getPlayerByName - not found testplayer +[2025-08-16T12:24:09.345Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:24:09.347Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:24:09.348Z] DB: getPlayerByName - not found testplayer +[2025-08-16T12:24:09.550Z] DB: createPlayer - start testplayer +[2025-08-16T12:24:09.551Z] DB: createPlayer - done testplayer rowid 114 +[2025-08-16T12:24:09.551Z] API: Created player testplayer +[2025-08-16T12:24:09.575Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:24:09.576Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:24:09.656Z] DB: createSession - start 738af3333406729b184d4fd052b7dfda7aa7247ae2dd6eb33f0a9679fd76cb62 expiresAt 2025-08-17T12:24:09.656Z +[2025-08-16T12:24:09.658Z] DB: createSession - done 738af3333406729b184d4fd052b7dfda7aa7247ae2dd6eb33f0a9679fd76cb62 +[2025-08-16T12:24:09.658Z] API: Player login testplayer 738af3333406729b184d4fd052b7dfda7aa7247ae2dd6eb33f0a9679fd76cb62 +[2025-08-16T12:24:09.723Z] DB: getSession - start 738af3333406729b184d4fd052b7dfda7aa7247ae2dd6eb33f0a9679fd76cb62 +[2025-08-16T12:24:09.723Z] DB: getSession - ok 738af3333406729b184d4fd052b7dfda7aa7247ae2dd6eb33f0a9679fd76cb62 +[2025-08-16T12:24:09.723Z] SESSION: Valid 738af3333406729b184d4fd052b7dfda7aa7247ae2dd6eb33f0a9679fd76cb62 testplayer GET /api/players +[2025-08-16T12:24:09.724Z] DB: getAllPlayers - start +[2025-08-16T12:24:09.726Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:24:09.726Z] API: Fetch all players (public) Found 8 players +[2025-08-16T12:24:09.828Z] DB: getAllPlayers - start +[2025-08-16T12:24:09.829Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:24:58.333Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:24:58.335Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:24:58.335Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:24:58.370Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:24:58.370Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:24:58.542Z] DB: createSession - start 99d7fbc95b381a190360bdbd4c1627980e5aafa29e02aa51f62380a432d3eeaa expiresAt 2025-08-17T12:24:58.542Z +[2025-08-16T12:24:58.543Z] DB: createSession - done 99d7fbc95b381a190360bdbd4c1627980e5aafa29e02aa51f62380a432d3eeaa +[2025-08-16T12:24:58.543Z] API: Player login testplayer 99d7fbc95b381a190360bdbd4c1627980e5aafa29e02aa51f62380a432d3eeaa +[2025-08-16T12:24:58.628Z] DB: getSession - start 99d7fbc95b381a190360bdbd4c1627980e5aafa29e02aa51f62380a432d3eeaa +[2025-08-16T12:24:58.629Z] DB: getSession - ok 99d7fbc95b381a190360bdbd4c1627980e5aafa29e02aa51f62380a432d3eeaa +[2025-08-16T12:24:58.629Z] SESSION: Valid 99d7fbc95b381a190360bdbd4c1627980e5aafa29e02aa51f62380a432d3eeaa testplayer GET /api/players +[2025-08-16T12:24:58.630Z] DB: getAllPlayers - start +[2025-08-16T12:24:58.631Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:24:58.632Z] API: Fetch all players (public) Found 8 players +[2025-08-16T12:24:58.724Z] DB: getAllPlayers - start +[2025-08-16T12:24:58.725Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:25:14.621Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:25:14.622Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:25:14.623Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:25:14.660Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:25:14.661Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:25:14.833Z] DB: createSession - start 354579339a3187eac16ea9852fa35c0dc99563eff10ccbe46c4843eb929641eb expiresAt 2025-08-17T12:25:14.833Z +[2025-08-16T12:25:14.834Z] DB: createSession - done 354579339a3187eac16ea9852fa35c0dc99563eff10ccbe46c4843eb929641eb +[2025-08-16T12:25:14.834Z] API: Player login testplayer 354579339a3187eac16ea9852fa35c0dc99563eff10ccbe46c4843eb929641eb +[2025-08-16T12:25:14.916Z] DB: getSession - start 354579339a3187eac16ea9852fa35c0dc99563eff10ccbe46c4843eb929641eb +[2025-08-16T12:25:14.917Z] DB: getSession - ok 354579339a3187eac16ea9852fa35c0dc99563eff10ccbe46c4843eb929641eb +[2025-08-16T12:25:14.917Z] SESSION: Valid 354579339a3187eac16ea9852fa35c0dc99563eff10ccbe46c4843eb929641eb testplayer GET /api/players +[2025-08-16T12:25:14.918Z] DB: getAllPlayers - start +[2025-08-16T12:25:14.920Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:25:14.920Z] API: Fetch all players (public) Found 8 players +[2025-08-16T12:25:15.027Z] DB: getAllPlayers - start +[2025-08-16T12:25:15.028Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:25:22.481Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:25:22.483Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:25:22.483Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:25:22.513Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:25:22.514Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:25:22.689Z] DB: createSession - start f38cc5264cc1da0088e4206bf63ebeceb24b0dbe048a83260467b06e384b073b expiresAt 2025-08-17T12:25:22.689Z +[2025-08-16T12:25:22.690Z] DB: createSession - done f38cc5264cc1da0088e4206bf63ebeceb24b0dbe048a83260467b06e384b073b +[2025-08-16T12:25:22.690Z] API: Player login testplayer f38cc5264cc1da0088e4206bf63ebeceb24b0dbe048a83260467b06e384b073b +[2025-08-16T12:25:41.766Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:25:41.768Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:25:41.768Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:25:41.803Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:25:41.803Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:25:41.969Z] DB: createSession - start 2eb49a82819f12c3e940693d1ad68c0533de2cfcb443d28f9b7aade1833b6ea2 expiresAt 2025-08-17T12:25:41.969Z +[2025-08-16T12:25:41.969Z] DB: createSession - done 2eb49a82819f12c3e940693d1ad68c0533de2cfcb443d28f9b7aade1833b6ea2 +[2025-08-16T12:25:41.969Z] API: Player login testplayer 2eb49a82819f12c3e940693d1ad68c0533de2cfcb443d28f9b7aade1833b6ea2 +[2025-08-16T12:25:42.056Z] DB: getAllPlayers - start +[2025-08-16T12:25:42.058Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:26:04.869Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:26:04.870Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:26:04.871Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:26:04.905Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:26:04.905Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:26:05.076Z] DB: createSession - start 9ce61036e2e1e63c80991738ecf3618d75561366c5147ef557b7f45a820f965d expiresAt 2025-08-17T12:26:05.076Z +[2025-08-16T12:26:05.078Z] DB: createSession - done 9ce61036e2e1e63c80991738ecf3618d75561366c5147ef557b7f45a820f965d +[2025-08-16T12:26:05.078Z] API: Player login testplayer 9ce61036e2e1e63c80991738ecf3618d75561366c5147ef557b7f45a820f965d +[2025-08-16T12:26:05.157Z] DB: getAllPlayers - start +[2025-08-16T12:26:05.159Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:26:25.171Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:26:25.174Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:26:25.174Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:26:42.098Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:26:42.098Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:26:42.293Z] DB: createSession - start b99ddcf98c9c1a862008b148e4653d06eb7ea187e93d7d196f2161651a384c9e expiresAt 2025-08-17T12:26:42.293Z +[2025-08-16T12:26:42.295Z] DB: createSession - done b99ddcf98c9c1a862008b148e4653d06eb7ea187e93d7d196f2161651a384c9e +[2025-08-16T12:26:42.295Z] API: Player login testplayer b99ddcf98c9c1a862008b148e4653d06eb7ea187e93d7d196f2161651a384c9e +[2025-08-16T12:26:50.086Z] DB: getSession - start b99ddcf98c9c1a862008b148e4653d06eb7ea187e93d7d196f2161651a384c9e +[2025-08-16T12:26:50.086Z] DB: getSession - ok b99ddcf98c9c1a862008b148e4653d06eb7ea187e93d7d196f2161651a384c9e +[2025-08-16T12:26:50.087Z] SESSION: Valid b99ddcf98c9c1a862008b148e4653d06eb7ea187e93d7d196f2161651a384c9e testplayer GET /api/players +[2025-08-16T12:26:50.088Z] DB: getAllPlayers - start +[2025-08-16T12:26:50.090Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:26:50.090Z] API: Fetch all players (public) Found 8 players +[2025-08-16T12:27:21.315Z] DB: getSession - start c36d64f88e4f0aa7226f51b1810c3b686a936e42c419252a98c3869c757f1b2f +[2025-08-16T12:27:21.316Z] DB: getSession - ok c36d64f88e4f0aa7226f51b1810c3b686a936e42c419252a98c3869c757f1b2f +[2025-08-16T12:27:21.316Z] SESSION: Validate success c36d64f88e4f0aa7226f51b1810c3b686a936e42c419252a98c3869c757f1b2f chris +[2025-08-16T12:27:24.240Z] DB: deleteSession - start c36d64f88e4f0aa7226f51b1810c3b686a936e42c419252a98c3869c757f1b2f +[2025-08-16T12:27:24.241Z] DB: deleteSession - changes 1 c36d64f88e4f0aa7226f51b1810c3b686a936e42c419252a98c3869c757f1b2f +[2025-08-16T12:27:24.242Z] SESSION: Logout success c36d64f88e4f0aa7226f51b1810c3b686a936e42c419252a98c3869c757f1b2f +[2025-08-16T12:27:28.758Z] SESSION: Missing sessionId GET /api/players +[2025-08-16T12:27:31.782Z] SESSION: Missing sessionId GET /api/players +[2025-08-16T12:27:34.942Z] DB: getAllPlayers - start +[2025-08-16T12:27:34.943Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:27:38.395Z] DB: getPlayerByName - start phillip +[2025-08-16T12:27:38.395Z] DB: getPlayerByName - found phillip +[2025-08-16T12:27:38.476Z] DB: createSession - start 1e7cae3d1c40bd9f1fff8cc9d1cb8c21a3bad85abe3721a7a55a389fe70924f1 expiresAt 2025-08-17T12:27:38.476Z +[2025-08-16T12:27:38.478Z] DB: createSession - done 1e7cae3d1c40bd9f1fff8cc9d1cb8c21a3bad85abe3721a7a55a389fe70924f1 +[2025-08-16T12:27:38.478Z] API: Player login phillip 1e7cae3d1c40bd9f1fff8cc9d1cb8c21a3bad85abe3721a7a55a389fe70924f1 +[2025-08-16T12:27:38.516Z] DB: getSession - start 1e7cae3d1c40bd9f1fff8cc9d1cb8c21a3bad85abe3721a7a55a389fe70924f1 +[2025-08-16T12:27:38.517Z] DB: getSession - ok 1e7cae3d1c40bd9f1fff8cc9d1cb8c21a3bad85abe3721a7a55a389fe70924f1 +[2025-08-16T12:27:38.517Z] SESSION: Validate success 1e7cae3d1c40bd9f1fff8cc9d1cb8c21a3bad85abe3721a7a55a389fe70924f1 phillip +[2025-08-16T12:27:38.519Z] DB: getSession - start 1e7cae3d1c40bd9f1fff8cc9d1cb8c21a3bad85abe3721a7a55a389fe70924f1 +[2025-08-16T12:27:38.519Z] DB: getSession - ok 1e7cae3d1c40bd9f1fff8cc9d1cb8c21a3bad85abe3721a7a55a389fe70924f1 +[2025-08-16T12:27:38.519Z] SESSION: Valid 1e7cae3d1c40bd9f1fff8cc9d1cb8c21a3bad85abe3721a7a55a389fe70924f1 phillip GET /api/players +[2025-08-16T12:27:38.519Z] DB: getAllPlayers - start +[2025-08-16T12:27:38.520Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:27:38.520Z] API: Fetch all players (public) Found 8 players +[2025-08-16T12:28:03.188Z] DB: deleteSession - start 1e7cae3d1c40bd9f1fff8cc9d1cb8c21a3bad85abe3721a7a55a389fe70924f1 +[2025-08-16T12:28:03.189Z] DB: deleteSession - changes 1 1e7cae3d1c40bd9f1fff8cc9d1cb8c21a3bad85abe3721a7a55a389fe70924f1 +[2025-08-16T12:28:03.190Z] SESSION: Logout success 1e7cae3d1c40bd9f1fff8cc9d1cb8c21a3bad85abe3721a7a55a389fe70924f1 +[2025-08-16T12:28:03.230Z] SESSION: Missing sessionId GET /api/players +[2025-08-16T12:28:07.078Z] DB: getPlayerByName - start anders +[2025-08-16T12:28:07.078Z] DB: getPlayerByName - found anders +[2025-08-16T12:28:07.158Z] DB: createSession - start 4caa0278088f75e524412f7d79d511363f65bd5e4fec30e20ede58deeefb4e4d expiresAt 2025-08-17T12:28:07.158Z +[2025-08-16T12:28:07.158Z] DB: createSession - done 4caa0278088f75e524412f7d79d511363f65bd5e4fec30e20ede58deeefb4e4d +[2025-08-16T12:28:07.158Z] API: Player login anders 4caa0278088f75e524412f7d79d511363f65bd5e4fec30e20ede58deeefb4e4d +[2025-08-16T12:28:07.213Z] DB: getSession - start 4caa0278088f75e524412f7d79d511363f65bd5e4fec30e20ede58deeefb4e4d +[2025-08-16T12:28:07.213Z] DB: getSession - ok 4caa0278088f75e524412f7d79d511363f65bd5e4fec30e20ede58deeefb4e4d +[2025-08-16T12:28:07.213Z] SESSION: Validate success 4caa0278088f75e524412f7d79d511363f65bd5e4fec30e20ede58deeefb4e4d anders +[2025-08-16T12:28:14.961Z] DB: deleteSession - start 4caa0278088f75e524412f7d79d511363f65bd5e4fec30e20ede58deeefb4e4d +[2025-08-16T12:28:14.963Z] DB: deleteSession - changes 1 4caa0278088f75e524412f7d79d511363f65bd5e4fec30e20ede58deeefb4e4d +[2025-08-16T12:28:14.963Z] SESSION: Logout success 4caa0278088f75e524412f7d79d511363f65bd5e4fec30e20ede58deeefb4e4d +[2025-08-16T12:28:15.008Z] SESSION: Missing sessionId GET /api/players +[2025-08-16T12:33:43.484Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:33:43.484Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:33:43.593Z] DB: createSession - start 0621e57d463c2951f4fcee3badcf674aeb2fc3ca588a1a9894eb5a74583ff7ed expiresAt 2025-08-17T12:33:43.593Z +[2025-08-16T12:33:43.594Z] DB: createSession - done 0621e57d463c2951f4fcee3badcf674aeb2fc3ca588a1a9894eb5a74583ff7ed +[2025-08-16T12:33:43.594Z] API: Player login testplayer 0621e57d463c2951f4fcee3badcf674aeb2fc3ca588a1a9894eb5a74583ff7ed +[2025-08-16T12:34:00.650Z] DB: getSession - start 0621e57d463c2951f4fcee3badcf674aeb2fc3ca588a1a9894eb5a74583ff7ed +[2025-08-16T12:34:00.650Z] DB: getSession - ok 0621e57d463c2951f4fcee3badcf674aeb2fc3ca588a1a9894eb5a74583ff7ed +[2025-08-16T12:34:00.651Z] SESSION: Valid 0621e57d463c2951f4fcee3badcf674aeb2fc3ca588a1a9894eb5a74583ff7ed testplayer GET /api/players +[2025-08-16T12:34:00.651Z] DB: getAllPlayers - start +[2025-08-16T12:34:00.652Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:34:00.652Z] API: Fetch all players (public) Found 8 players +[2025-08-16T12:34:13.700Z] DB: getSession - start 0621e57d463c2951f4fcee3badcf674aeb2fc3ca588a1a9894eb5a74583ff7ed +[2025-08-16T12:34:13.700Z] DB: getSession - ok 0621e57d463c2951f4fcee3badcf674aeb2fc3ca588a1a9894eb5a74583ff7ed +[2025-08-16T12:34:13.700Z] SESSION: Valid 0621e57d463c2951f4fcee3badcf674aeb2fc3ca588a1a9894eb5a74583ff7ed testplayer GET /api/players/anders +[2025-08-16T12:34:13.701Z] DB: getPlayerByName - start anders +[2025-08-16T12:34:13.702Z] DB: getPlayerByName - found anders +[2025-08-16T12:34:13.702Z] API: Fetch player anders +[2025-08-16T12:34:46.603Z] DB: getAllPlayers - start +[2025-08-16T12:34:46.604Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:34:50.282Z] DB: getPlayerByName - start christoffer +[2025-08-16T12:34:50.282Z] DB: getPlayerByName - found christoffer +[2025-08-16T12:34:50.386Z] DB: createSession - start 91e121d9a863d97489a7c1c7761dcacd71db331c830319ffa41346b6b50b02b4 expiresAt 2025-08-17T12:34:50.386Z +[2025-08-16T12:34:50.388Z] DB: createSession - done 91e121d9a863d97489a7c1c7761dcacd71db331c830319ffa41346b6b50b02b4 +[2025-08-16T12:34:50.388Z] API: Player login christoffer 91e121d9a863d97489a7c1c7761dcacd71db331c830319ffa41346b6b50b02b4 +[2025-08-16T12:34:50.435Z] DB: getSession - start 91e121d9a863d97489a7c1c7761dcacd71db331c830319ffa41346b6b50b02b4 +[2025-08-16T12:34:50.435Z] DB: getSession - ok 91e121d9a863d97489a7c1c7761dcacd71db331c830319ffa41346b6b50b02b4 +[2025-08-16T12:34:50.435Z] SESSION: Validate success 91e121d9a863d97489a7c1c7761dcacd71db331c830319ffa41346b6b50b02b4 christoffer +[2025-08-16T12:35:00.384Z] DB: deleteSession - start 91e121d9a863d97489a7c1c7761dcacd71db331c830319ffa41346b6b50b02b4 +[2025-08-16T12:35:00.384Z] DB: deleteSession - changes 1 91e121d9a863d97489a7c1c7761dcacd71db331c830319ffa41346b6b50b02b4 +[2025-08-16T12:35:00.384Z] SESSION: Logout success 91e121d9a863d97489a7c1c7761dcacd71db331c830319ffa41346b6b50b02b4 +[2025-08-16T12:35:00.438Z] SESSION: Missing sessionId GET /api/players +[2025-08-16T12:35:10.435Z] DB: getPlayerByName - start claes +[2025-08-16T12:35:10.436Z] DB: getPlayerByName - found claes +[2025-08-16T12:35:10.515Z] DB: createSession - start 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 expiresAt 2025-08-17T12:35:10.515Z +[2025-08-16T12:35:10.516Z] DB: createSession - done 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 +[2025-08-16T12:35:10.516Z] API: Player login claes 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 +[2025-08-16T12:35:10.553Z] DB: getSession - start 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 +[2025-08-16T12:35:10.553Z] DB: getSession - ok 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 +[2025-08-16T12:35:10.553Z] SESSION: Valid 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 claes GET /api/players +[2025-08-16T12:35:10.553Z] DB: getAllPlayers - start +[2025-08-16T12:35:10.554Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:35:10.554Z] API: Fetch all players (public) Found 8 players +[2025-08-16T12:35:10.561Z] DB: getSession - start 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 +[2025-08-16T12:35:10.561Z] DB: getSession - ok 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 +[2025-08-16T12:35:10.561Z] SESSION: Validate success 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 claes +[2025-08-16T12:41:47.684Z] DB: getSession - start 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 +[2025-08-16T12:41:47.684Z] DB: getSession - ok 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 +[2025-08-16T12:41:47.684Z] SESSION: Validate success 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 claes +[2025-08-16T12:45:22.358Z] DB: getSession - start 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 +[2025-08-16T12:45:22.358Z] DB: getSession - ok 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 +[2025-08-16T12:45:22.359Z] SESSION: Valid 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 claes GET /api/players +[2025-08-16T12:45:22.359Z] DB: getAllPlayers - start +[2025-08-16T12:45:22.359Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:45:22.359Z] API: Fetch all players (public) Found 8 players +[2025-08-16T12:45:29.081Z] DB: getSession - start 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 +[2025-08-16T12:45:29.082Z] DB: getSession - ok 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 +[2025-08-16T12:45:29.082Z] SESSION: Validate success 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 claes +[2025-08-16T12:45:30.290Z] DB: deleteSession - start 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 +[2025-08-16T12:45:30.292Z] DB: deleteSession - changes 1 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 +[2025-08-16T12:45:30.292Z] SESSION: Logout success 64fd4ca6b72775c9c30fbea0043ec7fb3359aed74aa0464141a26a51eeb37067 +[2025-08-16T12:45:32.183Z] DB: getAllPlayers - start +[2025-08-16T12:45:32.184Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:45:34.981Z] DB: getPlayerByName - start chris +[2025-08-16T12:45:34.982Z] DB: getPlayerByName - found chris +[2025-08-16T12:45:35.061Z] DB: createSession - start 529d346fab4d363afe03a93a4ae61500c554ee5bf41a446fed93d9b142ea9ffc expiresAt 2025-08-17T12:45:35.061Z +[2025-08-16T12:45:35.061Z] DB: createSession - done 529d346fab4d363afe03a93a4ae61500c554ee5bf41a446fed93d9b142ea9ffc +[2025-08-16T12:45:35.061Z] API: Player login chris 529d346fab4d363afe03a93a4ae61500c554ee5bf41a446fed93d9b142ea9ffc +[2025-08-16T12:45:35.103Z] DB: getSession - start 529d346fab4d363afe03a93a4ae61500c554ee5bf41a446fed93d9b142ea9ffc +[2025-08-16T12:45:35.103Z] DB: getSession - ok 529d346fab4d363afe03a93a4ae61500c554ee5bf41a446fed93d9b142ea9ffc +[2025-08-16T12:45:35.103Z] SESSION: Validate success 529d346fab4d363afe03a93a4ae61500c554ee5bf41a446fed93d9b142ea9ffc chris +[2025-08-16T12:49:41.809Z] DB: getSession - start 529d346fab4d363afe03a93a4ae61500c554ee5bf41a446fed93d9b142ea9ffc +[2025-08-16T12:49:41.809Z] DB: getSession - ok 529d346fab4d363afe03a93a4ae61500c554ee5bf41a446fed93d9b142ea9ffc +[2025-08-16T12:49:41.809Z] SESSION: Validate success 529d346fab4d363afe03a93a4ae61500c554ee5bf41a446fed93d9b142ea9ffc chris +[2025-08-16T12:49:49.106Z] DB: deleteSession - start 529d346fab4d363afe03a93a4ae61500c554ee5bf41a446fed93d9b142ea9ffc +[2025-08-16T12:49:49.109Z] DB: deleteSession - changes 1 529d346fab4d363afe03a93a4ae61500c554ee5bf41a446fed93d9b142ea9ffc +[2025-08-16T12:49:49.109Z] SESSION: Logout success 529d346fab4d363afe03a93a4ae61500c554ee5bf41a446fed93d9b142ea9ffc +[2025-08-16T12:49:49.146Z] SESSION: Missing sessionId GET /api/players +[2025-08-16T12:49:51.096Z] SESSION: Missing sessionId GET /api/players +[2025-08-16T12:49:52.463Z] DB: getAllPlayers - start +[2025-08-16T12:49:52.465Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:49:54.883Z] DB: getPlayerByName - start phillip +[2025-08-16T12:49:54.884Z] DB: getPlayerByName - found phillip +[2025-08-16T12:49:55.002Z] DB: createSession - start b97bb9a91f39155af22721889d4d5f8f4d43c24b129286e628d7d272b7d13261 expiresAt 2025-08-17T12:49:55.002Z +[2025-08-16T12:49:55.002Z] DB: createSession - done b97bb9a91f39155af22721889d4d5f8f4d43c24b129286e628d7d272b7d13261 +[2025-08-16T12:49:55.002Z] API: Player login phillip b97bb9a91f39155af22721889d4d5f8f4d43c24b129286e628d7d272b7d13261 +[2025-08-16T12:49:55.082Z] DB: getSession - start b97bb9a91f39155af22721889d4d5f8f4d43c24b129286e628d7d272b7d13261 +[2025-08-16T12:49:55.083Z] DB: getSession - ok b97bb9a91f39155af22721889d4d5f8f4d43c24b129286e628d7d272b7d13261 +[2025-08-16T12:49:55.083Z] SESSION: Validate success b97bb9a91f39155af22721889d4d5f8f4d43c24b129286e628d7d272b7d13261 phillip +[2025-08-16T12:54:02.298Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T12:54:02.299Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T12:54:02.300Z] DB: deletePlayer - start gmtest +[2025-08-16T12:54:02.300Z] DB: deletePlayer - changes 0 gmtest +[2025-08-16T12:54:02.347Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:54:02.348Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:54:02.348Z] DB: getPlayerByName - not found gmtest +[2025-08-16T12:54:02.522Z] DB: createPlayer - start gmtest +[2025-08-16T12:54:02.524Z] DB: createPlayer - done gmtest rowid 115 +[2025-08-16T12:54:02.525Z] API: Created player gmtest +[2025-08-16T12:54:02.544Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T12:54:02.545Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T12:54:02.545Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:54:02.545Z] DB: getPlayerByName - found gmtest +[2025-08-16T12:54:02.546Z] DB: updatePlayer - start gmtest +[2025-08-16T12:54:02.546Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T12:54:02.547Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:54:02.547Z] DB: getPlayerByName - found gmtest +[2025-08-16T12:54:02.547Z] API: Updated player gmtest +[2025-08-16T12:54:02.563Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T12:54:02.563Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T12:54:02.563Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:54:02.563Z] DB: getPlayerByName - found gmtest +[2025-08-16T12:54:02.646Z] DB: updatePlayer - start gmtest +[2025-08-16T12:54:02.646Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T12:54:02.646Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:54:02.646Z] DB: getPlayerByName - found gmtest +[2025-08-16T12:54:02.647Z] API: Updated player gmtest +[2025-08-16T12:54:02.663Z] DB: getPlayerByName - start gmtest +[2025-08-16T12:54:02.663Z] DB: getPlayerByName - found gmtest +[2025-08-16T12:54:02.744Z] DB: createSession - start 421c52f153fed049efa53d27ee53e9175da2856fea9bf53d46aa624453a22d85 expiresAt 2025-08-17T12:54:02.744Z +[2025-08-16T12:54:02.744Z] DB: createSession - done 421c52f153fed049efa53d27ee53e9175da2856fea9bf53d46aa624453a22d85 +[2025-08-16T12:54:02.745Z] API: Player login gmtest 421c52f153fed049efa53d27ee53e9175da2856fea9bf53d46aa624453a22d85 +[2025-08-16T12:54:02.760Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T12:54:02.760Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T12:54:02.761Z] DB: deletePlayer - start gmtest +[2025-08-16T12:54:02.762Z] DB: deletePlayer - changes 1 gmtest +[2025-08-16T12:54:02.762Z] API: Deleted player gmtest +[2025-08-16T12:54:02.778Z] SESSION: GM bypass accepted GET /api/players +[2025-08-16T12:54:02.778Z] DB: getAllPlayers - start +[2025-08-16T12:54:02.781Z] DB: getAllPlayers - resultCount 8 +[2025-08-16T12:54:02.781Z] API: Fetch all players (public) Found 8 players +[2025-08-16T12:54:03.142Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T12:54:03.142Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T12:54:03.142Z] DB: deletePlayer - start testplayer +[2025-08-16T12:54:03.143Z] DB: deletePlayer - changes 1 testplayer +[2025-08-16T12:54:03.143Z] API: Deleted player testplayer +[2025-08-16T12:54:03.159Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:54:03.160Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:54:03.160Z] DB: getPlayerByName - not found testplayer +[2025-08-16T12:54:03.241Z] DB: createPlayer - start testplayer +[2025-08-16T12:54:03.242Z] DB: createPlayer - done testplayer rowid 116 +[2025-08-16T12:54:03.242Z] API: Created player testplayer +[2025-08-16T12:54:03.269Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:54:03.269Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:54:03.350Z] DB: createSession - start c8c1fb3a2dadfdace411ed9e61fafaf199826f5e1a1de729f4b28ba4dac1021d expiresAt 2025-08-17T12:54:03.350Z +[2025-08-16T12:54:03.351Z] DB: createSession - done c8c1fb3a2dadfdace411ed9e61fafaf199826f5e1a1de729f4b28ba4dac1021d +[2025-08-16T12:54:03.351Z] API: Player login testplayer c8c1fb3a2dadfdace411ed9e61fafaf199826f5e1a1de729f4b28ba4dac1021d +[2025-08-16T12:54:03.368Z] DB: getSession - start c8c1fb3a2dadfdace411ed9e61fafaf199826f5e1a1de729f4b28ba4dac1021d +[2025-08-16T12:54:03.368Z] DB: getSession - ok c8c1fb3a2dadfdace411ed9e61fafaf199826f5e1a1de729f4b28ba4dac1021d +[2025-08-16T12:54:03.368Z] SESSION: Valid c8c1fb3a2dadfdace411ed9e61fafaf199826f5e1a1de729f4b28ba4dac1021d testplayer PUT /api/players/testplayer +[2025-08-16T12:54:03.368Z] DB: getSession - start c8c1fb3a2dadfdace411ed9e61fafaf199826f5e1a1de729f4b28ba4dac1021d +[2025-08-16T12:54:03.369Z] DB: getSession - ok c8c1fb3a2dadfdace411ed9e61fafaf199826f5e1a1de729f4b28ba4dac1021d +[2025-08-16T12:54:03.369Z] SESSION: Valid c8c1fb3a2dadfdace411ed9e61fafaf199826f5e1a1de729f4b28ba4dac1021d testplayer PUT /api/players/testplayer +[2025-08-16T12:54:03.369Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:54:03.369Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:54:03.369Z] DB: updatePlayer - start testplayer +[2025-08-16T12:54:03.370Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T12:54:03.370Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:54:03.370Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:54:03.370Z] API: Updated player testplayer +[2025-08-16T12:54:03.386Z] DB: getSession - start c8c1fb3a2dadfdace411ed9e61fafaf199826f5e1a1de729f4b28ba4dac1021d +[2025-08-16T12:54:03.386Z] DB: getSession - ok c8c1fb3a2dadfdace411ed9e61fafaf199826f5e1a1de729f4b28ba4dac1021d +[2025-08-16T12:54:03.386Z] SESSION: Valid c8c1fb3a2dadfdace411ed9e61fafaf199826f5e1a1de729f4b28ba4dac1021d testplayer PUT /api/players/testplayer +[2025-08-16T12:54:03.386Z] DB: getSession - start c8c1fb3a2dadfdace411ed9e61fafaf199826f5e1a1de729f4b28ba4dac1021d +[2025-08-16T12:54:03.386Z] DB: getSession - ok c8c1fb3a2dadfdace411ed9e61fafaf199826f5e1a1de729f4b28ba4dac1021d +[2025-08-16T12:54:03.386Z] SESSION: Valid c8c1fb3a2dadfdace411ed9e61fafaf199826f5e1a1de729f4b28ba4dac1021d testplayer PUT /api/players/testplayer +[2025-08-16T12:54:03.386Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:54:03.386Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:54:03.387Z] DB: updatePlayer - start testplayer +[2025-08-16T12:54:03.387Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T12:54:03.387Z] DB: getPlayerByName - start testplayer +[2025-08-16T12:54:03.387Z] DB: getPlayerByName - found testplayer +[2025-08-16T12:54:03.387Z] API: Updated player testplayer +[2025-08-16T12:54:03.404Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T12:54:03.404Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T12:54:03.404Z] DB: deletePlayer - start testplayer +[2025-08-16T12:54:03.405Z] DB: deletePlayer - changes 1 testplayer +[2025-08-16T12:54:03.405Z] API: Deleted player testplayer +[2025-08-16T12:54:03.881Z] DB: getAllPlayers - start +[2025-08-16T12:54:03.883Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T12:54:04.264Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T12:54:04.264Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T12:54:04.265Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T12:54:04.265Z] DB: deletePlayer - changes 0 shoptestplayer +[2025-08-16T12:54:04.279Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T12:54:04.279Z] DB: getPlayerByName - start shoptestplayer +[2025-08-16T12:54:04.279Z] DB: getPlayerByName - not found shoptestplayer +[2025-08-16T12:54:04.360Z] DB: createPlayer - start shoptestplayer +[2025-08-16T12:54:04.361Z] DB: createPlayer - done shoptestplayer rowid 117 +[2025-08-16T12:54:04.361Z] API: Created player shoptestplayer +[2025-08-16T12:54:04.376Z] SESSION: Missing sessionId GET /api/shop/inventory/shoptestplayer +[2025-08-16T12:54:04.389Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T12:54:04.389Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T12:54:04.389Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T12:54:04.389Z] DB: deletePlayer - changes 1 shoptestplayer +[2025-08-16T12:54:04.389Z] API: Deleted player shoptestplayer +[2025-08-16T13:00:58.601Z] SESSION: Missing sessionId GET /api/players +[2025-08-16T13:01:23.767Z] DB: getAllPlayers - start +[2025-08-16T13:01:23.770Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T13:01:26.108Z] DB: getPlayerByName - start phillip +[2025-08-16T13:01:26.108Z] DB: getPlayerByName - found phillip +[2025-08-16T13:01:26.308Z] DB: createSession - start 5f1cada324f61d87daece67a2303ad66fcc73c883742621472c8bb93d4ae4282 expiresAt 2025-08-17T13:01:26.308Z +[2025-08-16T13:01:26.310Z] DB: createSession - done 5f1cada324f61d87daece67a2303ad66fcc73c883742621472c8bb93d4ae4282 +[2025-08-16T13:01:26.310Z] API: Player login phillip 5f1cada324f61d87daece67a2303ad66fcc73c883742621472c8bb93d4ae4282 +[2025-08-16T13:01:26.361Z] DB: getSession - start 5f1cada324f61d87daece67a2303ad66fcc73c883742621472c8bb93d4ae4282 +[2025-08-16T13:01:26.361Z] DB: getSession - ok 5f1cada324f61d87daece67a2303ad66fcc73c883742621472c8bb93d4ae4282 +[2025-08-16T13:01:26.362Z] SESSION: Validate success 5f1cada324f61d87daece67a2303ad66fcc73c883742621472c8bb93d4ae4282 phillip +[2025-08-16T13:01:26.364Z] DB: getSession - start 5f1cada324f61d87daece67a2303ad66fcc73c883742621472c8bb93d4ae4282 +[2025-08-16T13:01:26.364Z] DB: getSession - ok 5f1cada324f61d87daece67a2303ad66fcc73c883742621472c8bb93d4ae4282 +[2025-08-16T13:01:26.364Z] SESSION: Valid 5f1cada324f61d87daece67a2303ad66fcc73c883742621472c8bb93d4ae4282 phillip GET /api/players +[2025-08-16T13:01:26.365Z] DB: getAllPlayers - start +[2025-08-16T13:01:26.366Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T13:01:26.366Z] API: Fetch all players (public) Found 7 players +[2025-08-16T13:04:52.687Z] DB: deleteSession - start 5f1cada324f61d87daece67a2303ad66fcc73c883742621472c8bb93d4ae4282 +[2025-08-16T13:04:52.688Z] DB: deleteSession - changes 1 5f1cada324f61d87daece67a2303ad66fcc73c883742621472c8bb93d4ae4282 +[2025-08-16T13:04:52.688Z] SESSION: Logout success 5f1cada324f61d87daece67a2303ad66fcc73c883742621472c8bb93d4ae4282 +[2025-08-16T13:04:52.870Z] SESSION: Missing sessionId GET /api/players +[2025-08-16T13:11:09.215Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T13:11:09.217Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T13:11:09.217Z] DB: deletePlayer - start gmtest +[2025-08-16T13:11:09.217Z] DB: deletePlayer - changes 0 gmtest +[2025-08-16T13:11:09.265Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T13:11:09.266Z] DB: getPlayerByName - start gmtest +[2025-08-16T13:11:09.266Z] DB: getPlayerByName - not found gmtest +[2025-08-16T13:11:09.434Z] DB: createPlayer - start gmtest +[2025-08-16T13:11:09.435Z] DB: createPlayer - done gmtest rowid 118 +[2025-08-16T13:11:09.435Z] API: Created player gmtest +[2025-08-16T13:11:09.455Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T13:11:09.456Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T13:11:09.456Z] DB: getPlayerByName - start gmtest +[2025-08-16T13:11:09.456Z] DB: getPlayerByName - found gmtest +[2025-08-16T13:11:09.457Z] DB: updatePlayer - start gmtest +[2025-08-16T13:11:09.457Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T13:11:09.458Z] DB: getPlayerByName - start gmtest +[2025-08-16T13:11:09.458Z] DB: getPlayerByName - found gmtest +[2025-08-16T13:11:09.458Z] API: Updated player gmtest +[2025-08-16T13:11:09.475Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T13:11:09.475Z] SESSION: GM bypass accepted PUT /api/players/gmtest +[2025-08-16T13:11:09.475Z] DB: getPlayerByName - start gmtest +[2025-08-16T13:11:09.475Z] DB: getPlayerByName - found gmtest +[2025-08-16T13:11:09.557Z] DB: updatePlayer - start gmtest +[2025-08-16T13:11:09.557Z] DB: updatePlayer - changes 1 gmtest +[2025-08-16T13:11:09.557Z] DB: getPlayerByName - start gmtest +[2025-08-16T13:11:09.557Z] DB: getPlayerByName - found gmtest +[2025-08-16T13:11:09.557Z] API: Updated player gmtest +[2025-08-16T13:11:09.573Z] DB: getPlayerByName - start gmtest +[2025-08-16T13:11:09.574Z] DB: getPlayerByName - found gmtest +[2025-08-16T13:11:09.653Z] DB: createSession - start a9eb22d26e3a9685ebb34cbe1441097302c8932d305185d74512e340675b4805 expiresAt 2025-08-17T13:11:09.653Z +[2025-08-16T13:11:09.654Z] DB: createSession - done a9eb22d26e3a9685ebb34cbe1441097302c8932d305185d74512e340675b4805 +[2025-08-16T13:11:09.654Z] API: Player login gmtest a9eb22d26e3a9685ebb34cbe1441097302c8932d305185d74512e340675b4805 +[2025-08-16T13:11:09.670Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T13:11:09.670Z] SESSION: GM bypass accepted DELETE /api/players/gmtest +[2025-08-16T13:11:09.671Z] DB: deletePlayer - start gmtest +[2025-08-16T13:11:09.673Z] DB: deletePlayer - changes 1 gmtest +[2025-08-16T13:11:09.673Z] API: Deleted player gmtest +[2025-08-16T13:11:09.688Z] SESSION: GM bypass accepted GET /api/players +[2025-08-16T13:11:09.689Z] DB: getAllPlayers - start +[2025-08-16T13:11:09.690Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T13:11:09.690Z] API: Fetch all players (public) Found 7 players +[2025-08-16T13:11:10.045Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T13:11:10.045Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T13:11:10.045Z] DB: deletePlayer - start testplayer +[2025-08-16T13:11:10.045Z] DB: deletePlayer - changes 0 testplayer +[2025-08-16T13:11:10.062Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T13:11:10.062Z] DB: getPlayerByName - start testplayer +[2025-08-16T13:11:10.062Z] DB: getPlayerByName - not found testplayer +[2025-08-16T13:11:10.144Z] DB: createPlayer - start testplayer +[2025-08-16T13:11:10.145Z] DB: createPlayer - done testplayer rowid 119 +[2025-08-16T13:11:10.145Z] API: Created player testplayer +[2025-08-16T13:11:10.175Z] DB: getPlayerByName - start testplayer +[2025-08-16T13:11:10.175Z] DB: getPlayerByName - found testplayer +[2025-08-16T13:11:10.254Z] DB: createSession - start 46f8c261e225aaaf0313422ee6c3641ac2f3b83398af905c9361082b7dff05cc expiresAt 2025-08-17T13:11:10.254Z +[2025-08-16T13:11:10.255Z] DB: createSession - done 46f8c261e225aaaf0313422ee6c3641ac2f3b83398af905c9361082b7dff05cc +[2025-08-16T13:11:10.255Z] API: Player login testplayer 46f8c261e225aaaf0313422ee6c3641ac2f3b83398af905c9361082b7dff05cc +[2025-08-16T13:11:10.272Z] DB: getSession - start 46f8c261e225aaaf0313422ee6c3641ac2f3b83398af905c9361082b7dff05cc +[2025-08-16T13:11:10.272Z] DB: getSession - ok 46f8c261e225aaaf0313422ee6c3641ac2f3b83398af905c9361082b7dff05cc +[2025-08-16T13:11:10.272Z] SESSION: Valid 46f8c261e225aaaf0313422ee6c3641ac2f3b83398af905c9361082b7dff05cc testplayer PUT /api/players/testplayer +[2025-08-16T13:11:10.272Z] DB: getSession - start 46f8c261e225aaaf0313422ee6c3641ac2f3b83398af905c9361082b7dff05cc +[2025-08-16T13:11:10.273Z] DB: getSession - ok 46f8c261e225aaaf0313422ee6c3641ac2f3b83398af905c9361082b7dff05cc +[2025-08-16T13:11:10.273Z] SESSION: Valid 46f8c261e225aaaf0313422ee6c3641ac2f3b83398af905c9361082b7dff05cc testplayer PUT /api/players/testplayer +[2025-08-16T13:11:10.273Z] DB: getPlayerByName - start testplayer +[2025-08-16T13:11:10.273Z] DB: getPlayerByName - found testplayer +[2025-08-16T13:11:10.273Z] DB: updatePlayer - start testplayer +[2025-08-16T13:11:10.274Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T13:11:10.274Z] DB: getPlayerByName - start testplayer +[2025-08-16T13:11:10.274Z] DB: getPlayerByName - found testplayer +[2025-08-16T13:11:10.274Z] API: Updated player testplayer +[2025-08-16T13:11:10.290Z] DB: getSession - start 46f8c261e225aaaf0313422ee6c3641ac2f3b83398af905c9361082b7dff05cc +[2025-08-16T13:11:10.290Z] DB: getSession - ok 46f8c261e225aaaf0313422ee6c3641ac2f3b83398af905c9361082b7dff05cc +[2025-08-16T13:11:10.290Z] SESSION: Valid 46f8c261e225aaaf0313422ee6c3641ac2f3b83398af905c9361082b7dff05cc testplayer PUT /api/players/testplayer +[2025-08-16T13:11:10.290Z] DB: getSession - start 46f8c261e225aaaf0313422ee6c3641ac2f3b83398af905c9361082b7dff05cc +[2025-08-16T13:11:10.290Z] DB: getSession - ok 46f8c261e225aaaf0313422ee6c3641ac2f3b83398af905c9361082b7dff05cc +[2025-08-16T13:11:10.290Z] SESSION: Valid 46f8c261e225aaaf0313422ee6c3641ac2f3b83398af905c9361082b7dff05cc testplayer PUT /api/players/testplayer +[2025-08-16T13:11:10.290Z] DB: getPlayerByName - start testplayer +[2025-08-16T13:11:10.290Z] DB: getPlayerByName - found testplayer +[2025-08-16T13:11:10.291Z] DB: updatePlayer - start testplayer +[2025-08-16T13:11:10.291Z] DB: updatePlayer - changes 1 testplayer +[2025-08-16T13:11:10.291Z] DB: getPlayerByName - start testplayer +[2025-08-16T13:11:10.291Z] DB: getPlayerByName - found testplayer +[2025-08-16T13:11:10.291Z] API: Updated player testplayer +[2025-08-16T13:11:10.307Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T13:11:10.307Z] SESSION: GM bypass accepted DELETE /api/players/testplayer +[2025-08-16T13:11:10.307Z] DB: deletePlayer - start testplayer +[2025-08-16T13:11:10.308Z] DB: deletePlayer - changes 1 testplayer +[2025-08-16T13:11:10.308Z] API: Deleted player testplayer +[2025-08-16T13:11:10.687Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T13:11:10.687Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T13:11:10.687Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T13:11:10.687Z] DB: deletePlayer - changes 0 shoptestplayer +[2025-08-16T13:11:10.701Z] SESSION: GM bypass accepted POST /api/players +[2025-08-16T13:11:10.701Z] DB: getPlayerByName - start shoptestplayer +[2025-08-16T13:11:10.701Z] DB: getPlayerByName - not found shoptestplayer +[2025-08-16T13:11:10.785Z] DB: createPlayer - start shoptestplayer +[2025-08-16T13:11:10.786Z] DB: createPlayer - done shoptestplayer rowid 120 +[2025-08-16T13:11:10.786Z] API: Created player shoptestplayer +[2025-08-16T13:11:10.801Z] SESSION: Missing sessionId GET /api/shop/inventory/shoptestplayer +[2025-08-16T13:11:10.814Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T13:11:10.814Z] SESSION: GM bypass accepted DELETE /api/players/shoptestplayer +[2025-08-16T13:11:10.814Z] DB: deletePlayer - start shoptestplayer +[2025-08-16T13:11:10.816Z] DB: deletePlayer - changes 1 shoptestplayer +[2025-08-16T13:11:10.816Z] API: Deleted player shoptestplayer +[2025-08-16T13:11:11.311Z] DB: getAllPlayers - start +[2025-08-16T13:11:11.313Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T13:13:43.781Z] DB: getAllPlayers - start +[2025-08-16T13:13:43.784Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T13:13:47.161Z] DB: getAllPlayers - start +[2025-08-16T13:13:47.162Z] DB: getAllPlayers - resultCount 7 +[2025-08-16T13:13:49.399Z] DB: getPlayerByName - start claes +[2025-08-16T13:13:49.399Z] DB: getPlayerByName - found claes +[2025-08-16T13:13:49.598Z] DB: createSession - start 38b1f5081b39e5083eea500ca88fe16ce4839b8ce402c2c7145a09c8728c3450 expiresAt 2025-08-17T13:13:49.598Z +[2025-08-16T13:13:49.600Z] DB: createSession - done 38b1f5081b39e5083eea500ca88fe16ce4839b8ce402c2c7145a09c8728c3450 +[2025-08-16T13:13:49.600Z] API: Player login claes 38b1f5081b39e5083eea500ca88fe16ce4839b8ce402c2c7145a09c8728c3450 +[2025-08-16T13:13:49.647Z] DB: getSession - start 38b1f5081b39e5083eea500ca88fe16ce4839b8ce402c2c7145a09c8728c3450 +[2025-08-16T13:13:49.647Z] DB: getSession - ok 38b1f5081b39e5083eea500ca88fe16ce4839b8ce402c2c7145a09c8728c3450 +[2025-08-16T13:13:49.648Z] SESSION: Validate success 38b1f5081b39e5083eea500ca88fe16ce4839b8ce402c2c7145a09c8728c3450 claes +[2025-08-16T13:13:57.871Z] DB: deleteSession - start 38b1f5081b39e5083eea500ca88fe16ce4839b8ce402c2c7145a09c8728c3450 +[2025-08-16T13:13:57.873Z] DB: deleteSession - changes 1 38b1f5081b39e5083eea500ca88fe16ce4839b8ce402c2c7145a09c8728c3450 +[2025-08-16T13:13:57.873Z] SESSION: Logout success 38b1f5081b39e5083eea500ca88fe16ce4839b8ce402c2c7145a09c8728c3450 +[2025-08-16T13:13:57.913Z] SESSION: Missing sessionId GET /api/players diff --git a/database/routes/playerRoutes-sqlite.js b/database/routes/playerRoutes-sqlite.js index 3e4f092..68eaef6 100644 --- a/database/routes/playerRoutes-sqlite.js +++ b/database/routes/playerRoutes-sqlite.js @@ -252,7 +252,8 @@ router.post('/', async (req, res) => { router.put('/:name', requireSession, async (req, res) => { try { const { name } = req.params; - const updateData = req.body; + // Ensure updateData is an object to avoid runtime TypeErrors when fields are missing + const updateData = req.body || {}; // Check if player exists const existingPlayer = playerHelpers.getByName(name); @@ -270,9 +271,9 @@ router.put('/:name', requireSession, async (req, res) => { // Merge the data properly const mergedData = { - rollerInfo: { ...existingPlayer.rollerInfo, ...updateData.rollerInfo }, - shopInfo: { ...existingPlayer.shopInfo, ...updateData.shopInfo }, - tabInfo: { ...existingPlayer.tabInfo, ...updateData.tabInfo }, + rollerInfo: { ...existingPlayer.rollerInfo, ...(updateData.rollerInfo || {}) }, + shopInfo: { ...existingPlayer.shopInfo, ...(updateData.shopInfo || {}) }, + tabInfo: { ...existingPlayer.tabInfo, ...(updateData.tabInfo || {}) }, // Never carry forward plaintext pw into validation; keep pw empty and use pwHash pw: '', pwHash: updateData.pwHash || existingPlayer.pwHash diff --git a/database/scripts/pm2_smoke_test.sh b/database/scripts/pm2_smoke_test.sh old mode 100644 new mode 100755 diff --git a/database/server.js b/database/server.js index fcbef84..17adf77 100755 --- a/database/server.js +++ b/database/server.js @@ -25,14 +25,38 @@ app.use(cors()); // API Routes (before static files) console.log('Registering API routes...'); -app.use('/api/players', playerRoutes); -console.log('Players routes registered'); -app.use('/api/sessions', sessionRoutes); -console.log('Sessions routes registered'); -app.use('/api/shop', shopRoutes); -console.log('Shop routes registered'); -app.use('/api/rules', rulesRoutes); -console.log('Rules routes registered'); +try { + console.log('Registering /api/players'); + app.use('/api/players', playerRoutes); + console.log('Players routes registered'); +} catch (e) { + console.error('Error mounting /api/players:', e && e.stack ? e.stack : e); + throw e; +} +try { + console.log('Registering /api/sessions'); + app.use('/api/sessions', sessionRoutes); + console.log('Sessions routes registered'); +} catch (e) { + console.error('Error mounting /api/sessions:', e && e.stack ? e.stack : e); + throw e; +} +try { + console.log('Registering /api/shop'); + app.use('/api/shop', shopRoutes); + console.log('Shop routes registered'); +} catch (e) { + console.error('Error mounting /api/shop:', e && e.stack ? e.stack : e); + throw e; +} +try { + console.log('Registering /api/rules'); + app.use('/api/rules', rulesRoutes); + console.log('Rules routes registered'); +} catch (e) { + console.error('Error mounting /api/rules:', e && e.stack ? e.stack : e); + throw e; +} // Serve static files from build directory (React app) const buildDir = path.join(__dirname, '..', 'build'); @@ -66,16 +90,38 @@ process.on('SIGTERM', () => { 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.'); -}); +// If a React build exists, serve it at root and fallback to index.html for client-side routing +const indexHtml = path.join(buildDir, 'index.html'); +if (fs.existsSync(indexHtml)) { + app.get('/', (req, res) => res.sendFile(indexHtml)); -// Use routes + // Catch-all for client-side routes (must be after API routes) + // Middleware fallback for client-side routing: serve index.html for non-API and non-avatar paths. + // Use a plain middleware (no path) to avoid route string parsing by path-to-regexp. + app.use((req, res, next) => { + if (req.path.startsWith('/api/') || req.path.startsWith('/avatars/')) return next(); + return res.sendFile(indexHtml); + }); +} else { + // Root route for friendly message when no build is present + app.get('/', (req, res) => { + res.send('Deathwatch Roller API is running with SQLite. Use /api/players for player data.'); + }); +} + +// Use routes (instrument mounts to debug invalid route patterns) +console.log('Mounting route: /api/shop'); app.use('/api/shop', shopRoutes); +console.log('Mounted /api/shop'); +console.log('Mounting route: /api/players'); app.use('/api/players', playerRoutes); +console.log('Mounted /api/players'); +console.log('Mounting route: /api/sessions'); app.use('/api/sessions', sessionRoutes); +console.log('Mounted /api/sessions'); +console.log('Mounting route: /api/rules'); app.use('/api/rules', rulesRoutes); +console.log('Mounted /api/rules'); // Start Server app.listen(PORT, '0.0.0.0', () => { diff --git a/database/sqlite/deathwatch.db b/database/sqlite/deathwatch.db index 9a2eb1142a8f751f65840ec0a9a8d65f28f29eb2..0af83a9d2954e7715e71cd498291b87da3acce59 100644 GIT binary patch literal 192512 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�kAz5C4iKs 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 YD<8V-TYAWEyvsi28xR))G`#ZvAMKHN>;M1& delta 3478 zcmeHJX>b$g8Q$;H?&`3#i*WI6uoZ?Q7_&NdS5gB=K4h>2zAa-QSjWl-mL zs;Y`WeM6N{UGMfgS{i&&-rm+#C=Q+m3+T0>4q@lY3<}d~wYxOw@z*nZ0NmCu=5a7f zx}&OWv)vo&Xb#Occ<1Yx`97vTSW~%XOU2saI(M;uYg;!PEvhKE-CSgCowu(>yurboOg7J;w;EZi$(SUK@iGd_)@pZl%|W-aZ_MPxbImrr z!DxCwS?pt?d}~Ejk<;Gn>?+^N^%{$ND;nKv`_?s9xVP|OHd0;PTvyc>?rQbB0}-d! z?J7i#X*pSJo}RU`7OS3tNy@?Hkiycn+CgHD=y?pzM|nA^;$a4++WTkFadoBDCGL)J zOLMT+=iq}??Y*1(gf>%OP1se{R`Ybh?vtl7mxpj3vo7 z)CVZ+5v_Jl=N$Blv_~Z}HNHxSgq}(t7TbXJ|-5Y+W=EYNu(!BBTZXI-zW3~do2dG;wRNur*1p(EOr6xD{h zn_D`Bt}gM|T$q{87U+VZ9w8`+byluG7w|+SlX`K}JZPO6Z(_jpB*Et(%wgy{` z>)Lv~mEHb8x39WA*uj}=1*6;2)@$a&T%oU|%UBgzQ^tMQYYryeqrSwPSf*B6HU}Nc z%@u`tkj|SK-nyciqSY4V=}epRtrISd&$6kn*V!q`sUjb%~IkZZwMdYoUH& z|NYLmf0^-+UV6xA)pLU;lLpJzYB!534!~@{B9OlDJC*kR+crB{V5~|S6$Cws(WCTD z`Z9fv{yTk=J{i|CzofHNvP{q%l%f3WsFUin#)iqUlJj3=TjV>HkCl$24=w?N#8qg<_db8*g>U;57l^bX=`vpj$ zH`YFeOUpFsT<1vpey@Z^LqDE2Pd(hcCTX5h$yZ@b_$`dSNS~&UB5D9FTq38)o8&n% zLH>&DC!5G(asjCzt`XzJJ|aTY632)P{1(AU6Vrf;-&}?o^yE%b{Ny^cPzEcL97u(n zo@vB}lJt4$UFFU^lao@E=iinnjTW^wyJU^H&qeONA@KM(U?*$0^I zid3u+jAB%}ERGM+-I~4fU9upoQ!gPy%1&~z`krE)Y@z%N;Z}ZwuS}VvTmnW_do=GT z4nrD#O5P?uqkfZekUUNtB($nx+1IMK6_@08Wdi;><)ZpEYNI-Wk4e8F>T1d}^oV+? zEJ!Z{%N6UA=|~>&fl3*`VIAf`Pkvm6CaQ+fxib!Qr21WyHQ_+LZ#&T4nqg#=?$Jqy zIHCkLP=gxAoaouQVKg%Ch)3%)0BV}BqeEj3B4s`10OAk;HganzKBxg=J_I%t8Yj_j z8-`KoTMh&y^LO8Kp!6|^_{IdVqdkr9;+j&d2yI%w0)2JLg~IO@qdk&V`CnF00_W3v zI6XEnWo_xW#J?Sd#~lQ96)Oek<<&Oy%!DgGxiJ+G^awT#(BK-I%OP=Mi|;Z1Ma3yw~hyJxsvtw|IDg)e8ZZ@f!>l4%>5|R4P*& z0i{@#iW^>pc5kv!_3!{Dn@^v_$LJ_sNL`_x#aGJh@?GR{Y6EeQO4FPrwDL2Wz2ss= zD&f|YsPC#@mA#^ls0&g)CqpSmQW{f!q?#Z;g9lW*RYl4>Z6#mF1o;?bS#Mk}+%f$b@0G+7hoI@-=3hnW+bGbnL zIVt6@aVp~P$ff*sOp@ZJW6&Y$B?Q)q2VaEFxZ?vUHj~L{vDZ&1btXK_Wp4>=mrpb zI`LBgVt-z!+>6a3MlgCkb%N4r+SRAkx|E-&CRL2$s-i@GNQuZMLh3JRKSD&pa|)CJ zXXc<O{GROJ@GbCr?9l%GyTwCiL9W%a3;b2EBAijDHOioD$dt-ul^b7Fm6vQxvN<5+kSHw#pkd5y! z#C(ZkUxKV@(>XD3;^GZ}Pvb{oo{&OJbW6A4EXvG|q@U{u~(v N_hC)~zX0yc`!Ch;Hjn@S diff --git a/database/sqlite/deathwatch.db-shm b/database/sqlite/deathwatch.db-shm index 00338a40cbb79f5edf9d2cf33101ffdf10cd9715..7654dc3a971eefc134570eb0e219d16f585b9876 100644 GIT binary patch literal 32768 zcmeI*1&~!$9LMqRj}DiTSh~Agx{+?AyF*I4q+5~h4pBf*y3=m#RumOQ#TFIuTxagg zCp$Z)8PXaZQQi zN?cbGx{}A0e6D!M2Y%+Y^*;CGgt+&~UGd}ieFV0NY`kl=@%jz!EAaThartA2t@FIr z+C((ZWX>l=z&ZrlT#vsOKfWve7`)u@a@9ADYTWD1Ex$i+JpSDLu|#DIUYrE3Byz>e zBQK_xx1QOpo@;n>jHu2(2Yd_fxZv@7b>STs>=<5KKUQHE!|NlwSmDJEt_$7|FOF9i zJf`6N$Q~2iU+}&c$Df;T=UwZ(djEPBm~+AHBa^?8JJ|d4` zyTCTrGw)#6n>p{`zJmAN(%WVNCSU?4U;-v!0w!PrCSU?4U;-v!0w!PrCSU?4U;-v! z0w!PrCSU?4U;-v!0w!PrCSU?4U;-v!0w!PrCSU?4U;-v!0w!PrCSU?4U;-v!0{?S? zcvPkzb9k2DNuVUErtW&g`>LmHCJ;q|MDBa`naD|gN>iP>+(!qxF_5uLW+wAl#u_%W zi-Ww(o4n6w+@}uetHBzf=~}22+M*rW69ZWopDOg{A)ez85-O>xtB2;rK)+FhwPvwE zAhEl{L}qfi`)8D)2K8t~N4hhJaZF(W%UR2#?B)=!@D?9%mL_VYj_Ri&8mSptq?LM1 zJGD0kvM>Rml%y)nxS!G76$3rSg5|Rz-jF~NQj&$-?#>%!sY!iW(}^AoW;|0_$O_i6 zg*_bRRo>=9KBtzNsZR(vH>I%QLC6;WALRc+N*XLZw1jn^#A z)oQKNQ#u@t=R`u%Q;KRdX8>b(m?wFjZ}@>Hk{z7pYraFuqXH_bl6p{+E$45iMYg#_Adrp{H05c2<}xXiTIrQpIaE%S z)KD9AR445`h(OM&W52*CcJd78`JOn+tAZ+~QktMCmh-WAY40MNd!}J++R}>&EM^1y zc%6@FuC^MWFe zkEN_;6IZ;!Aqd^+3ahjut^sts|tv2gXJ)zw? OWPeX$an@XW=>Gx@%^wB; literal 32768 zcmeI52apxT8img}O^!>>Ip>_S0Td)CNkl{uMFBAp6ci*XDp?5)g zNGc!-B1$rl6nN7R z&0ePtTKb@63|hXRIawzc)^qY10=bxwyt8LzYjoE>0)!>_yUZ{Z9XU{l%7d_ME@C>^e41H&ejI&db;_ zJ6rL6I{V?gZ4InWp`f|*!lCmb!Fkc375np^{VX0jFY)KO)wg{mL)X@h)sJ0U7l+Hq z=3;$Y8&2m!UEA2LEf-s<;5_FD`j!E39Yb zQUonk&>SP1gZ)ml-*a}%#J271M5`MqMmtucu~8+z?8P;9qm}zBKHOM39~+K|t7|xiaG$m5#+kG4Y8>Vr)``s|+!wYNJ8oBhYd5SD zt-XsKUpViC#Fr2qms5D`Ic^ED=XB$;n-F_(*>OG+VlO_OU|9IW6YvB)0Z+ga@B};o zPrwuK1Uvyxz!UHUJONL@6YvB)0Z+ga@B};oPrwuK1Uvyxz!UHUJONL@6YvB)0Z+ga z@B};oPrwuK1Uvyxz!UHUJONL@6G+4aeqw3O)_eNezINshPv8O&n2lsi$u!K!%*@7I zEY8v_&&sUM+N{T>Y{Bc;fd_cGTB@y{(p1gUi+Wve>tk)xxB5ZHbW+JpI+M#3H04ZH zQ`^3DcoB}DSw?g8zV`aje-Y^ZJ53VFgLlf)1@DmOL@wkG_HZZ^>~&Bwc+a^is-Ze+ zp&shv3N%40T#M__7Jcv_`r{D{#giC?(HMuPF%i@7EEZrP7U4xK!*aZeHQ0cS*o61+ z5kAIN?7&`pgKzOOe!;IejuSYEQ#g&^@H@`nERr)7)3P;hU@Ns#SM}1v8m*@_P4o4V z*63>8s)sa6&+9d<*9Y37?fORh^rL>#DaoWT=}k_P+Y~aTO?gwz)VWBr@ZUL3l4jNl z_7sXio?yp;Qo*hTHBcK@;wrR8Ukt`DjKO$J$8%VWm+%_a;$6ItZP8QQMakP?pALN)KHDlL`~NME!A4RqffL$ z`*lb^>$Fmu3?`2$Y$}-Qrmi2oNpr~kgR(Ng&Ig&01qFiLaLSYxFxMl;-iThSB! zFaX2xC?;SMW?~MOVg=s7oA>}*@CClYcQ}VaypnBsBX4C-_TfN|2A3dO98mSqYsh71<8}zPrYPSyS zH=R{RlhqV4#Z3)AEB-;_Cvk`JJbuKL{z*D<7tu$bKmwb&g9q(qB>wOO{&fVB1^d)x zMqU&Q_9-imny8D$Xo@y?07EbWWAPMbU>26(WxS4c*o+Uc9baNU+OZQ~{48jwb`p53J2{fI= z)ZXJx{@~aCBvsY6n+LSdNugjkWyfj|YCX z;FkfKqdlfzF5U*2pS^e=`*8rD;21u|$(+uaT){W_A;02brdK}I(&{ArpMZF9C5o=} ztjlYCg`%ic8dSz*=!9EvJG$d;+>e2H9INmd_TV6npeQfrRlJ3_vpWZKIOlR9KjJPP zp>&nHXn>v+O%7Aaul-Tb{(D3+Fos!}gZ0^rEqR@v2NAJn4}QU=1+K?4n1}Tc3-E5< z&jjvGVZn{^GXq+Z%k=E%m z9nh~zZSt8)roLaR{wUMYOJ6J>!38FemDjM9f8M{qRK2;xK_Dj^usN?~dtT2+*+zHh zJ`L7*P0?b#sn7Mjjw_AHZz`Jx{yQLXl+;HVHGzhH-H)1;7ffmdZsKEniA(u1U*%e^ z=R4fQ&$*S`_zl0~FZ`7!`8&_>EK?|rVw6c)l~cKtTZL6zWmQfURaMngUG;UDnyQ&v z=uX|G`_)fFG(wN+F-_1jdQLBBu9j%IR_GPIrA_)!A8D(;)K}W2gF31cIwNC}nY1R8 a$!xNi0;ZTLW6GK;rj}{wzjqSR!21tCqvLA; diff --git a/database/sqlite/deathwatch.db-wal b/database/sqlite/deathwatch.db-wal index 91548cba2ae8cc0db3c7f83d8726f1e9f23ab15d..fb6bf7b42c8b6622546ca3055a1992e9d6b35af2 100644 GIT binary patch delta 177091 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 diff --git a/package-lock.json b/package-lock.json index 01552eb..77144bc 100755 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "eslint-config-prettier": "^10.1.8", "html-webpack-plugin": "^5.6.4", "node-fetch": "^2.7.0", + "pm2": "^5.2.0", "postcss": "^8.4.23", "prettier-eslint": "^16.4.2", "react-scripts": "^5.0.1", @@ -4464,6 +4465,230 @@ "node": ">=14" } }, + "node_modules/@pm2/agent": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@pm2/agent/-/agent-2.0.4.tgz", + "integrity": "sha512-n7WYvvTJhHLS2oBb1PjOtgLpMhgImOq8sXkPBw6smeg9LJBWZjiEgPKOpR8mn9UJZsB5P3W4V/MyvNnp31LKeA==", + "dev": true, + "dependencies": { + "async": "~3.2.0", + "chalk": "~3.0.0", + "dayjs": "~1.8.24", + "debug": "~4.3.1", + "eventemitter2": "~5.0.1", + "fast-json-patch": "^3.0.0-1", + "fclone": "~1.0.11", + "nssocket": "0.6.0", + "pm2-axon": "~4.0.1", + "pm2-axon-rpc": "~0.7.0", + "proxy-agent": "~6.3.0", + "semver": "~7.5.0", + "ws": "~7.5.10" + } + }, + "node_modules/@pm2/agent/node_modules/dayjs": { + "version": "1.8.36", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.36.tgz", + "integrity": "sha512-3VmRXEtw7RZKAf+4Tv1Ym9AGeo8r8+CjDi26x+7SYQil1UqtqdaokhzoEJohqlzt0m5kacJSDhJQkG/LWhpRBw==", + "dev": true + }, + "node_modules/@pm2/agent/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@pm2/agent/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pm2/agent/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pm2/agent/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@pm2/io": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@pm2/io/-/io-6.0.1.tgz", + "integrity": "sha512-KiA+shC6sULQAr9mGZ1pg+6KVW9MF8NpG99x26Lf/082/Qy8qsTCtnJy+HQReW1A9Rdf0C/404cz0RZGZro+IA==", + "dev": true, + "dependencies": { + "async": "~2.6.1", + "debug": "~4.3.1", + "eventemitter2": "^6.3.1", + "require-in-the-middle": "^5.0.0", + "semver": "~7.5.4", + "shimmer": "^1.2.0", + "signal-exit": "^3.0.3", + "tslib": "1.9.3" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@pm2/io/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/@pm2/io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@pm2/io/node_modules/eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==", + "dev": true + }, + "node_modules/@pm2/io/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pm2/io/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pm2/io/node_modules/tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, + "node_modules/@pm2/io/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@pm2/js-api": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@pm2/js-api/-/js-api-0.8.0.tgz", + "integrity": "sha512-nmWzrA/BQZik3VBz+npRcNIu01kdBhWL0mxKmP1ciF/gTcujPTQqt027N9fc1pK9ERM8RipFhymw7RcmCyOEYA==", + "dev": true, + "dependencies": { + "async": "^2.6.3", + "debug": "~4.3.1", + "eventemitter2": "^6.3.1", + "extrareqp2": "^1.0.0", + "ws": "^7.0.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@pm2/js-api/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/@pm2/js-api/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@pm2/js-api/node_modules/eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==", + "dev": true + }, + "node_modules/@pm2/pm2-version-check": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@pm2/pm2-version-check/-/pm2-version-check-1.0.4.tgz", + "integrity": "sha512-SXsM27SGH3yTWKc2fKR4SYNxsmnvuBQ9dd6QHtEWmiZ/VqaOYPAIlS8+vMcn27YLtAEBGvNRSh3TPNvtjZgfqA==", + "dev": true, + "dependencies": { + "debug": "^4.3.1" + } + }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.6.1.tgz", @@ -5027,6 +5252,12 @@ "node": ">= 6" } }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -6199,6 +6430,21 @@ "ajv": "^6.9.1" } }, + "node_modules/amp": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/amp/-/amp-0.3.1.tgz", + "integrity": "sha512-OwIuC4yZaRogHKiuU5WlMR5Xk/jAcpPtawWL05Gj8Lvm2F6mwoJt4O/bHI+DHwG79vWd+8OFYM4/BzYqyRd3qw==", + "dev": true + }, + "node_modules/amp-message": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/amp-message/-/amp-message-0.1.2.tgz", + "integrity": "sha512-JqutcFwoU1+jhv7ArgW38bqrE+LQdcRv4NxNw0mp0JHQyB6tXesWRjtYKlDgHRY2o3JE5UTaBGUK8kSWUdxWUg==", + "dev": true, + "dependencies": { + "amp": "0.3.1" + } + }, "node_modules/anser": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/anser/-/anser-2.3.2.tgz", @@ -6206,6 +6452,15 @@ "dev": true, "license": "MIT" }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -6568,6 +6823,18 @@ "dev": true, "license": "MIT" }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -7016,6 +7283,15 @@ } ] }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -7107,6 +7383,18 @@ "readable-stream": "^3.4.0" } }, + "node_modules/blessed": { + "version": "0.1.81", + "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", + "integrity": "sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==", + "dev": true, + "bin": { + "blessed": "bin/tput.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -7114,6 +7402,12 @@ "dev": true, "license": "MIT" }, + "node_modules/bodec": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bodec/-/bodec-0.1.0.tgz", + "integrity": "sha512-Ylo+MAo5BDUq1KA3f3R/MFhh+g8cnHmo8bz3YPGhI1znrMaf77ol1sfvYJzsw3nTE+Y2GryfDxBaR+AqpAkEHQ==", + "dev": true + }, "node_modules/body-parser": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", @@ -7529,6 +7823,12 @@ "node": ">=10" } }, + "node_modules/charm": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/charm/-/charm-0.1.2.tgz", + "integrity": "sha512-syedaZ9cPe7r3hoQA9twWYKu5AIyCswN5+szkmPBe9ccdLrj4bYaCnLVPTLd2kgVRc7+zoX4tyPgRnFKCj5YjQ==", + "dev": true + }, "node_modules/check-types": { "version": "11.2.3", "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz", @@ -7875,6 +8175,18 @@ "node": ">=6" } }, + "node_modules/cli-tableau": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cli-tableau/-/cli-tableau-2.0.1.tgz", + "integrity": "sha512-he+WTicka9cl0Fg/y+YyxcN6/bfQ/1O3QmgxRXDhABKqLzvoOSM4fMzp39uMyLBulAFuywD2N7UaoQE7WaADxQ==", + "dev": true, + "dependencies": { + "chalk": "3.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -8272,6 +8584,12 @@ "node": ">=10" } }, + "node_modules/croner": { + "version": "4.1.97", + "resolved": "https://registry.npmjs.org/croner/-/croner-4.1.97.tgz", + "integrity": "sha512-/f6gpQuxDaqXu+1kwQYSckUglPaOrHdbIlBAu0YuW8/Cdb45XwXYNUBXg3r/9Mo6n540Kn/smKcZWko5x99KrQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -8716,6 +9034,12 @@ "license": "MIT", "peer": true }, + "node_modules/culvert": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/culvert/-/culvert-0.1.2.tgz", + "integrity": "sha512-yi1x3EAWKjQTreYWeSd98431AV+IEE0qoDyOoaHJ7KJ21gv6HtBXHVLX74opVSGqcR8/AbjJBHAHpcOy2bj5Gg==", + "dev": true + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -8723,6 +9047,15 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -8792,6 +9125,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -8981,6 +9320,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -9424,6 +9777,18 @@ "node": ">=10.13.0" } }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", @@ -10341,6 +10706,12 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz", + "integrity": "sha512-5EM1GHXycJBS6mauYAbVKT1cVs7POKWb2NXD4Vyt8dDqeZa7LaDK1/sjtL+Zb0lzTpSNil4596Dyu97hz37QLg==", + "dev": true + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -10496,6 +10867,15 @@ "node": ">= 0.6" } }, + "node_modules/extrareqp2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/extrareqp2/-/extrareqp2-1.0.0.tgz", + "integrity": "sha512-Gum0g1QYb6wpPJCVypWP3bbIuaibcFiJcpuPM10YSXp/tzqi84x9PJageob+eN4xVRIOto4wjSGNLyMD54D2xA==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -10533,6 +10913,12 @@ "node": ">= 6" } }, + "node_modules/fast-json-patch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", + "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==", + "dev": true + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -10597,6 +10983,12 @@ "bser": "2.1.1" } }, + "node_modules/fclone": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz", + "integrity": "sha512-GDqVQezKzRABdeqflsgMr7ktzgF9CyS+p2oe0jJqUY6izSSbhPIQJDpoU4PtGcD7VPM9xh/dVrTu6z1nwgmEGw==", + "dev": true + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -11284,6 +11676,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "dev": true, + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/git-node-fs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/git-node-fs/-/git-node-fs-1.0.0.tgz", + "integrity": "sha512-bLQypt14llVXBg0S0u8q8HmU7g9p3ysH+NvVlae5vILuUvs759665HvmR5+wb04KjHyjFcDRxdYb4kyNnluMUQ==", + "dev": true + }, + "node_modules/git-sha1": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/git-sha1/-/git-sha1-0.1.2.tgz", + "integrity": "sha512-2e/nZezdVlyCopOCYHeW0onkbZg7xP1Ad6pndPy1rCygeRykefUS6r7oA5cJRGEFvseiaz5a/qUHFVX1dd6Isg==", + "dev": true + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -12117,7 +12535,7 @@ "version": "10.0.1", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", - "optional": true, + "devOptional": true, "engines": { "node": ">= 12" } @@ -15713,6 +16131,18 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-git": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/js-git/-/js-git-0.7.8.tgz", + "integrity": "sha512-+E5ZH/HeRnoc/LW0AmAyhU+mNcWBzAKE+30+IDMLSLbbK+Tdt02AdkOKq9u15rlJsDEGFqtgckc8ZM59LhhiUA==", + "dev": true, + "dependencies": { + "bodec": "^0.1.0", + "culvert": "^0.1.2", + "git-sha1": "^0.1.2", + "pako": "^0.2.5" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -15828,6 +16258,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "optional": true + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -15976,6 +16413,15 @@ "shell-quote": "^1.8.3" } }, + "node_modules/lazy": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", + "integrity": "sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA==", + "dev": true, + "engines": { + "node": ">=0.2.0" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -16757,6 +17203,12 @@ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, + "node_modules/module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", + "dev": true + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -16777,6 +17229,12 @@ "multicast-dns": "cli.js" } }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -16827,6 +17285,44 @@ "dev": true, "license": "MIT" }, + "node_modules/needle": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", + "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==", + "dev": true, + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/needle/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/negotiator": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", @@ -16844,6 +17340,15 @@ "dev": true, "license": "MIT" }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -17108,6 +17613,25 @@ "set-blocking": "^2.0.0" } }, + "node_modules/nssocket": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/nssocket/-/nssocket-0.6.0.tgz", + "integrity": "sha512-a9GSOIql5IqgWJR3F/JXG4KpJTA3Z53Cj0MeMvGpglytB1nxE4PdFNC0jINe27CS7cGivoynwc054EzCcT3M3w==", + "dev": true, + "dependencies": { + "eventemitter2": "~0.4.14", + "lazy": "~1.0.11" + }, + "engines": { + "node": ">= 0.10.x" + } + }, + "node_modules/nssocket/node_modules/eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ==", + "dev": true + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -17478,6 +18002,87 @@ "node": ">=6" } }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "dev": true, + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -17485,6 +18090,12 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "dev": true + }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -17780,6 +18391,18 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidusage": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-3.0.2.tgz", + "integrity": "sha512-g0VU+y08pKw5M8EZ2rIGiEBaB8wrQMjYGFfW2QVIfyT8V+fq8YFLkvlz4bz5ljvFDJYNFCWT3PWqcRr2FKO81w==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -17948,6 +18571,185 @@ "node": ">=4" } }, + "node_modules/pm2": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/pm2/-/pm2-5.4.3.tgz", + "integrity": "sha512-4/I1htIHzZk1Y67UgOCo4F1cJtas1kSds31N8zN0PybO230id1nigyjGuGFzUnGmUFPmrJ0On22fO1ChFlp7VQ==", + "dev": true, + "dependencies": { + "@pm2/agent": "~2.0.0", + "@pm2/io": "~6.0.1", + "@pm2/js-api": "~0.8.0", + "@pm2/pm2-version-check": "latest", + "async": "~3.2.0", + "blessed": "0.1.81", + "chalk": "3.0.0", + "chokidar": "^3.5.3", + "cli-tableau": "^2.0.0", + "commander": "2.15.1", + "croner": "~4.1.92", + "dayjs": "~1.11.5", + "debug": "^4.3.1", + "enquirer": "2.3.6", + "eventemitter2": "5.0.1", + "fclone": "1.0.11", + "js-yaml": "~4.1.0", + "mkdirp": "1.0.4", + "needle": "2.4.0", + "pidusage": "~3.0", + "pm2-axon": "~4.0.1", + "pm2-axon-rpc": "~0.7.1", + "pm2-deploy": "~1.0.2", + "pm2-multimeter": "^0.1.2", + "promptly": "^2", + "semver": "^7.2", + "source-map-support": "0.5.21", + "sprintf-js": "1.1.2", + "vizion": "~2.2.1" + }, + "bin": { + "pm2": "bin/pm2", + "pm2-dev": "bin/pm2-dev", + "pm2-docker": "bin/pm2-docker", + "pm2-runtime": "bin/pm2-runtime" + }, + "engines": { + "node": ">=12.0.0" + }, + "optionalDependencies": { + "pm2-sysmonit": "^1.2.8" + } + }, + "node_modules/pm2-axon": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pm2-axon/-/pm2-axon-4.0.1.tgz", + "integrity": "sha512-kES/PeSLS8orT8dR5jMlNl+Yu4Ty3nbvZRmaAtROuVm9nYYGiaoXqqKQqQYzWQzMYWUKHMQTvBlirjE5GIIxqg==", + "dev": true, + "dependencies": { + "amp": "~0.3.1", + "amp-message": "~0.1.1", + "debug": "^4.3.1", + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=5" + } + }, + "node_modules/pm2-axon-rpc": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/pm2-axon-rpc/-/pm2-axon-rpc-0.7.1.tgz", + "integrity": "sha512-FbLvW60w+vEyvMjP/xom2UPhUN/2bVpdtLfKJeYM3gwzYhoTEEChCOICfFzxkxuoEleOlnpjie+n1nue91bDQw==", + "dev": true, + "dependencies": { + "debug": "^4.3.1" + }, + "engines": { + "node": ">=5" + } + }, + "node_modules/pm2-deploy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pm2-deploy/-/pm2-deploy-1.0.2.tgz", + "integrity": "sha512-YJx6RXKrVrWaphEYf++EdOOx9EH18vM8RSZN/P1Y+NokTKqYAca/ejXwVLyiEpNju4HPZEk3Y2uZouwMqUlcgg==", + "dev": true, + "dependencies": { + "run-series": "^1.1.8", + "tv4": "^1.3.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pm2-multimeter": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/pm2-multimeter/-/pm2-multimeter-0.1.2.tgz", + "integrity": "sha512-S+wT6XfyKfd7SJIBqRgOctGxaBzUOmVQzTAS+cg04TsEUObJVreha7lvCfX8zzGVr871XwCSnHUU7DQQ5xEsfA==", + "dev": true, + "dependencies": { + "charm": "~0.1.1" + } + }, + "node_modules/pm2-sysmonit": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/pm2-sysmonit/-/pm2-sysmonit-1.2.8.tgz", + "integrity": "sha512-ACOhlONEXdCTVwKieBIQLSi2tQZ8eKinhcr9JpZSUAL8Qy0ajIgRtsLxG/lwPOW3JEKqPyw/UaHmTWhUzpP4kA==", + "dev": true, + "optional": true, + "dependencies": { + "async": "^3.2.0", + "debug": "^4.3.1", + "pidusage": "^2.0.21", + "systeminformation": "^5.7", + "tx2": "~1.0.4" + } + }, + "node_modules/pm2-sysmonit/node_modules/pidusage": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz", + "integrity": "sha512-cv3xAQos+pugVX+BfXpHsbyz/dLzX+lr44zNMsYiGxUw+kV5sgQCIcLd1z+0vq+KyC7dJ+/ts2PsfgWfSC3WXA==", + "dev": true, + "optional": true, + "dependencies": { + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pm2/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/pm2/node_modules/commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "node_modules/pm2/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/pm2/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pm2/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==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pm2/node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -19731,6 +20533,15 @@ "node": ">= 4" } }, + "node_modules/promptly": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/promptly/-/promptly-2.2.0.tgz", + "integrity": "sha512-aC9j+BZsRSSzEsXBNBwDnAxujdx19HycZoKgRgzWnS8eOHg1asuf9heuLprfbe739zY3IdUQx+Egv6Jn135WHA==", + "dev": true, + "dependencies": { + "read": "^1.0.4" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -19786,6 +20597,83 @@ "node": ">= 0.10" } }, + "node_modules/proxy-agent": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", + "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-agent/node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -20677,6 +21565,18 @@ } } }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dev": true, + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -20925,6 +21825,20 @@ "node": ">=0.10.0" } }, + "node_modules/require-in-the-middle": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.2.0.tgz", + "integrity": "sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/require-relative": { "version": "0.8.7", "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", @@ -21210,6 +22124,26 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/run-series": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-series/-/run-series-1.1.9.tgz", + "integrity": "sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g==", + "dev": true, + "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/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -21696,6 +22630,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", + "dev": true + }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -21837,7 +22777,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "optional": true, + "devOptional": true, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -21859,7 +22799,7 @@ "version": "2.8.7", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", - "optional": true, + "devOptional": true, "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" @@ -22844,6 +23784,33 @@ "dev": true, "license": "MIT" }, + "node_modules/systeminformation": { + "version": "5.27.7", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.27.7.tgz", + "integrity": "sha512-saaqOoVEEFaux4v0K8Q7caiauRwjXC4XbD2eH60dxHXbpKxQ8kH9Rf7Jh+nryKpOUSEFxtCdBlSUx0/lO6rwRg==", + "dev": true, + "optional": true, + "os": [ + "darwin", + "linux", + "win32", + "freebsd", + "openbsd", + "netbsd", + "sunos", + "android" + ], + "bin": { + "systeminformation": "lib/cli.js" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "Buy me a coffee", + "url": "https://www.buymeacoffee.com/systeminfo" + } + }, "node_modules/tailwindcss": { "version": "3.4.17", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", @@ -23361,6 +24328,25 @@ "node": "*" } }, + "node_modules/tv4": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz", + "integrity": "sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/tx2": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tx2/-/tx2-1.0.5.tgz", + "integrity": "sha512-sJ24w0y03Md/bxzK4FU8J8JveYYUbSs2FViLJ2D/8bytSiyPRbuE3DyL/9UKYXTZlV3yXq0L8GLlhobTnekCVg==", + "dev": true, + "optional": true, + "dependencies": { + "json-stringify-safe": "^5.0.1" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -23814,6 +24800,30 @@ "node": ">= 0.8" } }, + "node_modules/vizion": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vizion/-/vizion-2.2.1.tgz", + "integrity": "sha512-sfAcO2yeSU0CSPFI/DmZp3FsFE9T+8913nv1xWBOyzODv13fwkn6Vl7HqxGpkr9F608M+8SuFId3s+BlZqfXww==", + "dev": true, + "dependencies": { + "async": "^2.6.3", + "git-node-fs": "^1.0.0", + "ini": "^1.3.5", + "js-git": "^0.7.8" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/vizion/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, "node_modules/vue-eslint-parser": { "version": "9.4.3", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", diff --git a/package.json b/package.json index d01ce5e..181caf7 100755 --- a/package.json +++ b/package.json @@ -20,17 +20,21 @@ "scripts": { "start": "npm test && react-scripts start", "dev": "npm test && react-scripts start", - "build": "npm test && react-scripts build", - "test": "jest --runInBand --passWithNoTests", + "build": "npm run test:unit && npm run test:integration && react-scripts build && npm run test:unit", + "test-build-test": "npm run test:unit && npm run test:integration && npm run build && npm run test:unit", + "test": "jest --runInBand --passWithNoTests", + "test:unit": "jest --runInBand --passWithNoTests --testPathPattern=src/tests", + "test:integration": "npm run pm2:start && sleep 1 && jest --runInBand --passWithNoTests --testPathPattern=tests && npm run pm2:stop", + "deploy:build": "npm run build && npm run pm2:start && npm run pm2:reload", "eject": "react-scripts eject", "serve": "npm test && serve -s build", "extract:armoury": "node scripts/extract-armoury.js \"$PWD/Deathwatch-Core-Rulebook.pdf\" public/deathwatch-armoury.json", "server": "npm test && cd database && node server.js", "server:notest": "cd database && node server.js", "db:sqlite": "cd database && ./switch-db.sh sqlite", - "pm2:start": "cd database && pm2 start pm2.config.js --env production --update-env", - "pm2:stop": "pm2 stop deathwatch-server || true", - "pm2:reload": "cd database && pm2 reload pm2.config.js --env production --update-env", + "pm2:start": "cd database && pm2 start pm2.config.js --env production --update-env", + "pm2:stop": "pm2 stop deathwatch-server || true", + "pm2:reload": "cd database && pm2 reload pm2.config.js --env production --update-env", "prestart": "", "preserver": "" }, diff --git a/scripts/local-ci.sh b/scripts/local-ci.sh new file mode 100755 index 0000000..be66a26 --- /dev/null +++ b/scripts/local-ci.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Local CI/CD helper script +# Usage: +# ./scripts/local-ci.sh # run install, unit tests, integration tests, build +# ./scripts/local-ci.sh --deploy # run above then reload pm2 (calls npm run pm2:reload) + +echo "[local-ci] Starting local CI run" +ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT_DIR" + +echo "[local-ci] Running npm ci to ensure deterministic install" +if npm ci; then + echo "[local-ci] npm ci succeeded" +else + echo "[local-ci] npm ci failed — falling back to npm install (local dev mode)" + npm install +fi + +echo "[local-ci] Running unit tests" +npm run test:unit --silent + +echo "[local-ci] Running integration tests (this will start/stop PM2 as configured)" +# test:integration starts pm2, runs tests, and stops pm2 +npm run test:integration --silent + +echo "[local-ci] Building production bundle" +npm run build --silent + +if [ "${1:-}" = "--deploy" ]; then + echo "[local-ci] Deploy flag detected: reloading PM2" + npm run pm2:reload || (echo "[local-ci] npm run pm2:reload failed, attempting pm2 reload directly" && pm2 reload database/pm2.config.js || true) +fi + +echo "[local-ci] Finished successfully" diff --git a/src/components/DeathwatchRoller.jsx b/src/components/DeathwatchRoller.jsx index 2395bce..21d58dd 100755 --- a/src/components/DeathwatchRoller.jsx +++ b/src/components/DeathwatchRoller.jsx @@ -1122,3 +1122,6 @@ if (process.env.NODE_ENV === 'development') { } export default DeathwatchRoller; + +// Named export for reuse in other components +export { Tooltip }; diff --git a/src/components/PlayerTab.jsx b/src/components/PlayerTab.jsx index b07f9ec..93ae9c8 100755 --- a/src/components/PlayerTab.jsx +++ b/src/components/PlayerTab.jsx @@ -1,6 +1,7 @@ import { useState, useEffect, useMemo, useCallback } from 'react'; import axios from 'axios'; import logger, { debug, info, warn, logApiCall, logApiError, logUserAction } from '../utils/logger'; +import { Tooltip } from './DeathwatchRoller'; const STORAGE_SHOP_AUTHED = 'dw:shop:authedPlayer'; const STORAGE_SHOP_PLAYERS = 'dw:shop:players:v1'; @@ -29,6 +30,8 @@ const CHARACTERISTICS = [ { key: 'fel', label: 'Fellowship (Fel)' }, ] + + // Updated default skills to Space Marine skills const SKILLS = [ 'Acrobatics (Ag)', 'Awareness (Per)', 'Charm (Fel)', 'Climb (S)', 'Command (Fel)', @@ -67,7 +70,7 @@ function PlayerTab({ const [pastEvent, setPastEvent] = useState(''); const [personalDemeanour, setPersonalDemeanour] = useState(''); const [characteristics, setCharacteristics] = useState({}); - const [skills, setSkills] = useState([]); + const [skills, setSkills] = useState({}); const [weapons, setWeapons] = useState([]); const [armour, setArmour] = useState({}); const [talents, setTalents] = useState(''); @@ -89,6 +92,37 @@ function PlayerTab({ // Debug state const [showLogs, setShowLogs] = useState(false); const [logs, setLogs] = useState([]); + + // Ability descriptions (used as hover tooltips) + const SPACE_MARINE_ABILITIES = [ + { name: 'Secondary Heart', desc: 'Once per long rest, when reduced to 0 Wounds you remain at 1 Wound instead of falling unconscious (stunned 1 round).' }, + { name: "Larraman's Organ", desc: 'When stabilised or treated with basic medicae, regain an extra 1–3 Wounds (GM roll) or gain +10 to recovery checks.' }, + { name: 'Catalepsean Node', desc: 'Ignore penalties from lack of sleep for ~48 hours and reduce exhaustion effects; can grant allies advantage on vigilance checks during extended watches.' }, + { name: 'Preomnor', desc: 'Strong bonus to toxin-resistance tests (GM: auto-success or +20) and halve duration/effect of ingested poisons.' }, + { name: 'Omophagea', desc: 'On consuming a biological sample (GM permission), gain a one-time clue or +10 to lore/identification checks related to that subject.' }, + { name: 'Multi-lung', desc: 'Ignore breathing penalties in toxic/low-oxygen environments and avoid short-term inhalation damage.' }, + { name: 'Sus-an Membrane', desc: 'Can enter suspended animation to avoid bleeding out when gravely wounded; buys time for evacuation/treatment.' }, + { name: 'Oolitic Kidney', desc: 'Reduce toxin damage/effects by half once per exposure or gain advantage on resisting chemical effects.' }, + { name: 'Neuroglottis', desc: 'Automatic/strong bonus to checks identifying substances by taste; +10 to Tracking vs a target you have tasted.' }, + { name: 'Mucranoid', desc: 'Small resistance to disease and contaminants; +10 on disease-resistance and recovery rolls.' }, + { name: "Betcher's Gland", desc: 'One-use close toxin/venom attack per rest or apply a minor toxin effect on a successful melee strike (GM adjudication).' }, + { name: 'Progenoid Glands', desc: 'Store genetic material for Chapter preservation; may enable long-term resurrection rituals (GM process).' }, + { name: 'Melanochrome', desc: '+10 or advantage to perception in varied lighting and camouflage detection.' }, + { name: 'Occulobe', desc: 'Ignore low-light penalties; see in darkness to short range.' }, + { name: "Lyman's Ear", desc: 'Advantage on hearing/perception checks and ability to detect faint sounds.' } + ]; + + const POWER_ARMOUR_ABILITIES = [ + { name: 'Servo-Augmented Musculature', desc: '+20 Strength while wearing Power Armour.' }, + { name: 'Auto-senses', desc: 'Dark sight; immune to Photon Flash and Stun Grenades; Called Shots are Half Actions; +10 to sight and hearing Awareness Tests (stacks with Heightened Senses).' }, + { name: 'Built-in Vox Link', desc: 'Integrated communications — no external vox gear required.' }, + { name: 'Built-in Magboots', desc: 'Grip in zero-G or slippery conditions; avoid slipping/falling penalties.' }, + { name: 'Nutrient Recycling', desc: 'Operate up to two weeks without resupply.' }, + { name: 'Recoil Suppression', desc: 'May fire Basic weapons one-handed without penalty while in Power Armour.' }, + { name: 'Black Carapace / Size: Hulking', desc: 'Large frame, but Black Carapace prevents enemies gaining a size-based bonus to hit you while in Power Armour.' }, + { name: 'Poor Manual Dexterity', desc: 'Delicate tasks suffer a −10 penalty unless using Space Marine-specific tools.' }, + { name: 'Osmotic Gill Life Sustainer', desc: 'Extended underwater/contaminated atmosphere operation support.' } + ]; // Initialize GM state is now handled in App.js @@ -97,6 +131,10 @@ function PlayerTab({ if (showLogs) setLogs(logger.getLogs({ limit: 200 })); }, [showLogs]); + // GM helpers (define early so hooks using them can call safely) + const isGMLoggedIn = useCallback(() => authedPlayer === 'gm', [authedPlayer]); + const isGMOrShopAuthed = useCallback(() => isGMLoggedIn() || !!authedPlayer, [isGMLoggedIn, authedPlayer]); + // Remove localStorage cache for players except after backend fetch const currentPlayer = useMemo(() => { @@ -129,7 +167,7 @@ function PlayerTab({ setPastEvent(''); setPersonalDemeanour(''); setCharacteristics({}); - setSkills([]); + setSkills({}); setWeapons([]); setArmour({}); setTalents(''); @@ -162,7 +200,7 @@ function PlayerTab({ setPastEvent(tabInfo.pastEvent || ''); setPersonalDemeanour(tabInfo.personalDemeanour || ''); setCharacteristics(tabInfo.characteristics || {}); - setSkills(tabInfo.skills || []); + setSkills(normalizeIncomingSkills(tabInfo.skills || [])); setWeapons(tabInfo.weapons || []); setArmour(tabInfo.armour || {}); setPicture(tabInfo.picture || tabInfo.avatar || ''); @@ -337,7 +375,7 @@ function PlayerTab({ pastEvent: updatedPlayer.pastEvent, personalDemeanour: updatedPlayer.personalDemeanour, characteristics: updatedPlayer.characteristics, - skills: updatedPlayer.skills, + skills: serializeSkillsForSave(updatedPlayer.skills), weapons: updatedPlayer.weapons, armour: updatedPlayer.armour, talents: updatedPlayer.talents, @@ -432,9 +470,7 @@ function PlayerTab({ // Define shopAuthed for use in the component const shopAuthed = getShopAuthed(); - // Stable callbacks so hooks depending on them don't change each render - const isGMLoggedIn = useCallback(() => authedPlayer === 'gm', [authedPlayer]); - const isGMOrShopAuthed = useCallback(() => isGMLoggedIn() || !!authedPlayer, [isGMLoggedIn, authedPlayer]); + // Stable callbacks defined earlier // GM session is now managed in App.js @@ -695,6 +731,33 @@ function PlayerTab({ return headers; } + // Skills normalization helpers + function normalizeIncomingSkills(raw) { + // Backend stores skills as array of names; frontend expects an object map + if (Array.isArray(raw)) { + const obj = {}; + for (const s of raw) { + if (!s) continue; + obj[s] = { trained: true, plus10: false, plus20: false }; + } + return obj; + } + if (raw && typeof raw === 'object') return raw; + return {}; + } + + function serializeSkillsForSave(skillsObj) { + // Convert frontend skills object back to an array of skill names for backend + if (Array.isArray(skillsObj)) return skillsObj.filter(Boolean); + if (!skillsObj || typeof skillsObj !== 'object') return []; + const out = []; + for (const [k, v] of Object.entries(skillsObj)) { + if (!k) continue; + if (v && (v.trained || v.plus10 || v.plus20)) out.push(k); + } + return out; + } + function refreshLogs() { setLogs(logger.getLogs({ limit: 200 })); } function clearLogs() { logger.clearLogs(); setLogs([]); } @@ -901,11 +964,29 @@ function PlayerTab({ - {/* Space Marine Abilities */} + {/* Space Marine Abilities - hover tooltips */}
Space Marine Abilities
-
- Secondary Heart, Larraman’s Organ, Catalepsean Node, Preomnor, Omophagea, Multi-lung, Sus-an Membrane, Oolitic Kidney, Neuroglottis, Mucranoid, Betcher’s Gland, Progenoid Glands, Melanochrome, Occulobe, Lyman’s Ear +
+ {SPACE_MARINE_ABILITIES.map(a => ( +
+ {a.name} + ? +
+ ))} +
+
+ + {/* Power Armour Abilities - hover tooltips */} +
+
Power Armour Abilities (standard)
+
+ {POWER_ARMOUR_ABILITIES.map(a => ( +
+ {a.name} + ? +
+ ))}