Form.io 应用程序服务器 - 核心
Form.io应用程序服务器是用于服务单个项目模板(包括表格,资源,角色等)的表演者运行时间应用程序服务器,位于一个部署的端口. 目标是实现快速的单一租户部署,在您希望管理统一地点内所有表格和资源的地方,但将租户数据存储在自己的数据库中.
此库是 Form.io 应用程序服务器的企业核心. 它打算与@formio/appserver 库同时使用.
更多信息,请参见: https://github.com/formio/appserver 页面存档备份,存于互联网档案馆
许可证发放
企业项目需要持有许可证 appserver 选项; 通用代理网关(UAG)特性需要 uag 选项,包括 appserver。许可证配置的环境变量与 Form.io Entertainment 和 PDF 服务器相同,因此一个许可证可以运行全部.
Form.io主机服务项目(a) https: 结束点 form.io 或它的子域,例如 https://myproject.form.io 或者说 https://api.form.io/project/<id>)不需要许可证:它运行在Form.io's下,作为企业号版,并且允许Agentic Formflow(理由) hosted) (中文(简体) ). 当应用服务器拥有自己的数据库时,该豁免不适用(MONGO),当 TLS 证书验证关闭时(NODE_TLS_REJECT_UNAUTHORIZED=0),或时 NODE_EXTRA_CA_CERTS, (中文(简体) ), NODE_USE_SYSTEM_CA, (中文(简体) ), --use-system-ca 或者说 --use-openssl-ca 已经设定。 自营企业的部署总是需要许可证.
| 变量 | 配置 | 说明 |
|---|---|---|
| 英国语NAME | 一个Form.io许可证密钥。 为企业服务器签发的密钥由Form.io许可证服务器验证;离线许可证由本地验证. | |
(服务器本身,例如 UAG_LICENSE 或者说 APPSERVER_LICENSE(中文(简体) ) | license, (中文(简体) ), licenseName | 服务器自己的许可密钥,同样经过验证. licenseName 是它来自的变量,由哪个消息命名它. |
| 缩写 | licenseRemote | 设定为 true 当密钥是离线许可时, 所以许可证服务器从不联系, 对于任何密钥. |
| 服务器 | licenseServer | 验证在线密钥的许可服务器: Form.io的(form.io)之一https://*.form.io) (中文(简体) ). 默认为 https://license.form.io。 。 。 . |
licenseType | 许可证服务器的用法类型计算此服务器的用法, 例如 uagServer默认为 appServer. 见下文. | |
licenseFeature | 服务器需要运行的特性, 例如 uag默认为 appserver. 见下文. |
一个特征是,当任一键为项目的终点授予它,所以,一个表格.io许可证与 uag 选项在 LICENSE_KEY 本身就足够了,服务器自己的密钥只能添加到它上. LICENSE_KEY 仅在服务器本身的密钥丢失、无法使用或缺少时进行校验 uag 对于终点,所以一个部署 它自己的钥匙足够 从来没有发送 LICENSE_KEY 到许可服务器。 每把钥匙都靠自己来验证:一把失败会让对方的结果完好无损. 当两个特性都没有被许可时,其理由按其变量命名每个密钥.
向许可证服务器报告在线密钥 appServer 使用类型,每个项目终点一个环境,因此其范围必须包括 appServer设置的服务器 licenseType 改为在该类别下报告。 一个设置的服务器 licenseFeature 需要这一特点,而不是 appserver 装入工程。 无人机集 licenseType: 'uagServer' 财务报告和审定财务报表 licenseFeature: 'uag',因此它运行在许可证服务器接受的在线密钥上 uagServer (根据许可证计算) uagServers),或带有以下内容的离线许可证: uag 选项,为它所配置的项目服务 appserver 选项本身并不运行。 其条款每15分钟再次检查一次。 如果无法到达许可服务器,服务器继续按它最后一次确认的条件运行,从确认开始12小时;在许可的特性关闭后,直到可以再次到达.
在线密钥未验证时 NODE_TLS_REJECT_UNAUTHORIZED=0, (中文(简体) ), NODE_EXTRA_CA_CERTS, (中文(简体) ), NODE_USE_SYSTEM_CA=1, (中文(简体) ), --use-openssl-ca 或者说 --use-system-ca 设置; 使用离线许可证(LICENSE_REMOTE这里.
一旦12小时用完,或在重启之后,企业号项目在许可证服务器确认许可证之前不会加载.
模块
server.use(module) 添加一个模块:它的配置,动作,钩子,提供者,处理器,插件类型和插件,下面还有它的模块密钥. 它还需要一份模块清单,依次使用,以便: server.use([first, second])。 。 。 .
server.use({
name: 'word-count', title: 'Word count', version: '1.0.0', apiVersion: 1,
settings: (config, env) => ({ max: Number(config.wordCount?.max ?? env.WORD_COUNT_MAX ?? 1000) }),
routes: {
form: {
'POST /words/count': {
handler: (req, res, api) => {
const words = String(req.body?.text || '').split(/\s+/).filter(Boolean).length;
if (words > api.settings.max) throw api.errors.create(413, `At most ${api.settings.max} words.`, 'too_many_words');
return { words };
}
}
}
},
components: [{ type: 'wordcount', script: new URL('./dist/wordcount.js', import.meta.url) }],
initialize: (api) => api.log.info(`at most ${api.settings.max} words`),
plugins: [/* ... */]
});
- 身份证明.
name(小写字母和数字,单词加上-, (中文(简体) ),title, (中文(简体) ),version财务报告和审定财务报表apiVersion,该模块的 API 版本被写到对面。 一个模块与settings, (中文(简体) ),routes, (中文(简体) ),components, (中文(简体) ),initialize, (中文(简体) ),close函数pluginTypes需要一个name还有一个apiVersion; 服务器不提供停止启动的API版本.host将服务器命名为模块需要( n)'uag':其他服务器在启动时拒绝. 一个模块name已使用不再使用;其另一个版本停止启动。 一个命名于config.modules.disabled未使用.use()使用 ES 模块命名空间( S)require()a 默认导出. settings(config, env)在项目加载前,使用服务器的配置和环境,称为一次。 它返回一个普通对象(从不承诺),即api.settings,冷冻;一个会抛出停止启动的错误,命名模块。 模块键在之前使用router()或者说initialize();一个用后停止启动.routes.form地图"<METHOD> /<path>"改为{ name?, access?, rateLimit?, handler(req, res, api) }每张表格上挂有formRoutes(见下文).name(默认: 模块的名称) 是主机速率限制的操作, 并且rateLimit: { limit, windowMs }默认操作员设置的覆盖。 接线员跑之前 呼叫员需要一个access关于表格上提交的表格:默认['create', 'update']· ;access: false把支票交给处理者.req.form是窗体( e)id, (中文(简体) ),name, (中文(简体) ),path, (中文(简体) ),title,以及definition,它的JSON, ,req.auth打电话者和req.signal当客户端消失时中止 。 操作者回答,或返回 JSON 回答,或扔api.errors.create(status, message, code)任何其它错误被回答500, 没有细节, 处理器开始后的一个错误 以一个failed活动。 有两个模块可能不会定义相同的路由(与之相匹配的快递:大小写不敏感,没有后继斜线),一个路由可能不会使用窗体自己的路由(S/, (中文(简体) ),/submission, (中文(简体) ),/exists, (中文(简体) ),/action, (中文(简体) ),/actions和那些宿主加起来.components: formio.js组件,{ type, script, style? }作为文件路径或文件 URL.script是一个经典的浏览器脚本,可以注册组件(已构建,而不是ES模块):服务器在启动时解析,并拒绝不解析,因为浏览器将每个组件都用一个脚本来获取. 它检查文件是否存在,没有两个模块定义一个类型,并保存在其中server.modules.components();宿主为他们服务.pluginTypes可能是模块的API的一个函数,(api) => [...],用于需要它的插件类型.initialize(api)运行一次,在插件启动后,在任何请求之前;错误停止启动.close(api)当服务器被销毁时运行( r)await server.destroy()),按倒序使用模块.- 列表(上册). 一个模块
apiVersion列在GET {project}/plugins作为module:<name>,带有路径和组件类型,从未设置过.
模块 API
一个模块得到一个 api,第1版的路线, initialize, (中文(简体) ), close 函数 pluginTypes数字 :
| 成员 | 它会做什么 |
|---|---|
apiVersion, (中文(简体) ), name, (中文(简体) ), settings | 模块的API版本,名称和解析设置. |
host | 运行它的服务器 : { name, version } (单位:千美元)appserver,或东道主自己的名字). |
log.info, (中文(简体) ), log.warn, (中文(简体) ), log.error | 以模块名称为前缀的日志行. |
plugins.get(type, name), (中文(简体) ), plugins.list(type), (中文(简体) ), plugins.settings(type, name) | 启用的插件和插件的操作员设置 . |
requireAccess(auth, form, methods) | 投出403 除非呼叫者拥有其中之一 methods 在表格的提交. |
files.read(form, value, auth, { maxBytes, component, signal }) | 一个被存储的文件的字节,随呼叫者的证书和组件的存储检查一起读取. |
forms.componentAt(form, path), (中文(简体) ), forms.componentByKey(form, key) | 存储窗体的一个组件,通过数据路径(忽略了行索引)或密钥. |
errors.create(status, message, code), (中文(简体) ), errors.is(err) | 一个错误用该状态、消息和代码回答 . |
stream(req, res, run, { heartbeatMs }) | 答复 { result } 带什么 run({ signal, progress }) 返回,或向接受 text/event-stream, (中文(简体) ), progress 事件,然后 done 或者说 failed。 。 。 . |
一个主机服务器扩展了模块:一个子类可以覆盖这些.
| 覆盖 | 它的设定 |
|---|---|
hostName, (中文(简体) ), hostVersion | 服务器名称, 模块需要使用 host,及其版本(api.host) (中文(简体) ). |
moduleApiVersions | 服务器提供的API版本([1]) (中文(简体) ). |
createModuleApi(module) | API,每个模块一个: { ...super.createModuleApi(module), ...more }。一个东道主会增加成员,但不会删除成员. |
moduleClaims(definition) | 一个模块还声称,其他任何人都不能(如主机的MCP工具)作为标签; server.modules.claim(label, owner) 记录服务器本身. |
reservedRouteNames | 路由名称(速率限制操作)模块可能不取. |
moduleRouteHandlers(project, route, module) | { before, wrap }: 在每个模块路径之前的中间软件( 呼叫器检查、 身体限制、 请求 id) , 以及环绕其运行的包装器( 速率限制由 route.name,错误形状). |
moduleErrorBody(req, err) | 机体一个失败的路径答案,和一个 failed 事件载体( e){ status, error, code }) (中文(简体) ). |
reservedFormPaths | 第一个路径段模块路由可能无法使用. |
一个插件可以给定义其类型的模块命名 module (单位:千美元)module: '@scope/package/modules/name':当该类型缺失时,启动会说使用该模块.
窗体路线
routes.form 是模块在每个窗体上添加路由的通常方式。 在它下面,模块可以将任何快取路由器添加到每个形式上,并带有 formRoutes: 快速路由器(或 (req, res, next) 中间软件),或其中的列表,被挂入到每个窗体的路径上,没有访问权限检查,冲突检查或主机中间软件.
const { Router } = require('express');
const routes = Router();
routes.post('/echo', (req, res) => res.json({ form: req.form.form.name, user: req.auth?.user?._id }));
server.use({ formRoutes: routes });
- 路由器按其路径安装在每个表格的路线上(
/{formPath}/echo并按其编号/form/{formId}/echo),包括管理员在内的每个呼叫者,在表格本身的提供者和提交路线之前.req.form是窗体和req.auth呼叫者,已经认证。 路线检查了打电话者对它所作所为的访问. - 团队下方的门户团队成员通过路径联系他们,从而产生提交需求(
team_write如代理操作; 以 id 表示, 仅作为管理员. - 请求路由器不回答( 它呼叫
next(),对于一个它不服务的形式,比如说)继续到下一个路由器,然后继续到表单自己的路线,仿佛模块不存在一样. 路由器从req.form因此,他们总是看到现在的表格. - 每个模块的路由器附加在
use()命令,在模块的其余部分被应用后:一个失败的模块将不添加。 他们修过一次router()已构建服务器的路径: 在拒绝后添加更多 . - 路由错误
next(err),或者抛出,去服务器的错误处理,就像其他任何路径一样. use()拒绝formRoutes不属于路由器或中间软件(窗体的函数,(form) => router,没有。 信件以 调试,将列表中的每个模块命名为source[i]: 运行DEBUG=formio:*看到他们.