fix: 修复中危问题:注册接口、模型名、LLM 超时、WebSocket 断线、未公开任务泄露等
服务端 - 删掉没人用的 /account/register,外网不能再批量注册小号 - 模型名只放行豆包和配置的 LLM_MODEL,浏览器里存的旧模型名换成默认模型; 聊天默认模型改读 LLM_MODEL - 流式生成改成 120 秒无数据才超时(另有 15 分钟总上限),思考模型不会被中途掐断; SSE 最后一行没有换行时不再丢内容 - WebSocket:token 无效 401、任务不存在 404,建对话失败时关闭连接; 引导出错时把没有回复的提问移出上下文 - 未公开的教程只有超管能看,未公开的挑战和素材管理员也能看,其他人 404 - Caddy:/media 加 CSP sandbox 和 nosniff,上传的 html/svg 直接打开不能跑脚本; 加 CORS,预览 iframe 里的代码可以读取字体和 fetch 素材 前端 - 新增 ReconnectingSocket:意外断开后退避重连,握手失败先刷新 token; 断线时清掉“生成中”状态,旧连接的事件不再改写新连接 - marked 扩展抽到 utils/markdown.ts:教程用独立实例,不再改全局 marked; 学生内容的渲染保留高亮,不带会渲染原始 HTML 的 preview 扩展 - 挑战页、创作页加载完成前离开时不再建立连接,离开时关闭引导弹窗 - 教程、提交列表、用户管理跟随浏览器前进后退;快速翻页只用最后一次结果, 改筛选条件不再请求两次 - 随机评分弹窗在 AI 回复完之后不再弹出 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
73788c4595
commit
cdeb7c18ef
22 files changed
+489
-210
No files matched your search
@@ -163,6 +163,7 @@ import {
|
||||
removeMessagePair,
|
||||
markMessageSubmitted,
|
||||
} from "../store/prompt"
|
||||
import { closeGuidance } from "../store/guidance"
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -221,6 +222,7 @@ async function loadChallenge() {
|
||||
Challenge.get(display),
|
||||
TaskAssets.listChallenge(display),
|
||||
])
|
||||
if (disposed) return
|
||||
taskId.value = data.task_ptr
|
||||
challengeAuthor.value = data.author_name ?? ""
|
||||
if (data.example_html || data.example_css || data.example_js) {
|
||||
@@ -236,6 +238,7 @@ async function loadChallenge() {
|
||||
async: true,
|
||||
renderer: challengeRenderer,
|
||||
} as MarkedOptions)
|
||||
if (disposed) return
|
||||
assets.value = fetchedAssets
|
||||
if (!authed.value) return
|
||||
connectPrompt(data.task_ptr)
|
||||
@@ -291,11 +294,18 @@ function back() {
|
||||
router.push({ name: "home-challenge-list" })
|
||||
}
|
||||
|
||||
// 加载还没完成就离开页面时,不要再建立连接
|
||||
let disposed = false
|
||||
|
||||
onMounted(() => {
|
||||
setupCodeCopy()
|
||||
loadChallenge()
|
||||
})
|
||||
onUnmounted(disconnectPrompt)
|
||||
onUnmounted(() => {
|
||||
disposed = true
|
||||
disconnectPrompt()
|
||||
closeGuidance()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -125,6 +125,7 @@ import {
|
||||
setOnCodeComplete,
|
||||
streaming,
|
||||
} from "../store/prompt"
|
||||
import { closeGuidance } from "../store/guidance"
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -146,6 +147,7 @@ watch(streaming, (val) => {
|
||||
|
||||
async function load() {
|
||||
const data = await Creation.get(display)
|
||||
if (disposed) return
|
||||
taskId.value = data.task_id
|
||||
title.value = data.title
|
||||
content.value = data.content
|
||||
@@ -212,8 +214,15 @@ function back() {
|
||||
router.push({ name: "creation-list" })
|
||||
}
|
||||
|
||||
// 加载还没完成就离开页面时,不要再建立连接
|
||||
let disposed = false
|
||||
|
||||
onMounted(load)
|
||||
onUnmounted(disconnectPrompt)
|
||||
onUnmounted(() => {
|
||||
disposed = true
|
||||
disconnectPrompt()
|
||||
closeGuidance()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -341,16 +341,25 @@ function rowClassName(row: SubmissionOut) {
|
||||
return submission.value.id === row.id ? "row-active" : ""
|
||||
}
|
||||
|
||||
// 快速翻页时只用最后一次请求的结果
|
||||
let initSeq = 0
|
||||
|
||||
async function init() {
|
||||
const seq = ++initSeq
|
||||
expandedKeys.value = []
|
||||
expandedData.clear()
|
||||
const res = await Submission.list(query)
|
||||
if (seq !== initSeq) return
|
||||
data.value = res.items
|
||||
count.value = res.count
|
||||
}
|
||||
|
||||
let detailSeq = 0
|
||||
|
||||
async function getSubmissionByID(id: string) {
|
||||
submission.value = await Submission.get(id)
|
||||
const seq = ++detailSeq
|
||||
const res = await Submission.get(id)
|
||||
if (seq === detailSeq) submission.value = res
|
||||
}
|
||||
|
||||
function afterScore() {
|
||||
@@ -374,26 +383,34 @@ watch(
|
||||
router.push({ params: { page: v } })
|
||||
},
|
||||
)
|
||||
// 浏览器前进 / 后退时,页码跟着地址栏走
|
||||
watch(
|
||||
() => route.params.page,
|
||||
(v) => {
|
||||
const page = Number(v) || 1
|
||||
if (page !== query.page) query.page = page
|
||||
},
|
||||
)
|
||||
watchDebounced(
|
||||
() => query.username,
|
||||
() => {
|
||||
query.page = 1
|
||||
init()
|
||||
if (query.page !== 1) query.page = 1
|
||||
else init()
|
||||
},
|
||||
{ debounce: 500, maxWait: 1000 },
|
||||
)
|
||||
watch(
|
||||
() => query.flag,
|
||||
() => {
|
||||
query.page = 1
|
||||
init()
|
||||
if (query.page !== 1) query.page = 1
|
||||
else init()
|
||||
},
|
||||
)
|
||||
watch(
|
||||
() => query.zone,
|
||||
() => {
|
||||
query.page = 1
|
||||
init()
|
||||
if (query.page !== 1) query.page = 1
|
||||
else init()
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -151,8 +151,13 @@ async function batchCreateUsers() {
|
||||
}
|
||||
}
|
||||
|
||||
// 快速翻页时只用最后一次请求的结果
|
||||
let initSeq = 0
|
||||
|
||||
async function init() {
|
||||
const seq = ++initSeq
|
||||
const data = await Account.list(query)
|
||||
if (seq !== initSeq) return
|
||||
users.value = data.items
|
||||
count.value = data.count
|
||||
}
|
||||
@@ -160,15 +165,15 @@ async function init() {
|
||||
watch(
|
||||
() => query.role,
|
||||
() => {
|
||||
query.page = 1
|
||||
init()
|
||||
if (query.page !== 1) query.page = 1
|
||||
else init()
|
||||
},
|
||||
)
|
||||
watchDebounced(
|
||||
() => query.username,
|
||||
() => {
|
||||
query.page = 1
|
||||
init()
|
||||
if (query.page !== 1) query.page = 1
|
||||
else init()
|
||||
},
|
||||
{ debounce: 500, maxWait: 1000 },
|
||||
)
|
||||
@@ -179,6 +184,14 @@ watch(
|
||||
router.push({ params: { page: v } })
|
||||
},
|
||||
)
|
||||
// 浏览器前进 / 后退时,页码跟着地址栏走
|
||||
watch(
|
||||
() => route.params.page,
|
||||
(v) => {
|
||||
const page = Number(v) || 1
|
||||
if (page !== query.page) query.page = page
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(init)
|
||||
</script>
|
||||
|
||||
Reference in new issue
Block a user