import { Hono } from "hono" import { join } from "node:path" import { $ } from "bun" import { IndexPage } from "./components/IndexPage" import { LogsPage } from "./components/LogsPage" import { ConnectingPage } from "./components/ConnectingPage" const app = new Hono() // Ping endpoint for connectivity check app.get("/ping", (c) => { return c.json({ ok: true }) }) // Serve static CSS app.get("/pico.css", async (c) => { const cssPath = join(import.meta.dir, "./static/pico.min.css") const file = Bun.file(cssPath) return new Response(file) }) // API endpoint to get available WiFi networks app.get("/api/networks", async (c) => { try { const result = await $`nmcli -t -f SSID device wifi list`.text() const networks = result .trim() .split("\n") .filter((ssid) => ssid && ssid !== "SSID") // Remove empty and header .filter((ssid, index, self) => self.indexOf(ssid) === index) // Remove duplicates return c.json({ networks }) } catch (error) { return c.json({ networks: [], error: String(error) }, 500) } }) // Main WiFi configuration page app.get("/", (c) => { return c.html() }) // Service logs app.get("/logs", async (c) => { const service = c.req.query("service") || "phone-ap" const validServices = ["phone-ap", "phone-web", "phone"] // Default to phone-ap if invalid service const selectedService = validServices.includes(service) ? service : "phone-ap" try { const logs = await $`journalctl -u ${selectedService}.service -n 200 --no-pager --no-hostname`.text() return c.html() } catch (error) { return c.html() } }) // Handle WiFi configuration submission app.post("/save", async (c) => { const formData = await c.req.parseBody() const ssid = formData.ssid as string const password = formData.password as string // Return the connecting page immediately const response = c.html() // Trigger connection in background after a short delay (allows response to be sent) setTimeout(async () => { try { await $`sudo nmcli device wifi connect ${ssid} password ${password}` console.log(`[WiFi] Successfully connected to ${ssid}`) } catch (error) { console.error(`[WiFi] Failed to connect to ${ssid}:`, error) // Delete the failed connection profile so ap-monitor doesn't try to use it try { await $`sudo nmcli connection delete ${ssid}`.nothrow() console.log(`[WiFi] Deleted failed connection profile for ${ssid}`) } catch (deleteError) { console.error(`[WiFi] Failed to delete connection profile:`, deleteError) } } }, 1000) // 1 second delay return response }) const port = process.env.PORT ? Number(process.env.PORT) : 80 export default { port, fetch: app.fetch } console.log(`Server running on http://0.0.0.0:${port}`) console.log("Access via WiFi or AP at http://phone.local")