4bnode 1.0.0
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.
- package/README.md +199 -0
- package/README_4bnode.md +197 -0
- package/README_final_4bnode.md +199 -0
- package/add-api.js +200 -0
- package/add-login.js +395 -0
- package/add-mongo-delete.js +153 -0
- package/add-mongo-insert.js +231 -0
- package/add-mongo-read.js +235 -0
- package/add-mongo-schema.js +231 -0
- package/add-mongo-update.js +229 -0
- package/add-mongo.js +257 -0
- package/add-serialport.js +124 -0
- package/add-socket.js +110 -0
- package/add-websocket.js +199 -0
- package/index.js +226 -0
- package/package.json +40 -0
- package/skeleton/.env.development +1 -0
- package/skeleton/.env.production +1 -0
- package/skeleton/index.js +45 -0
- package/skeleton/package.json +19 -0
- package/skeleton/public/index.html +90 -0
- package/src/auth/passport.js +21 -0
- package/src/db.js +13 -0
- package/test-server/.env.development +1 -0
- package/test-server/.env.production +1 -0
- package/test-server/index.js +82 -0
- package/test-server/package-lock.json +1267 -0
- package/test-server/package.json +20 -0
- package/test-server/public/index.html +90 -0
package/add-mongo.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import readline from "readline";
|
|
6
|
+
import { execSync } from "child_process";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
|
|
9
|
+
const rl = readline.createInterface({
|
|
10
|
+
input: process.stdin,
|
|
11
|
+
output: process.stdout,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
function isDatabaseConfigured() {
|
|
15
|
+
const projectRoot = process.cwd();
|
|
16
|
+
|
|
17
|
+
const devEnvPath = path.join(projectRoot, ".env.development");
|
|
18
|
+
const prodEnvPath = path.join(projectRoot, ".env.production");
|
|
19
|
+
|
|
20
|
+
const isDevConfigured =
|
|
21
|
+
fs.existsSync(devEnvPath) &&
|
|
22
|
+
fs.readFileSync(devEnvPath, "utf8").includes("MONGO_URI=");
|
|
23
|
+
const isProdConfigured =
|
|
24
|
+
fs.existsSync(prodEnvPath) &&
|
|
25
|
+
fs.readFileSync(prodEnvPath, "utf8").includes("MONGO_URI=");
|
|
26
|
+
|
|
27
|
+
return isDevConfigured || isProdConfigured;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function updateEnvFile(filePath, uri) {
|
|
31
|
+
const mongoUriLine = `MONGO_URI=${uri}\n`;
|
|
32
|
+
|
|
33
|
+
if (fs.existsSync(filePath)) {
|
|
34
|
+
let envContent = fs.readFileSync(filePath, "utf8");
|
|
35
|
+
if (envContent.includes("MONGO_URI=")) {
|
|
36
|
+
envContent = envContent.replace(/MONGO_URI=.*/, mongoUriLine.trim());
|
|
37
|
+
} else {
|
|
38
|
+
envContent = envContent.trim() + "\n" + mongoUriLine;
|
|
39
|
+
}
|
|
40
|
+
fs.writeFileSync(filePath, envContent, "utf8");
|
|
41
|
+
} else {
|
|
42
|
+
fs.writeFileSync(filePath, mongoUriLine, "utf8");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function addMongoDB(devUri, prodUri) {
|
|
47
|
+
const projectRoot = process.cwd();
|
|
48
|
+
|
|
49
|
+
// Add MongoDB connection file
|
|
50
|
+
const dbContent = `
|
|
51
|
+
import mongoose from 'mongoose';
|
|
52
|
+
|
|
53
|
+
const connectDB = async () => {
|
|
54
|
+
try {
|
|
55
|
+
const conn = await mongoose.connect(process.env.MONGO_URI);
|
|
56
|
+
console.log(\`MongoDB connected: \${conn.connection.host}\`);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
console.error(err);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default connectDB;
|
|
64
|
+
`;
|
|
65
|
+
if (!fs.existsSync(path.join(projectRoot, "src"))) {
|
|
66
|
+
fs.mkdirSync(path.join(projectRoot, "src"), { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
fs.writeFileSync(
|
|
69
|
+
path.join(projectRoot, "src", "db.js"),
|
|
70
|
+
dbContent.trim(),
|
|
71
|
+
"utf8"
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Update index.js to import and use MongoDB connection
|
|
75
|
+
const indexPath = path.join(projectRoot, "index.js");
|
|
76
|
+
const dbImport = `import connectDB from './src/db.js';\n`;
|
|
77
|
+
const dbUse = `connectDB();\n`;
|
|
78
|
+
|
|
79
|
+
let indexContent = fs.readFileSync(indexPath, "utf8");
|
|
80
|
+
if (!indexContent.includes(dbImport)) {
|
|
81
|
+
indexContent = dbImport + indexContent;
|
|
82
|
+
}
|
|
83
|
+
if (!indexContent.includes(dbUse)) {
|
|
84
|
+
indexContent = indexContent.replace(
|
|
85
|
+
"const app = express();",
|
|
86
|
+
`${dbUse}const app = express();`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
fs.writeFileSync(indexPath, indexContent, "utf8");
|
|
90
|
+
|
|
91
|
+
// Update .env files
|
|
92
|
+
updateEnvFile(path.join(projectRoot, ".env.development"), devUri);
|
|
93
|
+
updateEnvFile(path.join(projectRoot, ".env.production"), prodUri);
|
|
94
|
+
|
|
95
|
+
// Add mongoose to dependencies
|
|
96
|
+
execSync("npm install mongoose", { stdio: "inherit" });
|
|
97
|
+
|
|
98
|
+
console.log(
|
|
99
|
+
chalk.green(
|
|
100
|
+
`MongoDB integration added with development URI: ${devUri} and production URI: ${prodUri}`
|
|
101
|
+
)
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (isDatabaseConfigured()) {
|
|
106
|
+
console.log(
|
|
107
|
+
chalk.yellow(
|
|
108
|
+
"MongoDB is already configured in your environment files. Aborting operation."
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
rl.close();
|
|
112
|
+
process.exit(0);
|
|
113
|
+
} else {
|
|
114
|
+
function promptForDatabaseDetails(env, callback) {
|
|
115
|
+
console.log(chalk.cyan(`\nEnter details for the ${env} database:`));
|
|
116
|
+
|
|
117
|
+
function askDatabaseName() {
|
|
118
|
+
rl.question(
|
|
119
|
+
chalk.yellow("Enter the MongoDB database name: "),
|
|
120
|
+
(dbName) => {
|
|
121
|
+
if (!dbName.trim()) {
|
|
122
|
+
console.log(chalk.red("Database name is required."));
|
|
123
|
+
askDatabaseName();
|
|
124
|
+
} else {
|
|
125
|
+
callback(dbName);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
askDatabaseName();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
promptForDatabaseDetails("development", (devDbName) => {
|
|
135
|
+
rl.question(
|
|
136
|
+
chalk.yellow("Enter the MongoDB IP address (default: 127.0.0.1): "),
|
|
137
|
+
(devIpAddress) => {
|
|
138
|
+
const devIp = devIpAddress.trim() || "127.0.0.1";
|
|
139
|
+
rl.question(
|
|
140
|
+
chalk.yellow("Enter the MongoDB port (default: 27017): "),
|
|
141
|
+
(devPort) => {
|
|
142
|
+
const devDbPort = devPort.trim() || "27017";
|
|
143
|
+
rl.question(
|
|
144
|
+
chalk.yellow("Enter the MongoDB user ID (leave blank if none): "),
|
|
145
|
+
(devUserId) => {
|
|
146
|
+
rl.question(
|
|
147
|
+
chalk.yellow(
|
|
148
|
+
"Enter the MongoDB password (leave blank if none): "
|
|
149
|
+
),
|
|
150
|
+
(devPassword) => {
|
|
151
|
+
let devUri = `mongodb://${devIp}:${devDbPort}/${devDbName}`;
|
|
152
|
+
if (devUserId && devPassword) {
|
|
153
|
+
devUri = `mongodb://${devUserId}:${devPassword}@${devIp}:${devDbPort}/${devDbName}`;
|
|
154
|
+
} else if (devUserId || devPassword) {
|
|
155
|
+
console.error(
|
|
156
|
+
chalk.red(
|
|
157
|
+
"Both user ID and password must be provided together."
|
|
158
|
+
)
|
|
159
|
+
);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Ask if the same details should be used for production
|
|
164
|
+
function askSameForProduction() {
|
|
165
|
+
rl.question(
|
|
166
|
+
chalk.blueBright(
|
|
167
|
+
"\nUse the same database details for production? (Yes/No): "
|
|
168
|
+
),
|
|
169
|
+
(answer) => {
|
|
170
|
+
const response = answer.trim().toLowerCase();
|
|
171
|
+
|
|
172
|
+
if (response === "y" || response === "yes") {
|
|
173
|
+
addMongoDB(devUri, devUri);
|
|
174
|
+
rl.close();
|
|
175
|
+
process.exit(0);
|
|
176
|
+
} else if (response === "n" || response === "no") {
|
|
177
|
+
promptForDatabaseDetails(
|
|
178
|
+
"production",
|
|
179
|
+
(prodDbName) => {
|
|
180
|
+
rl.question(
|
|
181
|
+
chalk.yellow(
|
|
182
|
+
"Enter the MongoDB IP address (default: 127.0.0.1): "
|
|
183
|
+
),
|
|
184
|
+
(prodIpAddress) => {
|
|
185
|
+
const prodIp =
|
|
186
|
+
prodIpAddress.trim() || "127.0.0.1";
|
|
187
|
+
rl.question(
|
|
188
|
+
chalk.yellow(
|
|
189
|
+
"Enter the MongoDB port (default: 27017): "
|
|
190
|
+
),
|
|
191
|
+
(prodPort) => {
|
|
192
|
+
const prodDbPort =
|
|
193
|
+
prodPort.trim() || "27017";
|
|
194
|
+
rl.question(
|
|
195
|
+
chalk.yellow(
|
|
196
|
+
"Enter the MongoDB user ID (leave blank if none): "
|
|
197
|
+
),
|
|
198
|
+
(prodUserId) => {
|
|
199
|
+
rl.question(
|
|
200
|
+
chalk.yellow(
|
|
201
|
+
"Enter the MongoDB password (leave blank if none): "
|
|
202
|
+
),
|
|
203
|
+
(prodPassword) => {
|
|
204
|
+
let prodUri = `mongodb://${prodIp}:${prodDbPort}/${prodDbName}`;
|
|
205
|
+
if (
|
|
206
|
+
prodUserId &&
|
|
207
|
+
prodPassword
|
|
208
|
+
) {
|
|
209
|
+
prodUri = `mongodb://${prodUserId}:${prodPassword}@${prodIp}:${prodDbPort}/${prodDbName}`;
|
|
210
|
+
} else if (
|
|
211
|
+
prodUserId ||
|
|
212
|
+
prodPassword
|
|
213
|
+
) {
|
|
214
|
+
console.error(
|
|
215
|
+
chalk.red(
|
|
216
|
+
"Both user ID and password must be provided together."
|
|
217
|
+
)
|
|
218
|
+
);
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
addMongoDB(devUri, prodUri);
|
|
223
|
+
rl.close();
|
|
224
|
+
process.exit(0);
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
} else {
|
|
236
|
+
console.log(
|
|
237
|
+
chalk.red(
|
|
238
|
+
"Invalid input. Please enter Yes or No."
|
|
239
|
+
)
|
|
240
|
+
);
|
|
241
|
+
askSameForProduction();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
askSameForProduction();
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import readline from "readline";
|
|
6
|
+
import { execSync } from "child_process";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
|
|
9
|
+
const rl = readline.createInterface({
|
|
10
|
+
input: process.stdin,
|
|
11
|
+
output: process.stdout,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
function updateEnvFile(filePath, serialPortPath, baudRate) {
|
|
15
|
+
const serialPortPathLine = `SERIALPORT_PATH=${serialPortPath}\n`;
|
|
16
|
+
const baudRateLine = `BAUDRATE=${baudRate}\n`;
|
|
17
|
+
|
|
18
|
+
if (fs.existsSync(filePath)) {
|
|
19
|
+
let envContent = fs.readFileSync(filePath, "utf8");
|
|
20
|
+
if (envContent.includes("SERIALPORT_PATH=")) {
|
|
21
|
+
envContent = envContent.replace(
|
|
22
|
+
/SERIALPORT_PATH=.*/,
|
|
23
|
+
serialPortPathLine.trim()
|
|
24
|
+
);
|
|
25
|
+
} else {
|
|
26
|
+
envContent = envContent.trim() + "\n" + serialPortPathLine;
|
|
27
|
+
}
|
|
28
|
+
if (envContent.includes("BAUDRATE=")) {
|
|
29
|
+
envContent = envContent.replace(/BAUDRATE=.*/, baudRateLine.trim());
|
|
30
|
+
} else {
|
|
31
|
+
envContent = envContent.trim() + "\n" + baudRateLine;
|
|
32
|
+
}
|
|
33
|
+
fs.writeFileSync(filePath, envContent, "utf8");
|
|
34
|
+
} else {
|
|
35
|
+
fs.writeFileSync(filePath, serialPortPathLine + baudRateLine, "utf8");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function addSerialPort(serialPortPath, baudRate) {
|
|
40
|
+
const projectRoot = process.cwd();
|
|
41
|
+
const indexPath = path.join(projectRoot, "index.js");
|
|
42
|
+
|
|
43
|
+
const serialportImport = `
|
|
44
|
+
import { SerialPort } from 'serialport';
|
|
45
|
+
import { ReadlineParser } from '@serialport/parser-readline';
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
const serialportSetup = `
|
|
49
|
+
try {
|
|
50
|
+
const serialPort = new SerialPort({
|
|
51
|
+
baudRate: parseInt(process.env.BAUDRATE, 10) || ${baudRate},
|
|
52
|
+
path: process.env.SERIALPORT_PATH || '${serialPortPath}',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const parser = serialPort.pipe(new ReadlineParser({ delimiter: "\\r\\n" }));
|
|
56
|
+
|
|
57
|
+
parser.on("data", async (data) => {
|
|
58
|
+
console.log('Received data:', data);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
serialPort.on("error", function (err) {
|
|
62
|
+
console.error("SerialPort Error: ", err.message);
|
|
63
|
+
});
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error("Error initializing SerialPort or Parser:", error);
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
let indexContent = fs.readFileSync(indexPath, "utf8");
|
|
70
|
+
|
|
71
|
+
// Add imports if they don't already exist
|
|
72
|
+
if (!indexContent.includes("import { SerialPort }")) {
|
|
73
|
+
indexContent = serialportImport + indexContent;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Ensure SerialPort setup is added just before app.listen or server.listen
|
|
77
|
+
const listenMatch = indexContent.match(/(app|server)\.listen\(.*\);/);
|
|
78
|
+
if (listenMatch) {
|
|
79
|
+
indexContent = indexContent.replace(
|
|
80
|
+
listenMatch[0],
|
|
81
|
+
serialportSetup + "\n" + listenMatch[0]
|
|
82
|
+
);
|
|
83
|
+
} else {
|
|
84
|
+
indexContent += serialportSetup;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
fs.writeFileSync(indexPath, indexContent, "utf8");
|
|
88
|
+
|
|
89
|
+
// Update .env files
|
|
90
|
+
updateEnvFile(
|
|
91
|
+
path.join(projectRoot, ".env.development"),
|
|
92
|
+
serialPortPath,
|
|
93
|
+
baudRate
|
|
94
|
+
);
|
|
95
|
+
updateEnvFile(
|
|
96
|
+
path.join(projectRoot, ".env.production"),
|
|
97
|
+
serialPortPath,
|
|
98
|
+
baudRate
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Install serialport and @serialport/parser-readline
|
|
102
|
+
execSync("npm install serialport @serialport/parser-readline", {
|
|
103
|
+
stdio: "inherit",
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
console.log(chalk.green("SerialPort integration added successfully."));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
rl.question(
|
|
110
|
+
chalk.yellow(
|
|
111
|
+
"Enter the serial port path (e.g., /dev/tty-usbserial1) [default: /dev/tty-usbserial1]: "
|
|
112
|
+
),
|
|
113
|
+
(serialPortPath) => {
|
|
114
|
+
serialPortPath = serialPortPath.trim() || "/dev/tty-usbserial1";
|
|
115
|
+
rl.question(
|
|
116
|
+
chalk.yellow("Enter the baud rate (e.g., 9600) [default: 9600]: "),
|
|
117
|
+
(baudRate) => {
|
|
118
|
+
baudRate = baudRate.trim() || "9600";
|
|
119
|
+
addSerialPort(serialPortPath, baudRate);
|
|
120
|
+
rl.close();
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
);
|
package/add-socket.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import readline from "readline";
|
|
6
|
+
import { execSync } from "child_process";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
|
|
9
|
+
const rl = readline.createInterface({
|
|
10
|
+
input: process.stdin,
|
|
11
|
+
output: process.stdout,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
function addSocketIo(ipAddresses) {
|
|
15
|
+
const projectRoot = process.cwd();
|
|
16
|
+
const indexPath = path.join(projectRoot, "index.js");
|
|
17
|
+
|
|
18
|
+
const socketIoImport = `import { Server } from "socket.io";\n`;
|
|
19
|
+
const httpImport = `import http from "http";\n`;
|
|
20
|
+
|
|
21
|
+
let corsOrigins;
|
|
22
|
+
|
|
23
|
+
if (ipAddresses.trim() === "*") {
|
|
24
|
+
corsOrigins = `["*"]`; // Allow all origins
|
|
25
|
+
} else {
|
|
26
|
+
const ipArray = ipAddresses.split(",").map((ip) => ip.trim());
|
|
27
|
+
corsOrigins = `[${ipArray.map((ip) => `"http://${ip}"`).join(", ")}]`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const socketIoSetup = `
|
|
31
|
+
const server = http.createServer(app);
|
|
32
|
+
const io = new Server(server, {
|
|
33
|
+
cors: {
|
|
34
|
+
origin: ${corsOrigins},
|
|
35
|
+
methods: ["GET", "POST"]
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
io.on("connection", (socket) => {
|
|
40
|
+
console.log("A user connected");
|
|
41
|
+
|
|
42
|
+
socket.on("disconnect", () => {
|
|
43
|
+
console.log("User disconnected");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Middleware to pass io instance to routes
|
|
48
|
+
app.use((req, res, next) => {
|
|
49
|
+
req.io = io;
|
|
50
|
+
next();
|
|
51
|
+
});
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
let indexContent = fs.readFileSync(indexPath, "utf8");
|
|
55
|
+
|
|
56
|
+
// Add imports if they don't already exist
|
|
57
|
+
if (!indexContent.includes(socketIoImport)) {
|
|
58
|
+
indexContent = socketIoImport + indexContent;
|
|
59
|
+
}
|
|
60
|
+
if (!indexContent.includes(httpImport)) {
|
|
61
|
+
indexContent = httpImport + indexContent;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Ensure Socket.io setup is placed before routes
|
|
65
|
+
const routesStart = indexContent.indexOf("app.use(");
|
|
66
|
+
if (routesStart !== -1) {
|
|
67
|
+
indexContent =
|
|
68
|
+
indexContent.slice(0, routesStart) +
|
|
69
|
+
socketIoSetup +
|
|
70
|
+
indexContent.slice(routesStart);
|
|
71
|
+
} else {
|
|
72
|
+
indexContent += socketIoSetup;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Replace existing app.listen if it exists
|
|
76
|
+
indexContent = indexContent.replace(/app\.listen\(port,.*\);/s, "");
|
|
77
|
+
|
|
78
|
+
// Append server.listen at the end if not already present
|
|
79
|
+
if (!indexContent.includes("server.listen(port")) {
|
|
80
|
+
indexContent += `
|
|
81
|
+
server.listen(port, () => {
|
|
82
|
+
const localIp = getLocalNetworkIp();
|
|
83
|
+
console.log(\`App is running on http://localhost:\${port}\`);
|
|
84
|
+
if (localIp) {
|
|
85
|
+
console.log(
|
|
86
|
+
\`App is also accessible on your local network at http://\${localIp}:\${port}\`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
fs.writeFileSync(indexPath, indexContent, "utf8");
|
|
94
|
+
|
|
95
|
+
// Install socket.io
|
|
96
|
+
console.log(chalk.green("Installing socket.io..."));
|
|
97
|
+
execSync("npm install socket.io", { stdio: "inherit" });
|
|
98
|
+
|
|
99
|
+
console.log(chalk.green("Socket.io integration added successfully."));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
rl.question(
|
|
103
|
+
chalk.yellow(
|
|
104
|
+
"Enter the IP addresses for socket.io (comma-separated or '*' to allow all): "
|
|
105
|
+
),
|
|
106
|
+
(ipAddresses) => {
|
|
107
|
+
addSocketIo(ipAddresses);
|
|
108
|
+
rl.close();
|
|
109
|
+
}
|
|
110
|
+
);
|
package/add-websocket.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import readline from "readline";
|
|
6
|
+
import { execSync } from "child_process";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
|
|
9
|
+
const rl = readline.createInterface({
|
|
10
|
+
input: process.stdin,
|
|
11
|
+
output: process.stdout,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
function updateEnvFile(filePath, wsPort) {
|
|
15
|
+
if (!wsPort) return; // Don't update .env if using the same port as the server
|
|
16
|
+
|
|
17
|
+
const wsPortLine = `WS_PORT=${wsPort}\n`;
|
|
18
|
+
|
|
19
|
+
if (fs.existsSync(filePath)) {
|
|
20
|
+
let envContent = fs.readFileSync(filePath, "utf8");
|
|
21
|
+
if (envContent.includes("WS_PORT=")) {
|
|
22
|
+
envContent = envContent.replace(/WS_PORT=.*/, wsPortLine.trim());
|
|
23
|
+
} else {
|
|
24
|
+
envContent = envContent.trim() + "\n" + wsPortLine;
|
|
25
|
+
}
|
|
26
|
+
fs.writeFileSync(filePath, envContent, "utf8");
|
|
27
|
+
} else {
|
|
28
|
+
fs.writeFileSync(filePath, wsPortLine, "utf8");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function addWebSocket(samePort, wsPort = null) {
|
|
33
|
+
const projectRoot = process.cwd();
|
|
34
|
+
const indexPath = path.join(projectRoot, "index.js");
|
|
35
|
+
|
|
36
|
+
let indexContent = fs.readFileSync(indexPath, "utf8");
|
|
37
|
+
|
|
38
|
+
const wsImport = `import { WebSocketServer } from 'ws';\nimport http from 'http';\n`;
|
|
39
|
+
|
|
40
|
+
// Define WebSocket setup based on `samePort`
|
|
41
|
+
const websocketSetup = samePort
|
|
42
|
+
? `
|
|
43
|
+
const server = http.createServer(app);
|
|
44
|
+
const wsServer = new WebSocketServer({ server });
|
|
45
|
+
console.log(\`WebSocket server is running on the same port as the server (\${port})\`);
|
|
46
|
+
`
|
|
47
|
+
: `
|
|
48
|
+
const wsPort = process.env.WS_PORT || ${wsPort};
|
|
49
|
+
const wsServer = new WebSocketServer({ port: wsPort });
|
|
50
|
+
console.log(\`WebSocket server is running on WebSocket port \${wsPort}\`);
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const wsLogic = `
|
|
54
|
+
wsServer.on('connection', (ws) => {
|
|
55
|
+
console.log('A client connected');
|
|
56
|
+
|
|
57
|
+
ws.on('message', (message) => {
|
|
58
|
+
try {
|
|
59
|
+
const parsedMessage = JSON.parse(message);
|
|
60
|
+
console.log('Received:', parsedMessage);
|
|
61
|
+
|
|
62
|
+
// Broadcast to all clients except sender
|
|
63
|
+
wsServer.clients.forEach((client) => {
|
|
64
|
+
if (client !== ws && client.readyState === ws.OPEN) {
|
|
65
|
+
client.send(JSON.stringify(parsedMessage));
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error('Invalid message format:', error);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
ws.on('close', () => {
|
|
74
|
+
console.log('Client disconnected');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
app.use((req, res, next) => {
|
|
79
|
+
req.wss = wsServer;
|
|
80
|
+
next();
|
|
81
|
+
});
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
// Select correct `listen()` method
|
|
85
|
+
const listenSetup = samePort
|
|
86
|
+
? `
|
|
87
|
+
server.listen(port, () => {
|
|
88
|
+
const localIp = getLocalNetworkIp();
|
|
89
|
+
console.log(\`App is running on http://localhost:\${port}\`);
|
|
90
|
+
if (localIp) {
|
|
91
|
+
console.log(
|
|
92
|
+
\`App is also accessible on your local network at http://\${localIp}:\${port}\`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
`
|
|
97
|
+
: `
|
|
98
|
+
app.listen(port, () => {
|
|
99
|
+
const localIp = getLocalNetworkIp();
|
|
100
|
+
console.log(\`App is running on http://localhost:\${port}\`);
|
|
101
|
+
if (localIp) {
|
|
102
|
+
console.log(
|
|
103
|
+
\`App is also accessible on your local network at http://\${localIp}:\${port}\`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
// Add imports if they don't already exist
|
|
110
|
+
if (!indexContent.includes(wsImport)) {
|
|
111
|
+
indexContent = wsImport + indexContent;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Remove existing `app.listen()` or `server.listen()`
|
|
115
|
+
indexContent = indexContent.replace(/server\.listen\(port.*\);/s, "");
|
|
116
|
+
indexContent = indexContent.replace(/app\.listen\(port.*\);/s, "");
|
|
117
|
+
|
|
118
|
+
// Ensure WebSocket setup is placed before routes
|
|
119
|
+
if (!indexContent.includes("const wsServer = new WebSocketServer({")) {
|
|
120
|
+
const routesStart = indexContent.indexOf("app.use(");
|
|
121
|
+
if (routesStart !== -1) {
|
|
122
|
+
indexContent =
|
|
123
|
+
indexContent.slice(0, routesStart) +
|
|
124
|
+
websocketSetup +
|
|
125
|
+
wsLogic +
|
|
126
|
+
indexContent.slice(routesStart);
|
|
127
|
+
} else {
|
|
128
|
+
indexContent += websocketSetup + wsLogic;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Append the correct `listen()` function
|
|
133
|
+
if (
|
|
134
|
+
!indexContent.includes("app.listen(port)") &&
|
|
135
|
+
!indexContent.includes("server.listen(port)")
|
|
136
|
+
) {
|
|
137
|
+
indexContent += listenSetup;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
fs.writeFileSync(indexPath, indexContent, "utf8");
|
|
141
|
+
|
|
142
|
+
// Update `.env` files only if using a separate WebSocket port
|
|
143
|
+
if (!samePort) {
|
|
144
|
+
updateEnvFile(path.join(projectRoot, ".env.development"), wsPort);
|
|
145
|
+
updateEnvFile(path.join(projectRoot, ".env.production"), wsPort);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Install ws library
|
|
149
|
+
console.log(chalk.green("Installing ws..."));
|
|
150
|
+
execSync("npm install ws", { stdio: "inherit" });
|
|
151
|
+
|
|
152
|
+
console.log(chalk.green("WebSocket integration added successfully."));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Get the server port from environment variables, default to 3000 if undefined
|
|
156
|
+
const serverPort = process.env.PORT || 3000;
|
|
157
|
+
|
|
158
|
+
function askWebSocketPort() {
|
|
159
|
+
rl.question(
|
|
160
|
+
chalk.yellow("Enter a different WebSocket port (default: 3001): "),
|
|
161
|
+
(wsPortInput) => {
|
|
162
|
+
let wsPort = parseInt(wsPortInput.trim()) || 3001;
|
|
163
|
+
|
|
164
|
+
if (wsPort === serverPort) {
|
|
165
|
+
console.log(
|
|
166
|
+
chalk.red("WebSocket port cannot be the same as the server port.")
|
|
167
|
+
);
|
|
168
|
+
askWebSocketPort(); // Re-prompt if user enters the same port
|
|
169
|
+
} else {
|
|
170
|
+
addWebSocket(false, wsPort); // samePort = false, pass wsPort
|
|
171
|
+
rl.close();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function askUseSamePort() {
|
|
178
|
+
rl.question(
|
|
179
|
+
chalk.yellow(
|
|
180
|
+
"Do you want to use the server port for WebSocket? (Yes/No): "
|
|
181
|
+
),
|
|
182
|
+
(useSamePort) => {
|
|
183
|
+
const answer = useSamePort.trim().toLowerCase();
|
|
184
|
+
|
|
185
|
+
if (answer === "y" || answer === "yes") {
|
|
186
|
+
addWebSocket(true); // samePort = true, no wsPort needed
|
|
187
|
+
rl.close();
|
|
188
|
+
} else if (answer === "n" || answer === "no") {
|
|
189
|
+
askWebSocketPort(); // Ask for a different WebSocket port
|
|
190
|
+
} else {
|
|
191
|
+
console.log(chalk.red("Invalid input. Please enter Yes or No."));
|
|
192
|
+
askUseSamePort(); // Re-prompt if invalid input
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Start asking user input
|
|
199
|
+
askUseSamePort();
|