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 BackendApp } from '$apps'
|
||||||
import type { App as SharedApp } from '@types'
|
import type { App as SharedApp } from '@types'
|
||||||
import { generateTemplates, type TemplateType } from '%templates'
|
import { generateTemplates, type TemplateType } from '%templates'
|
||||||
|
|
@ -101,6 +101,9 @@ router.post('/', async c => {
|
||||||
// Create current symlink
|
// Create current symlink
|
||||||
symlinkSync(ts, currentPath)
|
symlinkSync(ts, currentPath)
|
||||||
|
|
||||||
|
// Register and start the app
|
||||||
|
registerApp(name)
|
||||||
|
|
||||||
return c.json({ ok: true, 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 { computeHash, generateManifest } from '../sync'
|
||||||
import { loadGitignore } from '@gitignore'
|
import { loadGitignore } from '@gitignore'
|
||||||
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, symlinkSync, unlinkSync, watch, writeFileSync } from 'fs'
|
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}`)
|
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)
|
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 {
|
try {
|
||||||
await restartApp(appName)
|
await restartApp(appName)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import type { App as SharedApp, AppState } from '@types'
|
import type { App as SharedApp, AppState } from '@types'
|
||||||
import type { Subprocess } from 'bun'
|
import type { Subprocess } from 'bun'
|
||||||
import { DEFAULT_EMOJI } from '@types'
|
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 { join, resolve } from 'path'
|
||||||
import { appLog, hostLog, setApps } from './tui'
|
import { appLog, hostLog, setApps } from './tui'
|
||||||
|
|
||||||
|
|
@ -59,7 +59,6 @@ export function initApps() {
|
||||||
setupShutdownHandlers()
|
setupShutdownHandlers()
|
||||||
discoverApps()
|
discoverApps()
|
||||||
runApps()
|
runApps()
|
||||||
watchAppsDir()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onChange(cb: () => void) {
|
export function onChange(cb: () => void) {
|
||||||
|
|
@ -86,6 +85,20 @@ export function removeApp(dir: string) {
|
||||||
update()
|
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 } {
|
export function renameApp(oldName: string, newName: string): { ok: boolean, error?: string } {
|
||||||
const app = _apps.get(oldName)
|
const app = _apps.get(oldName)
|
||||||
if (!app) return { ok: false, error: 'App not found' }
|
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 {
|
function loadApp(dir: string): LoadResult {
|
||||||
try {
|
try {
|
||||||
const pkgPath = join(APPS_DIR, dir, 'current', 'package.json')
|
const pkgPath = join(APPS_DIR, dir, 'current', 'package.json')
|
||||||
|
|
@ -599,84 +604,3 @@ function startShutdownTimeout(app: App) {
|
||||||
}, SHUTDOWN_TIMEOUT)
|
}, 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