don't hardcode localhost
This commit is contained in:
parent
d99f80cd0e
commit
6f03954850
|
|
@ -22,10 +22,9 @@ Plug it in, turn it on, and forget about the cloud.
|
|||
by default, the CLI connects to `localhost:3000` in dev and `toes.local:80` in production.
|
||||
|
||||
```bash
|
||||
toes config # show current host
|
||||
TOES_URL=http://192.168.1.50:3000 toes list # full URL
|
||||
TOES_HOST=mypi.local toes list # hostname (port 80)
|
||||
TOES_HOST=mypi.local PORT=3000 toes list # hostname + port
|
||||
toes config # show current host
|
||||
TOES_URL=http://192.168.1.50:3000 toes list # connect to IP
|
||||
TOES_URL=http://mypi.local toes list # connect to hostname
|
||||
```
|
||||
|
||||
set `NODE_ENV=production` to default to `toes.local:80`.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { generateTemplates, type TemplateType } from '%templates'
|
|||
import color from 'kleur'
|
||||
import { existsSync, mkdirSync, writeFileSync } from 'fs'
|
||||
import { basename, join } from 'path'
|
||||
import { del, get, getManifest, HOST, post } from '../http'
|
||||
import { del, get, getManifest, HOST, makeAppUrl, post } from '../http'
|
||||
import { confirm, prompt } from '../prompts'
|
||||
import { resolveAppName } from '../name'
|
||||
import { pushApp } from './sync'
|
||||
|
|
@ -20,23 +20,15 @@ export async function configShow() {
|
|||
|
||||
const source = process.env.TOES_URL
|
||||
? 'TOES_URL'
|
||||
: process.env.TOES_HOST
|
||||
? 'TOES_HOST' + (process.env.PORT ? ' + PORT' : '')
|
||||
: process.env.NODE_ENV === 'production'
|
||||
? 'default (production)'
|
||||
: 'default (development)'
|
||||
: process.env.NODE_ENV === 'production'
|
||||
? '(production)'
|
||||
: '(development)'
|
||||
|
||||
console.log(`Source: ${color.gray(source)}`)
|
||||
|
||||
if (process.env.TOES_URL) {
|
||||
console.log(` TOES_URL=${process.env.TOES_URL}`)
|
||||
}
|
||||
if (process.env.TOES_HOST) {
|
||||
console.log(` TOES_HOST=${process.env.TOES_HOST}`)
|
||||
}
|
||||
if (process.env.PORT) {
|
||||
console.log(` PORT=${process.env.PORT}`)
|
||||
}
|
||||
if (process.env.NODE_ENV) {
|
||||
console.log(` NODE_ENV=${process.env.NODE_ENV}`)
|
||||
}
|
||||
|
|
@ -57,7 +49,7 @@ export async function infoApp(arg?: string) {
|
|||
console.log(` State: ${app.state}`)
|
||||
if (app.port) {
|
||||
console.log(` Port: ${app.port}`)
|
||||
console.log(` URL: http://localhost:${app.port}`)
|
||||
console.log(` URL: ${makeAppUrl(app.port)}`)
|
||||
}
|
||||
if (app.started) {
|
||||
const uptime = Date.now() - app.started
|
||||
|
|
@ -185,7 +177,7 @@ export async function openApp(arg?: string) {
|
|||
console.error(`App is not running: ${name}`)
|
||||
return
|
||||
}
|
||||
const url = `http://localhost:${app.port}`
|
||||
const url = makeAppUrl(app.port!)
|
||||
console.log(`Opening ${url}`)
|
||||
Bun.spawn(['open', url])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,20 +7,40 @@ function getDefaultHost(): string {
|
|||
return `http://localhost:${process.env.PORT ?? 3000}`
|
||||
}
|
||||
|
||||
const defaultPort = process.env.NODE_ENV === 'production' ? 80 : 3000
|
||||
const normalizeUrl = (url: string) =>
|
||||
url.startsWith('http://') || url.startsWith('https://') ? url : `http://${url}`
|
||||
|
||||
function tryParseError(text: string): string | undefined {
|
||||
try {
|
||||
const json = JSON.parse(text)
|
||||
return json.error
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
export const HOST = process.env.TOES_URL
|
||||
?? (process.env.TOES_HOST ? `http://${process.env.TOES_HOST}:${process.env.PORT ?? defaultPort}` : undefined)
|
||||
?? getDefaultHost()
|
||||
? normalizeUrl(process.env.TOES_URL)
|
||||
: getDefaultHost()
|
||||
|
||||
export function makeUrl(path: string): string {
|
||||
return `${HOST}${path}`
|
||||
}
|
||||
|
||||
export function makeAppUrl(port: number): string {
|
||||
const url = new URL(HOST)
|
||||
url.port = String(port)
|
||||
return url.toString().replace(/\/$/, '')
|
||||
}
|
||||
|
||||
export function handleError(error: unknown): void {
|
||||
if (error instanceof Error && 'code' in error && error.code === 'ConnectionRefused') {
|
||||
console.error(`🐾 Can't connect to toes server at ${HOST}`)
|
||||
console.error(` Set TOES_URL or TOES_HOST to connect to a different host`)
|
||||
console.error(` Set TOES_URL to connect to a different host`)
|
||||
return
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
console.error(`Error: ${error.message}`)
|
||||
return
|
||||
}
|
||||
console.error(error)
|
||||
|
|
@ -29,7 +49,11 @@ export function handleError(error: unknown): void {
|
|||
export async function get<T>(url: string): Promise<T | undefined> {
|
||||
try {
|
||||
const res = await fetch(makeUrl(url))
|
||||
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
|
||||
if (!res.ok) {
|
||||
const text = await res.text()
|
||||
const msg = tryParseError(text) ?? `${res.status} ${res.statusText}`
|
||||
throw new Error(msg)
|
||||
}
|
||||
return await res.json()
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
|
|
@ -55,7 +79,11 @@ export async function post<T, B = unknown>(url: string, body?: B): Promise<T | u
|
|||
headers: body !== undefined ? { 'Content-Type': 'application/json' } : undefined,
|
||||
body: body !== undefined ? JSON.stringify(body) : undefined,
|
||||
})
|
||||
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
|
||||
if (!res.ok) {
|
||||
const text = await res.text()
|
||||
const msg = tryParseError(text) ?? `${res.status} ${res.statusText}`
|
||||
throw new Error(msg)
|
||||
}
|
||||
return await res.json()
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
|
|
@ -68,7 +96,11 @@ export async function put(url: string, body: Buffer | Uint8Array): Promise<boole
|
|||
method: 'PUT',
|
||||
body: body as BodyInit,
|
||||
})
|
||||
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
|
||||
if (!res.ok) {
|
||||
const text = await res.text()
|
||||
const msg = tryParseError(text) ?? `${res.status} ${res.statusText}`
|
||||
throw new Error(msg)
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
|
|
@ -80,7 +112,11 @@ export async function download(url: string): Promise<Buffer | undefined> {
|
|||
try {
|
||||
const fullUrl = makeUrl(url)
|
||||
const res = await fetch(fullUrl)
|
||||
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
|
||||
if (!res.ok) {
|
||||
const text = await res.text()
|
||||
const msg = tryParseError(text) ?? `${res.status} ${res.statusText}`
|
||||
throw new Error(msg)
|
||||
}
|
||||
return Buffer.from(await res.arrayBuffer())
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
|
|
@ -92,7 +128,11 @@ export async function del(url: string): Promise<boolean> {
|
|||
const res = await fetch(makeUrl(url), {
|
||||
method: 'DELETE',
|
||||
})
|
||||
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
|
||||
if (!res.ok) {
|
||||
const text = await res.text()
|
||||
const msg = tryParseError(text) ?? `${res.status} ${res.statusText}`
|
||||
throw new Error(msg)
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
handleError(error)
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ export function AppDetail({ app, render }: { app: App, render: () => void }) {
|
|||
<InfoRow>
|
||||
<InfoLabel>URL</InfoLabel>
|
||||
<InfoValue>
|
||||
<Link href={`http://localhost:${app.port}`} target="_blank">
|
||||
http://localhost:{app.port}
|
||||
<Link href={`${location.protocol}//${location.hostname}:${app.port}`} target="_blank">
|
||||
{location.protocol}//{location.hostname}:{app.port}
|
||||
</Link>
|
||||
</InfoValue>
|
||||
</InfoRow>
|
||||
|
|
|
|||
|
|
@ -47,15 +47,17 @@ export function initToolIframes() {
|
|||
if (iframes.size === 0) {
|
||||
const existingIframes = container.querySelectorAll('iframe')
|
||||
existingIframes.forEach(iframe => {
|
||||
const match = iframe.src.match(/localhost:(\d+)/)
|
||||
if (match && match[1]) {
|
||||
const port = parseInt(match[1], 10)
|
||||
try {
|
||||
const url = new URL(iframe.src)
|
||||
const port = parseInt(url.port, 10)
|
||||
const toolName = iframe.dataset.toolName
|
||||
const appName = iframe.dataset.appName
|
||||
if (toolName) {
|
||||
if (port && toolName) {
|
||||
const cacheKey = appName ? `${toolName}:${appName}` : toolName
|
||||
iframes.set(cacheKey, { iframe, port })
|
||||
}
|
||||
} catch {
|
||||
// Invalid URL, skip
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -68,11 +70,13 @@ export function initToolIframes() {
|
|||
`
|
||||
}
|
||||
|
||||
// Build URL with params
|
||||
// Build URL with params using TOES_URL base
|
||||
function buildToolUrl(port: number, params: Record<string, string>): string {
|
||||
const base = new URL(window.location.origin)
|
||||
base.port = String(port)
|
||||
const searchParams = new URLSearchParams(params)
|
||||
const query = searchParams.toString()
|
||||
return query ? `http://localhost:${port}?${query}` : `http://localhost:${port}`
|
||||
return query ? `${base.origin}?${query}` : base.origin
|
||||
}
|
||||
|
||||
// Build cache key from tool name and params
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { allApps, initApps } from '$apps'
|
||||
import { allApps, initApps, TOES_URL } from '$apps'
|
||||
import appsRouter from './api/apps'
|
||||
import syncRouter from './api/sync'
|
||||
import { Hype } from '@because/hype'
|
||||
|
|
@ -16,7 +16,9 @@ app.get('/tool/:tool', c => {
|
|||
return c.text(`Tool "${toolName}" not found or not running`, 404)
|
||||
}
|
||||
const params = new URLSearchParams(c.req.query()).toString()
|
||||
const url = params ? `http://localhost:${tool.port}?${params}` : `http://localhost:${tool.port}`
|
||||
const base = new URL(TOES_URL)
|
||||
base.port = String(tool.port)
|
||||
const url = params ? `${base.origin}?${params}` : base.origin
|
||||
return c.redirect(url)
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user