Schema-First 方法与 ThingsDB
Source: Dev.to
设置舞台:空白画布
我们首先为应用创建一个全新的集合。
new_collection('OrderApp');
理解类型化集合(核心防御)
保护的第一步是建立 schema(模式)。默认情况下,新的 ThingsDB 集合在根部只有一个空的、无限制的对象(一个基本的 thing)。任何拥有写权限的用户都可以向其中添加 任何东西。
我们通过创建一个类型 App 来锁定它,使其成为集合整体存在的结构化蓝图。
// 1. 为我的集合根部创建一个类型
new_type('App');
// 2. 将集合根部转换为 'App' 类型
.to_type('App');
为什么重要: 现在集合受 App 类型限制。除非修改符合 App 类型定义的更改,否则无法添加或修改数据。这是你的主要模式防御。
定义数据模型
接下来,我们定义想要存储的数据结构——Order 对象——以及它在 App 根部的存放位置。
// 创建一个枚举器,以保持订单状态值的一致性
set_enum('OrderStatus', {
OPEN: 'Open',
PENDING: 'Pending',
CLOSED: 'Closed',
});
// 定义 Order 结构
set_type('Order', {
id: '#', // 包装后返回的 ID 为 `id`
name: 'str',
price: 'float',
status: 'OrderStatus', // 强制状态一致性
created: 'datetime',
});
// 更新 App 类型:添加一个属性 'orders',其类型为 Order 对象数组
mod_type('App', 'add', 'orders', '[Order]');
mod_type(..) 调用会自动在集合根部创建一个空的 orders 列表,准备存放我们的数据。
建立事件通道
对于现代微服务或组件化应用来说,轮询数据变化效率低下。我们使用 Room 设置一个事件通道,以实现实时通信。
// 为 App 类型添加一个用于事件处理的 room 属性
mod_type('App', 'add', 'order_events', 'room');
// 为了便于识别,我们给房间一个固定名称
.order_events.set_name('api_order_events');
好处: 任何组件现在都可以加入此房间,立即对 new-order 或 change-order-status 等事件作出响应,而无需不断查询集合。
用过程封装逻辑(API 层)
为了确保只能通过 Procedures 对集合进行有效更改,我们把所有操作 仅 通过过程暴露。我们给这些过程加上 api_ 前缀,以便后续安全白名单的管理。
// 1. 添加新订单(对输入类型进行验证)
new_procedure('api_order_add', |name, price| {
order = Order{
name:,
price:,
};
.orders.push(order);
// 为所有监听者发送 'new-order' 事件
.order_events.emit('new-order', order.wrap());
nil;
});
// 2. 更改订单状态
new_procedure('api_order_set_status', |order_id, status| {
// 输入验证:确保新状态是有效的枚举值
new_status = OrderStatus{||status.upper()};
// 访问并更新指定的 Order 对象
Order(order_id).status = new_status;
// 发送 'change-order-status' 事件
.order_events.emit('change-order-status', order_id, new_status.value());
nil;
});
// 3. 按状态检索订单
new_procedure('api_orders_by_status', |status| {
order_status = OrderStatus{||status.upper()};
.orders.filter(|order| order.status == order_status).map_wrap();
});
严格安全:受限的 API 用户
最后也是最关键的一步是创建一个只能 执行这些特定过程 的用户。这可以防止任何外部服务绕过你定义的业务逻辑。
// 为微服务访问创建新用户
new_user('api');
// 授予集合访问权限:
// CHANGE(修改数据),JOIN(监听事件),RUN(执行过程)
grant('//OrderApi', 'api', CHANGE|JOIN|RUN);
// 白名单:这是安全锁。
// 用户只能执行以 'api_' 开头的过程并加入以 'api_' 开头的房间
whitelist_add('api', 'procedures', /^api_.*/);
whitelist_add('api', 'rooms', /^api_.*/);
// 生成并返回认证令牌
new_token('api');
通过此设置,api 用户被完全限制。它不能随意删除集合、改变结构或在不经过你在 api_ 过程里定义的验证输入和逻辑的情况下修改数据。你的集合现在真正实现了代码层面的保护!
在下一篇文章中,我们将转向客户端,演示微服务如何使用生成的令牌连接到 OrderApp 集合,执行已认证的 api_ 查询,并通过 api_order_events 房间监听实时事件变化。