mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-25 17:54:34 +08:00
fix: protect self update and migration retry
This commit is contained in:
1 parent
e05016c512
commit
19a45f9e5f
5 files changed
+51
-2
No files matched your search
@@ -0,0 +1,17 @@
|
||||
import { describe, it } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { createServer } from './server'
|
||||
import { setAuthToken } from './auth'
|
||||
|
||||
describe('CLI HTTP auth hook', () => {
|
||||
it('requires bearer token for the CLI self-update endpoint under /_web', async () => {
|
||||
setAuthToken('clb_test_token')
|
||||
const app = createServer()
|
||||
app.post('/_web/system/update', async () => ({ success: true }))
|
||||
|
||||
const response = await app.inject({ method: 'POST', url: '/_web/system/update' })
|
||||
await app.close()
|
||||
|
||||
assert.equal(response.statusCode, 401)
|
||||
})
|
||||
})
|
||||
@@ -10,6 +10,7 @@ import { unauthorized, errorResponse } from './errors'
|
||||
|
||||
let cachedToken: string | null = null
|
||||
let webModeEnabled = false
|
||||
const protectedWebPaths = new Set(['/_web/system/update'])
|
||||
|
||||
/**
|
||||
* 设置 auth hook 使用的 token(由 server 启动时注入)
|
||||
@@ -36,12 +37,22 @@ function safeTokenCompare(a: string, b: string): boolean {
|
||||
export async function authHook(request: FastifyRequest, reply: FastifyReply): Promise<void> {
|
||||
if (!cachedToken) return
|
||||
|
||||
if (protectedWebPaths.has(request.url.split('?')[0])) {
|
||||
return requireBearerToken(request, reply)
|
||||
}
|
||||
|
||||
// /_web/ internal API: no auth required (same-origin Web UI only)
|
||||
if (request.url.startsWith('/_web/')) return
|
||||
|
||||
// Web mode: only /api/ routes require auth; static files and SPA are public
|
||||
if (webModeEnabled && !request.url.startsWith('/api/')) return
|
||||
|
||||
return requireBearerToken(request, reply)
|
||||
}
|
||||
|
||||
function requireBearerToken(request: FastifyRequest, reply: FastifyReply): void {
|
||||
if (!cachedToken) return
|
||||
|
||||
const authHeader = request.headers.authorization
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
const err = unauthorized()
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { describe, it } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { shouldMarkUnifiedDirMigrationDone } from './paths'
|
||||
|
||||
describe('desktop path migration', () => {
|
||||
it('marks unified directory migration done only when no directory failed', () => {
|
||||
assert.equal(shouldMarkUnifiedDirMigrationDone([]), true)
|
||||
assert.equal(shouldMarkUnifiedDirMigrationDone(['settings']), false)
|
||||
})
|
||||
})
|
||||
@@ -573,6 +573,10 @@ export function removeLegacyDir(): boolean {
|
||||
|
||||
const SYSTEM_SUBDIRS = ['ai', 'settings', 'cache', 'logs', 'temp', 'nlp']
|
||||
|
||||
export function shouldMarkUnifiedDirMigrationDone(failedDirs: string[]): boolean {
|
||||
return failedDirs.length === 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否需要从 Electron 旧目录结构迁移到新的双根目录结构
|
||||
*
|
||||
@@ -712,7 +716,9 @@ export function migrateToUnifiedDirs(): { success: boolean; error?: string } {
|
||||
writeMigrationLog(getLogsDir(), summary, ensureDir)
|
||||
console.log(`[Migration] ${summary}`)
|
||||
|
||||
writeConfigField('data', 'electron_migration_done', true)
|
||||
if (shouldMarkUnifiedDirMigrationDone(failedDirs)) {
|
||||
writeConfigField('data', 'electron_migration_done', true)
|
||||
}
|
||||
|
||||
return { success: failedDirs.length === 0 }
|
||||
} catch (error) {
|
||||
|
||||
@@ -74,7 +74,12 @@ export class WebPlatformAdapter implements PlatformAdapter {
|
||||
|
||||
async performUpdate(): Promise<PerformUpdateResult> {
|
||||
try {
|
||||
const resp = await fetch('/_web/system/update', { method: 'POST' })
|
||||
const configResp = await fetch('/_web/automation/config')
|
||||
const config = (await configResp.json()) as { token?: string }
|
||||
const headers: Record<string, string> = {}
|
||||
if (config.token) headers.Authorization = `Bearer ${config.token}`
|
||||
|
||||
const resp = await fetch('/_web/system/update', { method: 'POST', headers })
|
||||
return await resp.json()
|
||||
} catch (err) {
|
||||
return { success: false, error: err instanceof Error ? err.message : String(err) }
|
||||
|
||||
Reference in new issue
Block a user