@necrolab/dashboard 0.4.221 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/.prettierrc +27 -1
  2. package/.vscode/extensions.json +1 -1
  3. package/README.md +64 -2
  4. package/artwork/image.png +0 -0
  5. package/backend/api.js +26 -24
  6. package/backend/auth.js +2 -2
  7. package/backend/batching.js +1 -1
  8. package/backend/endpoints.js +8 -11
  9. package/backend/index.js +2 -2
  10. package/backend/mock-data.js +27 -36
  11. package/backend/mock-src/classes/logger.js +5 -7
  12. package/backend/mock-src/classes/utils.js +3 -2
  13. package/backend/mock-src/ticketmaster.js +4 -4
  14. package/backend/validator.js +2 -2
  15. package/config/configs.json +0 -1
  16. package/dev-server.js +134 -0
  17. package/exit +209 -0
  18. package/index.html +80 -8
  19. package/index.js +1 -1
  20. package/jsconfig.json +16 -0
  21. package/package.json +39 -25
  22. package/postcss.config.js +1 -1
  23. package/postinstall.js +124 -20
  24. package/public/android-chrome-192x192.png +0 -0
  25. package/public/android-chrome-512x512.png +0 -0
  26. package/public/apple-touch-icon.png +0 -0
  27. package/public/favicon-16x16.png +0 -0
  28. package/public/favicon-32x32.png +0 -0
  29. package/public/favicon.ico +0 -0
  30. package/public/img/logo_trans.png +0 -0
  31. package/public/img/necro_logo.png +0 -0
  32. package/public/manifest.json +16 -10
  33. package/public/reconnect-logo.png +0 -0
  34. package/run +176 -9
  35. package/src/App.vue +498 -85
  36. package/src/assets/css/base/reset.scss +43 -0
  37. package/src/assets/css/base/scroll.scss +114 -0
  38. package/src/assets/css/base/typography.scss +37 -0
  39. package/src/assets/css/components/buttons.scss +216 -0
  40. package/src/assets/css/components/forms.scss +221 -0
  41. package/src/assets/css/components/modals.scss +13 -0
  42. package/src/assets/css/components/tables.scss +27 -0
  43. package/src/assets/css/components/toasts.scss +100 -0
  44. package/src/assets/css/main.scss +202 -122
  45. package/src/assets/img/background.svg +2 -2
  46. package/src/assets/img/background.svg.backup +11 -0
  47. package/src/assets/img/logo_trans.png +0 -0
  48. package/src/components/Auth/LoginForm.vue +95 -11
  49. package/src/components/Editors/Account/Account.vue +116 -40
  50. package/src/components/Editors/Account/AccountCreator.vue +88 -39
  51. package/src/components/Editors/Account/AccountView.vue +102 -34
  52. package/src/components/Editors/Account/CreateAccount.vue +80 -32
  53. package/src/components/Editors/Profile/CreateProfile.vue +269 -83
  54. package/src/components/Editors/Profile/Profile.vue +132 -47
  55. package/src/components/Editors/Profile/ProfileCountryChooser.vue +82 -20
  56. package/src/components/Editors/Profile/ProfileView.vue +89 -32
  57. package/src/components/Editors/TagLabel.vue +67 -6
  58. package/src/components/Editors/TagToggle.vue +7 -2
  59. package/src/components/Filter/Filter.vue +288 -71
  60. package/src/components/Filter/FilterPreview.vue +202 -31
  61. package/src/components/Filter/PriceSortToggle.vue +76 -6
  62. package/src/components/Table/Header.vue +1 -1
  63. package/src/components/Table/Row.vue +1 -1
  64. package/src/components/Table/Table.vue +19 -2
  65. package/src/components/Tasks/CheckStock.vue +6 -8
  66. package/src/components/Tasks/Controls/DesktopControls.vue +27 -17
  67. package/src/components/Tasks/Controls/MobileControls.vue +8 -45
  68. package/src/components/Tasks/CreateTaskAXS.vue +80 -72
  69. package/src/components/Tasks/CreateTaskTM.vue +95 -141
  70. package/src/components/Tasks/MassEdit.vue +4 -6
  71. package/src/components/Tasks/QuickSettings.vue +199 -30
  72. package/src/components/Tasks/ScrapeVenue.vue +5 -6
  73. package/src/components/Tasks/Stats.vue +50 -24
  74. package/src/components/Tasks/Task.vue +384 -179
  75. package/src/components/Tasks/TaskLabel.vue +2 -2
  76. package/src/components/Tasks/TaskView.vue +136 -48
  77. package/src/components/Tasks/Utilities.vue +25 -10
  78. package/src/components/Tasks/ViewTask.vue +321 -0
  79. package/src/components/icons/Bag.vue +1 -1
  80. package/src/components/icons/Check.vue +5 -0
  81. package/src/components/icons/Close.vue +21 -0
  82. package/src/components/icons/CloseX.vue +5 -0
  83. package/src/components/icons/Eye.vue +6 -0
  84. package/src/components/icons/Key.vue +21 -0
  85. package/src/components/icons/Loyalty.vue +1 -1
  86. package/src/components/icons/Mail.vue +2 -2
  87. package/src/components/icons/Pencil.vue +21 -0
  88. package/src/components/icons/Play.vue +2 -2
  89. package/src/components/icons/Profile.vue +18 -0
  90. package/src/components/icons/Reload.vue +4 -5
  91. package/src/components/icons/Sandclock.vue +2 -2
  92. package/src/components/icons/Sell.vue +21 -0
  93. package/src/components/icons/Spinner.vue +42 -0
  94. package/src/components/icons/SquareCheck.vue +18 -0
  95. package/src/components/icons/SquareUncheck.vue +18 -0
  96. package/src/components/icons/Stadium.vue +1 -1
  97. package/src/components/icons/Wildcard.vue +18 -0
  98. package/src/components/icons/index.js +26 -1
  99. package/src/components/ui/Modal.vue +107 -13
  100. package/src/components/ui/Navbar.vue +175 -40
  101. package/src/components/ui/ReconnectIndicator.vue +351 -55
  102. package/src/components/ui/Splash.vue +5 -35
  103. package/src/components/ui/controls/CountryChooser.vue +200 -62
  104. package/src/components/ui/controls/atomic/Checkbox.vue +119 -10
  105. package/src/components/ui/controls/atomic/Dropdown.vue +216 -39
  106. package/src/components/ui/controls/atomic/LoadingButton.vue +45 -0
  107. package/src/components/ui/controls/atomic/MultiDropdown.vue +300 -37
  108. package/src/components/ui/controls/atomic/Switch.vue +53 -25
  109. package/src/composables/useClickOutside.js +21 -0
  110. package/src/composables/useDropdownPosition.js +174 -0
  111. package/src/libs/Filter.js +60 -24
  112. package/src/registerServiceWorker.js +1 -1
  113. package/src/stores/connection.js +4 -4
  114. package/src/stores/sampleData.js +172 -199
  115. package/src/stores/ui.js +55 -20
  116. package/src/stores/utils.js +30 -4
  117. package/src/types/index.js +41 -0
  118. package/src/utils/debug.js +1 -0
  119. package/src/views/Accounts.vue +116 -50
  120. package/src/views/Console.vue +394 -77
  121. package/src/views/Editor.vue +1176 -123
  122. package/src/views/FilterBuilder.vue +528 -250
  123. package/src/views/Login.vue +75 -14
  124. package/src/views/Profiles.vue +119 -34
  125. package/src/views/Tasks.vue +266 -98
  126. package/static/offline.html +192 -50
  127. package/switch-branch.sh +41 -0
  128. package/tailwind.config.js +119 -27
  129. package/vite.config.js +73 -16
  130. package/workbox-config.cjs +63 -0
  131. package/ICONS.md +0 -21
  132. package/public/img/background.svg +0 -14
  133. package/public/img/logo.png +0 -0
  134. package/public/img/logo_icon.png +0 -0
  135. package/public/img/logo_icon_2.png +0 -0
  136. package/src/assets/css/_input.scss +0 -143
  137. package/src/assets/img/logo.png +0 -0
  138. package/src/assets/img/logo_icon.png +0 -0
  139. package/src/assets/img/logo_icon_2.png +0 -0
  140. package/vue.config.js +0 -32
  141. package/workbox-config.js +0 -7
