#!/usr/bin/env bun 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) } }) // API endpoint to get logs (for auto-refresh) app.get("/api/logs", async (c) => { try { const logs = await $`journalctl -u phone-ap.service -u phone-web.service -n 200 --no-pager`.text() return c.json({ logs: logs.trim() }) } catch (error) { return c.json({ logs: "", error: String(error) }, 500) } }) // Main WiFi configuration page app.get("/", (c) => { return c.html() }) // Service logs with auto-refresh app.get("/logs", async (c) => { try { const logs = await $`journalctl -u phone-ap.service -u phone-web.service -n 200 --no-pager`.text() return c.html() } catch (error) { throw new Error(`Failed to fetch logs: ${error}`) } }) // 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 }) export default { port: 80, fetch: app.fetch } console.log("Server running on http://0.0.0.0:80") console.log("Access via WiFi or AP at http://phone.local")