From 750e6948b67676b374fad949e7c3c86c43512335 Mon Sep 17 00:00:00 2001 From: Blake Matthes Date: Sat, 6 Sep 2025 17:01:19 -0500 Subject: [PATCH] refactor(controller): converted to socket.io --- app/src/main.ts | 47 +++++++------------- controller/build_app.go | 38 +++++++++++----- controller/build_channel.go | 89 +++++++++++++++++++++++++++++++++++++ controller/build_v2app.go | 53 +++++++++++++++------- 4 files changed, 170 insertions(+), 57 deletions(-) create mode 100644 controller/build_channel.go diff --git a/app/src/main.ts b/app/src/main.ts index c3c945d..cc99bcc 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -3,16 +3,16 @@ import morgan from "morgan"; import { createServer } from "http"; import { setupRoutes } from "./internal/routerHandler/routeHandler.js"; import { printers } from "./internal/ocp/printers/printers.js"; -import path, { dirname, join } from "path"; +import { dirname, join } from "path"; import { fileURLToPath } from "url"; import { db } from "./pkg/db/db.js"; -import { settings, type Setting } from "./pkg/db/schema/settings.js"; +import { settings } from "./pkg/db/schema/settings.js"; import { validateEnv } from "./pkg/utils/envValidator.js"; import { createLogger } from "./pkg/logger/logger.js"; import { returnFunc } from "./pkg/utils/return.js"; -import { closePool, initializeProdPool } from "./pkg/prodSql/prodSqlConnect.js"; +import { initializeProdPool } from "./pkg/prodSql/prodSqlConnect.js"; import { tryCatch } from "./pkg/utils/tryCatch.js"; -import os, { hostname } from "os"; +import os from "os"; import { sendNotify } from "./pkg/utils/notify.js"; const main = async () => { @@ -24,10 +24,6 @@ const main = async () => { // base path let basePath: string = ""; - if (process.env.NODE_ENV?.trim() !== "production") { - basePath = "/lst"; - } - const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -47,14 +43,14 @@ const main = async () => { if (res.data.length === 0) { //return - returnFunc({ - success: false, - module: "system", - level: "fatal", - message: `This seems to be the first time you have started the app please validate the settings have been intiated`, - notify: false, - data: [], - }); + // returnFunc({ + // success: false, + // module: "system", + // level: "fatal", + // message: `This seems to be the first time you have started the app please validate the settings have been intiated`, + // notify: false, + // data: [], + // }); } // connect to the prod sql @@ -64,22 +60,22 @@ const main = async () => { const app = express(); // global middleware - app.use(express.static(path.join(__dirname, "../lstDocs/build"))); - app.use(express.static(path.join(__dirname, "../frontend/dist"))); + app.use(express.json()); // global env that run only in dev if (process.env.NODE_ENV?.trim() !== "production") { app.use(morgan("tiny")); + basePath = "/lst"; } // docs and api stuff app.use( basePath + "/d", - express.static(join(__dirname, "../lstDocs/build")) + express.static(join(__dirname, "../../lstDocs/build")) ); app.use( basePath + "/app", - express.static(join(__dirname, "../frontend/dist")) + express.static(join(__dirname, "../../frontend/dist")) ); // register app @@ -129,7 +125,7 @@ const main = async () => { }); } - process.exit(1); + //process.exit(1); }); // setInterval(() => { @@ -145,12 +141,3 @@ const main = async () => { }; main(); - -// .catch((err) => { -// const log = createLogger({ module: "system", subModule: "main" }); -// log.fatal( -// { notify: true }, -// "There was a crash that occured and caused the app to restart." -// ); -// process.exit(1); -// }); diff --git a/controller/build_app.go b/controller/build_app.go index c56a791..2c232d4 100644 --- a/controller/build_app.go +++ b/controller/build_app.go @@ -3,15 +3,33 @@ package main import ( "os" "os/exec" + + socketio "github.com/googollee/go-socket.io" ) -// ---- Run npm build ---- -func runNpmBuild() error { - cmd := exec.Command("npm", "run", "build") - if os.Getenv("RUNNING_IN_DOCKER") == "true" { - cmd.Dir = "/app" - } - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return cmd.Run() -} \ No newline at end of file +// runNpmBuild runs npm build and streams logs into the "build" room +func runNpmBuild(server *socketio.Server) error { + cmd := exec.Command("npm", "run", "build") + if os.Getenv("RUNNING_IN_DOCKER") == "true" { + cmd.Dir = "/app" + } + + stdout, err := cmd.StdoutPipe() + if err != nil { + return err + } + stderr, err := cmd.StderrPipe() + if err != nil { + return err + } + + if err := cmd.Start(); err != nil { + return err + } + + // Launch goroutines to forward output + go streamOutput(stdout, server, "build") + go streamOutput(stderr, server, "build") + + return cmd.Wait() // Wait until process finishes +} diff --git a/controller/build_channel.go b/controller/build_channel.go new file mode 100644 index 0000000..1c58d3e --- /dev/null +++ b/controller/build_channel.go @@ -0,0 +1,89 @@ +package main + +import ( + "fmt" + "log" + "os" + "path/filepath" + "strings" + + socketio "github.com/googollee/go-socket.io" +) + +func registerBuildChannel(server *socketio.Server) { + // Example: When clients join "build" namespace or room + server.OnEvent("/", "subscribe:build", func(s socketio.Conn) { + s.Join("build") + s.Emit("buildlogs", "👋 Connected to build channel") // this is where all the messages are actually sent to + + }) + + // update channel events + server.OnEvent("/", "build", func(s socketio.Conn, target string) { + fmt.Println("🔨 Build triggered:", target) + + go func() { + host, err := os.Hostname() + if err != nil { + server.BroadcastToRoom("/", "build", "buildlogs", "Could not retrieve hostname") + return + } + + if strings.Contains(host, "VMS") || strings.Contains(host, "vms") { + server.BroadcastToRoom("/", "build", "buildlogs", "You are not allowed to run the build on a production server") + return + } + + server.BroadcastToRoom("/", "build", "buildlogs", "🔨 Starting build: Old App") + if err := runNpmV2Build(server); err != nil { + server.BroadcastToRoom("/", "build", "buildlogs", fmt.Sprintf("❌ Build failed: %v", err)) + return + + } + + server.BroadcastToRoom("/", "build", "buildlogs", "🔨 Starting build: App") + if err := runNpmBuild(server); err != nil { + server.BroadcastToRoom("/", "build", "buildlogs", fmt.Sprintf("❌ Build failed: %v", err)) + return + } + + server.BroadcastToRoom("/", "build", "buildlogs", "Zipping the app up") + buildNum, err := bumpBuild() + if err != nil { + server.BroadcastToRoom("/", "build", "buildlogs", fmt.Sprintf("failed updating build counter, details: %v", err.Error())) + return + } + + // run the zip + includes, _ := loadIncludePatterns("../.include") + + // Name the archive after build number if available + data, _ := os.ReadFile("../.build") + buildNum1 := strings.TrimSpace(string(data)) + if buildNum1 == "" { + buildNum1 = "0" + } + + buildDir, err := getBuildDir() + if err != nil { + log.Fatal(err) + server.BroadcastToRoom("/", "build", "buildlogs", fmt.Sprintf("Build dir error: %v", err)) + } + + if err != nil { + + server.BroadcastToRoom("/", "build", "buildlogs", fmt.Sprintf("Build dir error: %v", err.Error())) + return + } + + zipPath := filepath.Join(buildDir, fmt.Sprintf("release-%d.zip", buildNum)) + + if err := zipProject(server, "..", zipPath, includes); err != nil { + server.BroadcastToRoom("/", "build", "buildlogs", fmt.Sprintf("Build zip error: %v", err.Error())) + return + } + + server.BroadcastToRoom("/", "build", "buildlogs", fmt.Sprintf("✅ Build finished successfully, current build: %v", buildNum)) + }() + }) +} diff --git a/controller/build_v2app.go b/controller/build_v2app.go index bc192ae..c5859c0 100644 --- a/controller/build_v2app.go +++ b/controller/build_v2app.go @@ -4,23 +4,42 @@ import ( "os" "os/exec" "path/filepath" + + socketio "github.com/googollee/go-socket.io" ) // ---- Run npm build ---- -func runNpmV2Build() error { - cmd := exec.Command("npm", "run", "newBuild") - if os.Getenv("RUNNING_IN_DOCKER") == "true" { - cmd.Dir = "/app" - }else { - // Go three directories up from the current working directory - cwd, err := os.Getwd() - if err != nil { - return err - } - dir := filepath.Join(cwd, "..", "..", "lstV2") - cmd.Dir = dir - } - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return cmd.Run() -} \ No newline at end of file +func runNpmV2Build(server *socketio.Server) error { + cmd := exec.Command("npm", "run", "newBuild") + if os.Getenv("RUNNING_IN_DOCKER") == "true" { + cmd.Dir = "/app" + } else { + // Go three directories up from the current working directory + cwd, err := os.Getwd() + if err != nil { + return err + } + dir := filepath.Join(cwd, "..", "..", "lstV2") + cmd.Dir = dir + } + + stdout, err := cmd.StdoutPipe() + if err != nil { + return err + } + stderr, err := cmd.StderrPipe() + if err != nil { + return err + } + + if err := cmd.Start(); err != nil { + return err + } + + // Launch goroutines to forward output + go streamOutput(stdout, server, "build") + go streamOutput(stderr, server, "build") + + return cmd.Wait() // Wait until process finishes + +}