simplify server
This commit is contained in:
parent
27c1bfd969
commit
a81d61f910
|
|
@ -1,4 +1,4 @@
|
|||
import { APPS_DIR, allApps, onChange, renameApp, startApp, stopApp, updateAppIcon } from '$apps'
|
||||
import { APPS_DIR, allApps, onChange, registerApp, renameApp, startApp, stopApp, updateAppIcon } from '$apps'
|
||||
import type { App as BackendApp } from '$apps'
|
||||
import type { App as SharedApp } from '@types'
|
||||
import { generateTemplates, type TemplateType } from '%templates'
|
||||
|
|
@ -101,6 +101,9 @@ router.post('/', async c => {
|
|||
// Create current symlink
|
||||
symlinkSync(ts, currentPath)
|
||||
|
||||
// Register and start the app
|
||||
registerApp(name)
|
||||
|
||||
return c.json({ ok: true, name })
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { APPS_DIR, allApps, removeApp, restartApp, startApp, stopApp } from '$apps'
|
||||
import { APPS_DIR, allApps, registerApp, removeApp, restartApp } from '$apps'
|
||||
import { computeHash, generateManifest } from '../sync'
|
||||
import { loadGitignore } from '@gitignore'
|
||||
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, symlinkSync, unlinkSync, watch, writeFileSync } from 'fs'
|
||||
|
|
@ -223,9 +223,13 @@ router.post('/apps/:app/activate', async c => {
|
|||
console.error(`Failed to clean up old versions: ${e}`)
|
||||
}
|
||||
|
||||
// Restart app to use new version
|
||||
// Register new app or restart existing
|
||||
const app = allApps().find(a => a.name === appName)
|
||||
if (app?.state === 'running') {
|
||||
if (!app) {
|
||||
// New app - register it
|
||||
registerApp(appName)
|
||||
} else if (app.state === 'running') {
|
||||
// Existing app - restart it
|
||||
try {
|
||||
await restartApp(appName)
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { App as SharedApp, AppState } from '@types'
|
||||
import type { Subprocess } from 'bun'
|
||||
import { DEFAULT_EMOJI } from '@types'
|
||||
import { existsSync, readdirSync, readFileSync, realpathSync, renameSync, statSync, watch, writeFileSync } from 'fs'
|
||||
import { existsSync, readdirSync, readFileSync, realpathSync, renameSync, writeFileSync } from 'fs'
|
||||
import { join, resolve } from 'path'
|
||||
import { appLog, hostLog, setApps } from './tui'
|
||||
|
||||
|
|
@ -59,7 +59,6 @@ export function initApps() {
|
|||
setupShutdownHandlers()
|
||||
discoverApps()
|
||||
runApps()
|
||||
watchAppsDir()
|
||||
}
|
||||
|
||||
export function onChange(cb: () => void) {
|
||||
|
|
@ -86,6 +85,20 @@ export function removeApp(dir: string) {
|
|||
update()
|
||||
}
|
||||
|
||||
export function registerApp(dir: string) {
|
||||
if (_apps.has(dir)) return // Already registered
|
||||
|
||||
const { pkg, error } = loadApp(dir)
|
||||
const state: AppState = error ? 'invalid' : 'stopped'
|
||||
const icon = pkg.toes?.icon ?? DEFAULT_EMOJI
|
||||
const tool = pkg.toes?.tool
|
||||
_apps.set(dir, { name: dir, state, icon, error, tool })
|
||||
update()
|
||||
if (!error) {
|
||||
runApp(dir, getPort(dir))
|
||||
}
|
||||
}
|
||||
|
||||
export function renameApp(oldName: string, newName: string): { ok: boolean, error?: string } {
|
||||
const app = _apps.get(oldName)
|
||||
if (!app) return { ok: false, error: 'App not found' }
|
||||
|
|
@ -359,14 +372,6 @@ function initPortPool() {
|
|||
}
|
||||
}
|
||||
|
||||
function isDir(path: string): boolean {
|
||||
try {
|
||||
return statSync(path).isDirectory()
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function loadApp(dir: string): LoadResult {
|
||||
try {
|
||||
const pkgPath = join(APPS_DIR, dir, 'current', 'package.json')
|
||||
|
|
@ -599,84 +604,3 @@ function startShutdownTimeout(app: App) {
|
|||
}, SHUTDOWN_TIMEOUT)
|
||||
}
|
||||
|
||||
function watchAppsDir() {
|
||||
watch(APPS_DIR, { recursive: true }, (_event, filename) => {
|
||||
if (!filename) return
|
||||
|
||||
const parts = filename.split('/')
|
||||
const dir = parts[0]!
|
||||
|
||||
// Ignore changes inside old timestamp dirs (but allow current/)
|
||||
if (parts.length > 2 && parts[1] !== 'current') return
|
||||
|
||||
// For versioned apps, only care about changes to "current" directory
|
||||
if (parts.length === 2 && parts[1] !== 'current' && parts[1] !== 'package.json') return
|
||||
|
||||
// Handle new directory appearing
|
||||
if (!_apps.has(dir)) {
|
||||
// Make sure the directory actually exists (avoids race with rename)
|
||||
if (!isDir(join(APPS_DIR, dir))) return
|
||||
|
||||
const { pkg, error } = loadApp(dir)
|
||||
const state: AppState = error ? 'invalid' : 'stopped'
|
||||
const icon = pkg.toes?.icon
|
||||
const tool = pkg.toes?.tool
|
||||
_apps.set(dir, { name: dir, state, icon, error, tool })
|
||||
update()
|
||||
if (!error) {
|
||||
runApp(dir, getPort(dir))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const app = _apps.get(dir)!
|
||||
|
||||
// check if app was deleted
|
||||
if (!isDir(join(APPS_DIR, dir))) {
|
||||
clearTimers(app)
|
||||
if (app.port) releasePort(app.port)
|
||||
_apps.delete(dir)
|
||||
update()
|
||||
return
|
||||
}
|
||||
|
||||
// Only care about package.json changes for existing apps
|
||||
if (!filename.endsWith('package.json')) return
|
||||
|
||||
const { pkg, error } = loadApp(dir)
|
||||
|
||||
// Update icon, tool, and error from package.json
|
||||
const iconChanged = app.icon !== pkg.toes?.icon
|
||||
const toolChanged = app.tool !== pkg.toes?.tool
|
||||
app.icon = pkg.toes?.icon
|
||||
app.tool = pkg.toes?.tool
|
||||
app.error = error
|
||||
|
||||
// Broadcast if icon or tool changed
|
||||
if (iconChanged || toolChanged) update()
|
||||
|
||||
// App became valid - start it if stopped
|
||||
if (!error && app.state === 'invalid') {
|
||||
app.state = 'stopped'
|
||||
runApp(dir, getPort(dir))
|
||||
}
|
||||
|
||||
// App became invalid - stop it if running
|
||||
if (error && app.state === 'running') {
|
||||
app.state = 'invalid'
|
||||
clearTimers(app)
|
||||
app.proc?.kill()
|
||||
}
|
||||
|
||||
// Update state if already stopped/invalid
|
||||
if (error && app.state === 'stopped') {
|
||||
app.state = 'invalid'
|
||||
update()
|
||||
}
|
||||
|
||||
if (!error && app.state === 'invalid') {
|
||||
app.state = 'stopped'
|
||||
update()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user