package/.prettierrc CHANGED
@@ -6,5 +6,31 @@
6
6
  "trailingComma": "none",
7
7
  "bracketSpacing": true,
8
8
  "arrowParens": "always",
9
- "printWidth": 120
9
+ "printWidth": 120,
10
+ "vueIndentScriptAndStyle": false,
11
+ "htmlWhitespaceSensitivity": "ignore",
12
+ "bracketSameLine": true,
13
+ "singleAttributePerLine": false,
14
+ "endOfLine": "lf",
15
+ "overrides": [
16
+ {
17
+ "files": "*.vue",
18
+ "options": {
19
+ "vueIndentScriptAndStyle": false,
20
+ "htmlWhitespaceSensitivity": "ignore",
21
+ "singleAttributePerLine": false,
22
+ "bracketSameLine": true,
23
+ "printWidth": 120,
24
+ "tabWidth": 4,
25
+ "useTabs": false,
26
+ "semi": true,
27
+ "singleQuote": false,
28
+ "trailingComma": "none",
29
+ "bracketSpacing": true,
30
+ "arrowParens": "always"
31
+ }
32
+ }
33
+ ],
34
+ "plugins": ["prettier-plugin-tailwindcss"],
35
+ "tailwindConfig": "./tailwind.config.js"
10
36
  }
