4bnode 4.0.7 → 4.0.9

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/add-login.js CHANGED
@@ -197,12 +197,9 @@ router.post('/login', async (req, res) => {
197
197
  showFileAction("updated", `src/routes/${selectedRoute}`);
198
198
 
199
199
  const projectRoot = getProjectRoot();
200
- const devSecret = crypto.randomBytes(32).toString("hex");
201
- const prodSecret = crypto.randomBytes(32).toString("hex");
202
- updateEnvFile(path.join(projectRoot, ".env.development"), "JWT_SECRET", devSecret);
203
- showFileAction("updated", ".env.development");
204
- updateEnvFile(path.join(projectRoot, ".env.production"), "JWT_SECRET", prodSecret);
205
- showFileAction("updated", ".env.production");
200
+ const secret = crypto.randomBytes(32).toString("hex");
201
+ updateEnvFile(path.join(projectRoot, ".env"), "JWT_SECRET", secret);
202
+ showFileAction("updated", ".env");
206
203
 
207
204
  await installDeps(["jsonwebtoken", "bcrypt"]);
208
205
 
package/add-mongo.js CHANGED
@@ -17,19 +17,11 @@ import {
17
17
  } from "./lib/ui.js";
18
18
 
19
19
  function isDatabaseConfigured() {
20
- const projectRoot = getProjectRoot();
21
- const devEnvPath = path.join(projectRoot, ".env.development");
22
- const prodEnvPath = path.join(projectRoot, ".env.production");
23
- const hasDev =
24
- fs.existsSync(devEnvPath) &&
25
- fs.readFileSync(devEnvPath, "utf8").includes("MONGO_URI=");
26
- const hasProd =
27
- fs.existsSync(prodEnvPath) &&
28
- fs.readFileSync(prodEnvPath, "utf8").includes("MONGO_URI=");
29
- return hasDev || hasProd;
20
+ const envPath = path.join(getProjectRoot(), ".env");
21
+ return fs.existsSync(envPath) && fs.readFileSync(envPath, "utf8").includes("MONGO_URI=");
30
22
  }
31
23
 
32
- function addMongoDB(devUri, prodUri) {
24
+ function addMongoDB(uri) {
33
25
  const projectRoot = getProjectRoot();
34
26
 
35
27
  const dbContent = `import mongoose from 'mongoose';
@@ -65,10 +57,8 @@ export default connectDB;`;
65
57
  }
66
58
  showFileAction("updated", "index.js");
67
59
 
68
- updateEnvFile(path.join(projectRoot, ".env.development"), "MONGO_URI", devUri);
69
- showFileAction("updated", ".env.development");
70
- updateEnvFile(path.join(projectRoot, ".env.production"), "MONGO_URI", prodUri);
71
- showFileAction("updated", ".env.production");
60
+ updateEnvFile(path.join(projectRoot, ".env"), "MONGO_URI", uri);
61
+ showFileAction("updated", ".env");
72
62
  }
73
63
 
74
64
  async function main() {
@@ -128,32 +118,13 @@ async function main() {
128
118
  devUri = devUri.trim();
129
119
  }
130
120
 
131
- const useSameForProd = await confirm({
132
- message: "Use the same URI for production?",
133
- default: true,
134
- });
135
-
136
- let prodUri = devUri;
137
- if (!useSameForProd) {
138
- prodUri = await input({
139
- message: "Production MongoDB URI:",
140
- default: devUri,
141
- validate: (v) =>
142
- v.trim().startsWith("mongodb://") || v.trim().startsWith("mongodb+srv://")
143
- ? true
144
- : "Must start with mongodb:// or mongodb+srv://",
145
- });
146
- prodUri = prodUri.trim();
147
- }
148
-
149
121
  console.log();
150
- addMongoDB(devUri, prodUri);
122
+ addMongoDB(devUri);
151
123
 
152
124
  await installDeps("mongoose");
153
125
 
154
126
  showTaskDone("MongoDB integration complete", [
155
- `Dev: ${devUri}`,
156
- `Prod: ${prodUri}`,
127
+ `URI: ${devUri}`,
157
128
  ]);
158
129
  }
159
130
 
package/add-serialport.js CHANGED
@@ -56,12 +56,9 @@ try {
56
56
  insertBeforeListen(serialportSetup);
57
57
 
58
58
  const projectRoot = getProjectRoot();
59
- updateEnvFile(path.join(projectRoot, ".env.development"), "SERIALPORT_PATH", serialPortPath);
60
- updateEnvFile(path.join(projectRoot, ".env.development"), "BAUDRATE", baudRate);
61
- updateEnvFile(path.join(projectRoot, ".env.production"), "SERIALPORT_PATH", serialPortPath);
62
- updateEnvFile(path.join(projectRoot, ".env.production"), "BAUDRATE", baudRate);
63
- showFileAction("updated", ".env.development");
64
- showFileAction("updated", ".env.production");
59
+ updateEnvFile(path.join(projectRoot, ".env"), "SERIALPORT_PATH", serialPortPath);
60
+ updateEnvFile(path.join(projectRoot, ".env"), "BAUDRATE", baudRate);
61
+ showFileAction("updated", ".env");
65
62
 
66
63
  await installDeps(["serialport", "@serialport/parser-readline"]);
67
64
 
package/add-websocket.js CHANGED
@@ -105,10 +105,8 @@ server.listen(port, () => {
105
105
 
106
106
  if (!samePort) {
107
107
  const projectRoot = getProjectRoot();
108
- updateEnvFile(path.join(projectRoot, ".env.development"), "WS_PORT", wsPort);
109
- updateEnvFile(path.join(projectRoot, ".env.production"), "WS_PORT", wsPort);
110
- showFileAction("updated", ".env.development");
111
- showFileAction("updated", ".env.production");
108
+ updateEnvFile(path.join(projectRoot, ".env"), "WS_PORT", wsPort);
109
+ showFileAction("updated", ".env");
112
110
  }
113
111
 
114
112
  await installDeps("ws");
package/lib/project.js CHANGED
@@ -105,7 +105,7 @@ export function getModelSchemaDetailed(modelName) {
105
105
  }
106
106
 
107
107
  export function checkMongoConfig() {
108
- const envPath = path.join(getProjectRoot(), ".env.development");
108
+ const envPath = path.join(getProjectRoot(), ".env");
109
109
  return (
110
110
  fs.existsSync(envPath) &&
111
111
  fs.readFileSync(envPath, "utf8").includes("MONGO_URI")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "4bnode",
3
- "version": "4.0.7",
3
+ "version": "4.0.9",
4
4
  "description": "4bnode is a CLI-powered backend development platform with a built-in visual dashboard to generate, manage, and test Node.js/Express APIs faster.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -1,33 +1,27 @@
1
1
  import React, { useState, useEffect } from "react";
2
- import { motion, AnimatePresence } from "framer-motion";
2
+ import { motion } from "framer-motion";
3
3
  import { html, api } from "../lib.js";
4
4
  import { useApp } from "../context.js";
5
5
 
6
6
  export function EnvironmentPage() {
7
7
  const { toast } = useApp();
8
- const [selected, setSelected] = useState("development");
9
- const [devContent, setDevContent] = useState("");
10
- const [prodContent, setProdContent] = useState("");
8
+ const [content, setContent] = useState("");
11
9
  const [loading, setLoading] = useState(true);
12
10
  const [saving, setSaving] = useState(false);
13
- const [dirty, setDirty] = useState({ development: false, production: false });
11
+ const [dirty, setDirty] = useState(false);
14
12
 
15
13
  useEffect(() => {
16
14
  api("/env")
17
15
  .then((res) => {
18
- setDevContent(res.development || "");
19
- setProdContent(res.production || "");
16
+ setContent(res.content || "");
20
17
  setLoading(false);
21
18
  })
22
19
  .catch(() => setLoading(false));
23
20
  }, []);
24
21
 
25
- const currentContent = selected === "development" ? devContent : prodContent;
26
- const setCurrentContent = selected === "development" ? setDevContent : setProdContent;
27
-
28
22
  function handleChange(e) {
29
- setCurrentContent(e.target.value);
30
- setDirty((d) => ({ ...d, [selected]: true }));
23
+ setContent(e.target.value);
24
+ setDirty(true);
31
25
  }
32
26
 
33
27
  async function handleSave() {
@@ -35,26 +29,21 @@ export function EnvironmentPage() {
35
29
  try {
36
30
  const result = await api("/env", {
37
31
  method: "PUT",
38
- body: { environment: selected, content: currentContent },
32
+ body: { content },
39
33
  });
40
34
  toast(result.message, "success");
41
- setDirty((d) => ({ ...d, [selected]: false }));
35
+ setDirty(false);
42
36
  } catch (err) {
43
37
  toast(err.message, "error");
44
38
  }
45
39
  setSaving(false);
46
40
  }
47
41
 
48
- function countVars(content) {
49
- if (!content) return 0;
50
- return content.split('\n').filter(l => l.trim() && !l.trim().startsWith('#') && l.includes('=')).length;
42
+ function countVars(c) {
43
+ if (!c) return 0;
44
+ return c.split('\n').filter(l => l.trim() && !l.trim().startsWith('#') && l.includes('=')).length;
51
45
  }
52
46
 
53
- const files = [
54
- { id: "development", label: ".env.development", content: devContent },
55
- { id: "production", label: ".env.production", content: prodContent },
56
- ];
57
-
58
47
  return html`<div>
59
48
  <div className="flex items-center justify-between mb-4">
60
49
  <div>
@@ -63,79 +52,52 @@ export function EnvironmentPage() {
63
52
  </div>
64
53
  </div>
65
54
 
66
- <div className="routes-layout">
67
- <div className="card" style=${{ padding: 0, overflow: 'hidden' }}>
68
- <div style=${{ padding: '14px 16px 10px', borderBottom: '1px solid var(--border)' }}>
69
- <span style=${{ fontWeight: 600, fontSize: '12px', textTransform: 'uppercase', letterSpacing: '0.5px', color: 'var(--text-muted)' }}>Files</span>
55
+ <div className="card mb-4">
56
+ <div className="flex items-center justify-between mb-3">
57
+ <div>
58
+ <div style=${{ fontWeight: 600, fontSize: '16px', marginBottom: '2px' }}>.env</div>
59
+ <div className="text-sm text-muted">${() => countVars(content)} variable${() => countVars(content) !== 1 ? 's' : ''} defined</div>
70
60
  </div>
71
- <div className="routes-list">
72
- ${files.map(f => html`<div key=${f.id}
73
- className=${`route-item ${selected === f.id ? 'active' : ''}`}
74
- onClick=${() => setSelected(f.id)}>
75
- <div style=${{ display: 'flex', flexDirection: 'column', gap: '4px', flex: 1, minWidth: 0 }}>
76
- <span className="ri-name">${f.label}</span>
77
- <span className="ri-file">${countVars(f.content)} variable${countVars(f.content) !== 1 ? 's' : ''}</span>
78
- </div>
79
- ${dirty[f.id] && html`<span className="badge badge-warning" style=${{ fontSize: '10px' }}>Modified</span>`}
80
- </div>`)}
61
+ <div className="flex gap-2 items-center">
62
+ ${() => dirty && html`<span className="text-sm text-muted" style=${{ fontSize: '11px' }}>Unsaved</span>`}
63
+ <button className="btn btn-sm btn-primary" onClick=${handleSave} disabled=${saving || !dirty}>
64
+ ${saving ? html`<span className="spinner" />` : null}
65
+ ${saving ? "Saving..." : "Save"}
66
+ </button>
81
67
  </div>
82
68
  </div>
69
+ </div>
83
70
 
84
- <div className="routes-detail">
85
- <div className="card mb-4">
86
- <div className="flex items-center justify-between mb-3">
87
- <div>
88
- <div style=${{ fontWeight: 600, fontSize: '16px', marginBottom: '2px' }}>.env.${selected}</div>
89
- <div className="text-sm text-muted">${countVars(currentContent)} variable${countVars(currentContent) !== 1 ? 's' : ''} defined</div>
90
- </div>
91
- <div className="flex gap-2 items-center">
92
- ${dirty[selected] && html`<span className="text-sm text-muted" style=${{ fontSize: '11px' }}>Unsaved</span>`}
93
- <button className="btn btn-sm btn-primary" onClick=${handleSave} disabled=${saving || !dirty[selected]}>
94
- ${saving ? html`<span className="spinner" />` : null}
95
- ${saving ? "Saving..." : "Save"}
96
- </button>
97
- </div>
98
- </div>
99
- </div>
100
-
101
- <div className="card" style=${{ padding: 0, overflow: 'hidden' }}>
102
- <div style=${{ padding: '12px 16px', borderBottom: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
103
- <span style=${{ fontSize: '12px', fontWeight: 600, color: 'var(--text-secondary)' }}>Editor</span>
104
- <span className="field-count" style=${{ fontSize: '11px' }}>Cmd+S to save</span>
105
- </div>
106
- <${AnimatePresence} mode="wait">
107
- <${motion.div} key=${selected}
108
- initial=${{ opacity: 0, y: 6 }} animate=${{ opacity: 1, y: 0 }}
109
- exit=${{ opacity: 0, y: -6 }} transition=${{ duration: 0.12 }}>
110
- ${loading
111
- ? html`<div className="text-sm text-muted" style=${{ padding: '40px 0', textAlign: 'center' }}>Loading...</div>`
112
- : html`<textarea
113
- className="env-editor"
114
- value=${currentContent}
115
- onInput=${handleChange}
116
- placeholder=${"# Add environment variables\n# KEY=value"}
117
- spellCheck=${false}
118
- style=${{ borderRadius: 0, border: 'none' }}
119
- onKeyDown=${(e) => {
120
- if (e.key === "Tab") {
121
- e.preventDefault();
122
- const start = e.target.selectionStart;
123
- const end = e.target.selectionEnd;
124
- const val = e.target.value;
125
- setCurrentContent(val.substring(0, start) + " " + val.substring(end));
126
- setTimeout(() => { e.target.selectionStart = e.target.selectionEnd = start + 2; }, 0);
127
- }
128
- if ((e.metaKey || e.ctrlKey) && e.key === "s") {
129
- e.preventDefault();
130
- if (dirty[selected]) handleSave();
131
- }
132
- }}
133
- />`}
134
- <//>
135
- <//>
136
- </div>
71
+ <div className="card" style=${{ padding: 0, overflow: 'hidden' }}>
72
+ <div style=${{ padding: '12px 16px', borderBottom: '1px solid var(--border)', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
73
+ <span style=${{ fontSize: '12px', fontWeight: 600, color: 'var(--text-secondary)' }}>Editor</span>
74
+ <span className="field-count" style=${{ fontSize: '11px' }}>Cmd+S to save</span>
137
75
  </div>
76
+ <${motion.div} initial=${{ opacity: 0, y: 6 }} animate=${{ opacity: 1, y: 0 }} transition=${{ duration: 0.12 }}>
77
+ ${loading
78
+ ? html`<div className="text-sm text-muted" style=${{ padding: '40px 0', textAlign: 'center' }}>Loading...</div>`
79
+ : html`<textarea
80
+ className="env-editor"
81
+ value=${content}
82
+ onInput=${handleChange}
83
+ placeholder=${"# Add environment variables\n# KEY=value"}
84
+ spellCheck=${false}
85
+ style=${{ borderRadius: 0, border: 'none' }}
86
+ onKeyDown=${(e) => {
87
+ if (e.key === "Tab") {
88
+ e.preventDefault();
89
+ const start = e.target.selectionStart;
90
+ const end = e.target.selectionEnd;
91
+ setContent(e.target.value.substring(0, start) + " " + e.target.value.substring(end));
92
+ setTimeout(() => { e.target.selectionStart = e.target.selectionEnd = start + 2; }, 0);
93
+ }
94
+ if ((e.metaKey || e.ctrlKey) && e.key === "s") {
95
+ e.preventDefault();
96
+ if (dirty) handleSave();
97
+ }
98
+ }}
99
+ />`}
100
+ <//>
138
101
  </div>
139
102
  </div>`;
140
103
  }
141
-
@@ -134,7 +134,7 @@ function listRoutes() {
134
134
  }
135
135
 
136
136
  function checkMongoConfig() {
137
- const envPath = path.join(process.cwd(), ".env.development");
137
+ const envPath = path.join(process.cwd(), ".env");
138
138
  return (
139
139
  fs.existsSync(envPath) &&
140
140
  fs.readFileSync(envPath, "utf8").includes("MONGO_URI")
@@ -485,15 +485,12 @@ router.get('/api/dashboard', (req, res) => {
485
485
 
486
486
  const authConfigured = fs.existsSync(path.join(process.cwd(), 'src', 'middleware', 'auth.js'));
487
487
 
488
- let mongoUri = '', mongoProdUri = '';
488
+ let mongoUri = '';
489
489
  if (mongoConfigured) {
490
- const devEnv = path.join(process.cwd(), '.env.development');
491
- const prodEnv = path.join(process.cwd(), '.env.production');
492
- mongoUri = getEnvValue(devEnv, 'MONGO_URI');
493
- mongoProdUri = getEnvValue(prodEnv, 'MONGO_URI');
490
+ mongoUri = getEnvValue(path.join(process.cwd(), '.env'), 'MONGO_URI');
494
491
  }
495
492
 
496
- res.json({ models, routes, mongoConfigured, dbType, routesDirExists, authConfigured, mongoUri, mongoProdUri });
493
+ res.json({ models, routes, mongoConfigured, dbType, routesDirExists, authConfigured, mongoUri });
497
494
  } catch (err) {
498
495
  res.status(500).json({ error: err.message });
499
496
  }
@@ -539,8 +536,7 @@ router.get('/api/models/:name/data', async (req, res) => {
539
536
 
540
537
  // Connect if not already connected
541
538
  if (mongoose.connection.readyState === 0) {
542
- const devEnv = path.join(process.cwd(), '.env.development');
543
- const uri = getEnvValue(devEnv, 'MONGO_URI');
539
+ const uri = getEnvValue(path.join(process.cwd(), '.env'), 'MONGO_URI');
544
540
  if (!uri) return res.status(400).json({ error: 'MONGO_URI not configured.' });
545
541
  await mongoose.connect(uri);
546
542
  }
@@ -896,10 +892,8 @@ router.post('/api/routes/:name/add-method', async (req, res) => {
896
892
  }
897
893
 
898
894
  // Ensure JWT_SECRET
899
- const devEnv = path.join(process.cwd(), '.env.development');
900
- const prodEnv = path.join(process.cwd(), '.env.production');
901
- if (fs.existsSync(devEnv) && !fs.readFileSync(devEnv, 'utf8').includes('JWT_SECRET')) updateEnvFile(devEnv, 'JWT_SECRET', crypto.randomBytes(32).toString('hex'));
902
- if (fs.existsSync(prodEnv) && !fs.readFileSync(prodEnv, 'utf8').includes('JWT_SECRET')) updateEnvFile(prodEnv, 'JWT_SECRET', crypto.randomBytes(32).toString('hex'));
895
+ const envFile = path.join(process.cwd(), '.env');
896
+ if (!fs.existsSync(envFile) || !fs.readFileSync(envFile, 'utf8').includes('JWT_SECRET')) updateEnvFile(envFile, 'JWT_SECRET', crypto.randomBytes(32).toString('hex'));
903
897
 
904
898
  } else if (routeType === 'register' && modelName) {
905
899
  addImport(`import ${pascalModel} from '../models/${modelName}.js';`);
@@ -917,10 +911,8 @@ router.post('/api/routes/:name/add-method', async (req, res) => {
917
911
  handlers += `\n// Register\nrouter.post('/', async (req, res) => {\n try {\n const { ${allDestructured} } = req.body;\n const existing = await ${pascalModel}.findOne({ ${findField} });\n if (existing) return res.status(400).json({ message: '${pascalModel} already exists' });\n\n const item = new ${pascalModel}({ ${allDestructured} });\n const saved = await item.save();\n\n const token = jwt.sign({ id: saved._id }, process.env.JWT_SECRET, { expiresIn: '7d' });\n res.status(201).json({ token, ${modelName}: { id: saved._id, ${findField}: saved.${findField} } });\n } catch (err) {\n res.status(400).json({ message: err.message });\n }\n});\n`;
918
912
  }
919
913
 
920
- const devEnv = path.join(process.cwd(), '.env.development');
921
- const prodEnv = path.join(process.cwd(), '.env.production');
922
- if (fs.existsSync(devEnv) && !fs.readFileSync(devEnv, 'utf8').includes('JWT_SECRET')) updateEnvFile(devEnv, 'JWT_SECRET', crypto.randomBytes(32).toString('hex'));
923
- if (fs.existsSync(prodEnv) && !fs.readFileSync(prodEnv, 'utf8').includes('JWT_SECRET')) updateEnvFile(prodEnv, 'JWT_SECRET', crypto.randomBytes(32).toString('hex'));
914
+ const envFile = path.join(process.cwd(), '.env');
915
+ if (!fs.existsSync(envFile) || !fs.readFileSync(envFile, 'utf8').includes('JWT_SECRET')) updateEnvFile(envFile, 'JWT_SECRET', crypto.randomBytes(32).toString('hex'));
924
916
  }
925
917
 
926
918
  if (!handlers) {
@@ -1137,11 +1129,9 @@ router.post('/api/crud/generate', (req, res) => {
1137
1129
  // GET /api/env
1138
1130
  router.get('/api/env', (req, res) => {
1139
1131
  try {
1140
- const devEnv = path.join(process.cwd(), '.env.development');
1141
- const prodEnv = path.join(process.cwd(), '.env.production');
1142
- const devContent = fs.existsSync(devEnv) ? fs.readFileSync(devEnv, 'utf8') : '';
1143
- const prodContent = fs.existsSync(prodEnv) ? fs.readFileSync(prodEnv, 'utf8') : '';
1144
- res.json({ development: devContent, production: prodContent });
1132
+ const envPath = path.join(process.cwd(), '.env');
1133
+ const content = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf8') : '';
1134
+ res.json({ content });
1145
1135
  } catch (err) {
1146
1136
  res.status(500).json({ error: err.message });
1147
1137
  }
@@ -1150,13 +1140,10 @@ router.get('/api/env', (req, res) => {
1150
1140
  // PUT /api/env
1151
1141
  router.put('/api/env', (req, res) => {
1152
1142
  try {
1153
- const { environment, content } = req.body;
1154
- if (!environment || !['development', 'production'].includes(environment)) {
1155
- return res.status(400).json({ error: 'Invalid environment. Must be "development" or "production".' });
1156
- }
1157
- const envPath = path.join(process.cwd(), `.env.${environment}`);
1143
+ const { content } = req.body;
1144
+ const envPath = path.join(process.cwd(), '.env');
1158
1145
  fs.writeFileSync(envPath, content, 'utf8');
1159
- res.json({ message: `Environment file (.env.${environment}) updated.` });
1146
+ res.json({ message: '.env updated.' });
1160
1147
  } catch (err) {
1161
1148
  res.status(500).json({ error: err.message });
1162
1149
  }
@@ -1670,15 +1657,9 @@ export default router;
1670
1657
 
1671
1658
  // Ensure JWT_SECRET exists in env files for login/register routes
1672
1659
  if (type === 'login' || type === 'register') {
1673
- const devEnv = path.join(process.cwd(), '.env.development');
1674
- const prodEnv = path.join(process.cwd(), '.env.production');
1675
- const devContent = fs.existsSync(devEnv) ? fs.readFileSync(devEnv, 'utf8') : '';
1676
- const prodContent = fs.existsSync(prodEnv) ? fs.readFileSync(prodEnv, 'utf8') : '';
1677
- if (!devContent.includes('JWT_SECRET')) {
1678
- updateEnvFile(devEnv, 'JWT_SECRET', crypto.randomBytes(32).toString('hex'));
1679
- }
1680
- if (!prodContent.includes('JWT_SECRET')) {
1681
- updateEnvFile(prodEnv, 'JWT_SECRET', crypto.randomBytes(32).toString('hex'));
1660
+ const envFile = path.join(process.cwd(), '.env');
1661
+ if (!fs.existsSync(envFile) || !fs.readFileSync(envFile, 'utf8').includes('JWT_SECRET')) {
1662
+ updateEnvFile(envFile, 'JWT_SECRET', crypto.randomBytes(32).toString('hex'));
1682
1663
  }
1683
1664
  }
1684
1665
 
@@ -1713,24 +1694,19 @@ export default router;
1713
1694
  router.post('/api/add-mongo', async (req, res) => {
1714
1695
  try {
1715
1696
  const uri = (req.body.uri || '').trim();
1716
- const prodUri = (req.body.prodUri || '').trim();
1717
1697
  const alreadyConfigured = checkMongoConfig();
1718
1698
 
1719
1699
  if (!uri) {
1720
1700
  return res.status(400).json({ error: 'MongoDB URI is required' });
1721
1701
  }
1722
1702
 
1723
- // Update env files
1724
- const devEnv = path.join(process.cwd(), '.env.development');
1725
- const prodEnv = path.join(process.cwd(), '.env.production');
1726
- updateEnvFile(devEnv, 'MONGO_URI', uri);
1727
- updateEnvFile(prodEnv, 'MONGO_URI', prodUri || uri);
1703
+ updateEnvFile(path.join(process.cwd(), '.env'), 'MONGO_URI', uri);
1728
1704
 
1729
1705
  if (alreadyConfigured) {
1730
- // Edit mode — just update env files, no need to reinstall or modify index.js
1706
+ // Edit mode — just update .env, no need to reinstall or modify index.js
1731
1707
  res.json({
1732
1708
  message: 'MongoDB connection updated. Restart the server to apply changes.',
1733
- files: ['.env.development', '.env.production']
1709
+ files: ['.env']
1734
1710
  });
1735
1711
  return;
1736
1712
  }
@@ -1751,7 +1727,7 @@ const connectDB = async () => {
1751
1727
  console.log(\`MongoDB connected: \${conn.connection.host}\`);
1752
1728
  } catch (err) {
1753
1729
  console.error('MongoDB connection error:', err.message);
1754
- console.error('Check your MONGO_URI in .env.development and ensure MongoDB is running.');
1730
+ console.error('Check your MONGO_URI in .env and ensure MongoDB is running.');
1755
1731
  }
1756
1732
  };
1757
1733
 
@@ -1762,7 +1738,7 @@ export default connectDB;
1762
1738
  // Send response BEFORE modifying index.js (nodemon restart kills connection)
1763
1739
  res.json({
1764
1740
  message: 'MongoDB setup complete.',
1765
- files: ['src/db.js', '.env.development', '.env.production', 'index.js']
1741
+ files: ['src/db.js', '.env', 'index.js']
1766
1742
  });
1767
1743
 
1768
1744
  // Modify index.js LAST (triggers nodemon restart)
@@ -1916,15 +1892,10 @@ export default router;
1916
1892
  }
1917
1893
  }
1918
1894
 
1919
- // Generate JWT secrets and update env files
1920
- const devSecret = crypto.randomBytes(32).toString('hex');
1921
- const prodSecret = crypto.randomBytes(32).toString('hex');
1922
- const devEnv = path.join(process.cwd(), '.env.development');
1923
- const prodEnv = path.join(process.cwd(), '.env.production');
1924
- updateEnvFile(devEnv, 'JWT_SECRET', devSecret);
1925
- updateEnvFile(prodEnv, 'JWT_SECRET', prodSecret);
1895
+ // Generate JWT secret and update .env
1896
+ updateEnvFile(path.join(process.cwd(), '.env'), 'JWT_SECRET', crypto.randomBytes(32).toString('hex'));
1926
1897
 
1927
- const filesModified = ['src/middleware/auth.js', `src/routes/${routeFile}`, '.env.development', '.env.production'];
1898
+ const filesModified = ['src/middleware/auth.js', `src/routes/${routeFile}`, '.env'];
1928
1899
  if (tokenExpiry) filesModified.push('src/routes/refresh-token.js');
1929
1900
 
1930
1901
  // Send response BEFORE index.js modifications (nodemon restart kills connection)
@@ -2218,18 +2189,15 @@ router.post('/api/add-serialport', async (req, res) => {
2218
2189
  await execAsync('npm install serialport @serialport/parser-readline', { cwd: process.cwd() });
2219
2190
  }
2220
2191
 
2221
- // Update env files
2222
- const devEnv = path.join(process.cwd(), '.env.development');
2223
- const prodEnv = path.join(process.cwd(), '.env.production');
2224
- updateEnvFile(devEnv, envPathKey, portPath);
2225
- updateEnvFile(devEnv, envBaudKey, String(baud));
2226
- updateEnvFile(prodEnv, envPathKey, portPath);
2227
- updateEnvFile(prodEnv, envBaudKey, String(baud));
2192
+ // Update .env
2193
+ const envFile = path.join(process.cwd(), '.env');
2194
+ updateEnvFile(envFile, envPathKey, portPath);
2195
+ updateEnvFile(envFile, envBaudKey, String(baud));
2228
2196
 
2229
2197
  // Send response BEFORE modifying index.js
2230
2198
  res.json({
2231
2199
  message: 'Serial port "' + name + '" added.',
2232
- files: ['index.js', '.env.development', '.env.production']
2200
+ files: ['index.js', '.env']
2233
2201
  });
2234
2202
 
2235
2203
  // Modify index.js LAST
@@ -2295,14 +2263,12 @@ router.delete('/api/serialports/:name', (req, res) => {
2295
2263
  // Remove env vars
2296
2264
  const envPathKey = name.toUpperCase() + '_PATH';
2297
2265
  const envBaudKey = name.toUpperCase() + '_BAUD';
2298
- for (const envFile of ['.env.development', '.env.production']) {
2299
- const envPath = path.join(process.cwd(), envFile);
2300
- if (fs.existsSync(envPath)) {
2301
- let envContent = fs.readFileSync(envPath, 'utf8');
2302
- envContent = envContent.replace(new RegExp(`^${envPathKey}=.*\\n?`, 'm'), '');
2303
- envContent = envContent.replace(new RegExp(`^${envBaudKey}=.*\\n?`, 'm'), '');
2304
- fs.writeFileSync(envPath, envContent, 'utf8');
2305
- }
2266
+ const envFilePath = path.join(process.cwd(), '.env');
2267
+ if (fs.existsSync(envFilePath)) {
2268
+ let envContent = fs.readFileSync(envFilePath, 'utf8');
2269
+ envContent = envContent.replace(new RegExp(`^${envPathKey}=.*\\n?`, 'm'), '');
2270
+ envContent = envContent.replace(new RegExp(`^${envBaudKey}=.*\\n?`, 'm'), '');
2271
+ fs.writeFileSync(envFilePath, envContent, 'utf8');
2306
2272
  }
2307
2273
 
2308
2274
  res.json({ message: 'Serial port "' + name + '" removed.' });
@@ -2636,8 +2602,7 @@ router.post('/api/reports/preview', async (req, res) => {
2636
2602
  if (!fs.existsSync(projectMongoose)) return res.status(400).json({ error: 'Mongoose not installed.' });
2637
2603
  const mongoose = (await import(pathToFileURL(projectMongoose).href)).default;
2638
2604
  if (mongoose.connection.readyState === 0) {
2639
- const devEnv = path.join(process.cwd(), '.env.development');
2640
- const uri = getEnvValue(devEnv, 'MONGO_URI');
2605
+ const uri = getEnvValue(path.join(process.cwd(), '.env'), 'MONGO_URI');
2641
2606
  if (!uri) return res.status(400).json({ error: 'MONGO_URI not configured.' });
2642
2607
  await mongoose.connect(uri);
2643
2608
  }
@@ -2753,8 +2718,7 @@ router.post('/api/reports/generate', async (req, res) => {
2753
2718
  }
2754
2719
  const mongoose = (await import(pathToFileURL(projectMongoose).href)).default;
2755
2720
  if (mongoose.connection.readyState === 0) {
2756
- const devEnv = path.join(process.cwd(), '.env.development');
2757
- const uri = getEnvValue(devEnv, 'MONGO_URI');
2721
+ const uri = getEnvValue(path.join(process.cwd(), '.env'), 'MONGO_URI');
2758
2722
  if (!uri) return res.status(400).json({ error: 'MONGO_URI not configured.' });
2759
2723
  await mongoose.connect(uri);
2760
2724
  }
@@ -17,3 +17,7 @@ yarn-error.log*
17
17
  .vscode/
18
18
  .idea/
19
19
  *.swp
20
+
21
+ #Lock file
22
+ package-lock.json
23
+