锡塔里奥网
用于 数字病理学和空间生物学. Cytario Web让你探索,可视化,并注释全滑成像数据(WSI)和其他大型科学数据集(OME-TIFF,OME-Zarr,GeoTIFF,Parquet)存储在S3相容对象存储中——从原始幻灯片浏览到定量分析结果.
关于主机产品,见 citario.com (中文(简体) )。 。 。 .

建筑
| 层 | 技术 |
|---|---|
| 框架 | 反应路由器 v8 (SSR), 反应 19, Vite 7 |
| 语言 | 类型脚本( 限制模式) |
| 可视化 | deck.gl、Viv、 DuckDB-WASM、 Apache 箭头 |
| 样式 | 尾风 CSS v4, @ cytario/ 设计 |
| 认证 | OAuth 2.0通过 Keycloak, STS 获取 S3 证书 |
| 数据库 | PostgreSQL (Prisma ORM), Redis/Valkey (会议) |
| 云头 | AWS SDK v3 (S3, STS); RustFS 和其他 S3相容端点 |
| 排雷中心/发改委 | GitHub 动作、语义释放、GHCR |
插件模式
Cytario Web 支持提供额外图像的第三方插件
格式到查看器。 插件是默认的正则 npm 包
导出满足 @cytario/plugin-api 合同(见
packages/plugin-api/src/:
import type { CytarioPlugin } from "@cytario/plugin-api";
const plugin: CytarioPlugin = {
name: "@vendor/my-loader",
apiVersion: "^1.0.0",
register(ctx) {
// `extension` accepts a string, a string[] of aliases, or a RegExp
// tested against the URL. See `FormatExtension` in @cytario/plugin-api.
ctx.formats.register(["myext", "myext.gz"], {
load: async (url, opts) => {
const res = await opts.signedFetch(url, { signal: opts.signal });
// …parse and return { data: Loader, metadata: Image }
},
fileTypeMeta: { label: "My Format", icon: "Microscope" },
});
},
};
export default plugin;
装入中. 插件集固定在构建时间. 这个
CYTARIO_PLUGINS env var (comma- 分离 npm 包名) 驱动 a
写作的 Vite 码源步骤 app/plugins.generated.ts; 东道主
导入启动时的模块,调用 plugin.register(ctx) 每个
条目,并按下插件 a PluginContext 包含范围
FormatRegistry (单位:千美元) Logger。插件只能登记处理器
登记册拒绝交叉插件扩展
碰撞.
相相相相相门. 每个插件声明一个 apiVersion 铸造
区域,对照主机捆绑 @cytario/plugin-api 版本
在靴子上。 错误的插件被记录和跳过——主机保存
运行中.
安全边界. 所有S3的交通流量都通过东道主提供
signedFetch。插件供应头经过 sanitizeHeaders
(缩写: Range, (中文(简体) ), If-None-Match, (中文(简体) ), Accept, (中文(简体) ), Cache-Control· ;
一直否认: Authorization, (中文(简体) ), Host, (中文(简体) ), Cookie, (中文(简体) ), x-amz-*) 之前
正在合并到签名头后,所以插件不能覆盖
SigV4 签名或走私证书.
参考. 内建的 OME-TIFF 和 OME-Zarr 处理器
app/components/.client/ImageViewer/state/formats/builtins.ts 为:
根据同一合同执行。 一个最小的插件
__tests__/fixtures/noop-plugin/ 并行使登记册、登记册和
apiVersion门,以及 FILE TYPE 注册自动开发.
使用 @cytario/web 作为软件包
@cytario/web 可安装为 npm 依赖并组装成
与一个或多个格式处理器一起可部署的容器
插件 。 就是这样 Cytario 企业版 构建:
AGPL 许可的开放核心(@cytario/web)与专有性捆绑在一起
插件(例如供应商专用的缩微镜格式加载器)到
生成单一可部署图像。 任何具有满足插件的人
页:1 @cytario/plugin-api 合同可遵循
同样的模式.
许可证义务.
@cytario/web许可证根据 AGPL-3.0 导弹. 分配或操作一个集会 包括它——包括通过网络作为服务——触发 AGPL的消息来源披露要求:完整对应 组件的来源(包括链接到的任何专有插件) 根据AGPL-3.0,必须提供给用户。 如果是这样 与你的销售模式不符 商业执照是 - 联系我们 citario.com (中文(简体) )。 。 。 .
消费者的工作是安装软件包 CYTARIO_PLUGINS, (中文(简体) ),
并引用捆绑的CLI:
# .npmrc — only required if any of the plugins ship from a registry
# other than public npm. The example below routes a hypothetical
# closed plugin to GitHub Packages while keeping @cytario/web and
# @cytario/plugin-api on public npm.
@your-org/closed-plugin:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GH_TOKEN}
npm install @cytario/web @cytario/plugin-api @your-org/my-plugin
# Production build with the bundled plugin set.
CYTARIO_PLUGINS=@your-org/my-plugin npx cytario-web build
# Dev server with HMR.
CYTARIO_PLUGINS=@your-org/my-plugin npx cytario-web dev
# Production server against the existing build/ output.
npx cytario-web start
CYTARIO_PLUGINS 是 npm 包名的逗号分隔列表。 每个
包的默认导出必须满足
@cytario/plugin-api 合同.
国 际
| 命令 | 行为 |
|---|---|
cytario-web build | 代码( Vite 插件读取) CYTARIO_PLUGINS页:1 react-router build 对已安装的软件包根. |
cytario-web dev | 代码 + react-router dev. 额外参数(--port, (中文(简体) ), --host页:1. |
cytario-web start | NODE_ENV=production node server.ts 对抗捆绑的 build/server/index.js。 。 。 . |
所有子命令都针对 @cytario/web自己的安装目录;
消费者从来不需要知道 盘上布局.
参考多克文件
# syntax=docker/dockerfile:1.7
FROM node:24-slim AS build
WORKDIR /app
COPY .npmrc package.json package-lock.json ./
# BuildKit secret: GH_TOKEN is exposed as an env var only for this
# RUN. It is never written to a layer and never appears in `docker
# history` or `docker inspect`. Do NOT add `ARG GH_TOKEN` — that
# would bake the value into image metadata.
RUN --mount=type=secret,id=gh_token,env=GH_TOKEN \
npm ci
ENV CYTARIO_PLUGINS=@your-org/my-plugin
RUN npx cytario-web build
FROM node:24-slim
WORKDIR /app
COPY --from=build /app .
ENV NODE_ENV=production
EXPOSE 3000
CMD ["npx", "cytario-web", "start"]
使用 BuildKit 密钥从您环境中构建图像 (或文件):
# Docker — environment source (e.g. CI runner with a GH_TOKEN secret):
GH_TOKEN=… docker build --secret id=gh_token,env=GH_TOKEN -t my-cytario-image .
# Docker — file source:
docker build --secret id=gh_token,src=./gh_token.txt -t my-cytario-image .
# Podman — only file source is supported (no env= flavor); write the
# token to a temp file first:
printf '%s' "$GH_TOKEN" > /tmp/gh_token && \
podman build --secret id=gh_token,src=/tmp/gh_token -t my-cytario-image . && \
rm -f /tmp/gh_token
这个 # syntax=docker/dockerfile:1.7 解析指令在顶端
docker的"BuildKit"让Docker感到荣幸
波德曼的土生土长的解析器——其余文件(多相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相,
--mount=type=cache, (中文(简体) ), --mount=type=secret)在两个引擎上工作.
混合注册注.
@cytario/web财务报告和审定财务报表@cytario/plugin-api公开发布 npm. 插件在任何地方都可以自由发布 例如——公共npm,GitHub套件,或私人注册. 因为 单一范围@cytario:registry指令无法引导两者 相同范围的公共-npm和GitHub-ackages包, 消费者将每个包装的单个插件包固定<pkg>:registry=…上面显示的覆盖 .
许可证
这个项目的许可证根据 AGPL-3.0 导弹. 源代码公开提供,以提供充分的透明度并确保我们的用户长期访问,独立于Cytario作为一个公司.
地方发展
先决条件
该应用程序需要多个后端服务. 通过Podman提供本地集群:
cd devenv
podman kube play local-deployment.yaml
| 服务 | 端口 | 说明 |
|---|---|---|
| 键盘 | 8080 (韩语) | 身份提供商(主管/主管) |
| RustFS 红外线 | 9000,9001 (韩语) | S3相容对象存储( 位于 9001 上) |
| 邮递SQL | 第5433条 | 应用程序数据库 |
| 瓦尔基 | 6379 (英语) | 会话缓存( 重新兼容) |
停下来: podman kube down devenv/local-deployment.yaml
Postgres 18 数据布局页:1
postgres:18图像存储数据在一个 主版子目录并挂载在/var/lib/postgresql(无)/var/lib/postgresql/data看到了吗 docker-library/postgres# 37 (中文(简体) )) (中文(简体) ). 如果您之前运行过这个基于 16/17 的旧文件组, 那么您devenv/*-dataPostgres dirs 持有旧平面布局和18个容器 将拒绝从他们开始。 探测器数据是一次性的..rm -rf devenv/keycloak-db-data devenv/cytario-db-data并重放 集群(然后重新运行 Open Tofu 拖曳)。 17和16号邮袋 在母路径下安放精细, 因此没有被锁定的版本 .
键盘
集群运行一个股票 键盘 与
organization 特性已启用。 第一只靴子给你空了 master 领域——
规定 无论是 下方为一发OpenTofu靴子(建议)
或者说 通过管理员控制台(http://localhost:8080,管理员/管理人员:
- 创建一个域( 如 .
cytario切换 组织 打开( Realm) 设置 – 常规 . - 创建机密客户端
cytario-web带有密钥, 有效的方向 用于http://localhost:3000/*和网络来源http://localhost:3000。 。 。 . 点BASE_URL编号 :CLIENT_ID编号 :CLIENT_SECRET内.env临τ. - 为 Keycloak 管理API 创建服务账户客户端( 见
KC_ADMIN_CLIENT_ID内.env.template与manage-realm, (中文(简体) ),manage-users, (中文(简体) ),view-realm从realm-management客户端. - 创建用户、组织、指定用户并添加到 视需要(见 Keycloak组织) (中文(简体) ).
Open Tofu 拖曳( 建议)
devenv/terraform/ 创建一个应用程序需要的每样东西 apply页:1
与组织有关的领域 cytario-web 客户端( 机密) 1234567, (中文(简体) ),
匹配 .env.template与内置 organization 客户范围
鸟巢要求应用读取)和顶层 groups 索赔,a demo
联合国 Lab编号 :TeamX 类群加上 admins 赠款类别
页:1 * 管理范围,以及两个用户([email protected] 编号 : demo-admin, (中文(简体) ),
[email protected] 编号 : demo-viewer:
cd devenv/terraform
tofu init
tofu apply # ~10s against the running Keycloak
# Org-group membership goes through the org-scoped admin API, which the
# terraform provider cannot express — the companion script does it:
./org-membership.sh cytario "$(tofu output -raw organization_id)" [email protected]:admins [email protected]:Lab [email protected]:Lab
然后设置在 .env数字 :
BASE_URL=http://localhost:8080/realms/cytario
CLIENT_ID=cytario-web
CLIENT_SECRET=1234567
tofu destroy 删除域; 重运行 apply 之后
podman kube down (数据 dirs 保存) 聚合状态 .
为什么是Keycloak? Cytario的制作特性堆栈带有自定义 协议映射器( 用于 RustFS)
groups个人形象。 给 开源本地设置, 股票 Keycloak 覆盖认证和租户 路由; 仅在连接 RustFS 时需要存储映射器 (b) 提供木桶政策共享服务(见下文).
RustFS (S3兼容存储)
集群运行 RustFS 红外线 — S3 相容对象
存储平台目标,用于自备部署。 穿着靴子
针对当地Keycloak域(客户)的网络身份联合会
cytario-web,索赔 groups) (中文(简体) ).
- 控制台 —— 说
http://localhost:9001(rustfsadmin/rustfsadmin) (俄语). 创建 桶子,IAM政策,和小组在这里. - 从应用程序连接 —— RustFS 供应商连接需要
endpoint(单位:千美元)http://localhost:9000(为当地组别)和 用户指南中描述的操作员侧提供 RustFS 存储 入职 运行簿:每个组织IAM政策和索赔名称 被他们附着的团体。 在开放源码软件部署(无行政管理门户)中 从部署时目录文件读取的连接选择器 – 见devenv/providers.example.yaml用于AWS和RustFS两个条目.
对象存储于.env
浏览器会直接讨论对象存储(SigV4-已签名的抓取). 这个
应用程序的内容- 安全政策 connect-src 每项请求
所以每个存储端点
拥有一个供应商的连接,包括当地RustFS
没有允许列表配置 。 端点必须使用 https: 生产中;当地
dev环境可以承认一个平地..http 对象存储通过
CYTARIO_DEV_CONNECT_HOSTS数字 :
# .env — local dev only: admit the local RustFS object store
CYTARIO_DEV_CONNECT_HOSTS=http://localhost:9000
开始
npm install
cp .env.template .env # Pre-configured for the Podman cluster
npm run dev
Keycloak组织
该应用程序使用 Keycloak组织 作为租户的边界。 每届会议必须有一个活跃的组织——没有一届的会议被改用于 /onboarding 并且无法到达任何租户范围的道路.
当地波德曼集群的靴子是Keycloak的股票 KC_FEATURES=organization 启用但为空域 – 跟踪下面的设置步骤 键盘 上面创建域,客户,和组织。 要运行一个自定义的 Keycloak 部署 :
- 启用组织域特性( E)
KC_FEATURES=organization在 Keycloak 服务器上 财务报告和审定财务报表 (原始内容存档于2017-03-21). the per-realm toggle in the admin UI. - 允许
cytario-web-admin服务账户view-realm+ 键manage-realm编辑realm-management客户端。 无专用view-organizations需要发挥更广泛的作用. - 指定至少一个组织的每个登录合格用户。 无组织用户登陆
/onboarding。 。 。 .
组织内的小组成员(分组: /admin/users驱动租户授权。 A 类 /admins 某一范围的分组赋予该范围的管理权 * 哨兵范围覆盖整个组织.
会话缓存( Redis/ Valkey)
会话持有OAuth访问/更新/ID符和短寿命STS证书. 生产中需要TLS. 应用程序拒绝启动时 NODE_ENV !== "development" 除非下列情况之一属实:
| 新闻 var | 数值 | 含义 |
|---|---|---|
REDIS_TLS | "true" | 在 TLS (建议) 中环绕 ioredis 连接 . |
REDIS_CA_CERT | 总统 | 自签名部署的可选 CA 捆绑 。 多行 PEM 字符串. |
REDIS_TLS_SERVER_NAME | 主机名 | 可选 SNI / 证书主机名覆盖 . |
REDIS_INSECURE_ALLOW_PLAINTEXT | "true" | 明确选择退出信任的私人网络。 记录一个警告。 不用于共享基础设施. |
本地的Podman集群运行没有 TLS 的 Valkey , 因为 NODE_ENV=development. 管理下的Valkey部署(海螺图、AWS AlastiCache等)应当设置 REDIS_TLS=true。Valkey 重用标准 6379 tLS 时端口 tls.enabled 已设定 —— 它不会移动收听器到 6380 并拒绝在同一端口上的简单文字——请离开 REDIS_PORT 时间 6379 除非您的提供者明确发布单独的 TLS 端点 .
在生产集群中,一个常见的模式是集群内部CA(例如cert-manager + trust-manager 将 CA的公用证书作为ConfigMap 分发),其PEM然后通过普通程序反馈给应用 REDIS_CA_CERT env var path – 该应用程序对信任来源一无所知. CA 的公用证书以一个 cytario-internal-ca 由信任管理者配置地图 cytario-web 转向图的 redis.caCertConfigMap.{name,key} 把它接入舱内 REDIS_CA_CERT 通过 valueFrom.configMapKeyRef. app通过普通的env var路径看到PEM——不需要信任源的代码侧知识.
数据库
PostgreSQL 为 Prisma ORM 软件连接 DATABASE_URL 内 .env。 。 。 .
npx prisma migrate dev --name <migration-name> # Create + apply migration
npx prisma migrate deploy # Apply pending migrations
npx prisma studio # Database GUI
npx prisma generate # Regenerate client
克隆DEV数据库
scripts/clone-dev-db.mjs 将 DEV PostgreSQL (schema + 数据) 复制到本地的 Podman 集群的 Postgres 。 它使用 pg 驱动程序而不是 pg_dump编号 :psql,所以它通过pgbuncers(交易-集合模式)工作—— pg_dump 失败的会话级操作.
# 1. Make sure the local cluster is up and Postgres is reachable on :5433.
cd devenv && podman kube play local-deployment.yaml && cd ..
# 2. Clone. Pass the DEV DATABASE_URL as the source and the local URL
# (from .env.template) as the target. The script refuses to write to
# any host that doesn't resolve to loopback.
npm run clone-dev-db -- \
"$DEV_DATABASE_URL" \
"postgresql://cytario:cytario@localhost:5433/cytario"
# 3. Apply any migrations the local checkout has that DEV doesn't yet.
DATABASE_URL="postgresql://cytario:cytario@localhost:5433/cytario" \
npx prisma migrate deploy
克隆复制了每个公示表,包括 _prisma_migrations因此,Prisma认识到哪些移徙已经实施。 如果目标数据库已经有表格,则脚本列出并请求确认后再放弃.
测试
npm test # Unit & component tests (vitest, watch mode)
npm run coverage # Unit tests with coverage report
E2E测试(英语:Playwright)活在一个兄弟会寄存器中,通过交叉repo调度在每一个PR上自动被触发.
设计系统
发展 @cytario/design - 或任何 @cytario 软件包——本地、点 CYTARIO_LOCAL_PATHS 在开始 dev 服务器前的兄弟会结账 :
CYTARIO_LOCAL_PATHS='{"@cytario/design":"../cytario-design"}' npm run dev
每个条目都别名为兄弟会 src/index.ts,因此Vite直接服务于其TypeScript源,并通过HMR来取取编辑器——没有 npm link没有重建步骤。 亚伯路的进口遵循兄弟会树(styles/tokens.css → src/…); 编译样式表(styles.css → dist/index.css从兄弟那里得到的 dist/运行 npm run watch:css 在设计还原的同时.
在变量未设置的情况下,应用程序使用 npm 发布的软件包——没有关于构建变化的内容.
调试
该应用程序使用 苏斯坦德 国家管理 devtools 中间软件。 安装 Redux 开发工具 浏览器扩展以检查存储状态和动作 .
提交
常规武器委员会 通过 承诺 (单位:千美元)feat, (中文(简体) ), fix, (中文(简体) ), docs, (中文(简体) ), refactor, (中文(简体) ), test, (中文(简体) ), build, (中文(简体) ), ci, (中文(简体) ), chore(见a/c.5/49/70,附件).
部署
npm run build # Outputs build/server and build/client
npm start # Production server
关于集装箱部署,见 Dockerfile。图像建立在无穷无尽的基础上(gcr.io/distroless/nodejs24-debian12); 数据库迁移在启动时通过 docker-entrypoint.mjs。 。 。 .