症状: Storage 页面点击「复制链接」后,把链接粘贴到浏览器地址栏无法访问(401 / 路径异常)。
根因(两个叠加问题):
- 下载接口(
BucketResource.downloadObject/headObject)始终要求 Bearer token,浏览器地址栏不带Authorization头 →AuthFilter抛Token is missing401。BucketVisibility.PUBLIC(公开 Bucket)的匿名读 语义虽在模型中定义,却从未被认证层实现。 LocalStorageOperations用basePath.relativize(p).toString()返回相对路径,分隔符为 OS 相关;Windows 下 为反斜杠,导致子目录文件的复制链接、面包屑与目录导航失效(S3 实现用正斜杠,无此问题)。
修复:
flexmodel-server/.../storage/config/LocalStorageOperations.java:新增toPosixPath(Path),listFiles与getFile返回的path统一为 POSIX 正斜杠,跨平台一致。flexmodel-server/.../common/config/web/filter/AuthFilter.java:注入BucketService,新增isAnonymousPublicBucketRead。无 Bearer token 时,对GET/HEAD命中projects/{projectId}/buckets/{bucketName}/objects(/.*)?的请求解析 Bucket;visibility 为PUBLIC时放行匿名读 (下载/HEAD/元数据/列表),PRIVATE/AUTHENTICATED及写操作仍要求认证。解析失败回退标准认证流程。flexmodel-ui/.../Storage/components/FileBrowser.tsx:新增visibility入参;handleCopyLink对path做反斜杠→正斜杠兜底归一;非 PUBLIC Bucket 复制时给出 warning 提示需设为公开。flexmodel-ui/.../Storage/index.tsx:向FileBrowser传入activeBucket.visibility。- i18n 新增
copy_link_not_public(zh/en)。
验证:
mvn compile -pl flexmodel-server -am→ BUILD SUCCESS。mvn test -pl flexmodel-server -Dtest=LocalStorageOperationsTest→ Tests run: 14, Failures: 0, Errors: 0。- IDE inspections(FileBrowser.tsx / index.tsx / AuthFilter.java / LocalStorageOperations.java)无 error。
行为说明: 公开 Bucket 的复制链接现在可在浏览器直接打开下载;非公开 Bucket 的链接仍需认证(符合可见性 设计),前端复制时会提示用户将 Bucket 设为「公开」以获得匿名访问。
Last Updated: 2026-08-04 Session ID: pages-deploy-eventloop-block Active Feature: 修复部署 pages 报错(事件循环被 JDBC 阻塞)
症状: 上传 zip 部署 pages 时报错;日志显示 LogEventConsumer 在 vert.x-eventloop-thread-0 上被 MySQL SSL socket
读阻塞 4176ms+(blocked-thread-checker 警告)。
根因(两个叠加问题):
LogEventConsumer.consume无@Blocking,阻塞 JDBC 写入直接跑在 Vert.x 事件循环线程上。- 生产 JDBC URL(docker-compose)无
connectTimeout/readTimeout(Connector/J 默认无限等待)。MySQL 容器重启后池中残留旧连接读到已死对端 → 无限阻塞 → 事件循环冻结 → 部署请求超时报错。
修复:
- 全部 5 个
@ConsumeEvent消费者统一改为@ConsumeEvent(value=..., blocking = true)(工作线程/虚拟线程执行,不再阻塞事件循环):LogEventConsumer(JDBC 写入,本次故障元凶)ProjectDeletedSchedulingConsumer(Quartz 操作走 JDBC f_qrtz_*)TriggerFlowEventConsumer(流程执行含 DB + HTTP 函数调用)SettingsEventConsumer/GraphQLEventConsumer(纯内存/日志,顺带统一)
-
docker-compose.ymlJDBC URL 添加connectTimeout=5000&readTimeout=15000(Connector/J 9.x 命名) -
PageResource.java类级@Blocking(zip 解包 + DB 写入均阻塞)
验证: mvn compile -pl flexmodel-server -am -q -o 通过。
遗留风险: JAX-RS 端点默认仍在事件循环上运行(本仓库无全局 @Blocking 约定),仅 pages 特性包已处理;其他含 DB 访问的
Resource 若遇 DB 挂死同样会冻结事件循环,建议后续按特性包逐个加 @Blocking。
-
deno.json— Deno project configuration with Hono.js dependency -
src/types.ts— Full TypeScript type definitions (FunctionMeta, InvokeRequest/Result, Worker messages, etc.) -
src/sdk/flexmodel.ts— RPC Dispatcher (proxies Worker SDK requests to Java REST API) -
src/runner/registry.ts— Function Registry (metadata cache + LRU source cache, lazy load from Java) -
src/runner/worker-entry.ts— Worker internal entry (addEventListener message handling, dynamic import, ctx SDK injection) -
src/runner/worker.ts— Worker executor (create Worker, timeout/terminate, proxy SDK RPC) -
src/router/health.ts— Health check endpoint (GET /health) -
src/router/functions.ts— Functions routes (POST /deploy, DELETE /:projectId/:name, POST /invoke) -
src/server.ts— Hono.js server initialization and route registration -
src/main.ts— Entry point (Deno.serve on configurable port)
- Extended
enum TriggerTypewithHTTP - Added
f_functionmodel (id, project_id, name, slug, description, entry_point, status, current_version, timeout, memory_limit, timestamps, indexes) - Added
f_function_versionmodel (id, function_id, version, source_code, timestamps, index) - Code generation verified —
Function.java,FunctionVersion.java,TriggerType.HTTPgenerated successfully
-
dto/FunctionCreateRequest.java— Create request with validation -
dto/FunctionUpdateRequest.java— Update request DTO -
dto/FunctionInvokeRequest.java— Invocation request (method, headers, body, query) -
dto/FunctionInvokeResponse.java— Invocation response with _meta (executionTimeMs, logs) -
dto/FunctionDeployRequest.java— Deploy request to Deno (metadata only) -
dto/FunctionResponse.java— API response with TriggerRef list -
dto/FunctionVersionResponse.java— Version list response -
dto/FunctionPageRequest.java— Pagination request -
FunctionException.java— Business exception for functions -
FunctionRepository.java— Repository interface -
FunctionFmRepository.java— Repository implementation (AbstractRepository pattern) -
FunctionVersionRepository.java— Version repository interface -
FunctionVersionFmRepository.java— Version repository implementation -
FunctionInvoker.java— HTTP client to Deno functions runtime (deploy, invoke, delete, healthCheck) -
FunctionService.java— Core service (CRUD, state machine, version management, invocation, auth validation, startup recovery) -
FunctionResource.java— REST endpoints (CRUD, trigger management, public invoke entry) -
FunctionInternalResource.java— Internal API for runtime lazy source loading
-
src/services/function.ts— API service layer (TypeScript interfaces + all CRUD/invoke/trigger endpoints) -
src/pages/Functions/index.tsx— Main list page (table with status tags, search/filter, pagination, create/edit/delete actions) -
src/pages/Functions/components/FunctionForm.tsx— Create/Edit modal (tabs: Basic Settings + Source Code + HTTP Trigger config) -
src/pages/Functions/components/FunctionDetail.tsx— Detail drawer (tabs: Overview + Code + Versions with rollback + Test Invoke) -
src/pages/Functions/components/FunctionInvokePanel.tsx— Test invoke panel (request builder with method/headers/body/query + response viewer with logs) -
src/locales/zh.json— Chinese translations (70+ keys for function management) -
src/locales/en.json— English translations (70+ keys) -
src/routes.tsx— Added Functions route with CodeOutlined icon under/project/:projectId/functions
- Phase 4: Integration Testing — Install Deno, start functions runtime, run end-to-end tests
- Source Code Viewing — Add frontend endpoint to retrieve source code for editing (currently requires re-pasting on update)
- V2 Enhancements — Worker Pool, Cron triggers, metrics, secrets management
- Deno not installed in current environment — functions runtime cannot be runtime-verified yet
- IDE lock on
flexmodel-server-dev.jarpreventsmvn clean(not caused by our changes)
- HTTP Client: Used Java 25 built-in
java.net.http.HttpClientinstead of Vert.x WebClient to avoid additional dependency - Configuration: Functions runtime host/port configurable via
flexmodel.functions-runtime.hostandflexmodel.functions-runtime.portproperties - Source Code Loading: Lazy load pattern — source code not sent at deploy time, loaded by runtime on first invoke via internal API
- Auth Validation: Implemented PUBLIC/JWT/API_KEY/INTERNAL auth modes per trigger config
- Startup Recovery: Only deploys metadata on startup (O(1) time), source code lazy-loaded
flexmodel-functions-runtime/deno.jsonflexmodel-functions-runtime/src/main.tsflexmodel-functions-runtime/src/server.tsflexmodel-functions-runtime/src/types.tsflexmodel-functions-runtime/src/router/functions.tsflexmodel-functions-runtime/src/router/health.tsflexmodel-functions-runtime/src/runner/registry.tsflexmodel-functions-runtime/src/runner/worker.tsflexmodel-functions-runtime/src/runner/worker-entry.tsflexmodel-functions-runtime/src/sdk/flexmodel.ts
flexmodel-server/src/main/java/dev/flexmodel/functions/FunctionException.javaflexmodel-server/src/main/java/dev/flexmodel/functions/FunctionRepository.javaflexmodel-server/src/main/java/dev/flexmodel/functions/FunctionFmRepository.javaflexmodel-server/src/main/java/dev/flexmodel/functions/FunctionVersionRepository.javaflexmodel-server/src/main/java/dev/flexmodel/functions/FunctionVersionFmRepository.javaflexmodel-server/src/main/java/dev/flexmodel/functions/FunctionInvoker.javaflexmodel-server/src/main/java/dev/flexmodel/functions/FunctionService.javaflexmodel-server/src/main/java/dev/flexmodel/functions/FunctionResource.javaflexmodel-server/src/main/java/dev/flexmodel/functions/FunctionInternalResource.javaflexmodel-server/src/main/java/dev/flexmodel/functions/dto/FunctionCreateRequest.javaflexmodel-server/src/main/java/dev/flexmodel/functions/dto/FunctionUpdateRequest.javaflexmodel-server/src/main/java/dev/flexmodel/functions/dto/FunctionInvokeRequest.javaflexmodel-server/src/main/java/dev/flexmodel/functions/dto/FunctionInvokeResponse.javaflexmodel-server/src/main/java/dev/flexmodel/functions/dto/FunctionDeployRequest.javaflexmodel-server/src/main/java/dev/flexmodel/functions/dto/FunctionResponse.javaflexmodel-server/src/main/java/dev/flexmodel/functions/dto/FunctionVersionResponse.javaflexmodel-server/src/main/java/dev/flexmodel/functions/dto/FunctionPageRequest.java
flexmodel-server/src/main/resources/project.fml— Added f_function, f_function_version models + HTTP to TriggerTypeflexmodel-ui/src/routes.tsx— Added Functions route with CodeOutlined iconflexmodel-ui/src/locales/zh.json— Added 70+ function management translation keysflexmodel-ui/src/locales/en.json— Added 70+ function management translation keysfeature_list.json— Added feat-011progress.md— This file
- Compile:
mvn compile -pl flexmodel-server→ BUILD SUCCESS (424 source files, 0 errors) - Engine tests:
mvn test -pl flexmodel-engine -q→ all passed - Frontend TypeScript:
tsc --noEmit→ 0 errors - Frontend build:
npm run build(Vite) → built in 42.98s - Deno type-check:
deno check src/main.ts(Deno not installed in environment) - End-to-end test: create → deploy → invoke → update → delete (requires running functions runtime)
完善 TriggerResourceTest e2e 用例,在调用 Trigger REST 接口后,通过注入的 Scheduler 直接查询 Quartz 作业状态,确保
Quartz 作业被正确创建/移除/状态变更。
flexmodel-server/src/test/java/dev/flexmodel/rest/TriggerResourceTest.java- 新增 Quartz 断言辅助方法:
jobKey/triggerKey/assertScheduledInQuartz/assertNotScheduledInQuartz/unscheduleFromScheduler,镜像TriggerService.buildJobKey/buildTriggerKey/getJobGroup的命名规则(group =dev_test_flow_{jobId})。 testCreateIntervalTrigger/testCreateCronTrigger:创建 state=true SCHEDULED 触发器后断言 JobDetail/Trigger 存在、JobDataMap 携带 triggerId/jobId/projectId。testCreateEventTrigger:断言 EVENT 类型不创建 Quartz 作业。testCreateDisabledTriggerNotScheduled:state=false SCHEDULED 不创建 Quartz 作业。testPatchTriggerEnable/testPatchTriggerDisable:启用→创建调度任务,禁用→移除调度任务。testUpdateTrigger/testUpdateTriggerReschedules(新增):update 先 unschedule 再 schedule 的重建路径,禁用态不残留调度任务,且全程保留 seed 原始名称。testDeleteTrigger:删除前断言调度任务存在、删除后断言已移除。@AfterEach增强:清理 created/启用的 seed 触发器的 Quartz 调度任务(兜底deleteJob/unscheduleJob),并还原 seed 禁用态。
- 新增 Quartz 断言辅助方法:
flexmodel-server/src/main/java/dev/flexmodel/scheduling/TriggerService.java- 修复
create():对 state=false 的 SCHEDULED 触发器不再调用scheduleTrigger(与update()行为一致),避免禁用态触发器误注册 Quartz 作业。这是被新 e2e 用例暴露出的预存缺陷。
- 修复
mvn -pl flexmodel-server -am test -Dtest=TriggerResourceTest -Dsurefire.failIfNoSpecifiedTests=false→ BUILD SUCCESS,Tests run: 16, Failures: 0, Errors: 0, Skipped: 0。- TriggerResourceIT(@QuarkusIntegrationTest extends TriggerResourceTest)保持兼容(用例均为普通 HTTP+Scheduler 断言,无 JVM 模式不兼容 API)。
assertScheduledInQuartz不再强断言getTriggerState==NORMAL与nextFireTime:短间隔 SimpleTrigger 在断言前可能已触发完成被移除(含 startup-restore 触发的实际 Job 执行,会出现SessionContextNPE 噪声日志,属已知环境问题,不影响断言)。改为断言 JobDetail/Trigger 存在 + JobDataMap 内容 + 状态非 ERROR,保证稳定性。testDeleteTrigger改用 1 小时间隔创建触发器,避免调度任务在断言前被触发清理。- 启动恢复
restoreScheduledTriggersOnStartup对 seed EVENT 触发器不调度(符合预期),dev_test seed 中无 state=true 的 SCHEDULED 触发器。
修复项目分析中发现的 15 项优化问题(#6–#25, #33, #35)。
| # | 问题 | 修复 | 文件 |
|---|---|---|---|
| 6 | TurboException extends RuntimeException | → extends BusinessException | TurboException.java |
| 7 | 51 处 bare RuntimeException | → NotFoundException/InternalServerException/ValidationException; BusinessExceptionMapper 更新 | 19 files |
| 11 | DataService/ModelingService 硬编码 databaseName | → @Inject SessionContext + resolveDatabaseName() fallback | DataService.java, ModelingService.java |
| 12 | 缺少权限注解 | → @RequiresPermissions on User/Role/ApiKey/Resource Resource | 4 files |
| 14 | AuthService 职责过重 | → 拆分到 UserService/RoleService/ResourceService | 4 files |
| 16 | ConcurrentHashMapCache 无 null 保护 | → computeIfAbsent + NULL_SENTINEL | ConcurrentHashMapCache.java |
| 17 | SqlSession.close() 静默提交 | → rollback() + warn log | SqlSession.java |
| 19 | Zustand selector 未用 useShallow | → 全部 wrap useShallow | appStore.ts, authStore.ts |
| 20 | ECharts 全量引入 | → modular imports (utils/echarts.ts) | 3 files |
| 21 | i18n 重复 key + 错误翻译 | → 去重 + 修正 | en.json, zh.json |
| 23 | POM 依赖版本分散 | → 父 POM 统一 dependencyManagement/pluginManagement | 7 POMs |
| 24 | CDI.current() 滥用 | → 5 类迁移 @Inject; 3 类保留 CDI.current() + 注释 | 8 files |
| 25 | e.printStackTrace() | → log.error() | DefinitionProcessor.java, LogFilter.java |
| 33 | Maven Wrapper 过旧 | → 3.3.1/3.9.6 | mvnw, mvnw.cmd |
| 35 | maven-source-plugin 过旧 | → 3.3.1 | pom.xml |
- AbstractRepository.java:
if (sessionContext != null)→isRequestContextActive()+resolveSessionWithoutContext()fallback (CDI proxy always non-null) - FmJobStore.java:
new FmJobRepository()→CDI.current().select(FmJobRepository.class).get()(Quartz-instantiated, @Inject 不可用) - LogFilter.java:
@Inject EventBus eventBus→@Inject jakarta.inject.Provider<EventBus> eventBusProvider(Vertx 是 RUNTIME_INIT synthetic bean, 直接注入导致 STATIC_INIT 冲突) - pom.xml:
junit.version5.10.3 → 6.0.3 (Quarkus 3.33.1 引入 junit-platform 6.0.3, engine 5.10.3 与之不兼容)
ScheduledJobExecutionTest.java— 11 个测试用例覆盖 JobListener lifecycle, Function/Flow Job missing params, FmJobStore CRUD/trigger state/calendar/trigger fire/disallow concurrent/reschedule/group queries
mvn clean compile -pl '!flexmodel-engine/flexmodel-maven-plugin'→ BUILD SUCCESSmvn test -pl flexmodel-engine→ BUILD SUCCESSmvn clean test -pl flexmodel-server→ @QuarkusTest 启动失败 (pre-existing: SessionFactory/Quartz tables 在测试环境不可用, JUnit 版本已修复)
- 所有
@QuarkusTest测试因SessionFactory在测试环境不可用而启动失败 - LSP 错误 (codegen 相关类型如 FlowDefinition 在 IDE 中未解析) — 非 our edits 引起