zyket 1.2.12 → 1.2.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zyket",
3
- "version": "1.2.12",
3
+ "version": "1.2.13",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -94,6 +94,10 @@ module.exports = class Express extends Service {
94
94
  }
95
95
  });
96
96
 
97
+ // In production, serve the built Vite frontend (static assets + SPA fallback).
98
+ // Registered after API routes so the API keeps precedence.
99
+ this.#serveFrontend();
100
+
97
101
  // Attach Express to HTTP server - this allows dynamic route registration
98
102
  this.#httpServer.removeAllListeners("request");
99
103
  this.#httpServer.on("request", this.#app);
@@ -101,6 +105,34 @@ module.exports = class Express extends Service {
101
105
  this.#container.get('logger').info(`Express is running on http://localhost:${httpServer.address().port}`);
102
106
  }
103
107
 
108
+ #serveFrontend() {
109
+ const viteEnabled = process.env.VITE_ROOT && process.env.DISABLE_VITE !== 'true';
110
+ if (process.env.NODE_ENV !== 'production' || !viteEnabled) return;
111
+
112
+ const vite = this.#container.has('vite') ? this.#container.get('vite') : null;
113
+ const distPath = vite?.outDir?.()
114
+ || path.resolve(process.cwd(), process.env.VITE_ROOT || 'frontend', 'dist');
115
+ const indexHtml = path.join(distPath, 'index.html');
116
+
117
+ if (!fs.existsSync(indexHtml)) {
118
+ this.#container.get('logger').warn(`Frontend build not found at ${distPath}. Skipping static serving.`);
119
+ return;
120
+ }
121
+
122
+ this.#app.use(express.static(distPath));
123
+
124
+ // SPA fallback: serve index.html for any GET not handled by API routes,
125
+ // except the Swagger docs path. Uses middleware (Express 5 dropped bare '*').
126
+ const docsPath = process.env.SWAGGER_PATH || '/docs';
127
+ this.#app.use((req, res, next) => {
128
+ if (req.method !== 'GET') return next();
129
+ if (req.path.startsWith(docsPath)) return next();
130
+ res.sendFile(indexHtml);
131
+ });
132
+
133
+ this.#container.get('logger').info(`Serving frontend from ${distPath}`);
134
+ }
135
+
104
136
  async registerRoutes(routes) {
105
137
  const methods = ['post', 'get', 'put', 'delete']
106
138
  for (const route of routes) {
@@ -14,9 +14,10 @@ const schedulerActivated = process.env.DISABLE_SCHEDULER !== 'true';
14
14
  const socketActivated = process.env.DISABLE_SOCKET !== 'true';
15
15
  const expressActivated = process.env.DISABLE_EXPRESS !== 'true';
16
16
  const viteActivated = process.env.VITE_ROOT && process.env.DISABLE_VITE !== 'true';
17
+ const loggerActivated = process.env.DISABLE_LOGGER !== 'true';
17
18
 
18
19
  module.exports = [
19
- ["logger", require("./logger"), ["@service_container", process.env.LOG_DIRECTORY || `${process.cwd()}/logs`, process.env.DEBUG === "true"]],
20
+ ["logger", require("./logger"), ["@service_container", process.env.LOG_DIRECTORY || `${process.cwd()}/logs`, process.env.DEBUG === "true", !loggerActivated]],
20
21
  ["template-manager", require("./template-manager"), []],
21
22
  eventsActivated ? ["events", EventService, ["@service_container"]] : null,
22
23
  databaseActivated ? ["database", Database, ["@service_container", process.env.DATABASE_URL]] : null,
@@ -25,6 +26,6 @@ module.exports = [
25
26
  schedulerActivated ? ["scheduler", Scheduler, ["@service_container"]] : null,
26
27
  bullmqActivated ? ["bullmq", require("./bullmq"), ["@service_container"]] : null,
27
28
  socketActivated ? ["socketio", SocketIO, ["@service_container"]] : null,
28
- expressActivated ? ["express", Express, ["@service_container"]] : null,
29
29
  viteActivated ? ["vite", require("./vite"), ["@service_container", process.env.VITE_ROOT, Number(process.env.VITE_PORT) || 5173]] : null,
30
+ expressActivated ? ["express", Express, ["@service_container"]] : null,
30
31
  ].filter(Boolean);
@@ -6,6 +6,7 @@ module.exports = class Logger extends Service {
6
6
  #container
7
7
  #logDirectory;
8
8
  #debugEnabled;
9
+ #disabled;
9
10
  messageColors = {
10
11
  log: "white",
11
12
  info: "green",
@@ -15,19 +16,22 @@ module.exports = class Logger extends Service {
15
16
  };
16
17
  #storeTries = 0;
17
18
 
18
- constructor(container, logDirectory, debugEnabled) {
19
+ constructor(container, logDirectory, debugEnabled, disabled = false) {
19
20
  super("logger");
20
21
  this.#container = container;
21
22
  this.#logDirectory = logDirectory;
22
23
  this.#debugEnabled = debugEnabled;
24
+ this.#disabled = disabled;
23
25
  }
24
26
 
25
27
  async boot() {
28
+ if (this.#disabled) return this;
26
29
  if (!fs.existsSync(this.#logDirectory)) fs.mkdirSync(this.#logDirectory);
27
30
  return this;
28
31
  }
29
32
 
30
33
  async store(message) {
34
+ if (this.#disabled) return;
31
35
  if (this.#storeTries > 10) throw new Error("Failed to store log message");
32
36
  this.#storeTries++;
33
37
  try{
@@ -0,0 +1,29 @@
1
+ const path = require("path");
2
+
3
+ /**
4
+ * Builds the Vite frontend for production using Vite's JS API.
5
+ *
6
+ * @param {Object} options
7
+ * @param {string} options.root Absolute path to the Vite project root.
8
+ * @param {string|false} options.configFile Resolved vite config file, or false.
9
+ * @returns {Promise<string>} Absolute path to the generated output directory.
10
+ */
11
+ module.exports = async function buildViteApp({ root, configFile } = {}) {
12
+ const { build, resolveConfig } = await import("vite");
13
+
14
+ // Resolve config first so we know the real outDir (respects custom config).
15
+ const resolved = await resolveConfig(
16
+ { root, configFile },
17
+ "build",
18
+ "production"
19
+ );
20
+ const outDir = path.resolve(root, resolved.build.outDir);
21
+
22
+ await build({
23
+ root,
24
+ configFile,
25
+ logLevel: "warn",
26
+ });
27
+
28
+ return outDir;
29
+ };
@@ -2,26 +2,40 @@ const Service = require("../Service");
2
2
  const path = require("path");
3
3
  const fs = require("fs");
4
4
  const EnvManager = require("../../utils/EnvManager");
5
+ const buildViteApp = require("./builder");
5
6
 
6
7
  module.exports = class Vite extends Service {
7
8
  #container;
8
9
  #viteServer;
9
10
  #root;
10
11
  #port;
12
+ #isProduction;
13
+ #outDir;
11
14
 
12
15
  constructor(container, root, port) {
13
16
  super("vite");
14
17
  this.#container = container;
15
18
  this.#root = path.resolve(process.cwd(), root || process.cwd());
16
19
  this.#port = port || 5173;
20
+ this.#isProduction = process.env.NODE_ENV === "production";
17
21
  }
18
22
 
19
23
  async boot() {
20
24
  await this.#loadViteFolder();
21
- const { createServer } = await import("vite");
22
25
 
23
26
  const configFile = this.#resolveConfigFile();
24
27
 
28
+ // In production we build the frontend once and let Express serve the
29
+ // generated static files. No dev server is started.
30
+ if (this.#isProduction) {
31
+ this.#container.get("logger").info("Building Vite frontend for production...");
32
+ this.#outDir = await buildViteApp({ root: this.#root, configFile });
33
+ this.#container.get("logger").info(`Vite build complete (output: ${this.#outDir})`);
34
+ return;
35
+ }
36
+
37
+ const { createServer } = await import("vite");
38
+
25
39
  this.#viteServer = await createServer({
26
40
  root: this.#root,
27
41
  configFile,
@@ -115,4 +129,12 @@ module.exports = class Vite extends Service {
115
129
  server() {
116
130
  return this.#viteServer;
117
131
  }
132
+
133
+ isProduction() {
134
+ return this.#isProduction;
135
+ }
136
+
137
+ outDir() {
138
+ return this.#outDir;
139
+ }
118
140
  };
@@ -31,6 +31,7 @@ module.exports = class EnvManager {
31
31
  S3_USE_SSL: true,
32
32
  S3_ACCESS_KEY: '',
33
33
  S3_SECRET_KEY: '',
34
+ DISABLE_LOGGER: false,
34
35
  LOG_DIRECTORY: "./logs",
35
36
  QUEUES: '',
36
37
  VITE_ROOT: './frontend',