@@ -1,3 +1,3 @@
1
1
  {
2
- "recommendations": ["Vue.volar"]
2
+ "recommendations": ["Vue.volar", "esbenp.prettier-vscode"]
3
3
  }
package/README.md CHANGED
@@ -1,3 +1,65 @@
1
- # Necro
1
+ <div align="center">
2
+ <img src="./artwork/image.png" alt="Dashboard" width="400">
3
+ </div>
2
4
 
3
- PWA dashboard using vue.js and vite
5
+ # 🎫 Dashboard
6
+
7
+ A sophisticated PWA dashboard for managing ticket purchasing automation across multiple platforms. Built with Vue 3 and real-time WebSocket communication.
8
+
9
+ ## ⚡ Quick Start
10
+
11
+ ```bash
12
+ npm install
13
+ npm run dev
14
+ ```
15
+
16
+ Visit `http://localhost:5173` and you're live.
17
+
18
+ ## 🚀 Commands
19
+
20
+ | Command | Action |
21
+ | ---------------- | ----------------------------- |
22
+ | `npm run dev` | Development server |
23
+ | `npm run expose` | Network-accessible dev server |
24
+ | `npm run build` | Production build with PWA |
25
+ | `npm run lint` | Code formatting |
26
+
27
+ ### 🧱 Frontend Stack
28
+
29
+ - Vue 3 + Composition API
30
+ - Pinia state management
31
+ - TailwindCSS + SCSS
32
+ - PWA with offline support
33
+
34
+ ### 🛠 Backend Stack
35
+
36
+ - Express.js with WebSockets
37
+ - MessagePack serialization
38
+ - Real-time message batching
39
+ - Platform abstraction layer
40
+
41
+ ## 📱 PWA Support
42
+
43
+ - 📲 Install to home screen
44
+ - 🔄 Background sync
45
+ - 📶 Offline functionality
46
+ - 🎨 Native app feel
47
+
48
+ ## 🛠️ Development
49
+
50
+ Runs in mock mode by default. Backend simulates bot operations; frontend provides full UI functionality.
51
+
52
+ ### 🗂️ File Structure
53
+
54
+ ```
55
+ src/
56
+ ├─ components/ # Reusable Vue components
57
+ ├─ views/ # Page-level components
58
+ ├─ stores/ # Pinia state management
59
+ └─ assets/ # Static resources
60
+
61
+ backend/
62
+ ├─ api.js # Express server + WebSocket
63
+ ├─ mock-data.js # Development data
64
+ └─ endpoints.js # API route definitions
65
+ ```
Binary file
package/backend/api.js CHANGED
@@ -1,18 +1,22 @@
1
- const enableWs = require("express-ws");
2
- const express = require("express");
3
- const cors = require("cors");
4
- const cookieParser = require("cookie-parser");
5
- const { encode } = require("@msgpack/msgpack");
6
- const fs = require("node:fs");
7
- const path = require("node:path");
8
-
9
- const { createLogger } = require("./mock-src/classes/logger");
10
- const utils = require("./mock-src/classes/utils");
11
-
12
- const Batcher = require("./batching");
13
- const endpoints = require("./endpoints");
14
- const authSystem = require("./auth");
15
- const { users } = require("./mock-data.js");
1
+ import enableWs from "express-ws";
2
+ import express from "express";
3
+ import cors from "cors";
4
+ import cookieParser from "cookie-parser";
5
+ import { encode } from "@msgpack/msgpack";
6
+ import fs from "node:fs";
7
+ import path from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+
13
+ import { createLogger } from "./mock-src/classes/logger.js";
14
+ import utils from "./mock-src/classes/utils.js";
15
+
16
+ import Batcher from "./batching.js";
17
+ import endpoints from "./endpoints.js";
18
+ import authSystem from "./auth.js";
19
+ import { users } from "./mock-data.js";
16
20
 
