IKHSAN
ENID Baca dalam Bahasa Indonesia

homeio

Public

Forked from doctor-io/homeio

Branch
main
Latest commit
958d74616 October 2026

server.ts

Open raw
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
import next from "next";
import { parse } from "node:url";
import compression from "compression";
import { closeServerGracefully } from "./lib/server/http/graceful-shutdown";
import { LISTEN_BACKLOG, configureUpstreamKeepAlive } from "./lib/server/http/keep-alive";

const dev = process.env.NODE_ENV !== "production";
const hostname =
  process.env.HOMEIO_HTTP_HOST || (dev ? "localhost" : "0.0.0.0");
const port = parseInt(process.env.HOMEIO_HTTP_PORT || process.env.PORT || "3000", 10);

const app = next({ dev, hostname, port, turbopack: false });
const handle = app.getRequestHandler();

const compressionMiddleware = compression({
  threshold: 1024,
  filter: (req, res) => {
    const requestPath = req.url ?? "";
    if (requestPath.includes("/stream")) {
      return false;
    }

    const contentType = res.getHeader("Content-Type");
    if (typeof contentType === "string" && contentType.includes("text/event-stream")) {
      return false;
    }

    return compression.filter(req as never, res as never);
  },
}) as (
  req: IncomingMessage,
  res: ServerResponse<IncomingMessage>,
  next: (error?: unknown) => void,
) => void;

async function applyCompression(
  req: IncomingMessage,
  res: ServerResponse<IncomingMessage>,
) {
  await new Promise<void>((resolve, reject) => {
    compressionMiddleware(req, res, (error?: unknown) => {
      if (error) {
        reject(error);
        return;
      }

      resolve();
    });
  });
}

process.on("uncaughtException", (error) => {
  console.error("Uncaught exception:", error);
  process.exit(1);
});

process.on("unhandledRejection", (reason) => {
  console.error("Unhandled promise rejection:", reason);
});

async function main() {
  await app.prepare();
  void import("./lib/server/modules/files/path-resolver")
    .then((module) => module.ensureFilesRootDirectories())
    .catch((error: unknown) => {
      const message = error instanceof Error ? error.message : String(error);
      console.warn("⚠ Files root initialization warning:", message);
    });

  const shutdownHooks: Array<() => Promise<unknown> | unknown> = [];
  const server = createServer(async (req, res) => {
    try {
      await applyCompression(req, res);
      const parsedUrl = parse(req.url || "", true);
      await handle(req, res, parsedUrl);
    } catch (error) {
      console.error("Error occurred handling", req.url, error);
      res.statusCode = 500;
      res.end("internal server error");
    }
  });
  configureUpstreamKeepAlive(server);

  // Initialize WebSocket server for terminal (optional feature).
  // This gracefully fails if node-pty is not available.
  void import("./lib/server/modules/terminal/websocket-server")
    .then((module) => {
      module.initializeWebSocketServer(server);
      console.log("✓ Terminal WebSocket server initialized");
    })
    .catch((error: unknown) => {
      const message = error instanceof Error ? error.message : String(error);
      console.warn("⚠ Terminal feature not available:", message);
      console.log("  The application will work without terminal functionality");
      console.log("  To enable terminal, run: npm install @lydell/node-pty");
    });

  // Network storage watcher starts from files API route modules.
  // Avoid eager server-level import here to keep startup runtime-only.

  let shuttingDown = false;
  const shutdown = async (signal: string) => {
    if (shuttingDown) return;
    shuttingDown = true;

    console.log(`> Received ${signal}, shutting down...`);

    await Promise.allSettled(
      shutdownHooks.map(async (hook) => {
        try {
          await hook();
        } catch (error) {
          console.error("Shutdown hook failed", error);
        }
      }),
    );

    await closeServerGracefully(server);

    process.exit(0);
  };

  process.on("SIGINT", () => {
    void shutdown("SIGINT");
  });
  process.on("SIGTERM", () => {
    void shutdown("SIGTERM");
  });

  server.listen(port, hostname, LISTEN_BACKLOG, (error?: Error) => {
    if (error) throw error;
    console.log(`> Ready on http://${hostname}:${port}`);
  });
}

main().catch((error) => {
  console.error("Failed to start server", error);
  process.exit(1);
});
bytes
4,281
lines
141
Type
ts
Latest commit
16 October 2026