feat: persist APN relay secret across restarts, add dev logging and server identification to pair UI
- Electron desktop: auto-start PushRelay with secret persisted in electron-store - CLI serve (Tauri): persist relay secret to Global.Path.state/relay-secret (mode 0600) - Pair endpoint now returns relayURL, serverID, relaySecretHash for debugging - Desktop settings-pair component shows server name, relay URL, and secret hash above QR - Add console.debug logging for pairing fetch lifecycle - Export PushRelay from node.ts entry point for Electron consumption
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { spawnSync } from "node:child_process"
|
||||
import { createHash, randomBytes } from "node:crypto"
|
||||
import { writeFileSync } from "node:fs"
|
||||
import path from "node:path"
|
||||
import os from "node:os"
|
||||
import { Server } from "../../server/server"
|
||||
import { cmd } from "./cmd"
|
||||
@@ -10,10 +12,24 @@ import { Project } from "../../project"
|
||||
import { Installation } from "../../installation"
|
||||
import { PushRelay } from "../../server/push-relay"
|
||||
import { Log } from "../../util"
|
||||
import { Global } from "../../global"
|
||||
import * as QRCode from "qrcode"
|
||||
|
||||
const log = Log.create({ service: "serve" })
|
||||
|
||||
async function getOrCreatePersistedRelaySecret(): Promise<string> {
|
||||
const filePath = path.join(Global.Path.state, "relay-secret")
|
||||
try {
|
||||
const existing = (await Bun.file(filePath).text()).trim()
|
||||
if (existing.length > 0) return existing
|
||||
} catch {
|
||||
// file doesn't exist yet
|
||||
}
|
||||
const secret = randomBytes(18).toString("base64url")
|
||||
writeFileSync(filePath, secret, { mode: 0o600 })
|
||||
return secret
|
||||
}
|
||||
|
||||
type PairPayload = {
|
||||
serverID?: string
|
||||
relayURL: string
|
||||
@@ -225,7 +241,7 @@ export const ServeCommand = cmd({
|
||||
]
|
||||
|
||||
const input = (args["relay-secret"] ?? process.env.OPENCODE_EXPERIMENTAL_PUSH_RELAY_SECRET ?? "").trim()
|
||||
const relaySecret = input || randomBytes(18).toString("base64url")
|
||||
const relaySecret = input || (await getOrCreatePersistedRelaySecret())
|
||||
const connectQR = Boolean(args["connect-qr"])
|
||||
|
||||
if (connectQR) {
|
||||
@@ -236,10 +252,7 @@ export const ServeCommand = cmd({
|
||||
}
|
||||
|
||||
if (!input) {
|
||||
console.log("experimental push relay secret generated")
|
||||
console.log(
|
||||
"set --relay-secret or OPENCODE_EXPERIMENTAL_PUSH_RELAY_SECRET to keep push registrations stable across server restarts",
|
||||
)
|
||||
log.info("using persisted relay secret", { hash: secretHash(relaySecret) })
|
||||
}
|
||||
|
||||
console.log("printing connect qr without starting the server")
|
||||
@@ -259,10 +272,7 @@ export const ServeCommand = cmd({
|
||||
console.log(`opencode server listening on http://${server.hostname}:${server.port}`)
|
||||
|
||||
if (!input) {
|
||||
console.log("experimental push relay secret generated")
|
||||
console.log(
|
||||
"set --relay-secret or OPENCODE_EXPERIMENTAL_PUSH_RELAY_SECRET to keep push registrations stable across server restarts",
|
||||
)
|
||||
log.info("using persisted relay secret", { hash: secretHash(relaySecret) })
|
||||
}
|
||||
if (relayURL && relaySecret) {
|
||||
const host = server.hostname ?? opts.hostname
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export { Config } from "./config"
|
||||
export { Server } from "./server/server"
|
||||
export { PushRelay } from "./server/push-relay"
|
||||
export { bootstrap } from "./cli/bootstrap"
|
||||
export { Log } from "./util"
|
||||
export { Database } from "./storage"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { createHash } from "node:crypto"
|
||||
import { Hono } from "hono"
|
||||
import { describeRoute, validator, resolver } from "hono-openapi"
|
||||
import z from "zod"
|
||||
@@ -36,6 +37,9 @@ const PushPairResult = z
|
||||
z.object({
|
||||
enabled: z.literal(true),
|
||||
hosts: z.array(z.string()),
|
||||
relayURL: z.string(),
|
||||
serverID: z.string().optional(),
|
||||
relaySecretHash: z.string(),
|
||||
link: z.string(),
|
||||
qr: z.string(),
|
||||
}),
|
||||
@@ -488,10 +492,16 @@ export const ExperimentalRoutes = lazy(() =>
|
||||
|
||||
const link = pushPairLink(pair)
|
||||
const qr = await pushPairQRCode(pair)
|
||||
const relaySecretHash = pair.relaySecret
|
||||
? `${createHash("sha256").update(pair.relaySecret).digest("hex").slice(0, 12)}...`
|
||||
: "none"
|
||||
|
||||
return c.json({
|
||||
enabled: true,
|
||||
hosts: pair.hosts,
|
||||
relayURL: pair.relayURL,
|
||||
serverID: pair.serverID,
|
||||
relaySecretHash,
|
||||
link,
|
||||
qr,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user