17
21
  const logger = createLogger("WEB UI");
18
22
 
@@ -362,14 +366,9 @@ app.get("/api/cors", async (req, res) => {
362
366
 
363
367
  res.type(contentType).send(data);
364
368
 
365
- logger.info("Proxied", {
366
- url,
367
- status: response.status,
368
- contentType,
369
- size: data.length
370
- });
369
+ logger.Info("Proxied", url, response.status);
371
370
  } catch (error) {
372
- logger.error("Proxy error", { url, error: error.message });
371
+ logger.Error("Proxy error", { url, error: error.message });
373
372
  res.status(500).send({ error: error.message });
374
373
  }
375
374
  });
@@ -384,7 +383,10 @@ app.get("/api/quickconfig", async (req, res) => {
384
383
  CapMonster: "example",
385
384
  CapSolver: "example",
386
385
  Invizible: "example",
387
- TwoCaptcha: "example"
386
+ TwoCaptcha: "example",
387
+ AntiCaptcha: "example",
388
+ Multibot: "example",
389
+ Test: "Test"
388
390
  },
389
391
  SMS: {
390
392
  Quackr: "example",
@@ -425,7 +427,7 @@ app.get("/api/userconfig/balances", async (req, res) => {
425
427
  return res.send({ CapSolver: 100, TwoCaptcha: 40 });
426
428
  });
427
429
 
428
- module.exports = {
430
+ export default {
429
431
  start: async () => {
430
432
  // onChange = (await import('on-change')).default;
431
433
  app.listen(port, "0.0.0.0", () => {
package/backend/auth.js CHANGED
@@ -1,4 +1,4 @@
1
- const crypto = require("node:crypto");
1
+ import crypto from "node:crypto";
2
2
 
3
3
  const generateAuthToken = () => {
4
4
  return crypto.randomUUID();
@@ -59,4 +59,4 @@ class Auth {
59
59
  }
60
60
  }
61
61
 
62
- module.exports = Auth;
62
+ export default Auth;
@@ -40,4 +40,4 @@ class Batcher {
40
40
  }
41
41
  }
42
42
 
43
- module.exports = Batcher;
43
+ export default Batcher;
@@ -1,8 +1,8 @@
1
- const { createLogger } = require("./mock-src/classes/logger");
2
- const TicketMaster = require("./mock-src/ticketmaster");
3
- const utils = require("./mock-src/classes/utils");
1
+ import { createLogger } from "./mock-src/classes/logger.js";
2
+ import TicketMaster from "./mock-src/ticketmaster.js";
3
+ import utils from "./mock-src/classes/utils.js";
4
4
 
5
- const validateTaskData = require("./validator");
5
+ import validateTaskData from "./validator.js";
6
6
  const logger = createLogger("WEB UI");
7
7
 
8
8
  const none = (v) => {
@@ -170,7 +170,6 @@ const scrapeMap = async (data) => {
170
170
  taskId: "SCRAPER-" + eventId,
171
171
  account: utils.pickAccount(),
172
172
  eventId: eventId,
173
- incapsulaBypass: true,
174
173
  hidden: true,
175
174
  presaleCode: presaleCode
176
175
  });
@@ -188,7 +187,6 @@ const checkStock = async (data) => {
188
187
  var taskObj = new TicketMaster({
189
188
  taskId: "SCRAPER-" + eventId,
190
189
  eventId: eventId,
191
- incapsulaBypass: true,
192
190
  hidden: true
193
191
  });
194
192
  taskObj.scrapeSeats();
@@ -197,7 +195,6 @@ const checkStock = async (data) => {
197
195
 
198
196
  async function createPresaleModeTasks(eventId, ticketQty) {
199
197
  const presaleModeSettings = {
200
- incapsulaBypass: false,
201
198
  agedAccount: false,
202
199
  manual: false,
203
200
  openCart: false,
@@ -275,9 +272,9 @@ const saveProfile = async (profile) => {
275
272
  };
276
273
 
277
274
  const deleteProfile = async (profile) => {
278
- const { _id } = profile;
279
- Bot.Profiles = Bot.Profiles.filter((p) => p._id !== profile._id);
280
- pushWSUpdate({ event: "delete-profile", profile: { _id: _id } });
275
+ const { id } = profile;
276
+ Bot.Profiles = Bot.Profiles.filter((p) => p.id !== profile.id);
277
+ pushWSUpdate({ event: "delete-profile", profile: { id } });
281
278
  };
282
279
 
283
280
  async function handleWebsocketMessage(msg) {
@@ -326,7 +323,7 @@ async function handleWebsocketMessage(msg) {
326
323
  }
327
324
  }
328
325
 
329
- module.exports = {
326
+ export default {
330
327
  continueTask,
331
328
  tasksOpen,
332
329
  deleteTask,
package/backend/index.js CHANGED
@@ -1,4 +1,4 @@
1
- const { users, profiles, tmAccounts, axsAccounts } = require("./mock-data");
1
+ import { users, profiles, tmAccounts, axsAccounts } from "./mock-data.js";
2
2
 
3
3
  const Bot = {};
4
4
 
@@ -20,4 +20,4 @@ Bot.AXS = {
20
20
  Accounts: axsAccounts
21
21
  };
22
22
 
23
- module.exports = require("./api");
23
+ export { default } from "./api.js";
@@ -1,27 +1,19 @@
1
1
  const users = [
2
2
  {
3
3
  event: "auth",
4
- botChannels: {
5
- splash: "950761407119511582",
6
- carts: "961558093832011807",
7
- checkouts: "1025407596120776794",
8
- declines: "1027868055696572508",
9
- stockChecker: "1099079655962722375",
10
- venueMaps: "1099125114450214973"
11
- },
12
- _id: "641a5292b561088b64fe390b",
13
- name: "admin",
14
- password: "p33t",
15
- profilePicture: "https://cdn.discordapp.com/avatars/843244136945549312/14686df31bfe8889f3c7fb396e8869c9.png",
4
+ id: "641a5292b561088b64fe390b",
5
+ name: "Admin",
6
+ password: "admin",
7
+ profilePicture: "https://cdn.discordapp.com/avatars/435549216304267264/6cfd74ad7c5939a0bcbf218aa08be8cb.png",
16
8
  admin: true,
17
9
  proxyList: { checkout: "admin-proxies" },
18
- profileTags: ["Amex", "Citi", "Mercury", "CapOne", "Veridian", "WIO", "Kez", "Xpence"],
19
- accountTags: ["admin"]
10
+ profileTags: ["Citi", "Mercury", "Slash"],
11
+ tags: ["admin"]
20
12
  }
21
13
  ];
22
14
 
23
15
  const profile = {
24
- _id: "645a82606ef8b7201b728805",
16
+ id: "645a82606ef8b7201b728805",
25
17
  enabled: false,
26
18
  profileName: "Master US 43725 [admin]",
27
19
  address: "88081 Piper Ways",
@@ -42,25 +34,24 @@ const profile = {
42
34
  };
43
35
 
44
36
  const profiles = [];
45
- for (let i = 0; i < 1000; i++) profiles.push({ ...profile, _id: i });
37
+ for (let i = 0; i < 1000; i++) profiles.push({ ...profile, id: i });
46
38
 
47
- module.exports = {
48
- users,
49
- profiles,
50
- tmAccounts: [
51
- {
52
- tags: ["admin"],
53
- _id: 1,
54
- password: "123",
55
- email: "tm@tm.com"
56
- }
57
- ],
58
- axsAccounts: [
59
- {
60
- tags: ["admin"],
61
- _id: 2,
62
- password: "123",
63
- email: "axs@axs.com"
64
- }
65
- ]
66
- };
39
+ export { users, profiles };
40
+
41
+ export const tmAccounts = [
42
+ {
43
+ tags: ["admin"],
44
+ id: 1,
45
+ password: "123",
46
+ email: "tm@tm.com"
47
+ }
48
+ ];
49
+
50
+ export const axsAccounts = [
51
+ {
52
+ tags: ["admin"],
53
+ id: 2,
54
+ password: "123",
55
+ email: "axs@axs.com"
56
+ }
57
+ ];
@@ -1,4 +1,4 @@
1
- const util = require("node:util");
1
+ import util from "node:util";
2
2
 
3
3
  const zeroPadding = (num, length) => String(num).padStart(length, "0");
4
4
 
@@ -103,10 +103,8 @@ class Logger {
103
103
  }
104
104
  }
105
105
 
106
- module.exports = {
107
- createLogger: (args) => {
108
- const logger = new Logger();
109
- logger.defaultArgs = typeof args == "string" ? [args] : typeof args == "object" ? args : undefined;
110
- return logger;
111
- }
106
+ export const createLogger = (args) => {
107
+ const logger = new Logger();
108
+ logger.defaultArgs = typeof args == "string" ? [args] : typeof args == "object" ? args : undefined;
109
+ return logger;
112
110
  };
@@ -1,7 +1,8 @@
1
+ import crypto from "node:crypto";
2
+
1
3
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2
- const crypto = require("node:crypto");
3
4
 
4
- module.exports = {
5
+ export default {
5
6
  pickAccount: () => {
6
7
  return "demo@email.com";
7
8
  },
@@ -1,6 +1,6 @@
1
- const { createLogger } = require("./classes/logger");
1
+ import { createLogger } from "./classes/logger.js";
2
2
 
3
- module.exports = class TicketMaster {
3
+ export default class TicketMaster {
4
4
  constructor(taskData) {
5
5
  this.taskId = taskData.taskId || "T-" + ++Bot.CurrentTaskId;
6
6
  this.logger = createLogger(this.taskId);
@@ -24,7 +24,7 @@ module.exports = class TicketMaster {
24
24
  this.data.eventDate = Date.now();
25
25
  this.data.eventLocalDate = Date.now();
26
26
  this.data.eventName = "Test Event";
27
- this.data.reservedTicketsList = "ticket1 | ticket2 | ticket3\n$100";
27
+ this.data.reservedTicketsList = " 2x 301/E ($86.47) \n• 2x 306/U ($86.47) \n$345.88";
28
28
 
29
29
  refreshTaskOnFrontEnd(taskData);
30
30
  }
@@ -89,4 +89,4 @@ module.exports = class TicketMaster {
89
89
  scrapeSeats() {
90
90
  this.logger.Info("Scraping seats");
91
91
  }
92
- };
92
+ }
@@ -1,4 +1,4 @@
1
- const utils = require("./mock-src/classes/utils");
1
+ import utils from "./mock-src/classes/utils.js";
2
2
 
3
3
  const none = (v) => {
4
4
  return v === undefined;
@@ -59,4 +59,4 @@ const validateTaskData = (task) => {
59
59
  return task;
60
60
  };
61
61
 
62
- module.exports = validateTaskData;
62
+ export default validateTaskData;
@@ -7,7 +7,6 @@
7
7
  "quantity": 5,
8
8
  "manual": false,
9
9
  "doNotPay": false,
10
- "incapsulaBypass": false,
11
10
  "quickQueue": true,
12
11
  "loginAfterCart": false,
13
12
  "smartTimer": false,
package/dev-server.js ADDED
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createServer } from "vite";
4
+ import process from "process";
5
+ import { spawn, execFileSync } from "child_process";
6
+
7
+ const killPort = (port) => {
8
+ try {
9
+ const pid = execFileSync("lsof", ["-ti", `:${port}`], { encoding: "utf-8" }).trim();
10
+ if (pid) {
11
+ pid.split("\n").forEach((p) => {
12
+ try {
13
+ process.kill(Number(p), "SIGKILL");
14
+ } catch {}
15
+ });
16
+ }
17
+ } catch {}
18
+ };
19
+
20
+ const startBackend = () => {
21
+ return new Promise((resolve, reject) => {
22
+ const backend = spawn("node", ["index.js"], {
23
+ stdio: ["inherit", "pipe", "pipe"],
24
+ env: { ...process.env, NODE_ENV: "development" }
25
+ });
26
+
27
+ let backendReady = false;
28
+
29
+ backend.stdout.on("data", (data) => {
30
+ const output = data.toString();
31
+
32
+ if (output.includes("Web API started on port") || output.includes("8081")) {
33
+ backendReady = true;
34
+ resolve(backend);
35
+ }
36
+ });
37
+
38
+ backend.stderr.on("data", (data) => {
39
+ if (data.toString().includes("Error") || data.toString().includes("EADDRINUSE")) {
40
+ console.error("⚠️ Backend error:", data.toString().trim());
41
+ }
42
+ });
43
+
44
+ backend.on("error", (error) => {
45
+ reject(error);
46
+ });
47
+
48
+ backend.on("exit", (code) => {
49
+ if (code !== 0 && !backendReady) {
50
+ reject(new Error(`Backend failed to start`));
51
+ }
52
+ });
53
+
54
+ setTimeout(() => {
55
+ if (!backendReady) {
56
+ resolve(backend);
57
+ }
58
+ }, 5000);
59
+ });
60
+ };
61
+
62
+ const startServer = async () => {
63
+ let backend;
64
+ let server;
65
+ let isShuttingDown = false;
66
+
67
+ const cleanup = () => {
68
+ if (isShuttingDown) return;
69
+ isShuttingDown = true;
70
+
71
+ if (backend) {
72
+ backend.kill("SIGTERM");
73
+ setTimeout(() => backend.kill("SIGKILL"), 3000);
74
+ }
75
+ if (server) {
76
+ server.close();
77
+ }
78
+ process.exit(0);
79
+ };
80
+
81
+ process.on("SIGINT", cleanup);
82
+ process.on("SIGTERM", cleanup);
83
+
84
+ try {
85
+ killPort(5173);
86
+ killPort(8081);
87
+
88
+ try {
89
+ const fs = await import("node:fs");
90
+ const path = await import("node:path");
91
+ const cacheDir = path.resolve("./node_modules/.vite");
92
+ if (fs.existsSync(cacheDir)) {
93
+ fs.chmodSync(cacheDir, 0o755);
94
+ }
95
+ } catch (e) {}
96
+
97
+ backend = await startBackend();
98
+
99
+ server = await createServer({
100
+ configFile: "./vite.config.js",
101
+ server: {
102
+ port: 5173,
103
+ strictPort: true,
104
+ host: true,
105
+ cors: true,
106
+ hmr: {
107
+ overlay: false
108
+ }
109
+ }
110
+ });
111
+
112
+ process.on("uncaughtException", (error) => {
113
+ if (error.code === "ECONNRESET" || error.errno === -54) return;
114
+
115
+ console.error("❌ Server error:", error.message);
116
+ });
117
+
118
+ process.on("unhandledRejection", (reason) => {
119
+ if (reason?.code === "ECONNRESET" || reason?.errno === -54) return;
120
+
121
+ console.error("❌ Unhandled error:", reason?.message || reason);
122
+ });
123
+
124
+ await server.listen();
125
+
126
+ console.log("🌐 Dev: http://localhost:5173");
127
+ console.log("🌐 Staging: http://localhost:8081\n");
128
+ } catch (error) {
129
+ console.error("Failed to start server:", error);
130
+ process.exit(1);
131
+ }
132
+ };
133
+
134
+ startServer();