diff --git a/content/docs/00-intro/ai-codex.en.mdx b/content/docs/00-intro/ai-codex.en.mdx new file mode 100644 index 0000000..41c6ca7 --- /dev/null +++ b/content/docs/00-intro/ai-codex.en.mdx @@ -0,0 +1,373 @@ +--- +title: The AI Codex +description: Best practices for AI-assisted ObjectStack development +--- + +# The AI Codex + +This guide helps developers leverage AI tools to enhance ObjectStack development efficiency. + +## Why AI Collaboration + +ObjectStack, as a protocol-driven platform, involves a lot of: +- Schema definitions +- UI protocol configurations +- Repetitive CRUD code +- API documentation generation + +These are all well-suited for AI assistance, significantly improving development efficiency. + +## Core Terminology + +Using consistent terminology when collaborating with AI is crucial. Here are ObjectStack's core terms: + +### Data Layer Terms + +- **Schema**: Data model definition in JSON format describing table structure +- **Object**: Business object, corresponding to database table +- **Field**: Object field, corresponding to database column +- **Reference**: Relationship field, used to establish associations between objects +- **Virtual Field**: Computed field not stored in database + +### Query Layer Terms + +- **ObjectQL**: Unified cross-database query language +- **Query DSL**: Query Domain-Specific Language +- **Filter**: Query filter conditions +- **Aggregation**: Aggregation queries +- **Driver**: Database driver, e.g., `@objectql/driver-mysql` + +### UI Layer Terms + +- **ObjectUI**: JSON-driven UI protocol +- **Component**: Components like Form, Table, Card, etc. +- **Action**: Interactive actions like API requests, page navigation, etc. +- **Renderer**: Converts JSON protocol to actual UI + +### Platform Layer Terms + +- **ObjectOS**: Runtime platform +- **Plugin**: Extension for functionality +- **Hook**: Lifecycle hooks like beforeInsert, afterUpdate +- **RBAC**: Role-Based Access Control +- **FLS**: Field-Level Security + +## AI-Assisted Schema Design + +### Prompt Template + +``` +I need to create a Schema for [business scenario] in ObjectStack. + +Business requirements: +- [Requirement 1] +- [Requirement 2] +- [Requirement 3] + +Please generate a JSON Schema compliant with ObjectStack specifications, including: +1. All necessary field definitions +2. Field types (text, number, date, reference, etc.) +3. Field constraints (required, unique, etc.) +4. Relationship definitions between objects +``` + +### Example Conversation + +**User**: +``` +I need to create a Schema for an e-commerce order management system in ObjectStack. + +Business requirements: +- Manage customer information (name, phone, email) +- Manage product information (name, price, stock) +- Manage orders (order number, customer, product list, total amount, status) +- Order statuses include: pending, paid, shipped, completed, cancelled + +Please generate a JSON Schema compliant with ObjectStack specifications. +``` + +**AI Response**: +```json +{ + "objects": { + "customers": { + "label": "Customer", + "fields": { + "name": { "type": "text", "label": "Name", "required": true }, + "phone": { "type": "text", "label": "Phone" }, + "email": { "type": "email", "label": "Email", "unique": true } + } + }, + "products": { + "label": "Product", + "fields": { + "name": { "type": "text", "label": "Product Name", "required": true }, + "price": { "type": "number", "label": "Price", "required": true }, + "stock": { "type": "number", "label": "Stock", "default": 0 } + } + }, + "orders": { + "label": "Order", + "fields": { + "order_no": { "type": "text", "label": "Order Number", "unique": true }, + "customer": { "type": "reference", "ref": "customers", "label": "Customer" }, + "items": { "type": "grid", "label": "Order Items", "fields": { + "product": { "type": "reference", "ref": "products" }, + "quantity": { "type": "number", "default": 1 }, + "price": { "type": "number" } + }}, + "total": { "type": "number", "label": "Total Amount" }, + "status": { + "type": "select", + "label": "Status", + "options": ["pending", "paid", "shipped", "completed", "cancelled"], + "default": "pending" + } + } + } + } +} +``` + +## AI-Assisted UI Protocol Generation + +### Prompt Template + +``` +I need to create ObjectUI protocol for [object name]. + +UI requirements: +- [UI requirement 1] +- [UI requirement 2] + +Please generate JSON protocol compliant with ObjectStack ObjectUI specifications. +``` + +### Example Conversation + +**User**: +``` +I need to create a list page and detail form for the orders object. + +UI requirements: +- List page displays order number, customer name, total amount, status +- Support filtering by status +- Clicking an order shows details +- Detail form can edit order status +``` + +**AI Response**: +```json +{ + "pages": { + "order_list": { + "type": "page", + "title": "Order List", + "components": [ + { + "type": "table", + "object": "orders", + "columns": [ + { "field": "order_no", "label": "Order No" }, + { "field": "customer.name", "label": "Customer" }, + { "field": "total", "label": "Total" }, + { "field": "status", "label": "Status" } + ], + "filters": [ + { "field": "status", "type": "select", "label": "Status" } + ], + "actions": [ + { "type": "navigate", "target": "order_detail", "label": "View" } + ] + } + ] + }, + "order_detail": { + "type": "page", + "title": "Order Detail", + "components": [ + { + "type": "form", + "object": "orders", + "fields": [ + { "name": "order_no", "label": "Order No", "readonly": true }, + { "name": "customer", "label": "Customer", "readonly": true }, + { "name": "total", "label": "Total", "readonly": true }, + { "name": "status", "label": "Status", "type": "select" } + ], + "actions": [ + { "type": "submit", "label": "Save" } + ] + } + ] + } + } +} +``` + +## AI-Assisted Plugin Development + +### Prompt Template + +``` +I need to develop a server-side plugin for ObjectStack. + +Functional requirements: +- [Requirement description] + +Please generate code compliant with ObjectStack plugin specifications, including: +1. Plugin entry file +2. Necessary lifecycle hooks +3. Error handling +``` + +### Example Code Generation + +**User**: +``` +I need to develop a plugin that automatically sends email notifications after order creation. +``` + +**AI Response**: +```javascript +// plugins/order-notification.js +module.exports = { + name: 'order-notification', + + hooks: { + afterInsert: async (context, object, record) => { + if (object === 'orders') { + const { customer, total, order_no } = record; + + // Query customer email + const customerData = await context.db.query({ + object: 'customers', + filters: { _id: customer }, + fields: ['email', 'name'] + }); + + if (customerData && customerData[0]?.email) { + await context.email.send({ + to: customerData[0].email, + subject: `Order Confirmation - ${order_no}`, + body: ` + Dear ${customerData[0].name}, + + Your order ${order_no} has been created successfully. + Order amount: $${total} + + Thank you for your purchase! + ` + }); + } + } + } + } +}; +``` + +## AI-Assisted Documentation Generation + +### Auto-Generate API Documentation + +Use TypeDoc or JSDoc to automatically generate documentation from code comments: + +```typescript +/** + * Query object data + * @param {Object} options - Query options + * @param {string} options.object - Object name + * @param {Object} options.filters - Filter conditions + * @param {Object} options.sort - Sort rules + * @param {number} options.limit - Limit count + * @returns {Promise} Query results + * + * @example + * const users = await db.query({ + * object: 'users', + * filters: { age: { $gt: 18 } }, + * sort: { created_at: 'desc' }, + * limit: 10 + * }); + */ +async function query(options) { + // Implementation code +} +``` + +Then run: +```bash +npx typedoc --out docs/api src/ +``` + +## Best Practices + +### 1. Clear Context + +When conversing with AI, clearly specify: +- Using ObjectStack framework +- Protocol specifications to follow +- Target database type (if special requirements) + +### 2. Provide Complete Information + +Including: +- Detailed description of business requirements +- Relationships between data models +- UI interaction flows +- Special business rules + +### 3. Validate Generated Code + +AI-generated code needs: +- Verification of ObjectStack compliance +- Functional testing +- Security and performance review + +### 4. Iterative Optimization + +If results are not ideal: +- Provide more specific requirements +- Give examples or references +- Break down requirements step by step + +## Recommended Tools + +### Code Generation +- **GitHub Copilot**: Code completion and generation +- **ChatGPT / Claude**: Architecture design and problem solving +- **Cursor**: AI-assisted programming IDE + +### Documentation Generation +- **TypeDoc**: TypeScript API documentation +- **JSDoc**: JavaScript API documentation +- **Swagger/OpenAPI**: REST API documentation + +### Testing Assistance +- **AI-generated test cases**: Cover edge cases +- **AI code review**: Discover potential issues + +## Considerations + +### ⚠️ AI Limitations + +- AI may generate code not compliant with latest specifications +- Manual verification of business logic correctness needed +- Complex architecture design still requires human decision-making + +### ✅ AI Advantages + +- Quickly generate repetitive code +- Provide multiple implementation options +- Assist documentation writing +- Code refactoring suggestions + +## Summary + +AI is a powerful assistant for ObjectStack development, but not a replacement. Best practices: + +1. **Use AI to accelerate**: Schema definitions, UI protocols, repetitive code +2. **Manual review**: Business logic, security, performance optimization +3. **Continuous learning**: Understand AI capabilities and boundaries, use appropriately + +By properly using AI tools, development efficiency can be improved 3-5x, allowing developers to focus more on core business logic. diff --git a/content/docs/00-intro/ai-codex.zh-CN.mdx b/content/docs/00-intro/ai-codex.zh-CN.mdx new file mode 100644 index 0000000..856b9a3 --- /dev/null +++ b/content/docs/00-intro/ai-codex.zh-CN.mdx @@ -0,0 +1,373 @@ +--- +title: AI 协作指引 +description: 使用 AI 辅助开发 ObjectStack 项目的最佳实践 +--- + +# AI 协作指引(The AI Codex) + +本指南帮助开发者充分利用 AI 工具来提升 ObjectStack 项目的开发效率。 + +## 为什么需要 AI 协作 + +ObjectStack 作为一个协议驱动的平台,有大量的: +- Schema 定义 +- UI 协议配置 +- 重复性 CRUD 代码 +- API 文档生成 + +这些都非常适合使用 AI 辅助完成,可以显著提升开发效率。 + +## 核心术语表 + +在与 AI 协作时,使用统一的术语非常重要。以下是 ObjectStack 的核心术语: + +### 数据层术语 + +- **Schema**:数据模型定义,使用 JSON 格式描述表结构 +- **Object**:业务对象,对应数据库中的表 +- **Field**:对象的字段,对应数据库列 +- **Reference**:关系字段,用于建立对象间的关联 +- **Virtual Field**:虚拟字段,不存储在数据库中,通过计算得出 + +### 查询层术语 + +- **ObjectQL**:跨数据库的统一查询语言 +- **Query DSL**:查询领域特定语言 +- **Filter**:查询过滤条件 +- **Aggregation**:聚合查询 +- **Driver**:数据库驱动,如 `@objectql/driver-mysql` + +### UI 层术语 + +- **ObjectUI**:JSON 驱动的 UI 协议 +- **Component**:组件,如 Form、Table、Card 等 +- **Action**:交互动作,如 API 请求、页面跳转等 +- **Renderer**:渲染器,将 JSON 协议转换为实际 UI + +### 平台层术语 + +- **ObjectOS**:运行时平台 +- **Plugin**:插件,用于扩展功能 +- **Hook**:生命周期钩子,如 beforeInsert、afterUpdate +- **RBAC**:基于角色的访问控制 +- **FLS**:Field-Level Security,字段级安全 + +## AI 辅助 Schema 设计 + +### 提示词模板 + +``` +我需要为 ObjectStack 创建一个 [业务场景] 的 Schema。 + +业务需求: +- [需求 1] +- [需求 2] +- [需求 3] + +请生成符合 ObjectStack 规范的 JSON Schema,包括: +1. 所有必要的字段定义 +2. 字段类型(text, number, date, reference 等) +3. 字段约束(required, unique 等) +4. 对象之间的关系定义 +``` + +### 示例对话 + +**用户**: +``` +我需要为 ObjectStack 创建一个电商订单管理系统的 Schema。 + +业务需求: +- 管理客户信息(姓名、电话、邮箱) +- 管理产品信息(名称、价格、库存) +- 管理订单(订单号、客户、产品列表、总金额、状态) +- 订单状态包括:待支付、已支付、已发货、已完成、已取消 + +请生成符合 ObjectStack 规范的 JSON Schema。 +``` + +**AI 回复**: +```json +{ + "objects": { + "customers": { + "label": "客户", + "fields": { + "name": { "type": "text", "label": "姓名", "required": true }, + "phone": { "type": "text", "label": "电话" }, + "email": { "type": "email", "label": "邮箱", "unique": true } + } + }, + "products": { + "label": "产品", + "fields": { + "name": { "type": "text", "label": "产品名称", "required": true }, + "price": { "type": "number", "label": "价格", "required": true }, + "stock": { "type": "number", "label": "库存", "default": 0 } + } + }, + "orders": { + "label": "订单", + "fields": { + "order_no": { "type": "text", "label": "订单号", "unique": true }, + "customer": { "type": "reference", "ref": "customers", "label": "客户" }, + "items": { "type": "grid", "label": "订单项", "fields": { + "product": { "type": "reference", "ref": "products" }, + "quantity": { "type": "number", "default": 1 }, + "price": { "type": "number" } + }}, + "total": { "type": "number", "label": "总金额" }, + "status": { + "type": "select", + "label": "状态", + "options": ["pending", "paid", "shipped", "completed", "cancelled"], + "default": "pending" + } + } + } + } +} +``` + +## AI 辅助 UI 协议生成 + +### 提示词模板 + +``` +我需要为 [对象名称] 创建 ObjectUI 协议。 + +UI 需求: +- [UI 需求 1] +- [UI 需求 2] + +请生成符合 ObjectStack ObjectUI 规范的 JSON 协议。 +``` + +### 示例对话 + +**用户**: +``` +我需要为 orders 对象创建一个列表页面和详情表单。 + +UI 需求: +- 列表页显示订单号、客户名称、总金额、状态 +- 支持按状态筛选 +- 点击订单可以查看详情 +- 详情表单可以编辑订单状态 +``` + +**AI 回复**: +```json +{ + "pages": { + "order_list": { + "type": "page", + "title": "订单列表", + "components": [ + { + "type": "table", + "object": "orders", + "columns": [ + { "field": "order_no", "label": "订单号" }, + { "field": "customer.name", "label": "客户" }, + { "field": "total", "label": "总金额" }, + { "field": "status", "label": "状态" } + ], + "filters": [ + { "field": "status", "type": "select", "label": "状态" } + ], + "actions": [ + { "type": "navigate", "target": "order_detail", "label": "查看" } + ] + } + ] + }, + "order_detail": { + "type": "page", + "title": "订单详情", + "components": [ + { + "type": "form", + "object": "orders", + "fields": [ + { "name": "order_no", "label": "订单号", "readonly": true }, + { "name": "customer", "label": "客户", "readonly": true }, + { "name": "total", "label": "总金额", "readonly": true }, + { "name": "status", "label": "状态", "type": "select" } + ], + "actions": [ + { "type": "submit", "label": "保存" } + ] + } + ] + } + } +} +``` + +## AI 辅助插件开发 + +### 提示词模板 + +``` +我需要为 ObjectStack 开发一个服务端插件。 + +功能需求: +- [需求描述] + +请生成符合 ObjectStack 插件规范的代码,包括: +1. 插件入口文件 +2. 必要的生命周期钩子 +3. 错误处理 +``` + +### 示例代码生成 + +**用户**: +``` +我需要开发一个插件,在订单创建后自动发送邮件通知。 +``` + +**AI 回复**: +```javascript +// plugins/order-notification.js +module.exports = { + name: 'order-notification', + + hooks: { + afterInsert: async (context, object, record) => { + if (object === 'orders') { + const { customer, total, order_no } = record; + + // 查询客户邮箱 + const customerData = await context.db.query({ + object: 'customers', + filters: { _id: customer }, + fields: ['email', 'name'] + }); + + if (customerData && customerData[0]?.email) { + await context.email.send({ + to: customerData[0].email, + subject: `订单确认 - ${order_no}`, + body: ` + 尊敬的 ${customerData[0].name}: + + 您的订单 ${order_no} 已创建成功。 + 订单金额:¥${total} + + 感谢您的订购! + ` + }); + } + } + } + } +}; +``` + +## AI 辅助文档生成 + +### 自动生成 API 文档 + +使用 TypeDoc 或 JSDoc 从代码注释自动生成文档: + +```typescript +/** + * 查询对象数据 + * @param {Object} options - 查询选项 + * @param {string} options.object - 对象名称 + * @param {Object} options.filters - 过滤条件 + * @param {Object} options.sort - 排序规则 + * @param {number} options.limit - 限制数量 + * @returns {Promise} 查询结果 + * + * @example + * const users = await db.query({ + * object: 'users', + * filters: { age: { $gt: 18 } }, + * sort: { created_at: 'desc' }, + * limit: 10 + * }); + */ +async function query(options) { + // 实现代码 +} +``` + +然后运行: +```bash +npx typedoc --out docs/api src/ +``` + +## 最佳实践 + +### 1. 明确上下文 + +在与 AI 对话时,明确说明: +- 使用的是 ObjectStack 框架 +- 需要遵循的协议规范 +- 目标数据库类型(如果有特殊需求) + +### 2. 提供完整信息 + +包括: +- 业务需求的详细描述 +- 数据模型之间的关系 +- UI 交互流程 +- 特殊的业务规则 + +### 3. 验证生成的代码 + +AI 生成的代码需要: +- 检查是否符合 ObjectStack 规范 +- 测试功能是否正常 +- 审查安全性和性能 + +### 4. 迭代优化 + +如果生成的结果不理想: +- 提供更具体的要求 +- 给出示例或参考 +- 分步骤细化需求 + +## 工具推荐 + +### 代码生成 +- **GitHub Copilot**:代码补全和生成 +- **ChatGPT / Claude**:架构设计和问题解答 +- **Cursor**:AI 辅助编程 IDE + +### 文档生成 +- **TypeDoc**:TypeScript API 文档 +- **JSDoc**:JavaScript API 文档 +- **Swagger/OpenAPI**:REST API 文档 + +### 测试辅助 +- **AI 生成测试用例**:覆盖边界情况 +- **AI 代码审查**:发现潜在问题 + +## 注意事项 + +### ⚠️ AI 的局限性 + +- AI 可能生成不符合最新规范的代码 +- 需要人工验证业务逻辑的正确性 +- 复杂的架构设计仍需要人工决策 + +### ✅ AI 的优势 + +- 快速生成重复性代码 +- 提供多种实现方案 +- 辅助文档编写 +- 代码重构建议 + +## 总结 + +AI 是 ObjectStack 开发的强大助手,但不是替代品。最佳实践是: + +1. **使用 AI 加速**:Schema 定义、UI 协议、重复代码 +2. **人工审核**:业务逻辑、安全性、性能优化 +3. **持续学习**:了解 AI 的能力边界,合理使用 + +通过合理使用 AI 工具,可以将开发效率提升 3-5 倍,让开发者更专注于核心业务逻辑。 diff --git a/content/docs/00-intro/index.en.mdx b/content/docs/00-intro/index.en.mdx new file mode 100644 index 0000000..4cba704 --- /dev/null +++ b/content/docs/00-intro/index.en.mdx @@ -0,0 +1,14 @@ +--- +title: Preamble & Design +description: ObjectStack's worldview, values, and constitution +--- + +# Preamble & Design + +This section introduces ObjectStack's core philosophy, design principles, and AI collaboration methods. + +## Contents + +- [Welcome to ObjectStack](./welcome) - Learn about the project vision and core components +- [The Manifesto](./manifesto) - Core design principles and philosophy +- [The AI Codex](./ai-codex) - Best practices for AI-assisted development diff --git a/content/docs/00-intro/index.zh-CN.mdx b/content/docs/00-intro/index.zh-CN.mdx new file mode 100644 index 0000000..7964a4f --- /dev/null +++ b/content/docs/00-intro/index.zh-CN.mdx @@ -0,0 +1,14 @@ +--- +title: 序言与顶层设计 +description: ObjectStack 的世界观、价值观和总纲 +--- + +# 序言与顶层设计 + +本章节介绍 ObjectStack 的核心理念、设计原则和 AI 协作方法。 + +## 章节内容 + +- [欢迎来到 ObjectStack](./welcome) - 了解项目愿景和核心组件 +- [架构宪法](./manifesto) - 核心设计原则和理念 +- [AI 协作指引](./ai-codex) - 使用 AI 辅助开发的最佳实践 diff --git a/content/docs/00-intro/manifesto.en.mdx b/content/docs/00-intro/manifesto.en.mdx new file mode 100644 index 0000000..7ebe54d --- /dev/null +++ b/content/docs/00-intro/manifesto.en.mdx @@ -0,0 +1,244 @@ +--- +title: The Manifesto +description: ObjectStack's core design principles +--- + +# The Manifesto + +ObjectStack's design follows three core principles that form the foundation of the entire architecture. + +## Principle I: Protocol Neutrality + +### Definition + +The protocol layer must remain technology-neutral, not dependent on any specific implementation technology. + +### Core Philosophy + +**"Protocol is the standard, implementation is replaceable"** + +ObjectStack's protocol layer (ObjectQL, ObjectUI) is purely specification definition, containing no concrete implementation. This means: + +- ObjectQL protocol can have multiple implementations (JavaScript, Python, Java, etc.) +- ObjectUI protocol can have multiple renderers (React, Vue, Angular, etc.) +- Underlying databases can be switched freely (MySQL, PostgreSQL, Oracle, etc.) + +### Practice Guidelines + +**When designing protocols**: +- Use standard JSON Schema format +- Avoid introducing language or framework-specific concepts +- Keep protocols simple and extensible + +**When developing implementations**: +- Strictly follow protocol specifications +- Validate protocol compatibility through test suites +- Implementations should be interchangeable without affecting applications + +### Example + +```json +{ + "type": "query", + "object": "users", + "filters": { + "age": { "$gt": 18 } + }, + "sort": { "created_at": "desc" }, + "limit": 10 +} +``` + +This ObjectQL query protocol can be: +- Transpiled to MySQL queries by JavaScript implementation +- Transpiled to PostgreSQL queries by Python implementation +- Transpiled to Oracle queries by Java implementation + +## Principle II: Mechanism-Implementation Separation + +### Definition + +Completely separate "what to do" (mechanism) from "how to do it" (implementation). + +### Core Philosophy + +**"Define behavior, not implementation"** + +Developers only need to: +- Define data models (Schema) +- Define interface structure (UI Protocol) +- Define business rules (Validation Rules) + +Without worrying about: +- How data is stored +- How interfaces are rendered +- How queries are optimized + +### Practice Guidelines + +**Schema Definition**: + +```json +{ + "object": "orders", + "fields": { + "order_id": { "type": "text", "primary": true }, + "customer": { "type": "reference", "ref": "customers" }, + "total": { "type": "number" }, + "status": { "type": "select", "options": ["pending", "completed"] } + } +} +``` + +Developers only define the data model, and the system automatically: +- Creates database table structure +- Generates CRUD APIs +- Provides data validation +- Establishes relationship mappings + +**UI Definition**: + +```json +{ + "type": "page", + "components": [ + { + "type": "form", + "fields": [ + { "name": "customer", "label": "Customer", "type": "select" }, + { "name": "total", "label": "Total", "type": "number" } + ] + } + ] +} +``` + +Developers only define the interface structure, and the system automatically: +- Renders form components +- Handles data binding +- Executes form validation +- Manages state updates + +### Benefits + +1. **Lower learning curve**: Developers don't need to learn implementation details +2. **Higher development efficiency**: Focus on business logic rather than technical implementation +3. **Enhanced maintainability**: Business logic is independent of technology stack +4. **Easier technology upgrades**: Changing underlying implementation doesn't affect business code + +## Principle III: Data Sovereignty Declaration + +### Definition + +Users have complete control and ownership of their data. + +### Core Philosophy + +**"Data belongs to users, not platforms"** + +ObjectStack adheres to the Local-First philosophy: +- Data is stored locally by default +- Users can choose their own database +- No mandatory cloud service binding +- Supports fully offline data operation + +### Practice Guidelines + +**Local-First Architecture**: + +1. **Standalone Mode**: + - Use .oos files to store data (based on SQLite) + - Data completely local, no network required + - Suitable for personal application scenarios + +2. **Self-Hosted Mode**: + - Deploy to enterprise's own servers + - Use enterprise's existing databases (MySQL, Oracle, etc.) + - Enterprise has complete data control + +3. **Hybrid Mode**: + - Local-first with optional cloud sync + - Supports end-to-end encryption + - Users can export data anytime + +### Data Migration Freedom + +**Zero-Friction Migration**: + +```bash +# Migrate from SQLite to MySQL +objectstack migrate --from sqlite://app.oos --to mysql://localhost/mydb + +# Migrate from MySQL to PostgreSQL +objectstack migrate --from mysql://... --to postgresql://... +``` + +Due to protocol layer abstraction, data can freely migrate between different databases without vendor lock-in. + +### Privacy Protection + +- **Field-level encryption**: Sensitive fields can be encrypted in storage +- **Field-level permissions**: Fine-grained data access control +- **Audit logs**: Complete data access records +- **Data erasure**: Support for complete user data deletion + +### Open Source Commitment + +- Core protocol layer (ObjectQL, ObjectUI) uses MIT/Apache 2.0 open source license +- Ensures protocol specifications are permanently open +- Community can freely implement compatible engines +- Avoids single vendor control + +## Significance of the Manifesto + +These three principles are not technical details, but ObjectStack's **core values**: + +1. **Protocol Neutrality**: Ensures freedom of technology choice +2. **Mechanism-Implementation Separation**: Ensures development efficiency and maintainability +3. **Data Sovereignty**: Ensures user rights + +Features that violate these principles, even if they have short-term benefits, should not be added to ObjectStack. + +## Design Decision Examples + +### ✅ Compliant Design + +**Scenario**: Adding full-text search functionality + +**Decision**: +- Define unified full-text search syntax in protocol layer +- Allow different implementations to use different search engines (Elasticsearch, Meilisearch, etc.) +- Users can choose the search solution that suits them + +### ❌ Non-Compliant Design + +**Scenario**: Adding cloud storage functionality + +**Decision**: +- Mandatory use of a specific cloud service provider +- Data must be uploaded to platform servers +- No local storage option provided + +**Why it violates**: +- Violates Protocol Neutrality principle (binding to specific provider) +- Violates Data Sovereignty principle (mandatory cloud storage) + +## Practice Checklist + +When designing new features, ask yourself: + +- [ ] Is this feature defined in the protocol layer or implementation layer? +- [ ] Does it depend on specific technology or services? +- [ ] Do users maintain complete control over data? +- [ ] Can it have multiple implementation approaches? +- [ ] Does it increase vendor lock-in risk? + +## Summary + +ObjectStack's architectural manifesto is not just a technical guide, but a declaration of values. It ensures: + +- **Technical Freedom**: Developers can choose the most suitable technology stack +- **Data Freedom**: Users fully own and control their data +- **Evolution Freedom**: System can continuously evolve without breaking compatibility + +These principles are what fundamentally distinguish ObjectStack from other platforms. diff --git a/content/docs/00-intro/manifesto.zh-CN.mdx b/content/docs/00-intro/manifesto.zh-CN.mdx new file mode 100644 index 0000000..9c07063 --- /dev/null +++ b/content/docs/00-intro/manifesto.zh-CN.mdx @@ -0,0 +1,244 @@ +--- +title: 架构宪法 +description: ObjectStack 的核心设计原则 +--- + +# 架构宪法(The Manifesto) + +ObjectStack 的设计遵循三大核心原则,这些原则构成了整个架构的基础。 + +## 原则一:协议中立(Protocol Neutrality) + +### 定义 + +协议层必须保持技术中立,不依赖于任何特定的实现技术。 + +### 核心理念 + +**"协议即标准,实现可替换"** + +ObjectStack 的协议层(ObjectQL、ObjectUI)是纯粹的规范定义,不包含任何具体实现。这意味着: + +- ObjectQL 协议可以有多种实现(JavaScript、Python、Java 等) +- ObjectUI 协议可以有多种渲染器(React、Vue、Angular 等) +- 底层数据库可以随意切换(MySQL、PostgreSQL、Oracle 等) + +### 实践指南 + +**协议设计时**: +- 使用标准的 JSON Schema 格式 +- 避免引入特定语言或框架的概念 +- 保持协议的简洁性和可扩展性 + +**实现开发时**: +- 严格遵循协议规范 +- 通过测试套件验证协议兼容性 +- 实现之间可以相互替换而不影响应用 + +### 示例 + +```json +{ + "type": "query", + "object": "users", + "filters": { + "age": { "$gt": 18 } + }, + "sort": { "created_at": "desc" }, + "limit": 10 +} +``` + +这个 ObjectQL 查询协议可以被: +- JavaScript 实现转译成 MySQL 查询 +- Python 实现转译成 PostgreSQL 查询 +- Java 实现转译成 Oracle 查询 + +## 原则二:机制与实现分离(Mechanism-Implementation Separation) + +### 定义 + +将"做什么"(机制)与"怎么做"(实现)彻底分离。 + +### 核心理念 + +**"定义行为,不定义实现"** + +开发者只需要: +- 定义数据模型(Schema) +- 定义界面结构(UI Protocol) +- 定义业务规则(Validation Rules) + +而不需要关心: +- 数据如何存储 +- 界面如何渲染 +- 查询如何优化 + +### 实践指南 + +**Schema 定义**: + +```json +{ + "object": "orders", + "fields": { + "order_id": { "type": "text", "primary": true }, + "customer": { "type": "reference", "ref": "customers" }, + "total": { "type": "number" }, + "status": { "type": "select", "options": ["pending", "completed"] } + } +} +``` + +开发者只需要定义数据模型,系统自动: +- 创建数据库表结构 +- 生成 CRUD API +- 提供数据验证 +- 建立关系映射 + +**UI 定义**: + +```json +{ + "type": "page", + "components": [ + { + "type": "form", + "fields": [ + { "name": "customer", "label": "客户", "type": "select" }, + { "name": "total", "label": "总额", "type": "number" } + ] + } + ] +} +``` + +开发者只需要定义界面结构,系统自动: +- 渲染表单组件 +- 处理数据绑定 +- 执行表单验证 +- 管理状态更新 + +### 好处 + +1. **降低学习曲线**:开发者不需要学习底层实现细节 +2. **提高开发效率**:专注于业务逻辑而非技术实现 +3. **增强可维护性**:业务逻辑独立于技术栈 +4. **便于技术升级**:更换底层实现不影响业务代码 + +## 原则三:数据主权宣言(Data Sovereignty Declaration) + +### 定义 + +用户拥有对其数据的完全控制权和所有权。 + +### 核心理念 + +**"数据属于用户,而非平台"** + +ObjectStack 坚持本地优先(Local-First)理念: +- 数据默认存储在用户本地 +- 用户可以选择自己的数据库 +- 没有强制性的云服务绑定 +- 支持数据完全离线工作 + +### 实践指南 + +**本地优先架构**: + +1. **单机模式**: + - 使用 .oos 文件存储数据(基于 SQLite) + - 数据完全在本地,无需网络 + - 适合个人应用场景 + +2. **自托管模式**: + - 部署到企业自己的服务器 + - 使用企业现有数据库(MySQL、Oracle 等) + - 企业完全掌控数据 + +3. **混合模式**: + - 本地优先,可选云同步 + - 支持端到端加密 + - 用户随时可以导出数据 + +### 数据迁移自由 + +**零摩擦迁移**: + +```bash +# 从 SQLite 迁移到 MySQL +objectstack migrate --from sqlite://app.oos --to mysql://localhost/mydb + +# 从 MySQL 迁移到 PostgreSQL +objectstack migrate --from mysql://... --to postgresql://... +``` + +由于协议层的抽象,数据可以在不同数据库之间自由迁移,没有厂商锁定。 + +### 隐私保护 + +- **字段级加密**:敏感字段可以加密存储 +- **字段级权限**:细粒度的数据访问控制 +- **审计日志**:完整的数据访问记录 +- **数据擦除**:支持完全删除用户数据 + +### 开源承诺 + +- 核心协议层(ObjectQL、ObjectUI)采用 MIT/Apache 2.0 开源许可 +- 确保协议规范永久开放 +- 社区可以自由实现兼容的引擎 +- 避免单一厂商控制 + +## 宪法的意义 + +这三大原则不是技术细节,而是 ObjectStack 的**核心价值观**: + +1. **协议中立**:保证技术选型自由 +2. **机制实现分离**:保证开发效率和可维护性 +3. **数据主权**:保证用户权益 + +违背这些原则的功能,即使有短期利益,也不应该加入到 ObjectStack 中。 + +## 设计决策示例 + +### ✅ 符合宪法的设计 + +**场景**:添加全文搜索功能 + +**决策**: +- 在协议层定义统一的全文搜索语法 +- 允许不同实现使用不同的搜索引擎(Elasticsearch、Meilisearch 等) +- 用户可以选择适合自己的搜索方案 + +### ❌ 违反宪法的设计 + +**场景**:添加云存储功能 + +**决策**: +- 强制使用某个特定的云服务商 +- 数据必须上传到平台的服务器 +- 不提供本地存储选项 + +**为什么违反**: +- 违反了协议中立原则(绑定特定服务商) +- 违反了数据主权原则(强制云存储) + +## 实践检查清单 + +在设计新功能时,问自己: + +- [ ] 这个功能是定义在协议层还是实现层? +- [ ] 是否依赖于特定的技术或服务? +- [ ] 用户是否保持对数据的完全控制? +- [ ] 是否可以有多种实现方式? +- [ ] 是否增加了厂商锁定风险? + +## 总结 + +ObjectStack 的架构宪法不仅是技术指南,更是价值观宣言。它确保: + +- **技术自由**:开发者可以选择最适合的技术栈 +- **数据自由**:用户完全拥有和控制自己的数据 +- **进化自由**:系统可以持续演进而不破坏兼容性 + +这些原则是 ObjectStack 区别于其他平台的根本所在。 diff --git a/content/docs/00-intro/meta.en.json b/content/docs/00-intro/meta.en.json new file mode 100644 index 0000000..534f098 --- /dev/null +++ b/content/docs/00-intro/meta.en.json @@ -0,0 +1,4 @@ +{ + "title": "Preamble & Design", + "pages": ["index", "welcome", "manifesto", "ai-codex"] +} diff --git a/content/docs/00-intro/meta.zh-CN.json b/content/docs/00-intro/meta.zh-CN.json new file mode 100644 index 0000000..d0ab66d --- /dev/null +++ b/content/docs/00-intro/meta.zh-CN.json @@ -0,0 +1,4 @@ +{ + "title": "序言与顶层设计", + "pages": ["index", "welcome", "manifesto", "ai-codex"] +} diff --git a/content/docs/00-intro/welcome.en.mdx b/content/docs/00-intro/welcome.en.mdx new file mode 100644 index 0000000..26b5731 --- /dev/null +++ b/content/docs/00-intro/welcome.en.mdx @@ -0,0 +1,132 @@ +--- +title: Welcome to ObjectStack +description: Learn about ObjectStack's project vision and core components +--- + +# Welcome to ObjectStack + +## Project Vision + +ObjectStack is a local-first, database-agnostic, protocol-driven full-stack application development platform. + +### Core Value Propositions + +- **Local-First**: Data is stored locally, giving users complete control over their data +- **Database-Agnostic**: Write once, run on any database +- **Protocol-Driven**: Separation of business logic and technical implementation through standardized protocols + +## Core Components + +ObjectStack consists of three core components that work together to form a complete application development ecosystem: + +### ObjectQL - Data Query Engine + +**Position**: Backend core, cross-database protocol + +ObjectQL is a database-agnostic query engine that provides: +- Unified data query language +- Multi-database driver adaptation (MySQL, PostgreSQL, Oracle, SQL Server, SQLite) +- Abstract Syntax Tree compiler +- Automated database migration + +**Core Capabilities**: +- Write Schema once, freely migrate between different databases +- Virtual column indexing mechanism for improved query performance +- Support for complex aggregation and relational queries + +### ObjectUI - Interface Rendering Engine + +**Position**: Frontend core, JSON-driven interface + +ObjectUI adopts a declarative UI paradigm, describing interfaces through JSON protocol: +- Data-driven component rendering +- Rich built-in component library +- Custom themes and styles +- React renderer implementation + +**Core Capabilities**: +- Generate interfaces without writing frontend code +- Support for complex forms, tables, charts, and other components +- Fully responsive, adapts to various devices + +### ObjectOS - Runtime Platform + +**Position**: Integration layer, providing authentication, file management, and commercial features + +ObjectOS is the runtime platform that binds ObjectQL and ObjectUI together: +- Unified authentication center (RBAC, field-level security) +- Plugin and extension system +- Multi-tenant support +- Local-first implementation mechanism + +**Core Capabilities**: +- Out-of-the-box permission management +- Flexible plugin extension mechanism +- Support for standalone and server mode deployment +- Multi-environment configuration management + +## Component Relationship Diagram + +``` +┌─────────────────────────────────────────────────────┐ +│ ObjectOS │ +│ ┌───────────────┐ ┌─────────────────┐ │ +│ │ ObjectUI │◄──────────►│ ObjectQL │ │ +│ │ (JSON-driven) │ │ (Cross-DB) │ │ +│ └───────────────┘ └─────────────────┘ │ +│ │ │ │ +│ │ │ │ +│ React Renderer DB Drivers │ +│ │ │ │ +│ ┌────▼─────┐ ┌────▼────────┐ │ +│ │ Browser │ │ MySQL/PG │ │ +│ │ Frontend │ │ Oracle/... │ │ +│ └──────────┘ └─────────────┘ │ +└─────────────────────────────────────────────────────┘ +``` + +## Technical Advantages + +### 1. Data Sovereignty + +User data is stored locally or in databases they control, with no vendor lock-in risk. + +### 2. Development Efficiency + +Protocol-driven approach significantly reduces repetitive code writing and improves development efficiency. + +### 3. Flexible Deployment + +Supports various deployment scenarios from personal local applications to enterprise-grade SaaS platforms. + +### 4. Technical Decoupling + +Separation of protocol layer and implementation layer allows independent evolution and reduces technical debt. + +## Use Cases + +### Individual Developers +- Quickly build local applications (Todo, notes, accounting, etc.) +- No need to configure database servers +- Complete control over your data + +### Enterprise IT Departments +- Build internal enterprise management systems +- Connect to existing heterogeneous databases +- Unified data access layer + +### SaaS Startups +- Quickly build multi-tenant SaaS platforms +- Focus on core business logic +- Reduce technology stack complexity + +### Low-Code Platform Vendors +- Use as underlying data engine +- Avoid database vendor lock-in +- Provide more flexible deployment options + +## Next Steps + +- Check out [Quick Start](../01-quickstart) to run your first application in 5 minutes +- Read [The Manifesto](./manifesto) to understand the design principles in depth +- Learn about [The AI Codex](./ai-codex) to improve development efficiency diff --git a/content/docs/00-intro/welcome.zh-CN.mdx b/content/docs/00-intro/welcome.zh-CN.mdx new file mode 100644 index 0000000..0e7b53e --- /dev/null +++ b/content/docs/00-intro/welcome.zh-CN.mdx @@ -0,0 +1,132 @@ +--- +title: 欢迎来到 ObjectStack +description: 了解 ObjectStack 的项目愿景和核心组件 +--- + +# 欢迎来到 ObjectStack + +## 项目愿景 + +ObjectStack 是一个本地优先、跨数据库、协议驱动的全栈应用开发平台。 + +### 核心价值主张 + +- **本地优先(Local-First)**:数据存储在本地,用户拥有完整的数据控制权 +- **跨数据库(Database-Agnostic)**:一次开发,可在任意数据库上运行 +- **协议驱动(Protocol-Driven)**:通过标准化协议实现业务逻辑与技术实现的分离 + +## 核心组件 + +ObjectStack 由三个核心组件构成,它们相互协作,形成完整的应用开发生态: + +### ObjectQL - 数据查询引擎 + +**定位**:后端核心,跨数据库协议 + +ObjectQL 是一个数据库无关的查询引擎,它提供: +- 统一的数据查询语言 +- 多数据库驱动适配(MySQL、PostgreSQL、Oracle、SQL Server、SQLite) +- 抽象语法树编译器 +- 自动化数据库迁移 + +**核心能力**: +- 一次编写 Schema,可在不同数据库间自由迁移 +- 虚拟列索引机制,提升查询性能 +- 支持复杂的聚合查询和关系查询 + +### ObjectUI - 界面渲染引擎 + +**定位**:前端核心,JSON 驱动界面 + +ObjectUI 采用声明式 UI 范式,通过 JSON 协议描述界面: +- 数据驱动的组件渲染 +- 丰富的内置组件库 +- 自定义主题和样式 +- React 渲染器实现 + +**核心能力**: +- 无需编写前端代码即可生成界面 +- 支持复杂的表单、表格、图表等组件 +- 完全响应式,适配各种设备 + +### ObjectOS - 运行时平台 + +**定位**:融合层,提供权限、文件、商业化功能 + +ObjectOS 是粘合 ObjectQL 和 ObjectUI 的运行时平台: +- 统一鉴权中心(RBAC、字段级安全) +- 插件与扩展系统 +- 多租户支持 +- 本地优先的实现机制 + +**核心能力**: +- 开箱即用的权限管理 +- 灵活的插件扩展机制 +- 支持单机和服务器模式部署 +- 多环境配置管理 + +## 组件关系图 + +``` +┌─────────────────────────────────────────────────────┐ +│ ObjectOS │ +│ ┌───────────────┐ ┌─────────────────┐ │ +│ │ ObjectUI │◄──────────►│ ObjectQL │ │ +│ │ (JSON驱动UI) │ │ (跨数据库引擎) │ │ +│ └───────────────┘ └─────────────────┘ │ +│ │ │ │ +│ │ │ │ +│ React 渲染器 数据库驱动 │ +│ │ │ │ +│ ┌────▼─────┐ ┌────▼────────┐ │ +│ │ 浏览器 │ │ MySQL/PG │ │ +│ │ 前端 │ │ Oracle/... │ │ +│ └──────────┘ └─────────────┘ │ +└─────────────────────────────────────────────────────┘ +``` + +## 技术优势 + +### 1. 数据主权 + +用户数据存储在本地或自己控制的数据库中,没有厂商锁定风险。 + +### 2. 开发效率 + +通过协议驱动的方式,大幅减少重复性代码编写,提升开发效率。 + +### 3. 灵活部署 + +支持从个人本地应用到企业级 SaaS 平台的各种部署场景。 + +### 4. 技术解耦 + +协议层与实现层分离,可以独立演进,降低技术债务。 + +## 适用场景 + +### 个人开发者 +- 快速构建本地应用(Todo、笔记、记账等) +- 无需配置数据库服务器 +- 数据完全掌握在自己手中 + +### 企业 IT 部门 +- 构建企业内部管理系统 +- 对接现有异构数据库 +- 统一数据访问层 + +### SaaS 创业公司 +- 快速搭建多租户 SaaS 平台 +- 专注于核心业务逻辑 +- 降低技术栈复杂度 + +### 低代码平台厂商 +- 作为底层数据引擎 +- 避免数据库厂商锁定 +- 提供更灵活的部署方案 + +## 下一步 + +- 查看[快速开始](../01-quickstart),5 分钟内运行第一个应用 +- 阅读[架构宪法](./manifesto),深入理解设计原则 +- 了解[AI 协作指引](./ai-codex),提升开发效率 diff --git a/content/docs/01-quickstart/demos.en.mdx b/content/docs/01-quickstart/demos.en.mdx new file mode 100644 index 0000000..6f9fc88 --- /dev/null +++ b/content/docs/01-quickstart/demos.en.mdx @@ -0,0 +1,668 @@ +--- +title: Demo Projects +description: Explore ready-to-run ObjectStack demo applications +--- + +# Demo Projects Showcase + +Explore real-world ObjectStack applications. Download, install, and run them immediately to see ObjectStack in action. + +## Available Demos + +### 1. Personal Accounting App (本地记账应用) + +A complete personal finance tracker built with ObjectStack's local-first architecture. + +**Features:** +- 💰 Track income and expenses +- 📊 Visual reports and charts +- 🏷️ Categories and tags +- 📅 Monthly budgets +- 💾 100% local data storage (SQLite) +- 📦 Portable `.oos` package format + +**Technology Stack:** +- ObjectQL with SQLite driver +- ObjectUI dashboard layouts +- Chart components for visualization +- Triggers for automated calculations + +**Perfect For:** +- Learning ObjectStack basics +- Understanding local-first architecture +- Personal finance management +- Offline-first applications + +#### Quick Start + +**Option 1: Install from .oos Package** + +> **Note**: The download URLs below are placeholders for demonstration purposes. Please check the official ObjectStack repository for actual demo packages. + +```bash +# Download the package +curl -O https://demos.objectstack.io/accounting-app.oos + +# Install using ObjectOS CLI +objectos install accounting-app.oos + +# Run the app +objectos run accounting-app +``` + +Access at: `http://localhost:3000` + +**Option 2: Clone and Run from Source** + +```bash +# Clone the repository +git clone https://github.com/objectstack/demo-accounting-app.git +cd demo-accounting-app + +# Install dependencies +npm install + +# Start development server +npm run dev +``` + +#### Key Files to Explore + +``` +demo-accounting-app/ +├── src/ +│ ├── objects/ +│ │ ├── transaction.object.js # Income/expense records +│ │ ├── category.object.js # Transaction categories +│ │ ├── budget.object.js # Monthly budgets +│ │ └── account.object.js # Bank accounts +│ ├── pages/ +│ │ ├── dashboard.page.json # Main dashboard with charts +│ │ ├── transactions.page.json # Transaction list view +│ │ └── reports.page.json # Financial reports +│ └── triggers/ +│ ├── transaction.trigger.js # Auto-calculate balances +│ └── budget.trigger.js # Budget alerts +└── objectos.config.js # SQLite configuration +``` + +#### Sample Data Model: Transaction Object + +```javascript +// src/objects/transaction.object.js +export default { + name: "transaction", + label: "Transaction", + icon: "DollarSign", + fields: { + date: { + type: "date", + label: "Date", + required: true, + defaultValue: "TODAY" + }, + type: { + type: "select", + label: "Type", + options: [ + { label: "Income", value: "income" }, + { label: "Expense", value: "expense" } + ], + required: true + }, + amount: { + type: "currency", + label: "Amount", + required: true, + scale: 2 + }, + category: { + type: "lookup", + label: "Category", + reference_to: "category", + required: true + }, + account: { + type: "lookup", + label: "Account", + reference_to: "account" + }, + description: { + type: "text", + label: "Description" + }, + // Virtual field - computed + impact: { + type: "formula", + label: "Impact", + formula: "type == 'income' ? amount : -amount", + returnType: "currency" + } + }, + list_views: { + all: { + label: "All Transactions", + columns: ["date", "type", "amount", "category", "account"], + sort: [["date", "desc"]] + }, + income: { + label: "Income", + filter_scope: "type eq 'income'", + columns: ["date", "amount", "category", "description"] + }, + expenses: { + label: "Expenses", + filter_scope: "type eq 'expense'", + columns: ["date", "amount", "category", "description"] + } + } +}; +``` + +#### Dashboard Configuration + +```json +// src/pages/dashboard.page.json +{ + "name": "dashboard", + "label": "Dashboard", + "icon": "LayoutDashboard", + "layout": { + "type": "dashboard", + "widgets": [ + { + "type": "summary_card", + "title": "Total Income", + "object": "transaction", + "aggregation": "sum", + "field": "amount", + "filter": "type eq 'income'", + "size": "small" + }, + { + "type": "summary_card", + "title": "Total Expenses", + "object": "transaction", + "aggregation": "sum", + "field": "amount", + "filter": "type eq 'expense'", + "size": "small" + }, + { + "type": "chart", + "title": "Monthly Trend", + "chartType": "line", + "object": "transaction", + "groupBy": "date.month", + "metrics": [ + { "field": "amount", "aggregation": "sum", "label": "Total" } + ] + }, + { + "type": "chart", + "title": "Expenses by Category", + "chartType": "pie", + "object": "transaction", + "filter": "type eq 'expense'", + "groupBy": "category", + "metrics": [ + { "field": "amount", "aggregation": "sum" } + ] + } + ] + } +} +``` + +### 2. Enterprise CRM System (企业 CRM 系统) + +A full-featured Customer Relationship Management system demonstrating enterprise-grade ObjectStack capabilities. + +**Features:** +- 👥 Contact and company management +- 🎯 Lead tracking and conversion +- 📞 Activity logging (calls, emails, meetings) +- 📈 Sales pipeline visualization +- 🔐 Role-based access control +- 🐳 Docker deployment ready +- 🗄️ PostgreSQL backend + +**Technology Stack:** +- ObjectQL with PostgreSQL driver +- Multi-tenant configuration +- Advanced permissions (RBAC) +- ObjectUI complex forms and kanban boards +- Email integration triggers + +**Perfect For:** +- Understanding enterprise features +- Learning multi-tenant architecture +- Docker deployment practice +- Complex business logic implementation + +#### Quick Start with Docker + +```bash +# Clone the repository +git clone https://github.com/objectstack/demo-crm-system.git +cd demo-crm-system + +# Start with Docker Compose +docker-compose up -d + +# Wait for initialization (about 30 seconds) +docker-compose logs -f objectos +``` + +Access at: `http://localhost:3000` + +**Default Login:** +- Username: `admin@demo.com` +- Password: `demo123` (change after login!) + +#### Architecture Overview + +``` +┌─────────────────────────────────────────────┐ +│ Docker Compose Stack │ +│ ┌────────────┐ ┌──────────────────────┐ │ +│ │ ObjectOS │ │ PostgreSQL 15 │ │ +│ │ Container │──│ (CRM Database) │ │ +│ └────────────┘ └──────────────────────┘ │ +│ │ │ +│ ┌────────────┐ ┌──────────────────────┐ │ +│ │ Redis │ │ Nginx │ │ +│ │ (Session) │ │ (Reverse Proxy) │ │ +│ └────────────┘ └──────────────────────┘ │ +└─────────────────────────────────────────────┘ +``` + +#### Key Objects + +**Contact Object:** + +```javascript +// src/objects/contact.object.js +export default { + name: "contact", + label: "Contact", + icon: "User", + fields: { + firstName: { + type: "text", + label: "First Name", + required: true + }, + lastName: { + type: "text", + label: "Last Name", + required: true + }, + fullName: { + type: "formula", + label: "Full Name", + formula: "firstName + ' ' + lastName" + }, + email: { + type: "email", + label: "Email", + unique: true + }, + phone: { + type: "phone", + label: "Phone" + }, + company: { + type: "lookup", + label: "Company", + reference_to: "company" + }, + status: { + type: "select", + label: "Status", + options: [ + { label: "Lead", value: "lead" }, + { label: "Qualified", value: "qualified" }, + { label: "Customer", value: "customer" }, + { label: "Inactive", value: "inactive" } + ], + defaultValue: "lead" + }, + assignedTo: { + type: "lookup", + label: "Assigned To", + reference_to: "user" + } + }, + permissions: { + allowRead: "field(assignedTo) == $user.id or $user.role == 'admin'", + allowEdit: "field(assignedTo) == $user.id or $user.role == 'admin'", + allowDelete: "$user.role == 'admin'" + } +}; +``` + +**Lead Pipeline:** + +```json +// src/pages/pipeline.page.json +{ + "name": "pipeline", + "label": "Sales Pipeline", + "icon": "TrendingUp", + "layout": { + "type": "kanban", + "object": "opportunity", + "groupBy": "stage", + "cardFields": ["name", "amount", "closeDate", "contact"], + "stages": [ + { "value": "prospecting", "label": "Prospecting" }, + { "value": "qualification", "label": "Qualification" }, + { "value": "proposal", "label": "Proposal" }, + { "value": "negotiation", "label": "Negotiation" }, + { "value": "closed_won", "label": "Closed Won" }, + { "value": "closed_lost", "label": "Closed Lost" } + ], + "aggregation": { + "field": "amount", + "function": "sum", + "label": "Total Value" + } + } +} +``` + +#### Multi-Tenant Setup + +```javascript +// objectos.config.js +export default { + datasources: { + default: { + driver: "postgresql", + host: process.env.DB_HOST || "postgres", + port: 5432, + database: process.env.DB_NAME || "crm", + username: process.env.DB_USER || "crm_user", + password: process.env.DB_PASSWORD + } + }, + + multiTenant: { + enabled: true, + mode: "schema", + schemaPattern: "tenant_{tenantId}", + + // Resolve tenant from subdomain + resolver: async (request) => { + const host = request.headers.host; + const subdomain = host.split('.')[0]; + + // Lookup tenant in database + return { + id: subdomain, + name: subdomain + }; + } + }, + + auth: { + providers: { + local: { + enabled: true + }, + saml: { + enabled: process.env.SAML_ENABLED === 'true', + entryPoint: process.env.SAML_ENTRY_POINT, + cert: process.env.SAML_CERT + } + } + } +}; +``` + +#### Email Integration Trigger + +```javascript +// src/triggers/activity.trigger.js +export default { + listenTo: "activity", + + afterInsert: async function(context) { + const { doc, object } = context; + + // Send notification email when activity is assigned + if (doc.type === 'meeting' && doc.assignedTo) { + const user = await object.findOne('user', doc.assignedTo); + const contact = await object.findOne('contact', doc.contactId); + + await context.services.email.send({ + to: user.email, + subject: `New Meeting Scheduled with ${contact.fullName}`, + template: 'meeting_notification', + data: { + userName: user.name, + contactName: contact.fullName, + meetingDate: doc.scheduledDate, + notes: doc.notes + } + }); + } + } +}; +``` + +## Comparing the Demos + +| Feature | Accounting App | CRM System | +|---------|---------------|------------| +| **Database** | SQLite (local) | PostgreSQL (server) | +| **Deployment** | .oos package | Docker Compose | +| **Users** | Single user | Multi-user, multi-tenant | +| **Complexity** | Beginner | Advanced | +| **Use Case** | Personal | Enterprise | +| **Setup Time** | 2 minutes | 10 minutes | +| **Offline Support** | Full | Limited | + +## Learning Path + +### For Beginners + +1. **Start with Accounting App** + - Understand ObjectQL schema definitions + - Learn ObjectUI layouts + - Practice with triggers + - Master local-first concepts + +2. **Progress to CRM System** + - Explore multi-user features + - Study RBAC implementation + - Learn Docker deployment + - Understand multi-tenant architecture + +### For Enterprise Developers + +1. **Study CRM System First** + - Review enterprise patterns + - Examine security model + - Understand Docker setup + - Explore integration patterns + +2. **Simplify with Accounting App** + - See minimal configuration + - Understand core concepts + - Learn packaging strategies + +## Extending the Demos + +### Accounting App Extensions + +**Add Investment Tracking:** + +```javascript +// src/objects/investment.object.js +export default { + name: "investment", + label: "Investment", + fields: { + name: { type: "text", label: "Name", required: true }, + type: { + type: "select", + label: "Type", + options: [ + { label: "Stocks", value: "stocks" }, + { label: "Bonds", value: "bonds" }, + { label: "Real Estate", value: "real_estate" } + ] + }, + purchaseDate: { type: "date", label: "Purchase Date" }, + purchasePrice: { type: "currency", label: "Purchase Price" }, + currentValue: { type: "currency", label: "Current Value" }, + gain: { + type: "formula", + label: "Gain/Loss", + formula: "currentValue - purchasePrice", + returnType: "currency" + } + } +}; +``` + +### CRM System Extensions + +**Add Customer Support Tickets:** + +```javascript +// src/objects/ticket.object.js +export default { + name: "ticket", + label: "Support Ticket", + fields: { + subject: { type: "text", label: "Subject", required: true }, + description: { type: "textarea", label: "Description" }, + contact: { + type: "lookup", + label: "Contact", + reference_to: "contact", + required: true + }, + priority: { + type: "select", + label: "Priority", + options: [ + { label: "Low", value: "low" }, + { label: "Medium", value: "medium" }, + { label: "High", value: "high" }, + { label: "Critical", value: "critical" } + ] + }, + status: { + type: "select", + label: "Status", + options: [ + { label: "New", value: "new" }, + { label: "In Progress", value: "in_progress" }, + { label: "Waiting", value: "waiting" }, + { label: "Resolved", value: "resolved" } + ] + }, + assignedTo: { + type: "lookup", + label: "Assigned To", + reference_to: "user" + } + } +}; +``` + +## Demo Data + +Both demos include sample data for exploration: + +### Accounting App Sample Data +- 50+ sample transactions +- 10 expense categories +- 3 income categories +- 2 bank accounts +- 3 months of historical data + +### CRM System Sample Data +- 25 contacts +- 10 companies +- 15 opportunities +- 30+ activities +- 5 user accounts (different roles) + +## Download Links + +> **Note**: These are placeholder URLs for documentation purposes. Actual demo resources will be available through the ObjectStack GitHub repositories. + +### Accounting App +- **Source Code**: [github.com/objectstack/demo-accounting-app](https://github.com/objectstack/demo-accounting-app) +- **.oos Package**: [demos.objectstack.io/accounting-app.oos](https://demos.objectstack.io/accounting-app.oos) +- **Documentation**: [demos.objectstack.io/accounting-app/docs](https://demos.objectstack.io/accounting-app/docs) + +### CRM System +- **Source Code**: [github.com/objectstack/demo-crm-system](https://github.com/objectstack/demo-crm-system) +- **Docker Image**: `objectstack/demo-crm:latest` +- **Documentation**: [demos.objectstack.io/crm-system/docs](https://demos.objectstack.io/crm-system/docs) + +## Troubleshooting + +### Accounting App Issues + +**Database not found:** +```bash +# Reset database +rm ~/.objectos/accounting-app/database.sqlite +npm run dev +``` + +**Port conflict:** +```bash +# Change port in objectos.config.js +export default { + server: { port: 3001 } +}; +``` + +### CRM System Issues + +**Docker containers not starting:** +```bash +# Check logs +docker-compose logs + +# Restart clean +docker-compose down -v +docker-compose up -d +``` + +**Database connection error:** +```bash +# Wait for PostgreSQL to initialize +docker-compose logs postgres | grep "ready to accept" + +# Test connection +docker-compose exec postgres psql -U crm_user -d crm +``` + +## Next Steps + +After exploring the demos: + +1. **Customize** - Modify objects and pages to fit your needs +2. **Build** - Create your own application from scratch +3. **Deploy** - Take your app to production +4. **Share** - Package and distribute your creation + +## Community Demos + +Browse more demos from the community: + +- [Recipe Manager](https://github.com/objectstack-community/recipe-manager) +- [Project Tracker](https://github.com/objectstack-community/project-tracker) +- [Inventory System](https://github.com/objectstack-community/inventory) +- [Event Calendar](https://github.com/objectstack-community/event-calendar) + +Submit your own demo: [community/demos](https://github.com/objectstack/community/demos) diff --git a/content/docs/01-quickstart/demos.zh-CN.mdx b/content/docs/01-quickstart/demos.zh-CN.mdx new file mode 100644 index 0000000..15d77ed --- /dev/null +++ b/content/docs/01-quickstart/demos.zh-CN.mdx @@ -0,0 +1,668 @@ +--- +title: 示例项目 +description: 探索可直接运行的 ObjectStack 示例应用 +--- + +# 示例项目展示 + +探索真实的 ObjectStack 应用。下载、安装并立即运行,体验 ObjectStack 的实际效果。 + +## 可用示例 + +### 1. 个人记账应用 + +使用 ObjectStack 本地优先架构构建的完整个人财务追踪器。 + +**功能特性:** +- 💰 跟踪收入和支出 +- 📊 可视化报表和图表 +- 🏷️ 类别和标签 +- 📅 月度预算 +- 💾 100% 本地数据存储(SQLite) +- 📦 便携式 `.oos` 包格式 + +**技术栈:** +- ObjectQL 与 SQLite 驱动 +- ObjectUI 仪表板布局 +- 可视化图表组件 +- 自动计算触发器 + +**适用于:** +- 学习 ObjectStack 基础 +- 理解本地优先架构 +- 个人财务管理 +- 离线优先应用 + +#### 快速开始 + +**选项 1: 从 .oos 包安装** + +> **注意**: 下面的下载 URL 仅为演示目的的占位符。请查看 ObjectStack 官方仓库获取实际的示例包。 + +```bash +# 下载包 +curl -O https://demos.objectstack.io/accounting-app.oos + +# 使用 ObjectOS CLI 安装 +objectos install accounting-app.oos + +# 运行应用 +objectos run accounting-app +``` + +访问: `http://localhost:3000` + +**选项 2: 从源码克隆并运行** + +```bash +# 克隆仓库 +git clone https://github.com/objectstack/demo-accounting-app.git +cd demo-accounting-app + +# 安装依赖 +npm install + +# 启动开发服务器 +npm run dev +``` + +#### 探索关键文件 + +``` +demo-accounting-app/ +├── src/ +│ ├── objects/ +│ │ ├── transaction.object.js # 收支记录 +│ │ ├── category.object.js # 交易类别 +│ │ ├── budget.object.js # 月度预算 +│ │ └── account.object.js # 银行账户 +│ ├── pages/ +│ │ ├── dashboard.page.json # 主仪表板(含图表) +│ │ ├── transactions.page.json # 交易列表视图 +│ │ └── reports.page.json # 财务报表 +│ └── triggers/ +│ ├── transaction.trigger.js # 自动计算余额 +│ └── budget.trigger.js # 预算提醒 +└── objectos.config.js # SQLite 配置 +``` + +#### 示例数据模型: 交易对象 + +```javascript +// src/objects/transaction.object.js +export default { + name: "transaction", + label: "交易", + icon: "DollarSign", + fields: { + date: { + type: "date", + label: "日期", + required: true, + defaultValue: "TODAY" + }, + type: { + type: "select", + label: "类型", + options: [ + { label: "收入", value: "income" }, + { label: "支出", value: "expense" } + ], + required: true + }, + amount: { + type: "currency", + label: "金额", + required: true, + scale: 2 + }, + category: { + type: "lookup", + label: "类别", + reference_to: "category", + required: true + }, + account: { + type: "lookup", + label: "账户", + reference_to: "account" + }, + description: { + type: "text", + label: "描述" + }, + // 虚拟字段 - 计算得出 + impact: { + type: "formula", + label: "影响", + formula: "type == 'income' ? amount : -amount", + returnType: "currency" + } + }, + list_views: { + all: { + label: "所有交易", + columns: ["date", "type", "amount", "category", "account"], + sort: [["date", "desc"]] + }, + income: { + label: "收入", + filter_scope: "type eq 'income'", + columns: ["date", "amount", "category", "description"] + }, + expenses: { + label: "支出", + filter_scope: "type eq 'expense'", + columns: ["date", "amount", "category", "description"] + } + } +}; +``` + +#### 仪表板配置 + +```json +// src/pages/dashboard.page.json +{ + "name": "dashboard", + "label": "仪表板", + "icon": "LayoutDashboard", + "layout": { + "type": "dashboard", + "widgets": [ + { + "type": "summary_card", + "title": "总收入", + "object": "transaction", + "aggregation": "sum", + "field": "amount", + "filter": "type eq 'income'", + "size": "small" + }, + { + "type": "summary_card", + "title": "总支出", + "object": "transaction", + "aggregation": "sum", + "field": "amount", + "filter": "type eq 'expense'", + "size": "small" + }, + { + "type": "chart", + "title": "月度趋势", + "chartType": "line", + "object": "transaction", + "groupBy": "date.month", + "metrics": [ + { "field": "amount", "aggregation": "sum", "label": "总计" } + ] + }, + { + "type": "chart", + "title": "按类别的支出", + "chartType": "pie", + "object": "transaction", + "filter": "type eq 'expense'", + "groupBy": "category", + "metrics": [ + { "field": "amount", "aggregation": "sum" } + ] + } + ] + } +} +``` + +### 2. 企业 CRM 系统 + +展示企业级 ObjectStack 功能的全功能客户关系管理系统。 + +**功能特性:** +- 👥 联系人和公司管理 +- 🎯 线索跟踪和转化 +- 📞 活动日志(电话、邮件、会议) +- 📈 销售管道可视化 +- 🔐 基于角色的访问控制 +- 🐳 Docker 部署就绪 +- 🗄️ PostgreSQL 后端 + +**技术栈:** +- ObjectQL 与 PostgreSQL 驱动 +- 多租户配置 +- 高级权限(RBAC) +- ObjectUI 复杂表单和看板 +- 邮件集成触发器 + +**适用于:** +- 理解企业功能 +- 学习多租户架构 +- Docker 部署实践 +- 复杂业务逻辑实现 + +#### 使用 Docker 快速开始 + +```bash +# 克隆仓库 +git clone https://github.com/objectstack/demo-crm-system.git +cd demo-crm-system + +# 使用 Docker Compose 启动 +docker-compose up -d + +# 等待初始化(约 30 秒) +docker-compose logs -f objectos +``` + +访问: `http://localhost:3000` + +**默认登录:** +- 用户名: `admin@demo.com` +- 密码: `demo123` (登录后请更改!) + +#### 架构概览 + +``` +┌─────────────────────────────────────────────┐ +│ Docker Compose 技术栈 │ +│ ┌────────────┐ ┌──────────────────────┐ │ +│ │ ObjectOS │ │ PostgreSQL 15 │ │ +│ │ 容器 │──│ (CRM 数据库) │ │ +│ └────────────┘ └──────────────────────┘ │ +│ │ │ +│ ┌────────────┐ ┌──────────────────────┐ │ +│ │ Redis │ │ Nginx │ │ +│ │ (会话) │ │ (反向代理) │ │ +│ └────────────┘ └──────────────────────┘ │ +└─────────────────────────────────────────────┘ +``` + +#### 核心对象 + +**联系人对象:** + +```javascript +// src/objects/contact.object.js +export default { + name: "contact", + label: "联系人", + icon: "User", + fields: { + firstName: { + type: "text", + label: "名", + required: true + }, + lastName: { + type: "text", + label: "姓", + required: true + }, + fullName: { + type: "formula", + label: "全名", + formula: "firstName + ' ' + lastName" + }, + email: { + type: "email", + label: "邮箱", + unique: true + }, + phone: { + type: "phone", + label: "电话" + }, + company: { + type: "lookup", + label: "公司", + reference_to: "company" + }, + status: { + type: "select", + label: "状态", + options: [ + { label: "线索", value: "lead" }, + { label: "合格", value: "qualified" }, + { label: "客户", value: "customer" }, + { label: "未激活", value: "inactive" } + ], + defaultValue: "lead" + }, + assignedTo: { + type: "lookup", + label: "分配给", + reference_to: "user" + } + }, + permissions: { + allowRead: "field(assignedTo) == $user.id or $user.role == 'admin'", + allowEdit: "field(assignedTo) == $user.id or $user.role == 'admin'", + allowDelete: "$user.role == 'admin'" + } +}; +``` + +**销售管道:** + +```json +// src/pages/pipeline.page.json +{ + "name": "pipeline", + "label": "销售管道", + "icon": "TrendingUp", + "layout": { + "type": "kanban", + "object": "opportunity", + "groupBy": "stage", + "cardFields": ["name", "amount", "closeDate", "contact"], + "stages": [ + { "value": "prospecting", "label": "潜在客户" }, + { "value": "qualification", "label": "资格确认" }, + { "value": "proposal", "label": "提案" }, + { "value": "negotiation", "label": "谈判" }, + { "value": "closed_won", "label": "成交" }, + { "value": "closed_lost", "label": "丢失" } + ], + "aggregation": { + "field": "amount", + "function": "sum", + "label": "总价值" + } + } +} +``` + +#### 多租户设置 + +```javascript +// objectos.config.js +export default { + datasources: { + default: { + driver: "postgresql", + host: process.env.DB_HOST || "postgres", + port: 5432, + database: process.env.DB_NAME || "crm", + username: process.env.DB_USER || "crm_user", + password: process.env.DB_PASSWORD + } + }, + + multiTenant: { + enabled: true, + mode: "schema", + schemaPattern: "tenant_{tenantId}", + + // 从子域名解析租户 + resolver: async (request) => { + const host = request.headers.host; + const subdomain = host.split('.')[0]; + + // 在数据库中查找租户 + return { + id: subdomain, + name: subdomain + }; + } + }, + + auth: { + providers: { + local: { + enabled: true + }, + saml: { + enabled: process.env.SAML_ENABLED === 'true', + entryPoint: process.env.SAML_ENTRY_POINT, + cert: process.env.SAML_CERT + } + } + } +}; +``` + +#### 邮件集成触发器 + +```javascript +// src/triggers/activity.trigger.js +export default { + listenTo: "activity", + + afterInsert: async function(context) { + const { doc, object } = context; + + // 分配活动时发送通知邮件 + if (doc.type === 'meeting' && doc.assignedTo) { + const user = await object.findOne('user', doc.assignedTo); + const contact = await object.findOne('contact', doc.contactId); + + await context.services.email.send({ + to: user.email, + subject: `已安排与 ${contact.fullName} 的新会议`, + template: 'meeting_notification', + data: { + userName: user.name, + contactName: contact.fullName, + meetingDate: doc.scheduledDate, + notes: doc.notes + } + }); + } + } +}; +``` + +## 对比示例 + +| 功能 | 记账应用 | CRM 系统 | +|---------|---------------|------------| +| **数据库** | SQLite (本地) | PostgreSQL (服务器) | +| **部署** | .oos 包 | Docker Compose | +| **用户** | 单用户 | 多用户、多租户 | +| **复杂度** | 初学者 | 高级 | +| **使用场景** | 个人 | 企业 | +| **配置时间** | 2 分钟 | 10 分钟 | +| **离线支持** | 完全 | 有限 | + +## 学习路径 + +### 初学者 + +1. **从记账应用开始** + - 理解 ObjectQL 模式定义 + - 学习 ObjectUI 布局 + - 实践触发器 + - 掌握本地优先概念 + +2. **进阶到 CRM 系统** + - 探索多用户功能 + - 研究 RBAC 实现 + - 学习 Docker 部署 + - 理解多租户架构 + +### 企业开发者 + +1. **先学习 CRM 系统** + - 审查企业模式 + - 检查安全模型 + - 理解 Docker 设置 + - 探索集成模式 + +2. **通过记账应用简化** + - 查看最小配置 + - 理解核心概念 + - 学习打包策略 + +## 扩展示例 + +### 记账应用扩展 + +**添加投资追踪:** + +```javascript +// src/objects/investment.object.js +export default { + name: "investment", + label: "投资", + fields: { + name: { type: "text", label: "名称", required: true }, + type: { + type: "select", + label: "类型", + options: [ + { label: "股票", value: "stocks" }, + { label: "债券", value: "bonds" }, + { label: "房地产", value: "real_estate" } + ] + }, + purchaseDate: { type: "date", label: "购买日期" }, + purchasePrice: { type: "currency", label: "购买价格" }, + currentValue: { type: "currency", label: "当前价值" }, + gain: { + type: "formula", + label: "收益/损失", + formula: "currentValue - purchasePrice", + returnType: "currency" + } + } +}; +``` + +### CRM 系统扩展 + +**添加客户支持工单:** + +```javascript +// src/objects/ticket.object.js +export default { + name: "ticket", + label: "支持工单", + fields: { + subject: { type: "text", label: "主题", required: true }, + description: { type: "textarea", label: "描述" }, + contact: { + type: "lookup", + label: "联系人", + reference_to: "contact", + required: true + }, + priority: { + type: "select", + label: "优先级", + options: [ + { label: "低", value: "low" }, + { label: "中", value: "medium" }, + { label: "高", value: "high" }, + { label: "紧急", value: "critical" } + ] + }, + status: { + type: "select", + label: "状态", + options: [ + { label: "新建", value: "new" }, + { label: "处理中", value: "in_progress" }, + { label: "等待", value: "waiting" }, + { label: "已解决", value: "resolved" } + ] + }, + assignedTo: { + type: "lookup", + label: "分配给", + reference_to: "user" + } + } +}; +``` + +## 示例数据 + +两个示例都包含样本数据供探索: + +### 记账应用示例数据 +- 50+ 条示例交易 +- 10 个支出类别 +- 3 个收入类别 +- 2 个银行账户 +- 3 个月的历史数据 + +### CRM 系统示例数据 +- 25 个联系人 +- 10 家公司 +- 15 个机会 +- 30+ 条活动 +- 5 个用户账户(不同角色) + +## 下载链接 + +> **注意**: 这些是文档目的的占位符 URL。实际的示例资源将通过 ObjectStack GitHub 仓库提供。 + +### 记账应用 +- **源码**: [github.com/objectstack/demo-accounting-app](https://github.com/objectstack/demo-accounting-app) +- **.oos 包**: [demos.objectstack.io/accounting-app.oos](https://demos.objectstack.io/accounting-app.oos) +- **文档**: [demos.objectstack.io/accounting-app/docs](https://demos.objectstack.io/accounting-app/docs) + +### CRM 系统 +- **源码**: [github.com/objectstack/demo-crm-system](https://github.com/objectstack/demo-crm-system) +- **Docker 镜像**: `objectstack/demo-crm:latest` +- **文档**: [demos.objectstack.io/crm-system/docs](https://demos.objectstack.io/crm-system/docs) + +## 故障排除 + +### 记账应用问题 + +**数据库未找到:** +```bash +# 重置数据库 +rm ~/.objectos/accounting-app/database.sqlite +npm run dev +``` + +**端口冲突:** +```bash +# 在 objectos.config.js 中更改端口 +export default { + server: { port: 3001 } +}; +``` + +### CRM 系统问题 + +**Docker 容器无法启动:** +```bash +# 查看日志 +docker-compose logs + +# 清理重启 +docker-compose down -v +docker-compose up -d +``` + +**数据库连接错误:** +```bash +# 等待 PostgreSQL 初始化 +docker-compose logs postgres | grep "ready to accept" + +# 测试连接 +docker-compose exec postgres psql -U crm_user -d crm +``` + +## 下一步 + +探索示例后: + +1. **自定义** - 修改对象和页面以适应您的需求 +2. **构建** - 从零开始创建您自己的应用 +3. **部署** - 将应用部署到生产环境 +4. **分享** - 打包并分发您的创作 + +## 社区示例 + +浏览社区的更多示例: + +- [食谱管理器](https://github.com/objectstack-community/recipe-manager) +- [项目追踪器](https://github.com/objectstack-community/project-tracker) +- [库存系统](https://github.com/objectstack-community/inventory) +- [活动日历](https://github.com/objectstack-community/event-calendar) + +提交您自己的示例: [community/demos](https://github.com/objectstack/community/demos) diff --git a/content/docs/01-quickstart/enterprise-integrators.en.mdx b/content/docs/01-quickstart/enterprise-integrators.en.mdx new file mode 100644 index 0000000..5caedb6 --- /dev/null +++ b/content/docs/01-quickstart/enterprise-integrators.en.mdx @@ -0,0 +1,617 @@ +--- +title: Enterprise Integrators +description: Guide for connecting ObjectStack to enterprise databases and deploying with Docker +--- + +# Enterprise Integrators Guide + +This guide shows how to integrate ObjectStack with existing enterprise databases and deploy using Docker containers. + +## Why ObjectStack for Enterprises? + +Enterprise benefits include: + +- 🔌 **Database Agnostic** - Connect to MySQL, PostgreSQL, Oracle, SQL Server +- 🏢 **Multi-tenant** - Support multiple organizations in one deployment +- 🔐 **Enterprise Security** - RBAC, field-level permissions, audit logs +- 📦 **Containerized** - Docker-ready for modern DevOps workflows +- 🔄 **Legacy Integration** - Connect to existing databases without migration + +## Deployment Options + +### Option 1: Connect to Existing Database + +Use ObjectStack with your current MySQL or PostgreSQL database. + +**Best for**: Integrating with existing systems, gradual migration + +### Option 2: Docker Container Deployment + +Deploy ObjectOS in a Docker container with a managed database. + +**Best for**: New deployments, microservices architecture, cloud-native environments + +## Option 1: Connect to Existing Database + +### Prerequisites + +- Node.js 18+ installed +- Access to MySQL 5.7+ or PostgreSQL 12+ database +- Database credentials (host, port, username, password, database name) +- Network access to the database server + +### Step 1: Create Project + +```bash +npm create object-app my-enterprise-app +``` + +Select the appropriate template: + +``` +? Select a template: + SQLite Local +❯ MySQL Enterprise + PostgreSQL Enterprise + Custom Configuration +``` + +### Step 2: Configure Database Connection + +Edit `objectos.config.js`: + +#### MySQL Configuration + +```javascript +export default { + datasources: { + default: { + driver: "mysql", + host: "mysql.yourcompany.com", + port: 3306, + database: "objectstack_db", + username: "objectstack_user", + password: process.env.DB_PASSWORD, + options: { + charset: "utf8mb4", + timezone: "+00:00", + pool: { + min: 2, + max: 10 + } + } + } + }, + server: { + port: 3000, + host: "0.0.0.0" + }, + security: { + secret: process.env.APP_SECRET, + sessionTimeout: 7200 // 2 hours + } +}; +``` + +#### PostgreSQL Configuration + +```javascript +export default { + datasources: { + default: { + driver: "postgresql", + host: "postgres.yourcompany.com", + port: 5432, + database: "objectstack_db", + username: "objectstack_user", + password: process.env.DB_PASSWORD, + options: { + ssl: { + rejectUnauthorized: false + }, + pool: { + min: 2, + max: 10 + } + } + } + } +}; +``` + +### Step 3: Environment Variables + +Create `.env` file for sensitive credentials: + +```bash +# Database +DB_PASSWORD=your_secure_password + +# Application +APP_SECRET=your_random_secret_key_min_32_chars +NODE_ENV=production + +# Optional: Multiple Databases +DB_ANALYTICS_HOST=analytics.yourcompany.com +DB_ANALYTICS_PASSWORD=analytics_password +``` + +**Security Best Practice**: Never commit `.env` to version control! + +Add to `.gitignore`: + +``` +.env +.env.local +.env.*.local +``` + +### Step 4: Database Initialization + +Run migrations to create ObjectStack system tables: + +```bash +npm run migrate +``` + +This creates: + +- User authentication tables +- Permission and role tables +- Object metadata tables +- Audit log tables + +### Step 5: Connect to Legacy Tables (Optional) + +If you have existing tables, map them as external objects: + +```javascript +// src/objects/legacy_customer.object.js +export default { + name: "legacy_customer", + label: "Customer (Legacy)", + table_name: "customers", // Existing table name + external: true, + fields: { + customer_id: { + type: "number", + label: "Customer ID", + name: "id", // Map to existing column + primary: true + }, + customer_name: { + type: "text", + label: "Name", + name: "name" // Map to existing column + }, + email: { + type: "email", + label: "Email", + name: "email_address" + }, + // Virtual field - computed, not in database + full_info: { + type: "formula", + label: "Full Info", + formula: "customer_name + ' (' + email + ')'" + } + } +}; +``` + +### Step 6: Start the Server + +```bash +npm run start +``` + +Access at `http://localhost:3000` + +## Option 2: Docker Container Deployment + +### Prerequisites + +- Docker 20.10+ installed +- Docker Compose (optional, for multi-container setup) +- Basic Docker knowledge + +### Step 1: Pull ObjectOS Image + +```bash +docker pull objectstack/objectos:latest +``` + +### Step 2: Create Docker Compose File + +Create `docker-compose.yml`: + +```yaml +version: '3.8' + +services: + objectos: + image: objectstack/objectos:latest + container_name: objectos-app + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - DB_HOST=postgres + - DB_PORT=5432 + - DB_NAME=objectstack + - DB_USER=objectstack + - DB_PASSWORD=secure_password + - APP_SECRET=your_secret_key_min_32_characters + volumes: + - ./src:/app/src + - ./objectos.config.js:/app/objectos.config.js + - objectos-storage:/app/storage + depends_on: + - postgres + restart: unless-stopped + + postgres: + image: postgres:15-alpine + container_name: objectos-postgres + environment: + - POSTGRES_DB=objectstack + - POSTGRES_USER=objectstack + - POSTGRES_PASSWORD=secure_password + volumes: + - postgres-data:/var/lib/postgresql/data + ports: + - "5432:5432" + restart: unless-stopped + + # Optional: Redis for session storage + redis: + image: redis:7-alpine + container_name: objectos-redis + ports: + - "6379:6379" + volumes: + - redis-data:/data + restart: unless-stopped + +volumes: + postgres-data: + redis-data: + objectos-storage: +``` + +### Step 3: Create Application Directory Structure + +```bash +mkdir -p src/objects src/pages src/triggers +``` + +Create a sample object `src/objects/contact.object.js`: + +```javascript +export default { + name: "contact", + label: "Contact", + fields: { + name: { type: "text", label: "Name", required: true }, + email: { type: "email", label: "Email" }, + phone: { type: "phone", label: "Phone" }, + company: { type: "text", label: "Company" } + } +}; +``` + +### Step 4: Launch Containers + +```bash +docker-compose up -d +``` + +Check status: + +```bash +docker-compose ps +``` + +Expected output: + +``` +NAME IMAGE STATUS +objectos-app objectstack/objectos:latest Up 2 minutes +objectos-postgres postgres:15-alpine Up 2 minutes +objectos-redis redis:7-alpine Up 2 minutes +``` + +### Step 5: View Logs + +```bash +# All services +docker-compose logs -f + +# Just ObjectOS +docker-compose logs -f objectos + +# Last 100 lines +docker-compose logs --tail=100 objectos +``` + +### Step 6: Access Application + +Open browser: `http://localhost:3000` + +Default credentials (first run): + +- Username: `admin` +- Password: `admin` (change immediately!) + +## Multi-Database Configuration + +Connect to multiple databases simultaneously: + +```javascript +// objectos.config.js +export default { + datasources: { + // Primary database + default: { + driver: "postgresql", + host: "postgres.internal", + database: "main_db", + username: "app_user", + password: process.env.DB_PASSWORD + }, + + // Legacy MySQL database + legacy: { + driver: "mysql", + host: "mysql.legacy.internal", + database: "old_crm", + username: "readonly_user", + password: process.env.LEGACY_DB_PASSWORD + }, + + // Analytics database + analytics: { + driver: "postgresql", + host: "analytics.internal", + database: "warehouse", + username: "analytics_user", + password: process.env.ANALYTICS_PASSWORD, + options: { + readOnly: true + } + } + } +}; +``` + +Use specific datasource in object definition: + +```javascript +export default { + name: "legacy_order", + label: "Legacy Order", + datasource: "legacy", // Use legacy datasource + table_name: "orders", + external: true, + fields: { + // ... field definitions + } +}; +``` + +## Multi-Tenant Configuration + +Configure for multiple organizations: + +```javascript +// objectos.config.js +export default { + multiTenant: { + enabled: true, + mode: "schema", // or "database" or "discriminator" + + // Schema-based (PostgreSQL) + schemaPattern: "tenant_{tenantId}", + + // Database-based (MySQL) + // databasePattern: "tenant_{tenantId}_db", + + // Discriminator-based (shared tables) + // discriminatorField: "tenant_id" + }, + + tenantResolver: async (request) => { + // Extract tenant from subdomain + const hostname = request.headers.host; + const subdomain = hostname.split('.')[0]; + + // Or from header + // const tenantId = request.headers['x-tenant-id']; + + return { + id: subdomain, + name: subdomain + }; + } +}; +``` + +## Production Deployment Checklist + +### Security + +- [ ] Change default admin password +- [ ] Use strong `APP_SECRET` (min 32 random characters) +- [ ] Store passwords in environment variables or secrets manager +- [ ] Enable SSL/TLS for database connections +- [ ] Configure firewall rules +- [ ] Enable audit logging + +### Performance + +- [ ] Set appropriate database pool size +- [ ] Configure Redis for session storage +- [ ] Enable caching for static assets +- [ ] Set up CDN for file storage +- [ ] Configure load balancer (if using multiple instances) + +### Monitoring + +- [ ] Set up health check endpoint +- [ ] Configure application logging +- [ ] Monitor database performance +- [ ] Set up alerting for errors +- [ ] Track resource usage (CPU, memory, disk) + +### Backup + +- [ ] Automated database backups +- [ ] File storage backups +- [ ] Test restore procedures +- [ ] Document recovery processes + +## Advanced: Kubernetes Deployment + +Example Kubernetes manifests: + +### Deployment + +```yaml +# objectos-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: objectos +spec: + replicas: 3 + selector: + matchLabels: + app: objectos + template: + metadata: + labels: + app: objectos + spec: + containers: + - name: objectos + image: objectstack/objectos:latest + ports: + - containerPort: 3000 + env: + - name: DB_HOST + valueFrom: + configMapKeyRef: + name: objectos-config + key: db-host + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: objectos-secrets + key: db-password + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" + livenessProbe: + httpGet: + path: /health + port: 3000 + initialDelaySeconds: 30 + periodSeconds: 10 +``` + +### Service + +```yaml +# objectos-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: objectos +spec: + selector: + app: objectos + ports: + - port: 80 + targetPort: 3000 + type: LoadBalancer +``` + +## Database Migration Strategies + +### Zero-Downtime Migration + +1. **Dual-write phase**: Write to both old and new databases +2. **Backfill phase**: Copy historical data +3. **Validation phase**: Verify data consistency +4. **Switch phase**: Route reads to new database +5. **Cleanup phase**: Remove old database + +### Gradual Migration + +```javascript +// Use object-level datasource routing +export default { + name: "customer", + datasource: process.env.MIGRATION_PHASE === 'complete' + ? 'new_db' + : 'legacy_db', + // ... fields +}; +``` + +## Troubleshooting + +### Connection Refused + +```bash +# Check database is running +docker-compose ps postgres + +# Test connection +docker-compose exec postgres psql -U objectstack -d objectstack + +# Check logs +docker-compose logs postgres +``` + +### Permission Denied + +```sql +-- Grant necessary permissions +GRANT ALL PRIVILEGES ON DATABASE objectstack TO objectstack_user; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO objectstack_user; +``` + +### Container Won't Start + +```bash +# Check logs +docker-compose logs objectos + +# Validate config +docker-compose config + +# Restart containers +docker-compose restart +``` + +## Next Steps + +> **Note**: The links below point to future documentation sections that are under development. + +- Multi-tenant Configuration (coming soon) +- Security & Permissions (coming soon) +- Monitoring & Logging (coming soon) +- High Availability Setup (coming soon) + +## Getting Help + +> **Note**: Commercial documentation is under development. + +- 📚 Enterprise Documentation (coming soon) +- 💼 [Enterprise Support](https://objectstack.io/enterprise) +- 🔧 [Professional Services](https://objectstack.io/services) diff --git a/content/docs/01-quickstart/enterprise-integrators.zh-CN.mdx b/content/docs/01-quickstart/enterprise-integrators.zh-CN.mdx new file mode 100644 index 0000000..c4f78f4 --- /dev/null +++ b/content/docs/01-quickstart/enterprise-integrators.zh-CN.mdx @@ -0,0 +1,617 @@ +--- +title: 企业集成人员 +description: 连接 ObjectStack 到企业数据库并使用 Docker 部署的指南 +--- + +# 企业集成人员指南 + +本指南展示如何将 ObjectStack 与现有企业数据库集成,以及如何使用 Docker 容器部署。 + +## 为什么企业选择 ObjectStack? + +企业优势包括: + +- 🔌 **跨数据库** - 连接到 MySQL、PostgreSQL、Oracle、SQL Server +- 🏢 **多租户** - 在一个部署中支持多个组织 +- 🔐 **企业安全** - RBAC、字段级权限、审计日志 +- 📦 **容器化** - Docker 就绪,适配现代 DevOps 工作流 +- 🔄 **遗留系统集成** - 连接现有数据库,无需迁移 + +## 部署选项 + +### 选项 1: 连接到现有数据库 + +将 ObjectStack 与当前的 MySQL 或 PostgreSQL 数据库配合使用。 + +**适用于**: 与现有系统集成、渐进式迁移 + +### 选项 2: Docker 容器部署 + +在 Docker 容器中部署 ObjectOS,使用托管数据库。 + +**适用于**: 新部署、微服务架构、云原生环境 + +## 选项 1: 连接到现有数据库 + +### 前置要求 + +- Node.js 18+ 已安装 +- 可访问 MySQL 5.7+ 或 PostgreSQL 12+ 数据库 +- 数据库凭据(主机、端口、用户名、密码、数据库名) +- 数据库服务器的网络访问权限 + +### 步骤 1: 创建项目 + +```bash +npm create object-app my-enterprise-app +``` + +选择适当的模板: + +``` +? 选择一个模板: + SQLite 本地模板 +❯ MySQL 企业模板 + PostgreSQL 企业模板 + 自定义配置 +``` + +### 步骤 2: 配置数据库连接 + +编辑 `objectos.config.js`: + +#### MySQL 配置 + +```javascript +export default { + datasources: { + default: { + driver: "mysql", + host: "mysql.yourcompany.com", + port: 3306, + database: "objectstack_db", + username: "objectstack_user", + password: process.env.DB_PASSWORD, + options: { + charset: "utf8mb4", + timezone: "+00:00", + pool: { + min: 2, + max: 10 + } + } + } + }, + server: { + port: 3000, + host: "0.0.0.0" + }, + security: { + secret: process.env.APP_SECRET, + sessionTimeout: 7200 // 2 小时 + } +}; +``` + +#### PostgreSQL 配置 + +```javascript +export default { + datasources: { + default: { + driver: "postgresql", + host: "postgres.yourcompany.com", + port: 5432, + database: "objectstack_db", + username: "objectstack_user", + password: process.env.DB_PASSWORD, + options: { + ssl: { + rejectUnauthorized: false + }, + pool: { + min: 2, + max: 10 + } + } + } + } +}; +``` + +### 步骤 3: 环境变量 + +创建 `.env` 文件存储敏感凭据: + +```bash +# 数据库 +DB_PASSWORD=your_secure_password + +# 应用 +APP_SECRET=your_random_secret_key_min_32_chars +NODE_ENV=production + +# 可选: 多数据库 +DB_ANALYTICS_HOST=analytics.yourcompany.com +DB_ANALYTICS_PASSWORD=analytics_password +``` + +**安全最佳实践**: 永远不要将 `.env` 提交到版本控制! + +添加到 `.gitignore`: + +``` +.env +.env.local +.env.*.local +``` + +### 步骤 4: 数据库初始化 + +运行迁移以创建 ObjectStack 系统表: + +```bash +npm run migrate +``` + +这将创建: + +- 用户认证表 +- 权限和角色表 +- 对象元数据表 +- 审计日志表 + +### 步骤 5: 连接到遗留表(可选) + +如果有现有表,将它们映射为外部对象: + +```javascript +// src/objects/legacy_customer.object.js +export default { + name: "legacy_customer", + label: "客户(遗留)", + table_name: "customers", // 现有表名 + external: true, + fields: { + customer_id: { + type: "number", + label: "客户 ID", + name: "id", // 映射到现有列 + primary: true + }, + customer_name: { + type: "text", + label: "名称", + name: "name" // 映射到现有列 + }, + email: { + type: "email", + label: "邮箱", + name: "email_address" + }, + // 虚拟字段 - 计算得出,不在数据库中 + full_info: { + type: "formula", + label: "完整信息", + formula: "customer_name + ' (' + email + ')'" + } + } +}; +``` + +### 步骤 6: 启动服务器 + +```bash +npm run start +``` + +访问 `http://localhost:3000` + +## 选项 2: Docker 容器部署 + +### 前置要求 + +- Docker 20.10+ 已安装 +- Docker Compose(可选,用于多容器设置) +- 基本的 Docker 知识 + +### 步骤 1: 拉取 ObjectOS 镜像 + +```bash +docker pull objectstack/objectos:latest +``` + +### 步骤 2: 创建 Docker Compose 文件 + +创建 `docker-compose.yml`: + +```yaml +version: '3.8' + +services: + objectos: + image: objectstack/objectos:latest + container_name: objectos-app + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - DB_HOST=postgres + - DB_PORT=5432 + - DB_NAME=objectstack + - DB_USER=objectstack + - DB_PASSWORD=secure_password + - APP_SECRET=your_secret_key_min_32_characters + volumes: + - ./src:/app/src + - ./objectos.config.js:/app/objectos.config.js + - objectos-storage:/app/storage + depends_on: + - postgres + restart: unless-stopped + + postgres: + image: postgres:15-alpine + container_name: objectos-postgres + environment: + - POSTGRES_DB=objectstack + - POSTGRES_USER=objectstack + - POSTGRES_PASSWORD=secure_password + volumes: + - postgres-data:/var/lib/postgresql/data + ports: + - "5432:5432" + restart: unless-stopped + + # 可选: Redis 用于会话存储 + redis: + image: redis:7-alpine + container_name: objectos-redis + ports: + - "6379:6379" + volumes: + - redis-data:/data + restart: unless-stopped + +volumes: + postgres-data: + redis-data: + objectos-storage: +``` + +### 步骤 3: 创建应用目录结构 + +```bash +mkdir -p src/objects src/pages src/triggers +``` + +创建示例对象 `src/objects/contact.object.js`: + +```javascript +export default { + name: "contact", + label: "联系人", + fields: { + name: { type: "text", label: "姓名", required: true }, + email: { type: "email", label: "邮箱" }, + phone: { type: "phone", label: "电话" }, + company: { type: "text", label: "公司" } + } +}; +``` + +### 步骤 4: 启动容器 + +```bash +docker-compose up -d +``` + +检查状态: + +```bash +docker-compose ps +``` + +预期输出: + +``` +NAME IMAGE STATUS +objectos-app objectstack/objectos:latest Up 2 minutes +objectos-postgres postgres:15-alpine Up 2 minutes +objectos-redis redis:7-alpine Up 2 minutes +``` + +### 步骤 5: 查看日志 + +```bash +# 所有服务 +docker-compose logs -f + +# 仅 ObjectOS +docker-compose logs -f objectos + +# 最后 100 行 +docker-compose logs --tail=100 objectos +``` + +### 步骤 6: 访问应用 + +打开浏览器: `http://localhost:3000` + +默认凭据(首次运行): + +- 用户名: `admin` +- 密码: `admin` (立即更改!) + +## 多数据库配置 + +同时连接到多个数据库: + +```javascript +// objectos.config.js +export default { + datasources: { + // 主数据库 + default: { + driver: "postgresql", + host: "postgres.internal", + database: "main_db", + username: "app_user", + password: process.env.DB_PASSWORD + }, + + // 遗留 MySQL 数据库 + legacy: { + driver: "mysql", + host: "mysql.legacy.internal", + database: "old_crm", + username: "readonly_user", + password: process.env.LEGACY_DB_PASSWORD + }, + + // 分析数据库 + analytics: { + driver: "postgresql", + host: "analytics.internal", + database: "warehouse", + username: "analytics_user", + password: process.env.ANALYTICS_PASSWORD, + options: { + readOnly: true + } + } + } +}; +``` + +在对象定义中使用特定数据源: + +```javascript +export default { + name: "legacy_order", + label: "遗留订单", + datasource: "legacy", // 使用遗留数据源 + table_name: "orders", + external: true, + fields: { + // ... 字段定义 + } +}; +``` + +## 多租户配置 + +配置多组织支持: + +```javascript +// objectos.config.js +export default { + multiTenant: { + enabled: true, + mode: "schema", // 或 "database" 或 "discriminator" + + // 基于模式(PostgreSQL) + schemaPattern: "tenant_{tenantId}", + + // 基于数据库(MySQL) + // databasePattern: "tenant_{tenantId}_db", + + // 基于区分字段(共享表) + // discriminatorField: "tenant_id" + }, + + tenantResolver: async (request) => { + // 从子域名提取租户 + const hostname = request.headers.host; + const subdomain = hostname.split('.')[0]; + + // 或从请求头 + // const tenantId = request.headers['x-tenant-id']; + + return { + id: subdomain, + name: subdomain + }; + } +}; +``` + +## 生产部署检查清单 + +### 安全 + +- [ ] 更改默认管理员密码 +- [ ] 使用强 `APP_SECRET`(至少 32 个随机字符) +- [ ] 将密码存储在环境变量或密钥管理器中 +- [ ] 为数据库连接启用 SSL/TLS +- [ ] 配置防火墙规则 +- [ ] 启用审计日志 + +### 性能 + +- [ ] 设置适当的数据库连接池大小 +- [ ] 配置 Redis 用于会话存储 +- [ ] 为静态资源启用缓存 +- [ ] 为文件存储设置 CDN +- [ ] 配置负载均衡器(如使用多实例) + +### 监控 + +- [ ] 设置健康检查端点 +- [ ] 配置应用日志 +- [ ] 监控数据库性能 +- [ ] 为错误设置告警 +- [ ] 跟踪资源使用(CPU、内存、磁盘) + +### 备份 + +- [ ] 自动化数据库备份 +- [ ] 文件存储备份 +- [ ] 测试恢复流程 +- [ ] 记录恢复过程 + +## 高级: Kubernetes 部署 + +Kubernetes 清单示例: + +### Deployment + +```yaml +# objectos-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: objectos +spec: + replicas: 3 + selector: + matchLabels: + app: objectos + template: + metadata: + labels: + app: objectos + spec: + containers: + - name: objectos + image: objectstack/objectos:latest + ports: + - containerPort: 3000 + env: + - name: DB_HOST + valueFrom: + configMapKeyRef: + name: objectos-config + key: db-host + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: objectos-secrets + key: db-password + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" + livenessProbe: + httpGet: + path: /health + port: 3000 + initialDelaySeconds: 30 + periodSeconds: 10 +``` + +### Service + +```yaml +# objectos-service.yaml +apiVersion: v1 +kind: Service +metadata: + name: objectos +spec: + selector: + app: objectos + ports: + - port: 80 + targetPort: 3000 + type: LoadBalancer +``` + +## 数据库迁移策略 + +### 零停机迁移 + +1. **双写阶段**: 同时写入旧数据库和新数据库 +2. **回填阶段**: 复制历史数据 +3. **验证阶段**: 验证数据一致性 +4. **切换阶段**: 将读取路由到新数据库 +5. **清理阶段**: 删除旧数据库 + +### 渐进式迁移 + +```javascript +// 使用对象级数据源路由 +export default { + name: "customer", + datasource: process.env.MIGRATION_PHASE === 'complete' + ? 'new_db' + : 'legacy_db', + // ... 字段 +}; +``` + +## 故障排除 + +### 连接被拒绝 + +```bash +# 检查数据库是否运行 +docker-compose ps postgres + +# 测试连接 +docker-compose exec postgres psql -U objectstack -d objectstack + +# 查看日志 +docker-compose logs postgres +``` + +### 权限被拒绝 + +```sql +-- 授予必要权限 +GRANT ALL PRIVILEGES ON DATABASE objectstack TO objectstack_user; +GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO objectstack_user; +``` + +### 容器无法启动 + +```bash +# 查看日志 +docker-compose logs objectos + +# 验证配置 +docker-compose config + +# 重启容器 +docker-compose restart +``` + +## 下一步 + +> **注意**: 以下链接指向正在开发中的未来文档章节。 + +- 多租户配置(即将推出) +- 安全与权限(即将推出) +- 监控与日志(即将推出) +- 高可用性设置(即将推出) + +## 获取帮助 + +> **注意**: 商业文档正在开发中。 + +- 📚 企业文档(即将推出) +- 💼 [企业支持](https://objectstack.io/enterprise) +- 🔧 [专业服务](https://objectstack.io/services) diff --git a/content/docs/01-quickstart/index.en.mdx b/content/docs/01-quickstart/index.en.mdx new file mode 100644 index 0000000..30eccd0 --- /dev/null +++ b/content/docs/01-quickstart/index.en.mdx @@ -0,0 +1,72 @@ +--- +title: Quick Start +description: Get started with ObjectStack in 5 minutes +--- + +# Quick Start + +This section helps you quickly get started with ObjectStack, whether you're an individual developer or enterprise integrator. + +## Choose Your Path + +### For Individual Developers + +If you're looking to build personal applications quickly without setting up a database server: + +- [Individual Developers Guide](./individual-developers) - Learn how to create local-first applications with SQLite + - Install Node.js and set up your environment + - Use `npm create object-app` to scaffold a new project + - Build a Todo List application in minutes + +**Perfect for**: Personal projects, prototypes, learning ObjectStack + +### For Enterprise Integrators + +If you need to connect to existing databases or deploy in a Docker environment: + +- [Enterprise Integrators Guide](./enterprise-integrators) - Connect to MySQL/PostgreSQL and deploy with Docker + - Connect to enterprise databases (MySQL, PostgreSQL, Oracle, SQL Server) + - Deploy using ObjectOS Docker container + - Configure multi-tenant environments + +**Perfect for**: Enterprise internal tools, team collaboration, production deployments + +## Demo Projects + +Want to see ObjectStack in action? Check out our demo applications: + +- [Demo Projects Showcase](./demos) - Explore ready-to-run examples + - Personal Accounting App (.oos package version) + - Enterprise CRM System (Docker deployment version) + - Download, install, and explore immediately + +## What You'll Learn + +By the end of this section, you will: + +- ✅ Understand ObjectStack's local-first architecture +- ✅ Create your first ObjectStack application +- ✅ Connect to various databases +- ✅ Deploy applications in different environments +- ✅ Explore real-world demo applications + +## Time to Complete + +- **Individual Developer Path**: ~10 minutes +- **Enterprise Integrator Path**: ~20 minutes +- **Demo Exploration**: ~5 minutes per demo + +## Prerequisites + +- Basic command line knowledge +- Node.js 18+ installed (for individual developers) +- Docker installed (for enterprise integrators using Docker) +- A code editor (VS Code recommended) + +## Next Steps + +Choose your path and start building: + +1. **New to ObjectStack?** Start with [Individual Developers Guide](./individual-developers) +2. **Enterprise user?** Jump to [Enterprise Integrators Guide](./enterprise-integrators) +3. **Want to explore first?** Check out [Demo Projects](./demos) diff --git a/content/docs/01-quickstart/index.zh-CN.mdx b/content/docs/01-quickstart/index.zh-CN.mdx new file mode 100644 index 0000000..2001900 --- /dev/null +++ b/content/docs/01-quickstart/index.zh-CN.mdx @@ -0,0 +1,72 @@ +--- +title: 快速开始 +description: 5 分钟快速上手 ObjectStack +--- + +# 快速开始 + +本章节帮助您快速上手 ObjectStack,无论您是个人开发者还是企业集成人员。 + +## 选择您的路径 + +### 面向个人开发者 + +如果您希望快速构建个人应用,无需搭建数据库服务器: + +- [个人开发者指南](./individual-developers) - 学习如何创建本地优先的应用 + - 安装 Node.js 并配置环境 + - 使用 `npm create object-app` 快速创建项目 + - 几分钟内构建一个待办事项应用 + +**适用于**: 个人项目、原型开发、学习 ObjectStack + +### 面向企业集成人员 + +如果您需要连接现有数据库或在 Docker 环境中部署: + +- [企业集成人员指南](./enterprise-integrators) - 连接 MySQL/PostgreSQL 并使用 Docker 部署 + - 连接企业数据库(MySQL、PostgreSQL、Oracle、SQL Server) + - 使用 ObjectOS Docker 容器部署 + - 配置多租户环境 + +**适用于**: 企业内部工具、团队协作、生产环境部署 + +## 示例项目 + +想要看看 ObjectStack 的实际效果?查看我们的演示应用: + +- [示例项目展示](./demos) - 探索可直接运行的示例 + - 个人记账应用(.oos 包版本) + - 企业 CRM 系统(Docker 部署版本) + - 下载、安装并立即体验 + +## 您将学到什么 + +完成本章节后,您将: + +- ✅ 理解 ObjectStack 的本地优先架构 +- ✅ 创建您的第一个 ObjectStack 应用 +- ✅ 连接各种数据库 +- ✅ 在不同环境中部署应用 +- ✅ 探索真实的演示应用 + +## 完成时间 + +- **个人开发者路径**: 约 10 分钟 +- **企业集成人员路径**: 约 20 分钟 +- **示例探索**: 每个示例约 5 分钟 + +## 前置要求 + +- 基本的命令行知识 +- Node.js 18+ 已安装(个人开发者需要) +- Docker 已安装(使用 Docker 的企业集成人员需要) +- 代码编辑器(推荐 VS Code) + +## 下一步 + +选择您的路径并开始构建: + +1. **初次使用 ObjectStack?** 从[个人开发者指南](./individual-developers)开始 +2. **企业用户?** 跳转到[企业集成人员指南](./enterprise-integrators) +3. **想先探索一下?** 查看[示例项目](./demos) diff --git a/content/docs/01-quickstart/individual-developers.en.mdx b/content/docs/01-quickstart/individual-developers.en.mdx new file mode 100644 index 0000000..bf99c42 --- /dev/null +++ b/content/docs/01-quickstart/individual-developers.en.mdx @@ -0,0 +1,461 @@ +--- +title: Individual Developers +description: Quick start guide for individual developers building local-first applications +--- + +# Individual Developers Guide + +This guide will help you create your first ObjectStack application in under 10 minutes. No database server required! + +## Why Local-First? + +As an individual developer, you can: + +- 💾 **Own your data** - All data stored locally in SQLite +- 🚀 **Start instantly** - No database installation or configuration +- 🔒 **Privacy first** - Your data never leaves your machine +- 📦 **Portable apps** - Package and share as `.oos` files + +## Prerequisites + +Before starting, ensure you have: + +- **Node.js 18 or higher** installed +- A code editor (VS Code recommended) +- Basic JavaScript/TypeScript knowledge + +### Check Node.js Version + +```bash +node --version +# Should show v18.0.0 or higher +``` + +### Install Node.js (if needed) + +If you don't have Node.js installed, download it from [nodejs.org](https://nodejs.org/): + +- **Recommended**: LTS version (Long Term Support) +- **Minimum**: v18.0.0 + +## Step 1: Create Your First App + +Use the official scaffolding tool to create a new ObjectStack application: + +```bash +npm create object-app my-todo-app +``` + +This command will: + +1. Prompt you to choose a template +2. Download the template files +3. Set up the project structure +4. Install dependencies automatically + +### Template Selection + +When prompted, choose the **SQLite Local Template**: + +``` +? Select a template: (Use arrow keys) +❯ SQLite Local (Recommended for individual developers) + MySQL Enterprise + PostgreSQL Enterprise + Custom Configuration +``` + +## Step 2: Navigate to Your Project + +```bash +cd my-todo-app +``` + +### Project Structure + +Your new project contains: + +``` +my-todo-app/ +├── src/ +│ ├── objects/ # Data models (ObjectQL schemas) +│ ├── pages/ # UI definitions (ObjectUI JSON) +│ ├── triggers/ # Business logic +│ └── index.ts # Entry point +├── package.json +├── objectos.config.js # ObjectOS configuration +└── README.md +``` + +## Step 3: Start the Development Server + +```bash +npm run dev +``` + +This will: + +- Start the ObjectOS runtime +- Initialize the SQLite database +- Launch the web interface at `http://localhost:3000` + +### Expected Output + +``` +ObjectOS v1.0.0 +✓ Database initialized (SQLite) +✓ Objects loaded (5 objects) +✓ Server running at http://localhost:3000 +``` + +## Step 4: Build a Todo List Application + +Let's create a simple Todo List to understand ObjectStack's workflow. + +### Define the Data Model (ObjectQL) + +Create `src/objects/todo.object.js`: + +```javascript +export default { + name: "todo", + label: "Todo Item", + fields: { + title: { + type: "text", + label: "Title", + required: true + }, + description: { + type: "textarea", + label: "Description" + }, + completed: { + type: "boolean", + label: "Completed", + defaultValue: false + }, + priority: { + type: "select", + label: "Priority", + options: [ + { label: "Low", value: "low" }, + { label: "Medium", value: "medium" }, + { label: "High", value: "high" } + ], + defaultValue: "medium" + }, + dueDate: { + type: "date", + label: "Due Date" + }, + completedAt: { + type: "datetime", + label: "Completed At", + readonly: true + } + }, + list_views: { + all: { + label: "All Todos", + columns: ["title", "priority", "dueDate", "completed"], + filter_fields: ["priority", "completed"], + sort: [["createdAt", "desc"]] + }, + pending: { + label: "Pending", + filter_scope: "completed eq false", + columns: ["title", "priority", "dueDate"] + }, + completed: { + label: "Completed", + filter_scope: "completed eq true", + columns: ["title", "completedAt"] + } + } +}; +``` + +### Create the UI (ObjectUI) + +Create `src/pages/todos.page.json`: + +```json +{ + "name": "todos", + "label": "My Todos", + "icon": "CheckSquare", + "layout": { + "type": "standard_list", + "object": "todo", + "defaultView": "all", + "enableQuickCreate": true, + "enableSearch": true, + "actions": [ + { + "label": "Mark as Complete", + "name": "markComplete", + "on": "record", + "visible": "completed eq false" + }, + { + "label": "Delete Completed", + "name": "deleteCompleted", + "on": "list" + } + ] + } +} +``` + +### Add Business Logic (Triggers) + +Create `src/triggers/todo.trigger.js`: + +```javascript +export default { + listenTo: "todo", + + // Automatically set completion timestamp + beforeUpdate: async function(context) { + const { doc, previousDoc } = context; + + if (doc.completed && !previousDoc.completed) { + doc.completedAt = new Date(); + } + + if (!doc.completed && previousDoc.completed) { + doc.completedAt = null; + } + }, + + // Custom action: Mark as complete + actions: { + markComplete: async function(context) { + const { record, object } = context; + + await object.update(record._id, { + completed: true, + completedAt: new Date() + }); + + return { + success: true, + message: "Todo marked as complete!" + }; + }, + + deleteCompleted: async function(context) { + const { object } = context; + + const completed = await object.find({ + filters: [["completed", "=", true]] + }); + + for (const todo of completed) { + await object.delete(todo._id); + } + + return { + success: true, + message: `Deleted ${completed.length} completed todo(s)` + }; + } + } +}; +``` + +## Step 5: Test Your Application + +1. **Refresh the browser** - The dev server auto-reloads changes +2. **Navigate to "My Todos"** - Find it in the sidebar +3. **Create a todo** - Click "New" button +4. **Fill in details**: + - Title: "Learn ObjectStack" + - Priority: High + - Due Date: Tomorrow +5. **Test actions**: + - Mark the todo as complete + - Create more todos + - Delete completed items + +## Step 6: Understanding What Happened + +### Data Flow + +``` +User Action → ObjectUI → ObjectOS → ObjectQL → SQLite + ↓ ↓ ↓ ↓ ↓ + Click JSON UI Runtime Query Database + Protocol Engine Engine (local) +``` + +### Key Concepts + +1. **ObjectQL Schema** - Defines your data structure, works across all databases +2. **ObjectUI JSON** - Declarative UI, no React/Vue code needed +3. **Triggers** - Business logic runs on data changes +4. **Local SQLite** - Everything stored in a local file + +## Step 7: Build and Package + +### Development Build + +```bash +npm run build +``` + +Creates an optimized production build in `dist/` folder. + +### Create Portable Package + +```bash +npm run package +``` + +This creates a `.oos` file that includes: + +- Your application code +- The SQLite database +- All dependencies + +**Share it with others** - They can install and run it instantly! + +## Common Tasks + +### Adding New Fields + +Edit the object definition and restart the dev server. ObjectQL automatically migrates the database schema! + +```javascript +// Add to src/objects/todo.object.js +tags: { + type: "lookup", + label: "Tags", + reference_to: "tag", + multiple: true +} +``` + +### Changing UI Layout + +Modify the page JSON - changes reflect immediately: + +```json +{ + "layout": { + "type": "kanban", + "object": "todo", + "groupBy": "priority" + } +} +``` + +### Adding Validation + +Use field validators: + +```javascript +dueDate: { + type: "date", + label: "Due Date", + validators: [ + { + type: "custom", + function: (value) => { + if (value < new Date()) { + throw new Error("Due date cannot be in the past"); + } + } + } + ] +} +``` + +## Data Storage Location + +Your SQLite database is stored at: + +``` +~/.objectos/my-todo-app/database.sqlite +``` + +- **Backup**: Just copy this file +- **Migrate**: Move it to another computer +- **Reset**: Delete it to start fresh + +## Next Steps + +Congratulations! You've built your first ObjectStack application. + +### Learn More + +> **Note**: Some links below point to future documentation sections that are under development. + +- Explore [ObjectQL](../02-objectql) - Advanced data modeling +- Master [ObjectUI](../03-objectui) - Complex UI patterns +- Study Triggers & Logic (coming soon) - Business rules + +### Try These Projects + +- **Personal Finance Tracker** - Track income and expenses +- **Recipe Book** - Store and organize recipes +- **Reading List** - Manage books and reading progress +- **Project Tracker** - Track personal projects and tasks + +### Share Your App + +Package your application: + +```bash +npm run package +# Creates my-todo-app.oos +``` + +Others can install it: + +```bash +objectos install my-todo-app.oos +``` + +## Troubleshooting + +### Port Already in Use + +If port 3000 is busy, change it in `objectos.config.js`: + +```javascript +export default { + server: { + port: 3001 + } +}; +``` + +### Database Locked + +If you see "database is locked" error: + +```bash +# Stop all ObjectOS processes +npm run stop + +# Restart +npm run dev +``` + +### Module Not Found + +Reinstall dependencies: + +```bash +rm -rf node_modules package-lock.json +npm install +``` + +## Getting Help + +- 📚 [Documentation](../../) - Complete guides +- 💬 [Community Forum](https://github.com/objectstack/objectstack/discussions) +- 🐛 [Report Issues](https://github.com/objectstack/objectstack/issues) +- 💡 [Examples Repository](https://github.com/objectstack/examples) diff --git a/content/docs/01-quickstart/individual-developers.zh-CN.mdx b/content/docs/01-quickstart/individual-developers.zh-CN.mdx new file mode 100644 index 0000000..f6a4a93 --- /dev/null +++ b/content/docs/01-quickstart/individual-developers.zh-CN.mdx @@ -0,0 +1,461 @@ +--- +title: 个人开发者 +description: 个人开发者构建本地优先应用的快速入门指南 +--- + +# 个人开发者指南 + +本指南将帮助您在 10 分钟内创建第一个 ObjectStack 应用。无需数据库服务器! + +## 为什么选择本地优先? + +作为个人开发者,您可以: + +- 💾 **拥有数据** - 所有数据存储在本地 SQLite 中 +- 🚀 **即刻启动** - 无需安装或配置数据库 +- 🔒 **隐私优先** - 数据永不离开您的设备 +- 📦 **便携应用** - 打包并分享为 `.oos` 文件 + +## 前置要求 + +开始之前,请确保已安装: + +- **Node.js 18 或更高版本** +- 代码编辑器(推荐 VS Code) +- 基本的 JavaScript/TypeScript 知识 + +### 检查 Node.js 版本 + +```bash +node --version +# 应显示 v18.0.0 或更高 +``` + +### 安装 Node.js(如需要) + +如果尚未安装 Node.js,请从 [nodejs.org](https://nodejs.org/) 下载: + +- **推荐**: LTS 版本(长期支持版) +- **最低**: v18.0.0 + +## 步骤 1: 创建您的第一个应用 + +使用官方脚手架工具创建新的 ObjectStack 应用: + +```bash +npm create object-app my-todo-app +``` + +此命令将: + +1. 提示您选择模板 +2. 下载模板文件 +3. 设置项目结构 +4. 自动安装依赖 + +### 模板选择 + +当提示时,选择 **SQLite 本地模板**: + +``` +? 选择一个模板: (使用方向键) +❯ SQLite 本地模板(推荐个人开发者使用) + MySQL 企业模板 + PostgreSQL 企业模板 + 自定义配置 +``` + +## 步骤 2: 进入项目目录 + +```bash +cd my-todo-app +``` + +### 项目结构 + +新项目包含: + +``` +my-todo-app/ +├── src/ +│ ├── objects/ # 数据模型(ObjectQL 模式) +│ ├── pages/ # UI 定义(ObjectUI JSON) +│ ├── triggers/ # 业务逻辑 +│ └── index.ts # 入口文件 +├── package.json +├── objectos.config.js # ObjectOS 配置 +└── README.md +``` + +## 步骤 3: 启动开发服务器 + +```bash +npm run dev +``` + +这将: + +- 启动 ObjectOS 运行时 +- 初始化 SQLite 数据库 +- 在 `http://localhost:3000` 启动 Web 界面 + +### 预期输出 + +``` +ObjectOS v1.0.0 +✓ 数据库已初始化 (SQLite) +✓ 对象已加载 (5 个对象) +✓ 服务器运行于 http://localhost:3000 +``` + +## 步骤 4: 构建待办事项应用 + +让我们创建一个简单的待办事项列表来理解 ObjectStack 的工作流程。 + +### 定义数据模型(ObjectQL) + +创建 `src/objects/todo.object.js`: + +```javascript +export default { + name: "todo", + label: "待办事项", + fields: { + title: { + type: "text", + label: "标题", + required: true + }, + description: { + type: "textarea", + label: "描述" + }, + completed: { + type: "boolean", + label: "已完成", + defaultValue: false + }, + priority: { + type: "select", + label: "优先级", + options: [ + { label: "低", value: "low" }, + { label: "中", value: "medium" }, + { label: "高", value: "high" } + ], + defaultValue: "medium" + }, + dueDate: { + type: "date", + label: "截止日期" + }, + completedAt: { + type: "datetime", + label: "完成时间", + readonly: true + } + }, + list_views: { + all: { + label: "所有待办", + columns: ["title", "priority", "dueDate", "completed"], + filter_fields: ["priority", "completed"], + sort: [["createdAt", "desc"]] + }, + pending: { + label: "待完成", + filter_scope: "completed eq false", + columns: ["title", "priority", "dueDate"] + }, + completed: { + label: "已完成", + filter_scope: "completed eq true", + columns: ["title", "completedAt"] + } + } +}; +``` + +### 创建 UI(ObjectUI) + +创建 `src/pages/todos.page.json`: + +```json +{ + "name": "todos", + "label": "我的待办", + "icon": "CheckSquare", + "layout": { + "type": "standard_list", + "object": "todo", + "defaultView": "all", + "enableQuickCreate": true, + "enableSearch": true, + "actions": [ + { + "label": "标记为完成", + "name": "markComplete", + "on": "record", + "visible": "completed eq false" + }, + { + "label": "删除已完成", + "name": "deleteCompleted", + "on": "list" + } + ] + } +} +``` + +### 添加业务逻辑(触发器) + +创建 `src/triggers/todo.trigger.js`: + +```javascript +export default { + listenTo: "todo", + + // 自动设置完成时间戳 + beforeUpdate: async function(context) { + const { doc, previousDoc } = context; + + if (doc.completed && !previousDoc.completed) { + doc.completedAt = new Date(); + } + + if (!doc.completed && previousDoc.completed) { + doc.completedAt = null; + } + }, + + // 自定义操作: 标记为完成 + actions: { + markComplete: async function(context) { + const { record, object } = context; + + await object.update(record._id, { + completed: true, + completedAt: new Date() + }); + + return { + success: true, + message: "待办已标记为完成!" + }; + }, + + deleteCompleted: async function(context) { + const { object } = context; + + const completed = await object.find({ + filters: [["completed", "=", true]] + }); + + for (const todo of completed) { + await object.delete(todo._id); + } + + return { + success: true, + message: `已删除 ${completed.length} 个已完成的待办` + }; + } + } +}; +``` + +## 步骤 5: 测试您的应用 + +1. **刷新浏览器** - 开发服务器自动重新加载更改 +2. **导航到"我的待办"** - 在侧边栏中找到它 +3. **创建待办事项** - 点击"新建"按钮 +4. **填写详情**: + - 标题: "学习 ObjectStack" + - 优先级: 高 + - 截止日期: 明天 +5. **测试操作**: + - 标记待办为完成 + - 创建更多待办 + - 删除已完成的项目 + +## 步骤 6: 理解发生了什么 + +### 数据流 + +``` +用户操作 → ObjectUI → ObjectOS → ObjectQL → SQLite + ↓ ↓ ↓ ↓ ↓ + 点击 JSON UI 运行时 查询引擎 数据库 + 协议 引擎 (本地) +``` + +### 核心概念 + +1. **ObjectQL 模式** - 定义数据结构,适用于所有数据库 +2. **ObjectUI JSON** - 声明式 UI,无需编写 React/Vue 代码 +3. **触发器** - 业务逻辑在数据变化时运行 +4. **本地 SQLite** - 所有内容存储在本地文件中 + +## 步骤 7: 构建和打包 + +### 开发构建 + +```bash +npm run build +``` + +在 `dist/` 文件夹中创建优化的生产构建。 + +### 创建便携包 + +```bash +npm run package +``` + +这将创建一个 `.oos` 文件,包含: + +- 您的应用代码 +- SQLite 数据库 +- 所有依赖 + +**分享给他人** - 他们可以立即安装和运行! + +## 常见任务 + +### 添加新字段 + +编辑对象定义并重启开发服务器。ObjectQL 自动迁移数据库模式! + +```javascript +// 添加到 src/objects/todo.object.js +tags: { + type: "lookup", + label: "标签", + reference_to: "tag", + multiple: true +} +``` + +### 更改 UI 布局 + +修改页面 JSON - 更改立即生效: + +```json +{ + "layout": { + "type": "kanban", + "object": "todo", + "groupBy": "priority" + } +} +``` + +### 添加验证 + +使用字段验证器: + +```javascript +dueDate: { + type: "date", + label: "截止日期", + validators: [ + { + type: "custom", + function: (value) => { + if (value < new Date()) { + throw new Error("截止日期不能是过去的时间"); + } + } + } + ] +} +``` + +## 数据存储位置 + +您的 SQLite 数据库存储在: + +``` +~/.objectos/my-todo-app/database.sqlite +``` + +- **备份**: 只需复制此文件 +- **迁移**: 将其移动到另一台计算机 +- **重置**: 删除它以重新开始 + +## 下一步 + +恭喜!您已经构建了第一个 ObjectStack 应用。 + +### 深入学习 + +> **注意**: 以下某些链接指向正在开发中的未来文档章节。 + +- 探索 [ObjectQL](../02-objectql) - 高级数据建模 +- 掌握 [ObjectUI](../03-objectui) - 复杂 UI 模式 +- 学习触发器与逻辑(即将推出) - 业务规则 + +### 尝试这些项目 + +- **个人财务追踪器** - 跟踪收入和支出 +- **食谱书** - 存储和整理食谱 +- **阅读列表** - 管理书籍和阅读进度 +- **项目追踪器** - 跟踪个人项目和任务 + +### 分享您的应用 + +打包您的应用: + +```bash +npm run package +# 创建 my-todo-app.oos +``` + +他人可以安装: + +```bash +objectos install my-todo-app.oos +``` + +## 故障排除 + +### 端口已被占用 + +如果端口 3000 被占用,在 `objectos.config.js` 中更改: + +```javascript +export default { + server: { + port: 3001 + } +}; +``` + +### 数据库被锁定 + +如果看到"database is locked"错误: + +```bash +# 停止所有 ObjectOS 进程 +npm run stop + +# 重启 +npm run dev +``` + +### 模块未找到 + +重新安装依赖: + +```bash +rm -rf node_modules package-lock.json +npm install +``` + +## 获取帮助 + +- 📚 [文档](../../) - 完整指南 +- 💬 [社区论坛](https://github.com/objectstack/objectstack/discussions) +- 🐛 [报告问题](https://github.com/objectstack/objectstack/issues) +- 💡 [示例仓库](https://github.com/objectstack/examples) diff --git a/content/docs/01-quickstart/meta.en.json b/content/docs/01-quickstart/meta.en.json new file mode 100644 index 0000000..cde748f --- /dev/null +++ b/content/docs/01-quickstart/meta.en.json @@ -0,0 +1,9 @@ +{ + "title": "Quick Start", + "pages": [ + "index", + "individual-developers", + "enterprise-integrators", + "demos" + ] +} diff --git a/content/docs/01-quickstart/meta.zh-CN.json b/content/docs/01-quickstart/meta.zh-CN.json new file mode 100644 index 0000000..2918e35 --- /dev/null +++ b/content/docs/01-quickstart/meta.zh-CN.json @@ -0,0 +1,9 @@ +{ + "title": "快速开始", + "pages": [ + "index", + "individual-developers", + "enterprise-integrators", + "demos" + ] +} diff --git a/content/docs/02-objectql/core-concepts.en.mdx b/content/docs/02-objectql/core-concepts.en.mdx new file mode 100644 index 0000000..492b42e --- /dev/null +++ b/content/docs/02-objectql/core-concepts.en.mdx @@ -0,0 +1,389 @@ +--- +title: Core Concepts +description: Understanding Schema, Virtual City, and ObjectQL fundamentals +--- + +# Core Concepts + +This guide explains the fundamental concepts that power ObjectQL's architecture and capabilities. + +## What is Schema? + +A **Schema** is a declarative definition of your data structure. Instead of writing SQL CREATE TABLE statements, you define your data model as JavaScript objects. + +### Schema Structure + +A Schema consists of **Objects** (similar to tables) and their **Fields** (similar to columns): + +```typescript +const schema = { + objects: { + // Object name (singular) + customer: { + label: 'Customer', + fields: { + // Field definitions + name: { + type: 'text', + label: 'Full Name', + required: true + }, + email: { + type: 'email', + unique: true + }, + created_at: { + type: 'datetime', + defaultValue: () => new Date() + } + } + } + } +} +``` + +### Why Schema? + +**Benefits of Schema-driven architecture:** + +1. **Database Independence** - Define once, run anywhere +2. **Type Safety** - Automatic validation and type checking +3. **Auto-migration** - Schema changes automatically sync to database +4. **Self-documenting** - Schema serves as living documentation +5. **Tooling Support** - IDE autocomplete, validation, and generation + +### Schema Registration + +Register your schema with ObjectQL: + +```typescript +import { ObjectQL } from '@objectql/core' + +const db = new ObjectQL({ + driver: 'sqlite', + url: './myapp.db' +}) + +// Register schema +await db.registerSchema(schema) + +// Now you can query your objects +const customers = await db.query('customer', {}) +``` + +## Field Types + +ObjectQL supports rich field types that map to appropriate database types: + +### Basic Types + +```typescript +{ + // Text types + short_text: { type: 'text' }, // VARCHAR(255) + long_text: { type: 'textarea' }, // TEXT + rich_text: { type: 'html' }, // TEXT + + // Numeric types + age: { type: 'number' }, // INTEGER + price: { type: 'currency', scale: 2 }, // DECIMAL(15,2) + percentage: { type: 'percent' }, // DECIMAL(5,2) + + // Date/Time types + birthday: { type: 'date' }, // DATE + created_at: { type: 'datetime' }, // DATETIME + + // Boolean + is_active: { type: 'boolean' }, // BOOLEAN + + // Select (enum) + status: { + type: 'select', + options: ['draft', 'published', 'archived'] + }, + + // JSON + metadata: { type: 'json' } // JSON/TEXT +} +``` + +### Relationship Types + +```typescript +{ + // Master-detail (foreign key) + owner: { + type: 'master_detail', + reference_to: 'user', // References user._id + required: true + }, + + // Lookup (optional reference) + manager: { + type: 'lookup', + reference_to: 'user' + } +} +``` + +## Field Constraints + +Add validation and constraints to fields: + +```typescript +{ + fields: { + email: { + type: 'email', + required: true, // NOT NULL + unique: true, // UNIQUE constraint + index: true // Create index + }, + + username: { + type: 'text', + minLength: 3, // Validation + maxLength: 20, + pattern: '^[a-zA-Z0-9_]+$' // Regex validation + }, + + age: { + type: 'number', + min: 18, // Minimum value + max: 120 // Maximum value + }, + + status: { + type: 'select', + options: ['active', 'inactive'], + defaultValue: 'active' // Default value + } + } +} +``` + +## What is Virtual City? + +**Virtual City** is ObjectQL's mechanism for **multi-tenant data isolation**. It creates logical data partitions within a single database, enabling secure multi-tenancy without database duplication. + +### The Concept + +Imagine a city where each tenant has their own "virtual building": + +``` +┌─────────────────────────────────────────┐ +│ Physical Database │ +│ │ +│ ┌──────────────┐ ┌──────────────┐ │ +│ │ Virtual City │ │ Virtual City │ │ +│ │ (Tenant A) │ │ (Tenant B) │ │ +│ │ │ │ │ │ +│ │ ┌────────┐ │ │ ┌────────┐ │ │ +│ │ │Customers│ │ │ │Customers│ │ │ +│ │ │Orders │ │ │ │Orders │ │ │ +│ │ └────────┘ │ │ └────────┘ │ │ +│ └──────────────┘ └──────────────┘ │ +│ │ +└─────────────────────────────────────────┘ +``` + +### How It Works + +ObjectQL automatically adds a `space` field to every record and filters queries by tenant: + +```typescript +// Initialize main database +const db = new ObjectQL({ + driver: 'mysql', + url: 'mysql://localhost/myapp' +}) + +// Create virtual cities for tenants +const tenantA = db.virtualCity('tenant_a') +const tenantB = db.virtualCity('tenant_b') + +// Each tenant sees only their own data +await tenantA.mutation('customer', { + action: 'insert', + data: { name: 'Customer A1' } +}) + +await tenantB.mutation('customer', { + action: 'insert', + data: { name: 'Customer B1' } +}) + +// Tenant A only sees their customers +const customersA = await tenantA.query('customer', {}) +// Returns: [{ name: 'Customer A1', space: 'tenant_a' }] + +// Tenant B only sees their customers +const customersB = await tenantB.query('customer', {}) +// Returns: [{ name: 'Customer B1', space: 'tenant_b' }] +``` + +### Behind the Scenes + +ObjectQL implements Virtual City through: + +1. **Automatic Space Field** - Added to all tables +2. **Query Filtering** - Automatically adds `WHERE space = ?` +3. **Data Isolation** - Cross-tenant queries are impossible +4. **Index Optimization** - Composite indexes on `(space, ...)` + +### Virtual City Use Cases + +**Multi-tenant SaaS:** + +```typescript +// Each customer gets isolated data +const customerDb = db.virtualCity(`customer_${customerId}`) +``` + +**Department Isolation:** + +```typescript +// Separate data by department +const salesDb = db.virtualCity('dept_sales') +const hrDb = db.virtualCity('dept_hr') +``` + +**Environment Separation:** + +```typescript +// Separate dev/staging/prod data in same database +const devDb = db.virtualCity('env_dev') +const prodDb = db.virtualCity('env_prod') +``` + +## System Fields + +ObjectQL automatically adds system fields to every object: + +```typescript +{ + _id: string // Primary key (UUID) + space: string // Virtual City identifier + created: datetime // Creation timestamp + created_by: string // Creator user ID + modified: datetime // Last modification timestamp + modified_by: string // Last modifier user ID + owner: string // Record owner +} +``` + +These fields are: + +- ✅ Automatically managed by ObjectQL +- ✅ Indexed for performance +- ✅ Available in all queries +- ✅ Used for audit trails + +### Using System Fields + +```typescript +// Query by creator +const myRecords = await db.query('customer', { + filters: [['created_by', '=', currentUserId]] +}) + +// Sort by modification time +const recent = await db.query('customer', { + sort: 'modified desc', + limit: 10 +}) + +// Track changes +const record = await db.query('customer', { + filters: [['_id', '=', recordId]] +}) + +console.log(`Created by ${record.created_by} at ${record.created}`) +console.log(`Modified by ${record.modified_by} at ${record.modified}`) +``` + +## Schema Evolution + +ObjectQL handles schema changes automatically: + +### Adding Fields + +```typescript +// Original schema +{ + objects: { + customer: { + fields: { + name: { type: 'text' } + } + } + } +} + +// Updated schema - add email field +{ + objects: { + customer: { + fields: { + name: { type: 'text' }, + email: { type: 'email' } // New field + } + } + } +} + +// Re-register schema +await db.registerSchema(updatedSchema) +// ObjectQL automatically runs: ALTER TABLE customer ADD COLUMN email VARCHAR(255) +``` + +### Modifying Fields + +```typescript +// Change field type +{ + email: { type: 'text' } // Was 'email' +} + +// ObjectQL handles data migration automatically +``` + +### Removing Fields + +```typescript +// Remove field from schema +// ObjectQL marks field as deprecated but doesn't drop column +// This prevents data loss +``` + +## Best Practices + +### Schema Design + +1. **Use singular names** - `customer` not `customers` +2. **Be descriptive** - Use clear field names +3. **Add labels** - Help UI generation +4. **Set constraints** - Use required, unique, etc. +5. **Plan relationships** - Think about master-detail links + +### Virtual City + +1. **Consistent naming** - Use a convention like `tenant_{id}` +2. **Validate tenant IDs** - Ensure valid tenant before creating Virtual City +3. **Cache instances** - Reuse Virtual City instances +4. **Index strategy** - Ensure space is in composite indexes + +### Performance + +1. **Index frequently queried fields** +2. **Use appropriate field types** +3. **Limit JSON field usage** +4. **Leverage system field indexes** + +## Next Steps + +Now that you understand the core concepts: + +- Learn the [Protocol Specification](./protocol-spec) for detailed schema and query syntax +- Explore [Core Features](./core-features) for advanced optimizations +- Master the [Server SDK](./server-sdk) API diff --git a/content/docs/02-objectql/core-concepts.zh-CN.mdx b/content/docs/02-objectql/core-concepts.zh-CN.mdx new file mode 100644 index 0000000..d20ced5 --- /dev/null +++ b/content/docs/02-objectql/core-concepts.zh-CN.mdx @@ -0,0 +1,389 @@ +--- +title: 核心概念 +description: 理解 Schema、Virtual City 和 ObjectQL 基础 +--- + +# 核心概念 + +本指南解释了驱动 ObjectQL 架构和能力的基本概念。 + +## 什么是 Schema? + +**Schema** 是数据结构的声明式定义。您无需编写 SQL CREATE TABLE 语句,而是将数据模型定义为 JavaScript 对象。 + +### Schema 结构 + +Schema 由 **Objects**(类似于表)和 **Fields**(类似于列)组成: + +```typescript +const schema = { + objects: { + // 对象名称(单数) + customer: { + label: '客户', + fields: { + // 字段定义 + name: { + type: 'text', + label: '全名', + required: true + }, + email: { + type: 'email', + unique: true + }, + created_at: { + type: 'datetime', + defaultValue: () => new Date() + } + } + } + } +} +``` + +### 为什么使用 Schema? + +**Schema 驱动架构的优势:** + +1. **数据库独立** - 定义一次,随处运行 +2. **类型安全** - 自动验证和类型检查 +3. **自动迁移** - Schema 变更自动同步到数据库 +4. **自文档化** - Schema 作为动态文档 +5. **工具支持** - IDE 自动完成、验证和代码生成 + +### Schema 注册 + +向 ObjectQL 注册您的 Schema: + +```typescript +import { ObjectQL } from '@objectql/core' + +const db = new ObjectQL({ + driver: 'sqlite', + url: './myapp.db' +}) + +// 注册 Schema +await db.registerSchema(schema) + +// 现在可以查询您的对象 +const customers = await db.query('customer', {}) +``` + +## 字段类型 + +ObjectQL 支持丰富的字段类型,可映射到适当的数据库类型: + +### 基础类型 + +```typescript +{ + // 文本类型 + short_text: { type: 'text' }, // VARCHAR(255) + long_text: { type: 'textarea' }, // TEXT + rich_text: { type: 'html' }, // TEXT + + // 数值类型 + age: { type: 'number' }, // INTEGER + price: { type: 'currency', scale: 2 }, // DECIMAL(15,2) + percentage: { type: 'percent' }, // DECIMAL(5,2) + + // 日期/时间类型 + birthday: { type: 'date' }, // DATE + created_at: { type: 'datetime' }, // DATETIME + + // 布尔值 + is_active: { type: 'boolean' }, // BOOLEAN + + // 选择(枚举) + status: { + type: 'select', + options: ['draft', 'published', 'archived'] + }, + + // JSON + metadata: { type: 'json' } // JSON/TEXT +} +``` + +### 关系类型 + +```typescript +{ + // 主从关系(外键) + owner: { + type: 'master_detail', + reference_to: 'user', // 引用 user._id + required: true + }, + + // 查找(可选引用) + manager: { + type: 'lookup', + reference_to: 'user' + } +} +``` + +## 字段约束 + +为字段添加验证和约束: + +```typescript +{ + fields: { + email: { + type: 'email', + required: true, // NOT NULL + unique: true, // UNIQUE 约束 + index: true // 创建索引 + }, + + username: { + type: 'text', + minLength: 3, // 验证 + maxLength: 20, + pattern: '^[a-zA-Z0-9_]+$' // 正则验证 + }, + + age: { + type: 'number', + min: 18, // 最小值 + max: 120 // 最大值 + }, + + status: { + type: 'select', + options: ['active', 'inactive'], + defaultValue: 'active' // 默认值 + } + } +} +``` + +## 什么是 Virtual City? + +**Virtual City** 是 ObjectQL 实现**多租户数据隔离**的机制。它在单个数据库中创建逻辑数据分区,实现安全的多租户,而无需复制数据库。 + +### 概念说明 + +想象一座城市,每个租户都有自己的"虚拟建筑": + +``` +┌─────────────────────────────────────────┐ +│ 物理数据库 │ +│ │ +│ ┌──────────────┐ ┌──────────────┐ │ +│ │ Virtual City │ │ Virtual City │ │ +│ │ (租户 A) │ │ (租户 B) │ │ +│ │ │ │ │ │ +│ │ ┌────────┐ │ │ ┌────────┐ │ │ +│ │ │ 客户 │ │ │ │ 客户 │ │ │ +│ │ │ 订单 │ │ │ │ 订单 │ │ │ +│ │ └────────┘ │ │ └────────┘ │ │ +│ └──────────────┘ └──────────────┘ │ +│ │ +└─────────────────────────────────────────┘ +``` + +### 工作原理 + +ObjectQL 自动为每条记录添加 `space` 字段,并按租户过滤查询: + +```typescript +// 初始化主数据库 +const db = new ObjectQL({ + driver: 'mysql', + url: 'mysql://localhost/myapp' +}) + +// 为租户创建 Virtual City +const tenantA = db.virtualCity('tenant_a') +const tenantB = db.virtualCity('tenant_b') + +// 每个租户只能看到自己的数据 +await tenantA.mutation('customer', { + action: 'insert', + data: { name: '客户 A1' } +}) + +await tenantB.mutation('customer', { + action: 'insert', + data: { name: '客户 B1' } +}) + +// 租户 A 只能看到自己的客户 +const customersA = await tenantA.query('customer', {}) +// 返回: [{ name: '客户 A1', space: 'tenant_a' }] + +// 租户 B 只能看到自己的客户 +const customersB = await tenantB.query('customer', {}) +// 返回: [{ name: '客户 B1', space: 'tenant_b' }] +``` + +### 底层实现 + +ObjectQL 通过以下方式实现 Virtual City: + +1. **自动 Space 字段** - 添加到所有表 +2. **查询过滤** - 自动添加 `WHERE space = ?` +3. **数据隔离** - 跨租户查询不可能 +4. **索引优化** - 在 `(space, ...)` 上建立复合索引 + +### Virtual City 使用场景 + +**多租户 SaaS:** + +```typescript +// 每个客户获得隔离的数据 +const customerDb = db.virtualCity(`customer_${customerId}`) +``` + +**部门隔离:** + +```typescript +// 按部门分离数据 +const salesDb = db.virtualCity('dept_sales') +const hrDb = db.virtualCity('dept_hr') +``` + +**环境分离:** + +```typescript +// 在同一数据库中分离 dev/staging/prod 数据 +const devDb = db.virtualCity('env_dev') +const prodDb = db.virtualCity('env_prod') +``` + +## 系统字段 + +ObjectQL 自动为每个对象添加系统字段: + +```typescript +{ + _id: string // 主键 (UUID) + space: string // Virtual City 标识符 + created: datetime // 创建时间戳 + created_by: string // 创建者用户 ID + modified: datetime // 最后修改时间戳 + modified_by: string // 最后修改者用户 ID + owner: string // 记录所有者 +} +``` + +这些字段: + +- ✅ 由 ObjectQL 自动管理 +- ✅ 已建立索引以提高性能 +- ✅ 在所有查询中可用 +- ✅ 用于审计跟踪 + +### 使用系统字段 + +```typescript +// 按创建者查询 +const myRecords = await db.query('customer', { + filters: [['created_by', '=', currentUserId]] +}) + +// 按修改时间排序 +const recent = await db.query('customer', { + sort: 'modified desc', + limit: 10 +}) + +// 跟踪变更 +const record = await db.query('customer', { + filters: [['_id', '=', recordId]] +}) + +console.log(`由 ${record.created_by} 创建于 ${record.created}`) +console.log(`由 ${record.modified_by} 修改于 ${record.modified}`) +``` + +## Schema 演进 + +ObjectQL 自动处理 Schema 变更: + +### 添加字段 + +```typescript +// 原始 Schema +{ + objects: { + customer: { + fields: { + name: { type: 'text' } + } + } + } +} + +// 更新 Schema - 添加 email 字段 +{ + objects: { + customer: { + fields: { + name: { type: 'text' }, + email: { type: 'email' } // 新字段 + } + } + } +} + +// 重新注册 Schema +await db.registerSchema(updatedSchema) +// ObjectQL 自动运行: ALTER TABLE customer ADD COLUMN email VARCHAR(255) +``` + +### 修改字段 + +```typescript +// 更改字段类型 +{ + email: { type: 'text' } // 之前是 'email' +} + +// ObjectQL 自动处理数据迁移 +``` + +### 删除字段 + +```typescript +// 从 Schema 中删除字段 +// ObjectQL 将字段标记为已弃用但不删除列 +// 这可以防止数据丢失 +``` + +## 最佳实践 + +### Schema 设计 + +1. **使用单数名称** - `customer` 而不是 `customers` +2. **描述清晰** - 使用清晰的字段名 +3. **添加标签** - 帮助 UI 生成 +4. **设置约束** - 使用 required、unique 等 +5. **规划关系** - 考虑主从关系 + +### Virtual City + +1. **一致的命名** - 使用如 `tenant_{id}` 的约定 +2. **验证租户 ID** - 创建 Virtual City 前确保租户有效 +3. **缓存实例** - 重用 Virtual City 实例 +4. **索引策略** - 确保 space 在复合索引中 + +### 性能 + +1. **为频繁查询的字段建立索引** +2. **使用适当的字段类型** +3. **限制 JSON 字段使用** +4. **利用系统字段索引** + +## 下一步 + +现在您已理解核心概念: + +- 学习[协议规范](./protocol-spec)了解详细的 Schema 和查询语法 +- 探索[核心功能](./core-features)了解高级优化 +- 掌握[服务端 SDK](./server-sdk) API diff --git a/content/docs/02-objectql/core-features.en.mdx b/content/docs/02-objectql/core-features.en.mdx new file mode 100644 index 0000000..69833da --- /dev/null +++ b/content/docs/02-objectql/core-features.en.mdx @@ -0,0 +1,580 @@ +--- +title: Core Features +description: Advanced features including Virtual Column Index, cross-database drivers, and performance optimizations +--- + +# Core Features + +ObjectQL provides advanced features that optimize performance and enable seamless cross-database support. + +## Virtual Column Index for SQLite + +**Virtual Column Index** is ObjectQL's performance optimization for SQLite that dramatically improves query speed on computed and relationship fields. + +### The Problem + +In traditional ORMs, querying related data requires expensive JOINs: + +```sql +-- Traditional approach: JOIN query +SELECT o.*, u.name as owner_name +FROM orders o +LEFT JOIN users u ON o.owner_id = u._id +WHERE u.name LIKE '%john%' +-- This can be slow on large tables +``` + +### The Solution: Virtual Columns + +ObjectQL creates **virtual indexed columns** for relationship fields: + +```sql +-- ObjectQL automatically creates +ALTER TABLE orders ADD COLUMN owner_name_virtual TEXT; +CREATE INDEX idx_orders_owner_name ON orders(owner_name_virtual); + +-- Updates happen automatically via triggers +CREATE TRIGGER update_owner_name_virtual +AFTER UPDATE ON users +BEGIN + UPDATE orders SET owner_name_virtual = NEW.name + WHERE owner_id = NEW._id; +END; +``` + +### How It Works + +1. **Virtual Column Creation** - ObjectQL adds indexed virtual columns for lookup fields +2. **Automatic Sync** - Database triggers keep virtual columns synchronized +3. **Query Optimization** - Queries use indexed virtual columns instead of JOINs +4. **Transparent Operation** - Completely automatic, no code changes needed + +### Example + +```typescript +// Define schema with lookup +await db.registerSchema({ + objects: { + order: { + fields: { + product: { + type: 'lookup', + reference_to: 'product' + }, + customer: { + type: 'lookup', + reference_to: 'customer' + }, + amount: { type: 'currency' } + } + } + } +}) + +// Query with virtual column index +const orders = await db.query('order', { + filters: [ + ['product.name', 'contains', 'laptop'], // Uses virtual column + ['customer.email', 'endswith', '@acme.com'] // Uses virtual column + ] +}) + +// Behind the scenes: +// SELECT * FROM orders +// WHERE product_name_virtual LIKE '%laptop%' +// AND customer_email_virtual LIKE '%@acme.com' +// -- Fast! Uses indexes instead of JOINs +``` + +### Performance Comparison + +``` +Traditional JOIN Approach: +├── Orders: 100,000 records +├── Products: 10,000 records +└── Query time: ~2.5 seconds + +Virtual Column Index: +├── Orders: 100,000 records (with virtual columns) +├── Indexed virtual columns +└── Query time: ~0.05 seconds (50x faster!) +``` + +### Configuration + +Virtual Column Index is **enabled by default** for SQLite. Configure per field: + +```typescript +{ + fields: { + owner: { + type: 'lookup', + reference_to: 'user', + virtual_index: true, // Enable virtual index (default) + virtual_fields: ['name', 'email'] // Fields to index + } + } +} +``` + +### Limitations + +- **SQLite only** - Not needed for MySQL/PostgreSQL (they handle JOINs efficiently) +- **Write overhead** - Slight performance cost on updates (triggers execute) +- **Storage** - Additional disk space for virtual columns +- **Not real-time** - Brief delay during trigger execution + +### Best Practices + +1. **Enable for frequently queried fields** +2. **Limit virtual_fields** - Only index fields you search/filter on +3. **Monitor storage** - Virtual columns use disk space +4. **Use with large datasets** - Most beneficial for 10k+ records + +## Cross-Database Driver Adaptation + +ObjectQL provides a **unified API** across different database systems through intelligent driver adaptation. + +### Supported Databases + +```typescript +// SQLite (embedded) +const db = new ObjectQL({ + driver: 'sqlite', + url: './data/myapp.db' +}) + +// MySQL +const db = new ObjectQL({ + driver: 'mysql', + url: 'mysql://user:pass@localhost:3306/mydb' +}) + +// PostgreSQL +const db = new ObjectQL({ + driver: 'postgresql', + url: 'postgresql://user:pass@localhost:5432/mydb' +}) + +// SQL Server +const db = new ObjectQL({ + driver: 'mssql', + url: 'mssql://user:pass@localhost:1433/mydb' +}) + +// Oracle +const db = new ObjectQL({ + driver: 'oracle', + url: 'oracle://user:pass@localhost:1521/mydb' +}) +``` + +### Driver Architecture + +``` +┌─────────────────────────────────────────┐ +│ ObjectQL Unified API │ +└────────────────┬────────────────────────┘ + │ + │ +┌────────────────▼────────────────────────┐ +│ Driver Abstraction Layer │ +│ │ +│ ┌──────────────────────────────────┐ │ +│ │ SQL Dialect Translator │ │ +│ │ - Type mapping │ │ +│ │ - Syntax conversion │ │ +│ │ - Feature adaptation │ │ +│ └──────────────────────────────────┘ │ +└────────────────┬────────────────────────┘ + │ + ┌─────────┼─────────┐ + │ │ │ +┌──────▼───┐ ┌──▼──────┐ ┌▼────────┐ +│ SQLite │ │ MySQL │ │Postgres │ +│ Driver │ │ Driver │ │ Driver │ +└──────────┘ └─────────┘ └─────────┘ +``` + +### Type Mapping + +ObjectQL automatically maps field types to database-specific types: + +```typescript +// ObjectQL Schema +{ + created_at: { type: 'datetime' }, + metadata: { type: 'json' }, + is_active: { type: 'boolean' } +} + +// SQLite +CREATE TABLE ( + created_at TEXT, -- ISO8601 string + metadata TEXT, -- JSON string + is_active INTEGER -- 0 or 1 +) + +// MySQL +CREATE TABLE ( + created_at DATETIME, + metadata JSON, + is_active BOOLEAN +) + +// PostgreSQL +CREATE TABLE ( + created_at TIMESTAMP, + metadata JSONB, -- Binary JSON + is_active BOOLEAN +) +``` + +### Dialect Translation + +ObjectQL translates queries to database-specific SQL: + +```typescript +// ObjectQL Query +await db.query('user', { + filters: [['name', 'contains', 'john']], + sort: 'created desc', + limit: 10 +}) + +// SQLite SQL +SELECT * FROM user +WHERE name LIKE '%john%' COLLATE NOCASE +ORDER BY created DESC +LIMIT 10 + +// MySQL SQL +SELECT * FROM user +WHERE LOWER(name) LIKE LOWER('%john%') +ORDER BY created DESC +LIMIT 10 + +// PostgreSQL SQL +SELECT * FROM user +WHERE name ILIKE '%john%' +ORDER BY created DESC +LIMIT 10 +``` + +### Feature Adaptation + +ObjectQL adapts features based on database capabilities: + +| Feature | SQLite | MySQL | PostgreSQL | +|---------|--------|-------|------------| +| Auto Increment | AUTOINCREMENT | AUTO_INCREMENT | SERIAL | +| JSON Fields | TEXT + parse | JSON | JSONB | +| Full-Text Search | FTS5 extension | FULLTEXT index | tsvector | +| UUID | TEXT | CHAR(36) | UUID type | +| Arrays | JSON array | JSON | ARRAY type | +| Virtual Columns | Yes (optimized) | Calculated | Generated | + +## MySQL/PostgreSQL Driver Configuration + +### MySQL Driver + +#### Connection Options + +```typescript +const db = new ObjectQL({ + driver: 'mysql', + url: 'mysql://user:password@localhost:3306/database', + + // Or detailed config + host: 'localhost', + port: 3306, + user: 'myapp_user', + password: 'secure_password', + database: 'myapp_db', + + // MySQL-specific options + mysql: { + charset: 'utf8mb4', + timezone: '+00:00', + connectTimeout: 10000, + acquireTimeout: 10000, + waitForConnections: true, + connectionLimit: 10, + queueLimit: 0, + enableKeepAlive: true, + keepAliveInitialDelay: 0, + + // SSL configuration + ssl: { + ca: fs.readFileSync('/path/to/ca.pem'), + key: fs.readFileSync('/path/to/client-key.pem'), + cert: fs.readFileSync('/path/to/client-cert.pem') + } + } +}) +``` + +#### Performance Tuning + +```typescript +// Connection pooling +{ + mysql: { + connectionLimit: 20, // Max connections + queueLimit: 50, // Max queued requests + waitForConnections: true, // Queue when limit reached + + // Timeouts + connectTimeout: 10000, // 10 seconds + acquireTimeout: 10000, + timeout: 60000, // Query timeout + + // Keep-alive + enableKeepAlive: true, + keepAliveInitialDelay: 10000 + } +} +``` + +#### MySQL-Specific Features + +```typescript +// Full-text search +await db.registerSchema({ + objects: { + article: { + fields: { + title: { type: 'text' }, + content: { type: 'textarea' } + }, + indexes: [ + { + name: 'fulltext_search', + type: 'FULLTEXT', + fields: ['title', 'content'] + } + ] + } + } +}) + +// Query with full-text search +const results = await db.query('article', { + filters: [ + ['MATCH(title, content) AGAINST(?)', 'search terms'] + ] +}) +``` + +### PostgreSQL Driver + +#### Connection Options + +```typescript +const db = new ObjectQL({ + driver: 'postgresql', + url: 'postgresql://user:password@localhost:5432/database', + + // Or detailed config + host: 'localhost', + port: 5432, + user: 'myapp_user', + password: 'secure_password', + database: 'myapp_db', + + // PostgreSQL-specific options + postgres: { + max: 20, // Max pool size + min: 2, // Min pool size + idle: 10000, // Idle timeout + connectionTimeoutMillis: 10000, + idleTimeoutMillis: 30000, + + // SSL configuration + ssl: { + rejectUnauthorized: false, + ca: fs.readFileSync('/path/to/ca.pem'), + key: fs.readFileSync('/path/to/client-key.pem'), + cert: fs.readFileSync('/path/to/client-cert.pem') + }, + + // Application name + application_name: 'MyApp', + + // Statement timeout + statement_timeout: 60000 // 60 seconds + } +}) +``` + +#### Performance Tuning + +```typescript +{ + postgres: { + // Connection pool + max: 25, // Max connections + min: 5, // Min idle connections + + // Timeouts + connectionTimeoutMillis: 10000, + idleTimeoutMillis: 30000, + statement_timeout: 60000, + + // Keep-alive + keepAlive: true, + keepAliveInitialDelayMillis: 10000 + } +} +``` + +#### PostgreSQL-Specific Features + +```typescript +// JSONB queries +await db.query('user', { + filters: [ + ["metadata->>'city'", '=', 'New York'], + ["metadata->'age'", '>', '18'] + ] +}) + +// Array fields +await db.registerSchema({ + objects: { + post: { + fields: { + tags: { + type: 'array', + items: { type: 'text' } + } + } + } + } +}) + +// Array queries +await db.query('post', { + filters: [ + ['tags', '@>', ['javascript', 'nodejs']] // Contains + ] +}) + +// Full-text search +await db.query('article', { + filters: [ + ["to_tsvector('english', content) @@ to_tsquery('search & terms')"] + ] +}) +``` + +### Connection Pool Monitoring + +```typescript +// Get pool stats +const stats = await db.getPoolStats() +console.log({ + total: stats.total, // Total connections + idle: stats.idle, // Idle connections + waiting: stats.waiting // Waiting requests +}) + +// Graceful shutdown +await db.close() +``` + +## Performance Best Practices + +### Indexing Strategy + +```typescript +{ + objects: { + order: { + fields: { + customer_id: { type: 'lookup', reference_to: 'customer' }, + status: { type: 'select', options: [...] }, + created: { type: 'datetime' } + }, + indexes: [ + // Composite index for common query + { + name: 'idx_customer_status', + fields: ['customer_id', 'status'] + }, + // Date range queries + { + name: 'idx_created', + fields: ['created'] + }, + // Virtual City + filters + { + name: 'idx_space_status', + fields: ['space', 'status', 'created'] + } + ] + } + } +} +``` + +### Query Optimization + +```typescript +// ✅ Good: Use field selection +await db.query('customer', { + fields: ['_id', 'name', 'email'], // Only needed fields + limit: 100 +}) + +// ❌ Bad: Select all fields +await db.query('customer', { + fields: ['*'], // Retrieves all fields including large ones + limit: 100 +}) + +// ✅ Good: Use indexed filters +await db.query('order', { + filters: [ + ['status', '=', 'pending'], // Indexed + ['created', '>', '2024-01-01'] // Indexed + ] +}) + +// ❌ Bad: Function in filter +await db.query('order', { + filters: [ + ['LOWER(status)', '=', 'pending'] // Can't use index + ] +}) +``` + +### Batch Operations + +```typescript +// ✅ Good: Batch insert +await db.mutation('product', { + action: 'insert', + data: [ + { name: 'Product 1' }, + { name: 'Product 2' }, + // ... 100 records + ] +}) +// Single transaction, one round-trip + +// ❌ Bad: Individual inserts +for (const product of products) { + await db.mutation('product', { + action: 'insert', + data: product + }) +} +// 100 transactions, 100 round-trips +``` + +## Next Steps + +- Master the [Server SDK](./server-sdk) API +- Review [Protocol Spec](./protocol-spec) for query syntax +- Check [Core Concepts](./core-concepts) for fundamentals diff --git a/content/docs/02-objectql/core-features.zh-CN.mdx b/content/docs/02-objectql/core-features.zh-CN.mdx new file mode 100644 index 0000000..77db4ee --- /dev/null +++ b/content/docs/02-objectql/core-features.zh-CN.mdx @@ -0,0 +1,580 @@ +--- +title: 核心功能 +description: 高级功能,包括虚拟列索引、跨数据库驱动和性能优化 +--- + +# 核心功能 + +ObjectQL 提供高级功能,优化性能并实现无缝的跨数据库支持。 + +## SQLite 虚拟列索引 + +**虚拟列索引**是 ObjectQL 针对 SQLite 的性能优化,可显著提高计算字段和关系字段的查询速度。 + +### 问题所在 + +在传统 ORM 中,查询关联数据需要昂贵的 JOIN 操作: + +```sql +-- 传统方法: JOIN 查询 +SELECT o.*, u.name as owner_name +FROM orders o +LEFT JOIN users u ON o.owner_id = u._id +WHERE u.name LIKE '%john%' +-- 在大表上可能很慢 +``` + +### 解决方案: 虚拟列 + +ObjectQL 为关系字段创建**虚拟索引列**: + +```sql +-- ObjectQL 自动创建 +ALTER TABLE orders ADD COLUMN owner_name_virtual TEXT; +CREATE INDEX idx_orders_owner_name ON orders(owner_name_virtual); + +-- 通过触发器自动更新 +CREATE TRIGGER update_owner_name_virtual +AFTER UPDATE ON users +BEGIN + UPDATE orders SET owner_name_virtual = NEW.name + WHERE owner_id = NEW._id; +END; +``` + +### 工作原理 + +1. **虚拟列创建** - ObjectQL 为查找字段添加索引虚拟列 +2. **自动同步** - 数据库触发器保持虚拟列同步 +3. **查询优化** - 查询使用索引虚拟列而不是 JOIN +4. **透明操作** - 完全自动,无需更改代码 + +### 示例 + +```typescript +// 定义带查找的 Schema +await db.registerSchema({ + objects: { + order: { + fields: { + product: { + type: 'lookup', + reference_to: 'product' + }, + customer: { + type: 'lookup', + reference_to: 'customer' + }, + amount: { type: 'currency' } + } + } + } +}) + +// 使用虚拟列索引查询 +const orders = await db.query('order', { + filters: [ + ['product.name', 'contains', 'laptop'], // 使用虚拟列 + ['customer.email', 'endswith', '@acme.com'] // 使用虚拟列 + ] +}) + +// 底层实现: +// SELECT * FROM orders +// WHERE product_name_virtual LIKE '%laptop%' +// AND customer_email_virtual LIKE '%@acme.com' +// -- 快速! 使用索引而不是 JOIN +``` + +### 性能对比 + +``` +传统 JOIN 方法: +├── Orders: 100,000 条记录 +├── Products: 10,000 条记录 +└── 查询时间: ~2.5 秒 + +虚拟列索引: +├── Orders: 100,000 条记录(带虚拟列) +├── 索引虚拟列 +└── 查询时间: ~0.05 秒 (快 50 倍!) +``` + +### 配置 + +虚拟列索引对 SQLite **默认启用**。可按字段配置: + +```typescript +{ + fields: { + owner: { + type: 'lookup', + reference_to: 'user', + virtual_index: true, // 启用虚拟索引(默认) + virtual_fields: ['name', 'email'] // 要索引的字段 + } + } +} +``` + +### 限制 + +- **仅 SQLite** - MySQL/PostgreSQL 不需要(它们能高效处理 JOIN) +- **写入开销** - 更新时有轻微性能损耗(触发器执行) +- **存储空间** - 虚拟列需要额外磁盘空间 +- **非实时** - 触发器执行期间有短暂延迟 + +### 最佳实践 + +1. **为频繁查询的字段启用** +2. **限制 virtual_fields** - 仅索引搜索/过滤的字段 +3. **监控存储** - 虚拟列占用磁盘空间 +4. **用于大数据集** - 对 1 万条以上记录最有益 + +## 跨数据库驱动适配 + +ObjectQL 通过智能驱动适配提供跨不同数据库系统的**统一 API**。 + +### 支持的数据库 + +```typescript +// SQLite (嵌入式) +const db = new ObjectQL({ + driver: 'sqlite', + url: './data/myapp.db' +}) + +// MySQL +const db = new ObjectQL({ + driver: 'mysql', + url: 'mysql://user:pass@localhost:3306/mydb' +}) + +// PostgreSQL +const db = new ObjectQL({ + driver: 'postgresql', + url: 'postgresql://user:pass@localhost:5432/mydb' +}) + +// SQL Server +const db = new ObjectQL({ + driver: 'mssql', + url: 'mssql://user:pass@localhost:1433/mydb' +}) + +// Oracle +const db = new ObjectQL({ + driver: 'oracle', + url: 'oracle://user:pass@localhost:1521/mydb' +}) +``` + +### 驱动架构 + +``` +┌─────────────────────────────────────────┐ +│ ObjectQL 统一 API │ +└────────────────┬────────────────────────┘ + │ + │ +┌────────────────▼────────────────────────┐ +│ 驱动抽象层 │ +│ │ +│ ┌──────────────────────────────────┐ │ +│ │ SQL 方言转换器 │ │ +│ │ - 类型映射 │ │ +│ │ - 语法转换 │ │ +│ │ - 功能适配 │ │ +│ └──────────────────────────────────┘ │ +└────────────────┬────────────────────────┘ + │ + ┌─────────┼─────────┐ + │ │ │ +┌──────▼───┐ ┌──▼──────┐ ┌▼────────┐ +│ SQLite │ │ MySQL │ │Postgres │ +│ 驱动 │ │ 驱动 │ │ 驱动 │ +└──────────┘ └─────────┘ └─────────┘ +``` + +### 类型映射 + +ObjectQL 自动将字段类型映射到数据库特定类型: + +```typescript +// ObjectQL Schema +{ + created_at: { type: 'datetime' }, + metadata: { type: 'json' }, + is_active: { type: 'boolean' } +} + +// SQLite +CREATE TABLE ( + created_at TEXT, -- ISO8601 字符串 + metadata TEXT, -- JSON 字符串 + is_active INTEGER -- 0 或 1 +) + +// MySQL +CREATE TABLE ( + created_at DATETIME, + metadata JSON, + is_active BOOLEAN +) + +// PostgreSQL +CREATE TABLE ( + created_at TIMESTAMP, + metadata JSONB, -- 二进制 JSON + is_active BOOLEAN +) +``` + +### 方言转换 + +ObjectQL 将查询转换为数据库特定的 SQL: + +```typescript +// ObjectQL 查询 +await db.query('user', { + filters: [['name', 'contains', 'john']], + sort: 'created desc', + limit: 10 +}) + +// SQLite SQL +SELECT * FROM user +WHERE name LIKE '%john%' COLLATE NOCASE +ORDER BY created DESC +LIMIT 10 + +// MySQL SQL +SELECT * FROM user +WHERE LOWER(name) LIKE LOWER('%john%') +ORDER BY created DESC +LIMIT 10 + +// PostgreSQL SQL +SELECT * FROM user +WHERE name ILIKE '%john%' +ORDER BY created DESC +LIMIT 10 +``` + +### 功能适配 + +ObjectQL 根据数据库能力适配功能: + +| 功能 | SQLite | MySQL | PostgreSQL | +|------|--------|-------|------------| +| 自动递增 | AUTOINCREMENT | AUTO_INCREMENT | SERIAL | +| JSON 字段 | TEXT + 解析 | JSON | JSONB | +| 全文搜索 | FTS5 扩展 | FULLTEXT 索引 | tsvector | +| UUID | TEXT | CHAR(36) | UUID 类型 | +| 数组 | JSON 数组 | JSON | ARRAY 类型 | +| 虚拟列 | 是(优化) | 计算列 | 生成列 | + +## MySQL/PostgreSQL 驱动配置 + +### MySQL 驱动 + +#### 连接选项 + +```typescript +const db = new ObjectQL({ + driver: 'mysql', + url: 'mysql://user:password@localhost:3306/database', + + // 或详细配置 + host: 'localhost', + port: 3306, + user: 'myapp_user', + password: 'secure_password', + database: 'myapp_db', + + // MySQL 特定选项 + mysql: { + charset: 'utf8mb4', + timezone: '+00:00', + connectTimeout: 10000, + acquireTimeout: 10000, + waitForConnections: true, + connectionLimit: 10, + queueLimit: 0, + enableKeepAlive: true, + keepAliveInitialDelay: 0, + + // SSL 配置 + ssl: { + ca: fs.readFileSync('/path/to/ca.pem'), + key: fs.readFileSync('/path/to/client-key.pem'), + cert: fs.readFileSync('/path/to/client-cert.pem') + } + } +}) +``` + +#### 性能调优 + +```typescript +// 连接池 +{ + mysql: { + connectionLimit: 20, // 最大连接数 + queueLimit: 50, // 最大排队请求数 + waitForConnections: true, // 达到限制时排队 + + // 超时设置 + connectTimeout: 10000, // 10 秒 + acquireTimeout: 10000, + timeout: 60000, // 查询超时 + + // 保持连接 + enableKeepAlive: true, + keepAliveInitialDelay: 10000 + } +} +``` + +#### MySQL 特定功能 + +```typescript +// 全文搜索 +await db.registerSchema({ + objects: { + article: { + fields: { + title: { type: 'text' }, + content: { type: 'textarea' } + }, + indexes: [ + { + name: 'fulltext_search', + type: 'FULLTEXT', + fields: ['title', 'content'] + } + ] + } + } +}) + +// 使用全文搜索查询 +const results = await db.query('article', { + filters: [ + ['MATCH(title, content) AGAINST(?)', '搜索词'] + ] +}) +``` + +### PostgreSQL 驱动 + +#### 连接选项 + +```typescript +const db = new ObjectQL({ + driver: 'postgresql', + url: 'postgresql://user:password@localhost:5432/database', + + // 或详细配置 + host: 'localhost', + port: 5432, + user: 'myapp_user', + password: 'secure_password', + database: 'myapp_db', + + // PostgreSQL 特定选项 + postgres: { + max: 20, // 最大池大小 + min: 2, // 最小池大小 + idle: 10000, // 空闲超时 + connectionTimeoutMillis: 10000, + idleTimeoutMillis: 30000, + + // SSL 配置 + ssl: { + rejectUnauthorized: false, + ca: fs.readFileSync('/path/to/ca.pem'), + key: fs.readFileSync('/path/to/client-key.pem'), + cert: fs.readFileSync('/path/to/client-cert.pem') + }, + + // 应用名称 + application_name: 'MyApp', + + // 语句超时 + statement_timeout: 60000 // 60 秒 + } +}) +``` + +#### 性能调优 + +```typescript +{ + postgres: { + // 连接池 + max: 25, // 最大连接数 + min: 5, // 最小空闲连接数 + + // 超时设置 + connectionTimeoutMillis: 10000, + idleTimeoutMillis: 30000, + statement_timeout: 60000, + + // 保持连接 + keepAlive: true, + keepAliveInitialDelayMillis: 10000 + } +} +``` + +#### PostgreSQL 特定功能 + +```typescript +// JSONB 查询 +await db.query('user', { + filters: [ + ["metadata->>'city'", '=', 'New York'], + ["metadata->'age'", '>', '18'] + ] +}) + +// 数组字段 +await db.registerSchema({ + objects: { + post: { + fields: { + tags: { + type: 'array', + items: { type: 'text' } + } + } + } + } +}) + +// 数组查询 +await db.query('post', { + filters: [ + ['tags', '@>', ['javascript', 'nodejs']] // 包含 + ] +}) + +// 全文搜索 +await db.query('article', { + filters: [ + ["to_tsvector('english', content) @@ to_tsquery('search & terms')"] + ] +}) +``` + +### 连接池监控 + +```typescript +// 获取池统计 +const stats = await db.getPoolStats() +console.log({ + total: stats.total, // 总连接数 + idle: stats.idle, // 空闲连接数 + waiting: stats.waiting // 等待请求数 +}) + +// 优雅关闭 +await db.close() +``` + +## 性能最佳实践 + +### 索引策略 + +```typescript +{ + objects: { + order: { + fields: { + customer_id: { type: 'lookup', reference_to: 'customer' }, + status: { type: 'select', options: [...] }, + created: { type: 'datetime' } + }, + indexes: [ + // 常用查询的复合索引 + { + name: 'idx_customer_status', + fields: ['customer_id', 'status'] + }, + // 日期范围查询 + { + name: 'idx_created', + fields: ['created'] + }, + // Virtual City + 过滤器 + { + name: 'idx_space_status', + fields: ['space', 'status', 'created'] + } + ] + } + } +} +``` + +### 查询优化 + +```typescript +// ✅ 好: 使用字段选择 +await db.query('customer', { + fields: ['_id', 'name', 'email'], // 只要需要的字段 + limit: 100 +}) + +// ❌ 差: 选择所有字段 +await db.query('customer', { + fields: ['*'], // 检索所有字段包括大字段 + limit: 100 +}) + +// ✅ 好: 使用索引过滤器 +await db.query('order', { + filters: [ + ['status', '=', 'pending'], // 已索引 + ['created', '>', '2024-01-01'] // 已索引 + ] +}) + +// ❌ 差: 过滤器中使用函数 +await db.query('order', { + filters: [ + ['LOWER(status)', '=', 'pending'] // 无法使用索引 + ] +}) +``` + +### 批量操作 + +```typescript +// ✅ 好: 批量插入 +await db.mutation('product', { + action: 'insert', + data: [ + { name: '产品 1' }, + { name: '产品 2' }, + // ... 100 条记录 + ] +}) +// 单个事务,一次往返 + +// ❌ 差: 单独插入 +for (const product of products) { + await db.mutation('product', { + action: 'insert', + data: product + }) +} +// 100 个事务,100 次往返 +``` + +## 下一步 + +- 掌握[服务端 SDK](./server-sdk) API +- 查看[协议规范](./protocol-spec)了解查询语法 +- 查看[核心概念](./core-concepts)了解基础知识 diff --git a/content/docs/02-objectql/index.en.mdx b/content/docs/02-objectql/index.en.mdx new file mode 100644 index 0000000..0451577 --- /dev/null +++ b/content/docs/02-objectql/index.en.mdx @@ -0,0 +1,191 @@ +--- +title: ObjectQL - The Data Engine +description: Powerful data engine with schema-driven architecture and cross-database support +--- + +# ObjectQL - The Data Engine + +ObjectQL is the core data engine of ObjectStack, providing a unified data access layer with schema-driven architecture, powerful query capabilities, and cross-database support. + +## What is ObjectQL? + +ObjectQL is a **declarative data engine** that allows you to define your data structure once and access it consistently across different database systems. It abstracts database complexity while preserving SQL power and performance. + +### Key Capabilities + +- 🗂️ **Schema-Driven Architecture** - Define data structures declaratively +- 🔍 **Unified Query DSL** - Query any database with consistent syntax +- ⚡ **Performance Optimized** - Virtual Column Index for SQLite, optimized drivers +- 🔄 **Cross-Database Support** - SQLite, MySQL, PostgreSQL, Oracle, SQL Server +- 🏗️ **Virtual City Mechanism** - Multi-tenant data isolation +- 🛡️ **Type Safety** - Strong typing for queries and results + +## How It Works + +ObjectQL operates on three core principles: + +### 1. Schema Definition + +Define your data structure using simple JavaScript objects: + +```typescript +const schema = { + objects: { + todos: { + fields: { + title: { type: 'text', required: true }, + completed: { type: 'boolean', defaultValue: false }, + due_date: { type: 'datetime' } + } + } + } +} +``` + +### 2. Query Execution + +Query your data using an intuitive DSL: + +```typescript +const activeTodos = await db.query('todos', { + filters: [['completed', '=', false]], + sort: 'due_date asc' +}) +``` + +### 3. Database Abstraction + +ObjectQL translates your queries to optimized SQL for the target database, handling dialect differences automatically. + +## Architecture Overview + +``` +┌─────────────────────────────────────────┐ +│ Application Layer │ +│ (Your Business Logic) │ +└────────────────┬────────────────────────┘ + │ + │ ObjectQL API + │ +┌────────────────▼────────────────────────┐ +│ ObjectQL Engine │ +│ ┌────────────┬──────────────────────┐ │ +│ │ Schema │ Query Processor │ │ +│ │ Registry │ & Optimizer │ │ +│ └────────────┴──────────────────────┘ │ +│ ┌──────────────────────────────────┐ │ +│ │ Virtual City Manager │ │ +│ │ (Multi-tenant Isolation) │ │ +│ └──────────────────────────────────┘ │ +└────────────────┬────────────────────────┘ + │ + │ Driver Abstraction + │ +┌────────────────▼────────────────────────┐ +│ Database Drivers │ +│ ┌──────┬──────┬──────┬──────┬──────┐ │ +│ │SQLite│MySQL │Postgres│Oracle│SQL │ │ +│ │ │ │ │ │Server│ │ +│ └──────┴──────┴──────┴──────┴──────┘ │ +└─────────────────────────────────────────┘ +``` + +## Use Cases + +### Local-First Applications + +Use SQLite for personal applications: + +```typescript +const db = new ObjectQL({ + driver: 'sqlite', + url: './data/myapp.db' +}) +``` + +### Enterprise Systems + +Connect to existing databases: + +```typescript +const db = new ObjectQL({ + driver: 'mysql', + url: 'mysql://user:pass@localhost:3306/mydb' +}) +``` + +### Multi-Tenant SaaS + +Leverage Virtual City for tenant isolation: + +```typescript +const tenantDb = db.virtualCity('tenant_123') +const data = await tenantDb.query('customers', {}) +``` + +## What You'll Learn + +In this section, you will discover: + +- ✅ **Core Concepts** - Understanding Schema and Virtual City mechanism +- ✅ **Protocol Specification** - Schema definition, Query DSL, and Aggregations +- ✅ **Core Features** - Virtual Column Index, driver adaptation, and optimizations +- ✅ **Server SDK** - Complete API reference with practical examples + +## Getting Started + +Ready to dive in? Start with: + +1. **[Core Concepts](./core-concepts)** - Understand the fundamental concepts +2. **[Protocol Spec](./protocol-spec)** - Learn the schema and query language +3. **[Core Features](./core-features)** - Explore advanced features +4. **[Server SDK](./server-sdk)** - Master the API + +## Quick Example + +Here's a complete example to get you started: + +```typescript +import { ObjectQL } from '@objectql/core' + +// 1. Initialize ObjectQL +const db = new ObjectQL({ + driver: 'sqlite', + url: './myapp.db' +}) + +// 2. Define Schema +await db.registerSchema({ + objects: { + tasks: { + fields: { + name: { type: 'text', required: true }, + status: { type: 'select', options: ['todo', 'done'] }, + priority: { type: 'number' } + } + } + } +}) + +// 3. Insert Data +await db.mutation('tasks', { + action: 'insert', + data: { name: 'Learn ObjectQL', status: 'todo', priority: 1 } +}) + +// 4. Query Data +const tasks = await db.query('tasks', { + filters: [['status', '=', 'todo']], + sort: 'priority desc' +}) + +console.log(tasks) // [{ name: 'Learn ObjectQL', status: 'todo', priority: 1 }] +``` + +## Next Steps + +Begin your ObjectQL journey: + +- **New to ObjectQL?** Start with [Core Concepts](./core-concepts) +- **Want to see the full API?** Jump to [Server SDK](./server-sdk) +- **Need specific features?** Check [Core Features](./core-features) diff --git a/content/docs/02-objectql/index.zh-CN.mdx b/content/docs/02-objectql/index.zh-CN.mdx new file mode 100644 index 0000000..c32189b --- /dev/null +++ b/content/docs/02-objectql/index.zh-CN.mdx @@ -0,0 +1,191 @@ +--- +title: ObjectQL - 数据引擎 +description: 强大的数据引擎,提供 Schema 驱动架构和跨数据库支持 +--- + +# ObjectQL - 数据引擎 + +ObjectQL 是 ObjectStack 的核心数据引擎,提供统一的数据访问层,具有 Schema 驱动架构、强大的查询能力和跨数据库支持。 + +## 什么是 ObjectQL? + +ObjectQL 是一个**声明式数据引擎**,允许您只定义一次数据结构,就能在不同数据库系统中一致地访问数据。它抽象了数据库的复杂性,同时保留了 SQL 的强大功能和性能。 + +### 核心能力 + +- 🗂️ **Schema 驱动架构** - 声明式定义数据结构 +- 🔍 **统一查询 DSL** - 使用一致的语法查询任何数据库 +- ⚡ **性能优化** - SQLite 虚拟列索引,优化的数据库驱动 +- 🔄 **跨数据库支持** - SQLite、MySQL、PostgreSQL、Oracle、SQL Server +- 🏗️ **Virtual City 机制** - 多租户数据隔离 +- 🛡️ **类型安全** - 查询和结果的强类型支持 + +## 工作原理 + +ObjectQL 基于三个核心原则运作: + +### 1. Schema 定义 + +使用简单的 JavaScript 对象定义数据结构: + +```typescript +const schema = { + objects: { + todos: { + fields: { + title: { type: 'text', required: true }, + completed: { type: 'boolean', defaultValue: false }, + due_date: { type: 'datetime' } + } + } + } +} +``` + +### 2. 查询执行 + +使用直观的 DSL 查询数据: + +```typescript +const activeTodos = await db.query('todos', { + filters: [['completed', '=', false]], + sort: 'due_date asc' +}) +``` + +### 3. 数据库抽象 + +ObjectQL 自动将查询转换为目标数据库的优化 SQL,自动处理方言差异。 + +## 架构概览 + +``` +┌─────────────────────────────────────────┐ +│ 应用层 │ +│ (您的业务逻辑) │ +└────────────────┬────────────────────────┘ + │ + │ ObjectQL API + │ +┌────────────────▼────────────────────────┐ +│ ObjectQL 引擎 │ +│ ┌────────────┬──────────────────────┐ │ +│ │ Schema │ 查询处理器 │ │ +│ │ 注册表 │ 和优化器 │ │ +│ └────────────┴──────────────────────┘ │ +│ ┌──────────────────────────────────┐ │ +│ │ Virtual City 管理器 │ │ +│ │ (多租户隔离) │ │ +│ └──────────────────────────────────┘ │ +└────────────────┬────────────────────────┘ + │ + │ 驱动抽象层 + │ +┌────────────────▼────────────────────────┐ +│ 数据库驱动 │ +│ ┌──────┬──────┬──────┬──────┬──────┐ │ +│ │SQLite│MySQL │Postgres│Oracle│SQL │ │ +│ │ │ │ │ │Server│ │ +│ └──────┴──────┴──────┴──────┴──────┘ │ +└─────────────────────────────────────────┘ +``` + +## 使用场景 + +### 本地优先应用 + +使用 SQLite 构建个人应用: + +```typescript +const db = new ObjectQL({ + driver: 'sqlite', + url: './data/myapp.db' +}) +``` + +### 企业系统 + +连接到现有数据库: + +```typescript +const db = new ObjectQL({ + driver: 'mysql', + url: 'mysql://user:pass@localhost:3306/mydb' +}) +``` + +### 多租户 SaaS + +利用 Virtual City 实现租户隔离: + +```typescript +const tenantDb = db.virtualCity('tenant_123') +const data = await tenantDb.query('customers', {}) +``` + +## 您将学到什么 + +在本章节中,您将了解: + +- ✅ **核心概念** - 理解 Schema 和 Virtual City 机制 +- ✅ **协议规范** - Schema 定义、查询 DSL 和聚合操作 +- ✅ **核心功能** - 虚拟列索引、驱动适配和性能优化 +- ✅ **服务端 SDK** - 完整的 API 参考和实践示例 + +## 开始学习 + +准备好深入了解了吗?从这里开始: + +1. **[核心概念](./core-concepts)** - 理解基础概念 +2. **[协议规范](./protocol-spec)** - 学习 Schema 和查询语言 +3. **[核心功能](./core-features)** - 探索高级功能 +4. **[服务端 SDK](./server-sdk)** - 掌握 API + +## 快速示例 + +这里有一个完整的示例帮您入门: + +```typescript +import { ObjectQL } from '@objectql/core' + +// 1. 初始化 ObjectQL +const db = new ObjectQL({ + driver: 'sqlite', + url: './myapp.db' +}) + +// 2. 定义 Schema +await db.registerSchema({ + objects: { + tasks: { + fields: { + name: { type: 'text', required: true }, + status: { type: 'select', options: ['todo', 'done'] }, + priority: { type: 'number' } + } + } + } +}) + +// 3. 插入数据 +await db.mutation('tasks', { + action: 'insert', + data: { name: '学习 ObjectQL', status: 'todo', priority: 1 } +}) + +// 4. 查询数据 +const tasks = await db.query('tasks', { + filters: [['status', '=', 'todo']], + sort: 'priority desc' +}) + +console.log(tasks) // [{ name: '学习 ObjectQL', status: 'todo', priority: 1 }] +``` + +## 下一步 + +开始您的 ObjectQL 之旅: + +- **初次接触 ObjectQL?** 从[核心概念](./core-concepts)开始 +- **想查看完整 API?** 跳转到[服务端 SDK](./server-sdk) +- **需要特定功能?** 查看[核心功能](./core-features) diff --git a/content/docs/02-objectql/meta.en.json b/content/docs/02-objectql/meta.en.json new file mode 100644 index 0000000..a4a2ceb --- /dev/null +++ b/content/docs/02-objectql/meta.en.json @@ -0,0 +1,10 @@ +{ + "title": "ObjectQL", + "pages": [ + "index", + "core-concepts", + "protocol-spec", + "core-features", + "server-sdk" + ] +} diff --git a/content/docs/02-objectql/meta.zh-CN.json b/content/docs/02-objectql/meta.zh-CN.json new file mode 100644 index 0000000..a4a2ceb --- /dev/null +++ b/content/docs/02-objectql/meta.zh-CN.json @@ -0,0 +1,10 @@ +{ + "title": "ObjectQL", + "pages": [ + "index", + "core-concepts", + "protocol-spec", + "core-features", + "server-sdk" + ] +} diff --git a/content/docs/02-objectql/protocol-spec.en.mdx b/content/docs/02-objectql/protocol-spec.en.mdx new file mode 100644 index 0000000..ad78420 --- /dev/null +++ b/content/docs/02-objectql/protocol-spec.en.mdx @@ -0,0 +1,682 @@ +--- +title: Protocol Specification +description: Complete reference for Schema definition, Query DSL, and Aggregation operations +--- + +# Protocol Specification + +This document provides the complete specification for ObjectQL's Schema definition language, Query DSL, and Aggregation operations. + +## Schema Definition + +### Object Definition + +An object represents a database table: + +```typescript +{ + objects: { + [objectName: string]: { + label?: string // Display name + icon?: string // UI icon + enable_files?: boolean // Enable file attachments + enable_search?: boolean // Enable full-text search + enable_tasks?: boolean // Enable task tracking + enable_notes?: boolean // Enable notes + enable_api?: boolean // Expose via API (default: true) + enable_share?: boolean // Enable record sharing + fields: { /* field definitions */ } + list_views?: { /* view definitions */ } + triggers?: { /* trigger definitions */ } + actions?: { /* action definitions */ } + permissions?: { /* permission definitions */ } + } + } +} +``` + +### Field Types Reference + +#### Text Fields + +```typescript +{ + // Short text (VARCHAR) + field_name: { + type: 'text', + label?: string, + defaultValue?: string, + required?: boolean, + unique?: boolean, + index?: boolean, + minLength?: number, + maxLength?: number, + pattern?: string // Regex pattern + }, + + // Long text (TEXT) + description: { + type: 'textarea', + rows?: number // UI hint for rows + }, + + // Rich HTML content + content: { + type: 'html' + }, + + // Email with validation + email: { + type: 'email', + unique?: boolean + }, + + // URL with validation + website: { + type: 'url' + } +} +``` + +#### Numeric Fields + +```typescript +{ + // Integer + quantity: { + type: 'number', + scale: 0, // No decimals + min?: number, + max?: number, + defaultValue?: number + }, + + // Decimal + price: { + type: 'currency', + scale: 2, // 2 decimal places + precision?: number // Total digits (default: 15) + }, + + // Percentage + discount: { + type: 'percent', + scale: 2 + }, + + // Auto-number (sequence) + invoice_number: { + type: 'autonumber', + formula: 'INV-{0000}' // Format pattern + } +} +``` + +#### Date and Time Fields + +```typescript +{ + // Date only (YYYY-MM-DD) + birth_date: { + type: 'date', + min?: string, // '1900-01-01' + max?: string // '2100-12-31' + }, + + // Date and time + created_at: { + type: 'datetime', + defaultValue?: string | (() => Date) + }, + + // Time only + start_time: { + type: 'time' + } +} +``` + +#### Boolean and Select Fields + +```typescript +{ + // Boolean checkbox + is_active: { + type: 'boolean', + defaultValue?: boolean + }, + + // Single select (dropdown) + status: { + type: 'select', + options: [ + { label: 'Draft', value: 'draft' }, + { label: 'Published', value: 'published' } + ], + // Or simple array + // options: ['draft', 'published', 'archived'] + defaultValue?: string + }, + + // Multi-select + tags: { + type: 'multiSelect', + options: ['urgent', 'important', 'low-priority'] + } +} +``` + +#### Relationship Fields + +```typescript +{ + // Master-Detail (required foreign key) + // Child record deleted when parent deleted + account: { + type: 'master_detail', + reference_to: 'account', + required: true, + on_delete?: 'cascade' | 'restrict' // Default: cascade + }, + + // Lookup (optional foreign key) + // Relationship preserved when parent deleted + manager: { + type: 'lookup', + reference_to: 'user', + reference_to_field?: '_id' // Default: _id + }, + + // Formula field (computed) + full_name: { + type: 'formula', + formula: '{first_name} {last_name}', + data_type: 'text' + }, + + // Summary (rollup from related records) + total_opportunities: { + type: 'summary', + summary_object: 'opportunity', + summary_type: 'count', + summary_field: '_id', + summary_filters: [['stage', '=', 'closed_won']] + } +} +``` + +#### Special Fields + +```typescript +{ + // File attachment + avatar: { + type: 'image', + accept?: string // 'image/*' + }, + + // Multiple files + attachments: { + type: 'filesize', + multiple?: boolean // Allow multiple files + }, + + // JSON data + metadata: { + type: 'json', + schema?: object // JSON Schema for validation + }, + + // Geolocation + location: { + type: 'location' // Stores { lat, lng, address } + }, + + // Markdown content + notes: { + type: 'markdown' + } +} +``` + +### Field Constraints + +```typescript +{ + field_name: { + type: 'text', + + // Validation + required: boolean, // NOT NULL + unique: boolean, // UNIQUE constraint + index: boolean, // Create index + + // Text constraints + minLength: number, + maxLength: number, + pattern: string, // Regex + + // Numeric constraints + min: number, + max: number, + scale: number, // Decimal places + precision: number, // Total digits + + // Default value + defaultValue: any | (() => any), + + // Visibility + hidden: boolean, // Hide from UI + readonly: boolean, // Read-only + + // Dependencies + depends_on: string[], // Field dependencies + visible_on: string // Conditional visibility + } +} +``` + +## Query DSL + +### Basic Query + +```typescript +db.query(objectName: string, options: QueryOptions): Promise + +interface QueryOptions { + fields?: string[] // Fields to return + filters?: Filter[] // WHERE conditions + sort?: string | Sort[] // ORDER BY + limit?: number // LIMIT + skip?: number // OFFSET + top?: number // Alternative to limit +} +``` + +### Filters + +Filter syntax: `[field, operator, value]` + +#### Comparison Operators + +```typescript +// Equal +['status', '=', 'active'] +['status', 'eq', 'active'] + +// Not equal +['status', '!=', 'archived'] +['status', 'ne', 'archived'] + +// Greater than +['age', '>', 18] +['age', 'gt', 18] + +// Greater than or equal +['age', '>=', 18] +['age', 'gte', 18] + +// Less than +['price', '<', 100] +['price', 'lt', 100] + +// Less than or equal +['price', '<=', 100] +['price', 'lte', 100] +``` + +#### String Operators + +```typescript +// Contains (case-insensitive) +['name', 'contains', 'john'] +['name', 'like', '%john%'] + +// Starts with +['email', 'startswith', 'admin'] +['email', 'like', 'admin%'] + +// Ends with +['domain', 'endswith', '.com'] +['domain', 'like', '%.com'] + +// Not contains +['name', 'notcontains', 'test'] +['name', 'not like', '%test%'] +``` + +#### List Operators + +```typescript +// In list +['status', 'in', ['draft', 'published']] + +// Not in list +['status', 'notin', ['archived', 'deleted']] + +// Between +['age', 'between', [18, 65]] +``` + +#### Null Operators + +```typescript +// Is null +['description', '=', null] +['description', 'is null'] + +// Is not null +['description', '!=', null] +['description', 'is not null'] +``` + +#### Compound Filters + +```typescript +// AND (default) +filters: [ + ['status', '=', 'active'], + ['age', '>', 18] +] +// WHERE status = 'active' AND age > 18 + +// OR +filters: [ + 'or', + ['status', '=', 'active'], + ['status', '=', 'pending'] +] +// WHERE status = 'active' OR status = 'pending' + +// Complex combinations +filters: [ + 'and', + ['category', '=', 'electronics'], + [ + 'or', + ['price', '<', 100], + ['on_sale', '=', true] + ] +] +// WHERE category = 'electronics' AND (price < 100 OR on_sale = true) +``` + +### Sorting + +```typescript +// Simple string +sort: 'name' // ASC by default +sort: 'name asc' +sort: 'price desc' + +// Multiple fields +sort: 'category asc, price desc' + +// Array syntax +sort: [ + { field: 'category', order: 'asc' }, + { field: 'price', order: 'desc' } +] +``` + +### Pagination + +```typescript +// Limit and skip +{ + limit: 10, + skip: 20 // Skip first 20 records +} + +// Page-based +{ + top: 10, // Records per page + skip: pageNumber * 10 +} + +// Example: Get page 3 with 25 records per page +{ + limit: 25, + skip: 2 * 25 // Skip first 50 (pages 1 & 2) +} +``` + +### Field Selection + +```typescript +// Select specific fields +{ + fields: ['name', 'email', 'created'] +} + +// Select related fields +{ + fields: ['name', 'owner.name', 'owner.email'] +} + +// All fields (default) +{ + fields: ['*'] +} +``` + +### Complete Query Example + +```typescript +const results = await db.query('opportunity', { + fields: ['name', 'amount', 'stage', 'account.name'], + filters: [ + 'and', + ['stage', 'in', ['prospecting', 'qualification']], + ['amount', '>', 10000], + [ + 'or', + ['owner', '=', currentUserId], + ['team', 'contains', currentUserId] + ] + ], + sort: [ + { field: 'amount', order: 'desc' }, + { field: 'created', order: 'asc' } + ], + limit: 50, + skip: 0 +}) +``` + +## Aggregation Operations + +### GroupBy + +```typescript +db.aggregate(objectName: string, options: AggregateOptions) + +interface AggregateOptions { + group_by: string[] + having?: Filter[] + fields?: AggregateField[] + sort?: string | Sort[] + limit?: number +} +``` + +#### Basic GroupBy + +```typescript +// Count by category +await db.aggregate('product', { + group_by: ['category'], + fields: [ + { field: '_id', function: 'count', alias: 'total' } + ] +}) + +// Result: [ +// { category: 'Electronics', total: 45 }, +// { category: 'Books', total: 123 } +// ] +``` + +#### Multiple Groupings + +```typescript +// Sales by year and quarter +await db.aggregate('order', { + group_by: ['YEAR(created)', 'QUARTER(created)'], + fields: [ + { field: 'amount', function: 'sum', alias: 'total_sales' }, + { field: '_id', function: 'count', alias: 'order_count' } + ], + sort: 'YEAR(created) desc, QUARTER(created) desc' +}) +``` + +### Aggregate Functions + +```typescript +// Count +{ field: '_id', function: 'count' } + +// Sum +{ field: 'amount', function: 'sum' } + +// Average +{ field: 'price', function: 'avg' } + +// Min +{ field: 'price', function: 'min' } + +// Max +{ field: 'price', function: 'max' } + +// Distinct count +{ field: 'customer_id', function: 'count_distinct' } +``` + +### Having Clause + +Filter aggregated results: + +```typescript +await db.aggregate('order', { + group_by: ['customer_id'], + fields: [ + { field: 'amount', function: 'sum', alias: 'total_spent' }, + { field: '_id', function: 'count', alias: 'order_count' } + ], + having: [ + ['total_spent', '>', 1000] // Only customers who spent > 1000 + ] +}) +``` + +### Complete Aggregation Example + +```typescript +// Monthly sales report with filters +const monthlySales = await db.aggregate('invoice', { + // Pre-aggregation filters (WHERE clause) + filters: [ + ['status', '=', 'paid'], + ['created', '>=', '2024-01-01'] + ], + + // Group by + group_by: ['YEAR(created)', 'MONTH(created)'], + + // Aggregate calculations + fields: [ + { field: 'amount', function: 'sum', alias: 'revenue' }, + { field: '_id', function: 'count', alias: 'invoice_count' }, + { field: 'amount', function: 'avg', alias: 'avg_invoice' }, + { field: 'customer_id', function: 'count_distinct', alias: 'unique_customers' } + ], + + // Post-aggregation filters (HAVING clause) + having: [ + ['revenue', '>', 50000] // Only months with revenue > 50k + ], + + // Sort results + sort: 'YEAR(created) desc, MONTH(created) desc', + + // Limit results + limit: 12 // Last 12 months +}) + +// Result: [ +// { +// year: 2024, +// month: 3, +// revenue: 125000, +// invoice_count: 45, +// avg_invoice: 2777.78, +// unique_customers: 32 +// }, +// ... +// ] +``` + +## Mutation Operations + +### Insert + +```typescript +await db.mutation('customer', { + action: 'insert', + data: { + name: 'Acme Corp', + email: 'contact@acme.com', + status: 'active' + } +}) +``` + +### Update + +```typescript +await db.mutation('customer', { + action: 'update', + filters: [['_id', '=', customerId]], + data: { + status: 'inactive', + modified: new Date() + } +}) +``` + +### Delete + +```typescript +await db.mutation('customer', { + action: 'delete', + filters: [['status', '=', 'archived']] +}) +``` + +### Batch Operations + +```typescript +// Batch insert +await db.mutation('product', { + action: 'insert', + data: [ + { name: 'Product 1', price: 10 }, + { name: 'Product 2', price: 20 }, + { name: 'Product 3', price: 30 } + ] +}) + +// Batch update +await db.mutation('task', { + action: 'update', + filters: [['project_id', '=', projectId]], + data: { status: 'cancelled' } +}) +``` + +## Next Steps + +- Explore [Core Features](./core-features) for performance optimizations +- Master the [Server SDK](./server-sdk) API reference +- Check [Core Concepts](./core-concepts) for foundational knowledge diff --git a/content/docs/02-objectql/protocol-spec.zh-CN.mdx b/content/docs/02-objectql/protocol-spec.zh-CN.mdx new file mode 100644 index 0000000..4758de1 --- /dev/null +++ b/content/docs/02-objectql/protocol-spec.zh-CN.mdx @@ -0,0 +1,682 @@ +--- +title: 协议规范 +description: Schema 定义、查询 DSL 和聚合操作的完整参考 +--- + +# 协议规范 + +本文档提供 ObjectQL 的 Schema 定义语言、查询 DSL 和聚合操作的完整规范。 + +## Schema 定义 + +### 对象定义 + +对象表示数据库表: + +```typescript +{ + objects: { + [objectName: string]: { + label?: string // 显示名称 + icon?: string // UI 图标 + enable_files?: boolean // 启用文件附件 + enable_search?: boolean // 启用全文搜索 + enable_tasks?: boolean // 启用任务跟踪 + enable_notes?: boolean // 启用笔记 + enable_api?: boolean // 通过 API 公开(默认: true) + enable_share?: boolean // 启用记录共享 + fields: { /* 字段定义 */ } + list_views?: { /* 视图定义 */ } + triggers?: { /* 触发器定义 */ } + actions?: { /* 操作定义 */ } + permissions?: { /* 权限定义 */ } + } + } +} +``` + +### 字段类型参考 + +#### 文本字段 + +```typescript +{ + // 短文本 (VARCHAR) + field_name: { + type: 'text', + label?: string, + defaultValue?: string, + required?: boolean, + unique?: boolean, + index?: boolean, + minLength?: number, + maxLength?: number, + pattern?: string // 正则表达式 + }, + + // 长文本 (TEXT) + description: { + type: 'textarea', + rows?: number // UI 行数提示 + }, + + // 富文本 HTML + content: { + type: 'html' + }, + + // 带验证的邮箱 + email: { + type: 'email', + unique?: boolean + }, + + // 带验证的 URL + website: { + type: 'url' + } +} +``` + +#### 数值字段 + +```typescript +{ + // 整数 + quantity: { + type: 'number', + scale: 0, // 无小数位 + min?: number, + max?: number, + defaultValue?: number + }, + + // 小数 + price: { + type: 'currency', + scale: 2, // 2 位小数 + precision?: number // 总位数(默认: 15) + }, + + // 百分比 + discount: { + type: 'percent', + scale: 2 + }, + + // 自动编号(序列) + invoice_number: { + type: 'autonumber', + formula: 'INV-{0000}' // 格式模式 + } +} +``` + +#### 日期和时间字段 + +```typescript +{ + // 仅日期 (YYYY-MM-DD) + birth_date: { + type: 'date', + min?: string, // '1900-01-01' + max?: string // '2100-12-31' + }, + + // 日期和时间 + created_at: { + type: 'datetime', + defaultValue?: string | (() => Date) + }, + + // 仅时间 + start_time: { + type: 'time' + } +} +``` + +#### 布尔和选择字段 + +```typescript +{ + // 布尔复选框 + is_active: { + type: 'boolean', + defaultValue?: boolean + }, + + // 单选(下拉菜单) + status: { + type: 'select', + options: [ + { label: '草稿', value: 'draft' }, + { label: '已发布', value: 'published' } + ], + // 或简单数组 + // options: ['draft', 'published', 'archived'] + defaultValue?: string + }, + + // 多选 + tags: { + type: 'multiSelect', + options: ['urgent', 'important', 'low-priority'] + } +} +``` + +#### 关系字段 + +```typescript +{ + // 主从关系(必需的外键) + // 删除父记录时删除子记录 + account: { + type: 'master_detail', + reference_to: 'account', + required: true, + on_delete?: 'cascade' | 'restrict' // 默认: cascade + }, + + // 查找(可选的外键) + // 删除父记录时保留关系 + manager: { + type: 'lookup', + reference_to: 'user', + reference_to_field?: '_id' // 默认: _id + }, + + // 公式字段(计算) + full_name: { + type: 'formula', + formula: '{first_name} {last_name}', + data_type: 'text' + }, + + // 汇总(从相关记录汇总) + total_opportunities: { + type: 'summary', + summary_object: 'opportunity', + summary_type: 'count', + summary_field: '_id', + summary_filters: [['stage', '=', 'closed_won']] + } +} +``` + +#### 特殊字段 + +```typescript +{ + // 文件附件 + avatar: { + type: 'image', + accept?: string // 'image/*' + }, + + // 多个文件 + attachments: { + type: 'filesize', + multiple?: boolean // 允许多个文件 + }, + + // JSON 数据 + metadata: { + type: 'json', + schema?: object // 用于验证的 JSON Schema + }, + + // 地理位置 + location: { + type: 'location' // 存储 { lat, lng, address } + }, + + // Markdown 内容 + notes: { + type: 'markdown' + } +} +``` + +### 字段约束 + +```typescript +{ + field_name: { + type: 'text', + + // 验证 + required: boolean, // NOT NULL + unique: boolean, // UNIQUE 约束 + index: boolean, // 创建索引 + + // 文本约束 + minLength: number, + maxLength: number, + pattern: string, // 正则表达式 + + // 数值约束 + min: number, + max: number, + scale: number, // 小数位数 + precision: number, // 总位数 + + // 默认值 + defaultValue: any | (() => any), + + // 可见性 + hidden: boolean, // 从 UI 隐藏 + readonly: boolean, // 只读 + + // 依赖关系 + depends_on: string[], // 字段依赖 + visible_on: string // 条件可见性 + } +} +``` + +## 查询 DSL + +### 基本查询 + +```typescript +db.query(objectName: string, options: QueryOptions): Promise + +interface QueryOptions { + fields?: string[] // 要返回的字段 + filters?: Filter[] // WHERE 条件 + sort?: string | Sort[] // ORDER BY + limit?: number // LIMIT + skip?: number // OFFSET + top?: number // 替代 limit +} +``` + +### 过滤器 + +过滤器语法: `[field, operator, value]` + +#### 比较运算符 + +```typescript +// 等于 +['status', '=', 'active'] +['status', 'eq', 'active'] + +// 不等于 +['status', '!=', 'archived'] +['status', 'ne', 'archived'] + +// 大于 +['age', '>', 18] +['age', 'gt', 18] + +// 大于或等于 +['age', '>=', 18] +['age', 'gte', 18] + +// 小于 +['price', '<', 100] +['price', 'lt', 100] + +// 小于或等于 +['price', '<=', 100] +['price', 'lte', 100] +``` + +#### 字符串运算符 + +```typescript +// 包含(不区分大小写) +['name', 'contains', 'john'] +['name', 'like', '%john%'] + +// 以...开始 +['email', 'startswith', 'admin'] +['email', 'like', 'admin%'] + +// 以...结束 +['domain', 'endswith', '.com'] +['domain', 'like', '%.com'] + +// 不包含 +['name', 'notcontains', 'test'] +['name', 'not like', '%test%'] +``` + +#### 列表运算符 + +```typescript +// 在列表中 +['status', 'in', ['draft', 'published']] + +// 不在列表中 +['status', 'notin', ['archived', 'deleted']] + +// 在范围内 +['age', 'between', [18, 65]] +``` + +#### 空值运算符 + +```typescript +// 是空值 +['description', '=', null] +['description', 'is null'] + +// 不是空值 +['description', '!=', null] +['description', 'is not null'] +``` + +#### 复合过滤器 + +```typescript +// AND(默认) +filters: [ + ['status', '=', 'active'], + ['age', '>', 18] +] +// WHERE status = 'active' AND age > 18 + +// OR +filters: [ + 'or', + ['status', '=', 'active'], + ['status', '=', 'pending'] +] +// WHERE status = 'active' OR status = 'pending' + +// 复杂组合 +filters: [ + 'and', + ['category', '=', 'electronics'], + [ + 'or', + ['price', '<', 100], + ['on_sale', '=', true] + ] +] +// WHERE category = 'electronics' AND (price < 100 OR on_sale = true) +``` + +### 排序 + +```typescript +// 简单字符串 +sort: 'name' // 默认 ASC +sort: 'name asc' +sort: 'price desc' + +// 多个字段 +sort: 'category asc, price desc' + +// 数组语法 +sort: [ + { field: 'category', order: 'asc' }, + { field: 'price', order: 'desc' } +] +``` + +### 分页 + +```typescript +// Limit 和 skip +{ + limit: 10, + skip: 20 // 跳过前 20 条记录 +} + +// 基于页面 +{ + top: 10, // 每页记录数 + skip: pageNumber * 10 +} + +// 示例: 获取第 3 页,每页 25 条记录 +{ + limit: 25, + skip: 2 * 25 // 跳过前 50 条(第 1 页和第 2 页) +} +``` + +### 字段选择 + +```typescript +// 选择特定字段 +{ + fields: ['name', 'email', 'created'] +} + +// 选择相关字段 +{ + fields: ['name', 'owner.name', 'owner.email'] +} + +// 所有字段(默认) +{ + fields: ['*'] +} +``` + +### 完整查询示例 + +```typescript +const results = await db.query('opportunity', { + fields: ['name', 'amount', 'stage', 'account.name'], + filters: [ + 'and', + ['stage', 'in', ['prospecting', 'qualification']], + ['amount', '>', 10000], + [ + 'or', + ['owner', '=', currentUserId], + ['team', 'contains', currentUserId] + ] + ], + sort: [ + { field: 'amount', order: 'desc' }, + { field: 'created', order: 'asc' } + ], + limit: 50, + skip: 0 +}) +``` + +## 聚合操作 + +### GroupBy + +```typescript +db.aggregate(objectName: string, options: AggregateOptions) + +interface AggregateOptions { + group_by: string[] + having?: Filter[] + fields?: AggregateField[] + sort?: string | Sort[] + limit?: number +} +``` + +#### 基本 GroupBy + +```typescript +// 按类别计数 +await db.aggregate('product', { + group_by: ['category'], + fields: [ + { field: '_id', function: 'count', alias: 'total' } + ] +}) + +// 结果: [ +// { category: 'Electronics', total: 45 }, +// { category: 'Books', total: 123 } +// ] +``` + +#### 多重分组 + +```typescript +// 按年份和季度统计销售额 +await db.aggregate('order', { + group_by: ['YEAR(created)', 'QUARTER(created)'], + fields: [ + { field: 'amount', function: 'sum', alias: 'total_sales' }, + { field: '_id', function: 'count', alias: 'order_count' } + ], + sort: 'YEAR(created) desc, QUARTER(created) desc' +}) +``` + +### 聚合函数 + +```typescript +// 计数 +{ field: '_id', function: 'count' } + +// 求和 +{ field: 'amount', function: 'sum' } + +// 平均值 +{ field: 'price', function: 'avg' } + +// 最小值 +{ field: 'price', function: 'min' } + +// 最大值 +{ field: 'price', function: 'max' } + +// 去重计数 +{ field: 'customer_id', function: 'count_distinct' } +``` + +### Having 子句 + +过滤聚合结果: + +```typescript +await db.aggregate('order', { + group_by: ['customer_id'], + fields: [ + { field: 'amount', function: 'sum', alias: 'total_spent' }, + { field: '_id', function: 'count', alias: 'order_count' } + ], + having: [ + ['total_spent', '>', 1000] // 仅消费 > 1000 的客户 + ] +}) +``` + +### 完整聚合示例 + +```typescript +// 带过滤器的月度销售报告 +const monthlySales = await db.aggregate('invoice', { + // 聚合前过滤(WHERE 子句) + filters: [ + ['status', '=', 'paid'], + ['created', '>=', '2024-01-01'] + ], + + // 分组依据 + group_by: ['YEAR(created)', 'MONTH(created)'], + + // 聚合计算 + fields: [ + { field: 'amount', function: 'sum', alias: 'revenue' }, + { field: '_id', function: 'count', alias: 'invoice_count' }, + { field: 'amount', function: 'avg', alias: 'avg_invoice' }, + { field: 'customer_id', function: 'count_distinct', alias: 'unique_customers' } + ], + + // 聚合后过滤(HAVING 子句) + having: [ + ['revenue', '>', 50000] // 仅收入 > 5 万的月份 + ], + + // 排序结果 + sort: 'YEAR(created) desc, MONTH(created) desc', + + // 限制结果 + limit: 12 // 最近 12 个月 +}) + +// 结果: [ +// { +// year: 2024, +// month: 3, +// revenue: 125000, +// invoice_count: 45, +// avg_invoice: 2777.78, +// unique_customers: 32 +// }, +// ... +// ] +``` + +## 变更操作 + +### 插入 + +```typescript +await db.mutation('customer', { + action: 'insert', + data: { + name: 'Acme Corp', + email: 'contact@acme.com', + status: 'active' + } +}) +``` + +### 更新 + +```typescript +await db.mutation('customer', { + action: 'update', + filters: [['_id', '=', customerId]], + data: { + status: 'inactive', + modified: new Date() + } +}) +``` + +### 删除 + +```typescript +await db.mutation('customer', { + action: 'delete', + filters: [['status', '=', 'archived']] +}) +``` + +### 批量操作 + +```typescript +// 批量插入 +await db.mutation('product', { + action: 'insert', + data: [ + { name: '产品 1', price: 10 }, + { name: '产品 2', price: 20 }, + { name: '产品 3', price: 30 } + ] +}) + +// 批量更新 +await db.mutation('task', { + action: 'update', + filters: [['project_id', '=', projectId]], + data: { status: 'cancelled' } +}) +``` + +## 下一步 + +- 探索[核心功能](./core-features)了解性能优化 +- 掌握[服务端 SDK](./server-sdk) API 参考 +- 查看[核心概念](./core-concepts)了解基础知识 diff --git a/content/docs/02-objectql/server-sdk.en.mdx b/content/docs/02-objectql/server-sdk.en.mdx new file mode 100644 index 0000000..0063c14 --- /dev/null +++ b/content/docs/02-objectql/server-sdk.en.mdx @@ -0,0 +1,750 @@ +--- +title: Server SDK Reference +description: Complete API reference for ObjectQL server-side SDK +--- + +# Server SDK Reference + +Complete reference for the ObjectQL server-side SDK, covering initialization, query methods, mutations, and advanced features. + +## Installation + +```bash +npm install @objectql/core +``` + +## Initialization + +### new ObjectQL() + +Create a new ObjectQL instance. + +```typescript +import { ObjectQL } from '@objectql/core' + +const db = new ObjectQL(options: ObjectQLOptions) +``` + +#### ObjectQLOptions + +```typescript +interface ObjectQLOptions { + // Database driver + driver: 'sqlite' | 'mysql' | 'postgresql' | 'mssql' | 'oracle' + + // Connection URL + url?: string + + // Or individual connection parameters + host?: string + port?: number + user?: string + password?: string + database?: string + + // Driver-specific options + sqlite?: SQLiteOptions + mysql?: MySQLOptions + postgres?: PostgresOptions + + // ObjectQL options + enableVirtualColumns?: boolean // Default: true for SQLite + enableCache?: boolean // Default: true + cacheSize?: number // Default: 1000 + logLevel?: 'debug' | 'info' | 'warn' | 'error' // Default: 'info' +} +``` + +#### Examples + +**SQLite (Local Development):** + +```typescript +const db = new ObjectQL({ + driver: 'sqlite', + url: './data/myapp.db' +}) +``` + +**MySQL (Production):** + +```typescript +const db = new ObjectQL({ + driver: 'mysql', + host: 'db.example.com', + port: 3306, + user: 'myapp', + password: process.env.DB_PASSWORD, + database: 'production', + mysql: { + connectionLimit: 20, + waitForConnections: true, + ssl: { + ca: fs.readFileSync('./ca.pem') + } + } +}) +``` + +**PostgreSQL (Cloud):** + +```typescript +const db = new ObjectQL({ + driver: 'postgresql', + url: process.env.DATABASE_URL, + postgres: { + max: 25, + ssl: { rejectUnauthorized: false } + } +}) +``` + +## Schema Management + +### registerSchema() + +Register a schema with ObjectQL. + +```typescript +await db.registerSchema(schema: Schema): Promise +``` + +#### Parameters + +- `schema` - Schema definition object + +#### Example + +```typescript +await db.registerSchema({ + objects: { + customer: { + label: 'Customer', + fields: { + name: { type: 'text', required: true }, + email: { type: 'email', unique: true }, + status: { + type: 'select', + options: ['active', 'inactive'], + defaultValue: 'active' + } + } + }, + order: { + label: 'Order', + fields: { + customer: { + type: 'master_detail', + reference_to: 'customer' + }, + amount: { type: 'currency', scale: 2 }, + status: { + type: 'select', + options: ['pending', 'paid', 'cancelled'] + } + } + } + } +}) +``` + +### getSchema() + +Get registered schema. + +```typescript +const schema = db.getSchema(objectName?: string): Schema | ObjectSchema +``` + +#### Parameters + +- `objectName` - Optional. Return schema for specific object + +#### Example + +```typescript +// Get entire schema +const allSchema = db.getSchema() + +// Get specific object schema +const customerSchema = db.getSchema('customer') +console.log(customerSchema.fields) +``` + +## Query Operations + +### query() + +Query records from an object. + +```typescript +const results = await db.query( + objectName: string, + options?: QueryOptions +): Promise +``` + +#### QueryOptions + +```typescript +interface QueryOptions { + fields?: string[] // Fields to return + filters?: Filter[] // WHERE conditions + sort?: string | Sort[] // ORDER BY + limit?: number // LIMIT + skip?: number // OFFSET + top?: number // Alternative to limit +} + +type Filter = + | [string, Operator, any] // Simple filter + | ['and' | 'or', ...Filter[]] // Compound filter + +type Operator = + | '=' | '!=' | '>' | '>=' | '<' | '<=' + | 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' + | 'in' | 'notin' | 'between' + | 'contains' | 'startswith' | 'endswith' | 'notcontains' + | 'like' | 'not like' + | 'is null' | 'is not null' +``` + +#### Examples + +**Basic Query:** + +```typescript +// Get all active customers +const customers = await db.query('customer', { + filters: [['status', '=', 'active']] +}) +``` + +**Field Selection:** + +```typescript +// Select specific fields +const customers = await db.query('customer', { + fields: ['_id', 'name', 'email'], + filters: [['status', '=', 'active']] +}) +``` + +**Complex Filters:** + +```typescript +// Complex AND/OR conditions +const orders = await db.query('order', { + filters: [ + 'and', + ['status', 'in', ['pending', 'paid']], + ['amount', '>', 100], + [ + 'or', + ['customer.city', '=', 'New York'], + ['customer.city', '=', 'Los Angeles'] + ] + ] +}) +``` + +**Sorting and Pagination:** + +```typescript +// Sort and paginate +const page = 1 +const pageSize = 25 + +const customers = await db.query('customer', { + sort: [ + { field: 'created', order: 'desc' }, + { field: 'name', order: 'asc' } + ], + limit: pageSize, + skip: (page - 1) * pageSize +}) +``` + +**Related Fields:** + +```typescript +// Query with related fields +const orders = await db.query('order', { + fields: ['_id', 'amount', 'customer.name', 'customer.email'], + filters: [['status', '=', 'pending']] +}) + +// Result: [ +// { +// _id: '123', +// amount: 199.99, +// customer: { name: 'John Doe', email: 'john@example.com' } +// } +// ] +``` + +### queryOne() + +Query a single record. + +```typescript +const record = await db.queryOne( + objectName: string, + options?: QueryOptions +): Promise +``` + +#### Example + +```typescript +// Get customer by ID +const customer = await db.queryOne('customer', { + filters: [['_id', '=', customerId]] +}) + +if (!customer) { + throw new Error('Customer not found') +} +``` + +### count() + +Count records matching filters. + +```typescript +const total = await db.count( + objectName: string, + filters?: Filter[] +): Promise +``` + +#### Example + +```typescript +// Count active customers +const activeCount = await db.count('customer', [ + ['status', '=', 'active'] +]) + +// Total count +const totalCount = await db.count('customer') +``` + +## Mutation Operations + +### mutation() + +Insert, update, or delete records. + +```typescript +const result = await db.mutation( + objectName: string, + options: MutationOptions +): Promise +``` + +#### MutationOptions + +```typescript +interface MutationOptions { + action: 'insert' | 'update' | 'delete' + data?: Record | Record[] + filters?: Filter[] +} + +interface MutationResult { + success: boolean + ids?: string[] // Inserted/updated record IDs + count?: number // Number of affected records + errors?: Error[] +} +``` + +#### Insert Examples + +**Single Insert:** + +```typescript +const result = await db.mutation('customer', { + action: 'insert', + data: { + name: 'Acme Corporation', + email: 'contact@acme.com', + status: 'active' + } +}) + +console.log(result.ids[0]) // ID of created record +``` + +**Batch Insert:** + +```typescript +const result = await db.mutation('product', { + action: 'insert', + data: [ + { name: 'Product A', price: 29.99 }, + { name: 'Product B', price: 49.99 }, + { name: 'Product C', price: 79.99 } + ] +}) + +console.log(result.count) // 3 +console.log(result.ids) // ['id1', 'id2', 'id3'] +``` + +#### Update Examples + +**Update by ID:** + +```typescript +await db.mutation('customer', { + action: 'update', + filters: [['_id', '=', customerId]], + data: { + status: 'inactive', + notes: 'Account closed' + } +}) +``` + +**Batch Update:** + +```typescript +// Deactivate all customers from specific city +await db.mutation('customer', { + action: 'update', + filters: [['city', '=', 'Old City']], + data: { + status: 'inactive' + } +}) +``` + +#### Delete Examples + +**Delete by ID:** + +```typescript +await db.mutation('customer', { + action: 'delete', + filters: [['_id', '=', customerId]] +}) +``` + +**Conditional Delete:** + +```typescript +// Delete all archived orders older than 1 year +const oneYearAgo = new Date() +oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1) + +await db.mutation('order', { + action: 'delete', + filters: [ + ['status', '=', 'archived'], + ['created', '<', oneYearAgo.toISOString()] + ] +}) +``` + +## Aggregation Operations + +### aggregate() + +Perform aggregation queries. + +```typescript +const results = await db.aggregate( + objectName: string, + options: AggregateOptions +): Promise +``` + +#### AggregateOptions + +```typescript +interface AggregateOptions { + group_by: string[] // GROUP BY fields + fields?: AggregateField[] // Aggregate calculations + filters?: Filter[] // WHERE clause + having?: Filter[] // HAVING clause + sort?: string | Sort[] // ORDER BY + limit?: number // LIMIT +} + +interface AggregateField { + field: string + function: 'count' | 'sum' | 'avg' | 'min' | 'max' | 'count_distinct' + alias?: string +} +``` + +#### Examples + +**Simple Aggregation:** + +```typescript +// Count customers by status +const statusCounts = await db.aggregate('customer', { + group_by: ['status'], + fields: [ + { field: '_id', function: 'count', alias: 'total' } + ] +}) + +// Result: [ +// { status: 'active', total: 150 }, +// { status: 'inactive', total: 23 } +// ] +``` + +**Sales Report:** + +```typescript +// Monthly sales summary +const monthlySales = await db.aggregate('order', { + filters: [ + ['status', '=', 'paid'], + ['created', '>=', '2024-01-01'] + ], + group_by: ['YEAR(created)', 'MONTH(created)'], + fields: [ + { field: 'amount', function: 'sum', alias: 'revenue' }, + { field: '_id', function: 'count', alias: 'order_count' }, + { field: 'amount', function: 'avg', alias: 'avg_order' }, + { field: 'customer_id', function: 'count_distinct', alias: 'unique_customers' } + ], + having: [ + ['revenue', '>', 10000] + ], + sort: 'YEAR(created) desc, MONTH(created) desc' +}) +``` + +**Customer Spending:** + +```typescript +// Top spending customers +const topCustomers = await db.aggregate('order', { + group_by: ['customer_id'], + fields: [ + { field: 'amount', function: 'sum', alias: 'total_spent' }, + { field: '_id', function: 'count', alias: 'order_count' } + ], + having: [ + ['total_spent', '>', 1000] + ], + sort: 'total_spent desc', + limit: 10 +}) +``` + +## Virtual City + +### virtualCity() + +Create a Virtual City instance for multi-tenant data isolation. + +```typescript +const tenantDb = db.virtualCity(spaceId: string): ObjectQL +``` + +#### Parameters + +- `spaceId` - Unique identifier for the tenant/space + +#### Example + +```typescript +// Create tenant-specific instances +const tenant1Db = db.virtualCity('tenant_001') +const tenant2Db = db.virtualCity('tenant_002') + +// Each tenant sees only their data +await tenant1Db.mutation('customer', { + action: 'insert', + data: { name: 'Tenant 1 Customer' } +}) + +await tenant2Db.mutation('customer', { + action: 'insert', + data: { name: 'Tenant 2 Customer' } +}) + +// Tenant 1 query +const tenant1Customers = await tenant1Db.query('customer', {}) +// Returns: [{ name: 'Tenant 1 Customer', space: 'tenant_001' }] + +// Tenant 2 query +const tenant2Customers = await tenant2Db.query('customer', {}) +// Returns: [{ name: 'Tenant 2 Customer', space: 'tenant_002' }] +``` + +**SaaS Application:** + +```typescript +// Express.js middleware +app.use((req, res, next) => { + const tenantId = req.headers['x-tenant-id'] + req.db = db.virtualCity(tenantId) + next() +}) + +// Route handlers use tenant-specific db +app.get('/api/customers', async (req, res) => { + const customers = await req.db.query('customer', {}) + res.json(customers) +}) +``` + +## Transaction Support + +### transaction() + +Execute multiple operations in a transaction. + +```typescript +await db.transaction(async (trx) => { + // All operations use the transaction +}) +``` + +#### Example + +```typescript +// Transfer order between customers +await db.transaction(async (trx) => { + // Update order + await trx.mutation('order', { + action: 'update', + filters: [['_id', '=', orderId]], + data: { customer_id: newCustomerId } + }) + + // Log the transfer + await trx.mutation('audit_log', { + action: 'insert', + data: { + action: 'order_transfer', + order_id: orderId, + old_customer: oldCustomerId, + new_customer: newCustomerId + } + }) +}) +``` + +## Utility Methods + +### close() + +Close database connection and cleanup resources. + +```typescript +await db.close(): Promise +``` + +#### Example + +```typescript +// Graceful shutdown +process.on('SIGTERM', async () => { + await db.close() + process.exit(0) +}) +``` + +### getPoolStats() + +Get connection pool statistics. + +```typescript +const stats = await db.getPoolStats(): Promise + +interface PoolStats { + total: number // Total connections + idle: number // Idle connections + active: number // Active connections + waiting: number // Waiting requests +} +``` + +#### Example + +```typescript +// Monitor connection pool +setInterval(async () => { + const stats = await db.getPoolStats() + console.log('DB Pool:', stats) + + if (stats.waiting > 10) { + console.warn('High connection wait queue!') + } +}, 60000) +``` + +## Error Handling + +ObjectQL throws specific error types for different scenarios: + +```typescript +import { + ValidationError, + NotFoundError, + DuplicateError, + DatabaseError +} from '@objectql/core' + +try { + await db.mutation('customer', { + action: 'insert', + data: { email: 'duplicate@example.com' } + }) +} catch (error) { + if (error instanceof ValidationError) { + // Handle validation errors + console.error('Validation failed:', error.fields) + } else if (error instanceof DuplicateError) { + // Handle duplicate key errors + console.error('Duplicate:', error.field) + } else if (error instanceof DatabaseError) { + // Handle database errors + console.error('Database error:', error.message) + } +} +``` + +## TypeScript Support + +ObjectQL provides full TypeScript support with type inference: + +```typescript +import { ObjectQL, Schema } from '@objectql/core' + +// Define schema type +interface Customer { + _id: string + name: string + email: string + status: 'active' | 'inactive' + created: Date +} + +// Type-safe queries +const customers = await db.query('customer', { + filters: [['status', '=', 'active']] +}) + +// TypeScript knows the shape +customers.forEach(customer => { + console.log(customer.name) // ✓ Type-safe + console.log(customer.foo) // ✗ Compile error +}) +``` + +## Next Steps + +- Review [Protocol Spec](./protocol-spec) for detailed query syntax +- Explore [Core Features](./core-features) for advanced optimizations +- Check [Core Concepts](./core-concepts) for foundational knowledge diff --git a/content/docs/02-objectql/server-sdk.zh-CN.mdx b/content/docs/02-objectql/server-sdk.zh-CN.mdx new file mode 100644 index 0000000..ceb8958 --- /dev/null +++ b/content/docs/02-objectql/server-sdk.zh-CN.mdx @@ -0,0 +1,750 @@ +--- +title: 服务端 SDK 参考 +description: ObjectQL 服务端 SDK 的完整 API 参考 +--- + +# 服务端 SDK 参考 + +ObjectQL 服务端 SDK 的完整参考,涵盖初始化、查询方法、变更操作和高级功能。 + +## 安装 + +```bash +npm install @objectql/core +``` + +## 初始化 + +### new ObjectQL() + +创建新的 ObjectQL 实例。 + +```typescript +import { ObjectQL } from '@objectql/core' + +const db = new ObjectQL(options: ObjectQLOptions) +``` + +#### ObjectQLOptions + +```typescript +interface ObjectQLOptions { + // 数据库驱动 + driver: 'sqlite' | 'mysql' | 'postgresql' | 'mssql' | 'oracle' + + // 连接 URL + url?: string + + // 或单独的连接参数 + host?: string + port?: number + user?: string + password?: string + database?: string + + // 驱动特定选项 + sqlite?: SQLiteOptions + mysql?: MySQLOptions + postgres?: PostgresOptions + + // ObjectQL 选项 + enableVirtualColumns?: boolean // 默认: SQLite 为 true + enableCache?: boolean // 默认: true + cacheSize?: number // 默认: 1000 + logLevel?: 'debug' | 'info' | 'warn' | 'error' // 默认: 'info' +} +``` + +#### 示例 + +**SQLite (本地开发):** + +```typescript +const db = new ObjectQL({ + driver: 'sqlite', + url: './data/myapp.db' +}) +``` + +**MySQL (生产环境):** + +```typescript +const db = new ObjectQL({ + driver: 'mysql', + host: 'db.example.com', + port: 3306, + user: 'myapp', + password: process.env.DB_PASSWORD, + database: 'production', + mysql: { + connectionLimit: 20, + waitForConnections: true, + ssl: { + ca: fs.readFileSync('./ca.pem') + } + } +}) +``` + +**PostgreSQL (云端):** + +```typescript +const db = new ObjectQL({ + driver: 'postgresql', + url: process.env.DATABASE_URL, + postgres: { + max: 25, + ssl: { rejectUnauthorized: false } + } +}) +``` + +## Schema 管理 + +### registerSchema() + +向 ObjectQL 注册 Schema。 + +```typescript +await db.registerSchema(schema: Schema): Promise +``` + +#### 参数 + +- `schema` - Schema 定义对象 + +#### 示例 + +```typescript +await db.registerSchema({ + objects: { + customer: { + label: '客户', + fields: { + name: { type: 'text', required: true }, + email: { type: 'email', unique: true }, + status: { + type: 'select', + options: ['active', 'inactive'], + defaultValue: 'active' + } + } + }, + order: { + label: '订单', + fields: { + customer: { + type: 'master_detail', + reference_to: 'customer' + }, + amount: { type: 'currency', scale: 2 }, + status: { + type: 'select', + options: ['pending', 'paid', 'cancelled'] + } + } + } + } +}) +``` + +### getSchema() + +获取已注册的 Schema。 + +```typescript +const schema = db.getSchema(objectName?: string): Schema | ObjectSchema +``` + +#### 参数 + +- `objectName` - 可选。返回特定对象的 Schema + +#### 示例 + +```typescript +// 获取整个 Schema +const allSchema = db.getSchema() + +// 获取特定对象的 Schema +const customerSchema = db.getSchema('customer') +console.log(customerSchema.fields) +``` + +## 查询操作 + +### query() + +从对象查询记录。 + +```typescript +const results = await db.query( + objectName: string, + options?: QueryOptions +): Promise +``` + +#### QueryOptions + +```typescript +interface QueryOptions { + fields?: string[] // 要返回的字段 + filters?: Filter[] // WHERE 条件 + sort?: string | Sort[] // ORDER BY + limit?: number // LIMIT + skip?: number // OFFSET + top?: number // 替代 limit +} + +type Filter = + | [string, Operator, any] // 简单过滤器 + | ['and' | 'or', ...Filter[]] // 复合过滤器 + +type Operator = + | '=' | '!=' | '>' | '>=' | '<' | '<=' + | 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' + | 'in' | 'notin' | 'between' + | 'contains' | 'startswith' | 'endswith' | 'notcontains' + | 'like' | 'not like' + | 'is null' | 'is not null' +``` + +#### 示例 + +**基本查询:** + +```typescript +// 获取所有活跃客户 +const customers = await db.query('customer', { + filters: [['status', '=', 'active']] +}) +``` + +**字段选择:** + +```typescript +// 选择特定字段 +const customers = await db.query('customer', { + fields: ['_id', 'name', 'email'], + filters: [['status', '=', 'active']] +}) +``` + +**复杂过滤器:** + +```typescript +// 复杂 AND/OR 条件 +const orders = await db.query('order', { + filters: [ + 'and', + ['status', 'in', ['pending', 'paid']], + ['amount', '>', 100], + [ + 'or', + ['customer.city', '=', '纽约'], + ['customer.city', '=', '洛杉矶'] + ] + ] +}) +``` + +**排序和分页:** + +```typescript +// 排序和分页 +const page = 1 +const pageSize = 25 + +const customers = await db.query('customer', { + sort: [ + { field: 'created', order: 'desc' }, + { field: 'name', order: 'asc' } + ], + limit: pageSize, + skip: (page - 1) * pageSize +}) +``` + +**关联字段:** + +```typescript +// 查询关联字段 +const orders = await db.query('order', { + fields: ['_id', 'amount', 'customer.name', 'customer.email'], + filters: [['status', '=', 'pending']] +}) + +// 结果: [ +// { +// _id: '123', +// amount: 199.99, +// customer: { name: '张三', email: 'zhang@example.com' } +// } +// ] +``` + +### queryOne() + +查询单条记录。 + +```typescript +const record = await db.queryOne( + objectName: string, + options?: QueryOptions +): Promise +``` + +#### 示例 + +```typescript +// 按 ID 获取客户 +const customer = await db.queryOne('customer', { + filters: [['_id', '=', customerId]] +}) + +if (!customer) { + throw new Error('客户不存在') +} +``` + +### count() + +计算匹配过滤器的记录数。 + +```typescript +const total = await db.count( + objectName: string, + filters?: Filter[] +): Promise +``` + +#### 示例 + +```typescript +// 计算活跃客户数 +const activeCount = await db.count('customer', [ + ['status', '=', 'active'] +]) + +// 总数 +const totalCount = await db.count('customer') +``` + +## 变更操作 + +### mutation() + +插入、更新或删除记录。 + +```typescript +const result = await db.mutation( + objectName: string, + options: MutationOptions +): Promise +``` + +#### MutationOptions + +```typescript +interface MutationOptions { + action: 'insert' | 'update' | 'delete' + data?: Record | Record[] + filters?: Filter[] +} + +interface MutationResult { + success: boolean + ids?: string[] // 插入/更新的记录 ID + count?: number // 受影响的记录数 + errors?: Error[] +} +``` + +#### 插入示例 + +**单条插入:** + +```typescript +const result = await db.mutation('customer', { + action: 'insert', + data: { + name: 'Acme 公司', + email: 'contact@acme.com', + status: 'active' + } +}) + +console.log(result.ids[0]) // 创建的记录 ID +``` + +**批量插入:** + +```typescript +const result = await db.mutation('product', { + action: 'insert', + data: [ + { name: '产品 A', price: 29.99 }, + { name: '产品 B', price: 49.99 }, + { name: '产品 C', price: 79.99 } + ] +}) + +console.log(result.count) // 3 +console.log(result.ids) // ['id1', 'id2', 'id3'] +``` + +#### 更新示例 + +**按 ID 更新:** + +```typescript +await db.mutation('customer', { + action: 'update', + filters: [['_id', '=', customerId]], + data: { + status: 'inactive', + notes: '账户已关闭' + } +}) +``` + +**批量更新:** + +```typescript +// 停用特定城市的所有客户 +await db.mutation('customer', { + action: 'update', + filters: [['city', '=', '旧城市']], + data: { + status: 'inactive' + } +}) +``` + +#### 删除示例 + +**按 ID 删除:** + +```typescript +await db.mutation('customer', { + action: 'delete', + filters: [['_id', '=', customerId]] +}) +``` + +**条件删除:** + +```typescript +// 删除所有超过 1 年的已归档订单 +const oneYearAgo = new Date() +oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1) + +await db.mutation('order', { + action: 'delete', + filters: [ + ['status', '=', 'archived'], + ['created', '<', oneYearAgo.toISOString()] + ] +}) +``` + +## 聚合操作 + +### aggregate() + +执行聚合查询。 + +```typescript +const results = await db.aggregate( + objectName: string, + options: AggregateOptions +): Promise +``` + +#### AggregateOptions + +```typescript +interface AggregateOptions { + group_by: string[] // GROUP BY 字段 + fields?: AggregateField[] // 聚合计算 + filters?: Filter[] // WHERE 子句 + having?: Filter[] // HAVING 子句 + sort?: string | Sort[] // ORDER BY + limit?: number // LIMIT +} + +interface AggregateField { + field: string + function: 'count' | 'sum' | 'avg' | 'min' | 'max' | 'count_distinct' + alias?: string +} +``` + +#### 示例 + +**简单聚合:** + +```typescript +// 按状态统计客户数 +const statusCounts = await db.aggregate('customer', { + group_by: ['status'], + fields: [ + { field: '_id', function: 'count', alias: 'total' } + ] +}) + +// 结果: [ +// { status: 'active', total: 150 }, +// { status: 'inactive', total: 23 } +// ] +``` + +**销售报告:** + +```typescript +// 月度销售汇总 +const monthlySales = await db.aggregate('order', { + filters: [ + ['status', '=', 'paid'], + ['created', '>=', '2024-01-01'] + ], + group_by: ['YEAR(created)', 'MONTH(created)'], + fields: [ + { field: 'amount', function: 'sum', alias: 'revenue' }, + { field: '_id', function: 'count', alias: 'order_count' }, + { field: 'amount', function: 'avg', alias: 'avg_order' }, + { field: 'customer_id', function: 'count_distinct', alias: 'unique_customers' } + ], + having: [ + ['revenue', '>', 10000] + ], + sort: 'YEAR(created) desc, MONTH(created) desc' +}) +``` + +**客户消费:** + +```typescript +// 消费最多的客户 +const topCustomers = await db.aggregate('order', { + group_by: ['customer_id'], + fields: [ + { field: 'amount', function: 'sum', alias: 'total_spent' }, + { field: '_id', function: 'count', alias: 'order_count' } + ], + having: [ + ['total_spent', '>', 1000] + ], + sort: 'total_spent desc', + limit: 10 +}) +``` + +## Virtual City + +### virtualCity() + +创建 Virtual City 实例用于多租户数据隔离。 + +```typescript +const tenantDb = db.virtualCity(spaceId: string): ObjectQL +``` + +#### 参数 + +- `spaceId` - 租户/空间的唯一标识符 + +#### 示例 + +```typescript +// 创建租户特定实例 +const tenant1Db = db.virtualCity('tenant_001') +const tenant2Db = db.virtualCity('tenant_002') + +// 每个租户只能看到自己的数据 +await tenant1Db.mutation('customer', { + action: 'insert', + data: { name: '租户 1 客户' } +}) + +await tenant2Db.mutation('customer', { + action: 'insert', + data: { name: '租户 2 客户' } +}) + +// 租户 1 查询 +const tenant1Customers = await tenant1Db.query('customer', {}) +// 返回: [{ name: '租户 1 客户', space: 'tenant_001' }] + +// 租户 2 查询 +const tenant2Customers = await tenant2Db.query('customer', {}) +// 返回: [{ name: '租户 2 客户', space: 'tenant_002' }] +``` + +**SaaS 应用:** + +```typescript +// Express.js 中间件 +app.use((req, res, next) => { + const tenantId = req.headers['x-tenant-id'] + req.db = db.virtualCity(tenantId) + next() +}) + +// 路由处理器使用租户特定的 db +app.get('/api/customers', async (req, res) => { + const customers = await req.db.query('customer', {}) + res.json(customers) +}) +``` + +## 事务支持 + +### transaction() + +在事务中执行多个操作。 + +```typescript +await db.transaction(async (trx) => { + // 所有操作使用该事务 +}) +``` + +#### 示例 + +```typescript +// 在客户之间转移订单 +await db.transaction(async (trx) => { + // 更新订单 + await trx.mutation('order', { + action: 'update', + filters: [['_id', '=', orderId]], + data: { customer_id: newCustomerId } + }) + + // 记录转移日志 + await trx.mutation('audit_log', { + action: 'insert', + data: { + action: 'order_transfer', + order_id: orderId, + old_customer: oldCustomerId, + new_customer: newCustomerId + } + }) +}) +``` + +## 工具方法 + +### close() + +关闭数据库连接并清理资源。 + +```typescript +await db.close(): Promise +``` + +#### 示例 + +```typescript +// 优雅关闭 +process.on('SIGTERM', async () => { + await db.close() + process.exit(0) +}) +``` + +### getPoolStats() + +获取连接池统计信息。 + +```typescript +const stats = await db.getPoolStats(): Promise + +interface PoolStats { + total: number // 总连接数 + idle: number // 空闲连接数 + active: number // 活跃连接数 + waiting: number // 等待请求数 +} +``` + +#### 示例 + +```typescript +// 监控连接池 +setInterval(async () => { + const stats = await db.getPoolStats() + console.log('数据库池:', stats) + + if (stats.waiting > 10) { + console.warn('连接等待队列过长!') + } +}, 60000) +``` + +## 错误处理 + +ObjectQL 为不同场景抛出特定的错误类型: + +```typescript +import { + ValidationError, + NotFoundError, + DuplicateError, + DatabaseError +} from '@objectql/core' + +try { + await db.mutation('customer', { + action: 'insert', + data: { email: 'duplicate@example.com' } + }) +} catch (error) { + if (error instanceof ValidationError) { + // 处理验证错误 + console.error('验证失败:', error.fields) + } else if (error instanceof DuplicateError) { + // 处理重复键错误 + console.error('重复:', error.field) + } else if (error instanceof DatabaseError) { + // 处理数据库错误 + console.error('数据库错误:', error.message) + } +} +``` + +## TypeScript 支持 + +ObjectQL 提供完整的 TypeScript 支持和类型推断: + +```typescript +import { ObjectQL, Schema } from '@objectql/core' + +// 定义 Schema 类型 +interface Customer { + _id: string + name: string + email: string + status: 'active' | 'inactive' + created: Date +} + +// 类型安全的查询 +const customers = await db.query('customer', { + filters: [['status', '=', 'active']] +}) + +// TypeScript 知道形状 +customers.forEach(customer => { + console.log(customer.name) // ✓ 类型安全 + console.log(customer.foo) // ✗ 编译错误 +}) +``` + +## 下一步 + +- 查看[协议规范](./protocol-spec)了解详细的查询语法 +- 探索[核心功能](./core-features)了解高级优化 +- 查看[核心概念](./core-concepts)了解基础知识 diff --git a/content/docs/03-objectui/component-spec.en.mdx b/content/docs/03-objectui/component-spec.en.mdx new file mode 100644 index 0000000..c06e6eb --- /dev/null +++ b/content/docs/03-objectui/component-spec.en.mdx @@ -0,0 +1,787 @@ +--- +title: Component Specification +description: Complete protocol reference for all ObjectUI components +--- + +# Component Specification + +This comprehensive reference documents all ObjectUI components and their JSON protocols. + +## Basic Components + +Basic components are the fundamental building blocks for forms and data input. + +### Input + +Text input field for single-line text entry. + +**Protocol:** + +```typescript +{ + type: 'input', + name: string, // Field name + label?: string, // Display label + placeholder?: string, // Placeholder text + defaultValue?: string, // Default value + required?: boolean, // Required validation + disabled?: boolean, // Disabled state + maxLength?: number, // Maximum length + minLength?: number, // Minimum length + pattern?: string, // Regex pattern + inputType?: 'text' | 'email' | 'password' | 'url' | 'tel' | 'number', + onChange?: Action, // Change event handler +} +``` + +**Example:** + +```json +{ + "type": "input", + "name": "email", + "label": "Email Address", + "placeholder": "user@example.com", + "inputType": "email", + "required": true, + "maxLength": 255 +} +``` + +**Advanced Example - Dependent Fields:** + +```json +{ + "type": "form", + "fields": [ + { + "type": "input", + "name": "username", + "label": "Username", + "required": true, + "onChange": { + "type": "api", + "api": "/api/check-username", + "method": "POST", + "data": { "username": "${value}" } + } + } + ] +} +``` + +### Select + +Dropdown selection field. + +**Protocol:** + +```typescript +{ + type: 'select', + name: string, + label?: string, + placeholder?: string, + defaultValue?: string | number, + required?: boolean, + disabled?: boolean, + multiple?: boolean, // Allow multiple selections + searchable?: boolean, // Enable search/filter + clearable?: boolean, // Show clear button + options?: Array<{ // Static options + label: string, + value: string | number, + disabled?: boolean + }>, + api?: string, // API endpoint for dynamic options + optionLabel?: string, // Field name for option label + optionValue?: string, // Field name for option value + dependsOn?: string, // Field dependency + onChange?: Action, +} +``` + +**Example - Static Options:** + +```json +{ + "type": "select", + "name": "status", + "label": "Status", + "placeholder": "Select status", + "required": true, + "options": [ + { "label": "Active", "value": "active" }, + { "label": "Inactive", "value": "inactive" }, + { "label": "Pending", "value": "pending" } + ] +} +``` + +**Example - Dynamic Options from API:** + +```json +{ + "type": "select", + "name": "category", + "label": "Category", + "api": "/api/categories", + "optionLabel": "name", + "optionValue": "id", + "searchable": true +} +``` + +**Example - Dependent Select:** + +```json +{ + "type": "form", + "fields": [ + { + "type": "select", + "name": "country", + "label": "Country", + "api": "/api/countries" + }, + { + "type": "select", + "name": "state", + "label": "State", + "api": "/api/states?country=${country}", + "dependsOn": "country" + } + ] +} +``` + +### DatePicker + +Date and time selection component. + +**Protocol:** + +```typescript +{ + type: 'datepicker', + name: string, + label?: string, + placeholder?: string, + defaultValue?: string | Date, + required?: boolean, + disabled?: boolean, + mode?: 'date' | 'datetime' | 'time' | 'daterange', + format?: string, // Display format (e.g., 'YYYY-MM-DD') + minDate?: string | Date, // Minimum selectable date + maxDate?: string | Date, // Maximum selectable date + disabledDates?: string[], // Array of disabled dates + showTime?: boolean, // Show time picker + onChange?: Action, +} +``` + +**Example - Simple Date:** + +```json +{ + "type": "datepicker", + "name": "birthday", + "label": "Date of Birth", + "mode": "date", + "maxDate": "today", + "required": true +} +``` + +**Example - DateTime:** + +```json +{ + "type": "datepicker", + "name": "appointment", + "label": "Appointment Time", + "mode": "datetime", + "format": "YYYY-MM-DD HH:mm", + "minDate": "today", + "showTime": true +} +``` + +**Example - Date Range:** + +```json +{ + "type": "datepicker", + "name": "dateRange", + "label": "Select Period", + "mode": "daterange", + "defaultValue": { + "start": "2024-01-01", + "end": "2024-12-31" + } +} +``` + +### Switch + +Toggle switch for boolean values. + +**Protocol:** + +```typescript +{ + type: 'switch', + name: string, + label?: string, + defaultValue?: boolean, + disabled?: boolean, + checkedText?: string, // Text when checked + uncheckedText?: string, // Text when unchecked + onChange?: Action, +} +``` + +**Example:** + +```json +{ + "type": "switch", + "name": "isActive", + "label": "Active Status", + "defaultValue": true, + "checkedText": "Active", + "uncheckedText": "Inactive" +} +``` + +## Container Components + +Container components organize and structure the UI layout. + +### Page + +Top-level page container with header and actions. + +**Protocol:** + +```typescript +{ + type: 'page', + title: string, + description?: string, + icon?: string, + breadcrumbs?: Array<{ + label: string, + url?: string + }>, + actions?: Action[], // Header actions + tabs?: Array<{ + label: string, + key: string, + body: Component + }>, + body: Component | Component[], +} +``` + +**Example:** + +```json +{ + "type": "page", + "title": "User Management", + "description": "Manage system users and permissions", + "breadcrumbs": [ + { "label": "Home", "url": "/" }, + { "label": "Users" } + ], + "actions": [ + { + "label": "Add User", + "type": "dialog", + "body": { + "type": "form", + "api": "/api/users", + "fields": [...] + } + } + ], + "body": { + "type": "table", + "api": "/api/users" + } +} +``` + +### Grid + +Responsive grid layout. + +**Protocol:** + +```typescript +{ + type: 'grid', + columns?: number | { // Column configuration + xs?: number, // Extra small devices + sm?: number, // Small devices + md?: number, // Medium devices + lg?: number, // Large devices + xl?: number // Extra large devices + }, + gap?: number | string, // Gap between items + items: Component[], +} +``` + +**Example:** + +```json +{ + "type": "grid", + "columns": { "xs": 1, "sm": 2, "lg": 3 }, + "gap": "20px", + "items": [ + { + "type": "card", + "title": "Total Users", + "body": { "type": "text", "value": "${stats.users}" } + }, + { + "type": "card", + "title": "Total Orders", + "body": { "type": "text", "value": "${stats.orders}" } + }, + { + "type": "card", + "title": "Revenue", + "body": { "type": "text", "value": "$${stats.revenue}" } + } + ] +} +``` + +### Card + +Card container with header, body, and footer. + +**Protocol:** + +```typescript +{ + type: 'card', + title?: string, + description?: string, + cover?: string, // Cover image URL + avatar?: string, // Avatar image URL + actions?: Action[], + body: Component | Component[], + footer?: Component | Component[], +} +``` + +**Example:** + +```json +{ + "type": "card", + "title": "User Profile", + "avatar": "${user.avatar}", + "body": [ + { "type": "text", "value": "Name: ${user.name}" }, + { "type": "text", "value": "Email: ${user.email}" }, + { "type": "text", "value": "Role: ${user.role}" } + ], + "actions": [ + { "label": "Edit", "onClick": { "type": "navigate", "url": "/users/${user.id}/edit" } }, + { "label": "Delete", "onClick": { "type": "api", "api": "/api/users/${user.id}", "method": "DELETE" } } + ] +} +``` + +### Form + +Form container with fields and submission handling. + +**Protocol:** + +```typescript +{ + type: 'form', + title?: string, + api?: string, // Submit endpoint + method?: 'POST' | 'PUT' | 'PATCH', + initialValues?: object, // Initial form values + fields: FormField[], + layout?: 'horizontal' | 'vertical' | 'inline', + columns?: number, // Multi-column layout + submitText?: string, // Submit button text + resetText?: string, // Reset button text + showReset?: boolean, // Show reset button + onSubmit?: Action, + onSuccess?: Action, + onError?: Action, +} +``` + +**Example - Simple Form:** + +```json +{ + "type": "form", + "title": "Create User", + "api": "/api/users", + "method": "POST", + "fields": [ + { + "type": "input", + "name": "name", + "label": "Full Name", + "required": true + }, + { + "type": "input", + "name": "email", + "label": "Email", + "inputType": "email", + "required": true + }, + { + "type": "select", + "name": "role", + "label": "Role", + "options": [ + { "label": "Admin", "value": "admin" }, + { "label": "User", "value": "user" } + ] + } + ], + "submitText": "Create User", + "onSuccess": { + "type": "toast", + "message": "User created successfully!" + } +} +``` + +**Example - Multi-column Form:** + +```json +{ + "type": "form", + "title": "User Registration", + "columns": 2, + "fields": [ + { "type": "input", "name": "firstName", "label": "First Name" }, + { "type": "input", "name": "lastName", "label": "Last Name" }, + { "type": "input", "name": "email", "label": "Email", "inputType": "email" }, + { "type": "input", "name": "phone", "label": "Phone", "inputType": "tel" }, + { "type": "datepicker", "name": "birthday", "label": "Birthday" }, + { "type": "select", "name": "country", "label": "Country", "api": "/api/countries" } + ] +} +``` + +### Table + +Data table with sorting, filtering, and pagination. + +**Protocol:** + +```typescript +{ + type: 'table', + api?: string, // Data endpoint + data?: any[], // Static data + columns: Array<{ + name: string, + label: string, + type?: 'text' | 'number' | 'boolean' | 'date' | 'image', + sortable?: boolean, + filterable?: boolean, + width?: number | string, + render?: string, // Custom render template + }>, + rowActions?: Action[], // Actions per row + batchActions?: Action[], // Batch actions + pagination?: boolean, + pageSize?: number, + searchable?: boolean, // Global search + filters?: Array<{ + name: string, + label: string, + type: 'input' | 'select' | 'datepicker' + }>, + selectable?: boolean, // Row selection + expandable?: Component, // Expandable row content +} +``` + +**Example - Basic Table:** + +```json +{ + "type": "table", + "api": "/api/users", + "columns": [ + { "name": "id", "label": "ID", "width": 80 }, + { "name": "name", "label": "Name", "sortable": true }, + { "name": "email", "label": "Email", "sortable": true }, + { "name": "role", "label": "Role", "filterable": true }, + { "name": "createdAt", "label": "Created", "type": "date", "sortable": true } + ], + "rowActions": [ + { "label": "Edit", "onClick": { "type": "navigate", "url": "/users/${id}/edit" } }, + { "label": "Delete", "onClick": { "type": "api", "api": "/api/users/${id}", "method": "DELETE", "confirm": "Delete this user?" } } + ], + "pagination": true, + "pageSize": 20, + "searchable": true +} +``` + +**Example - CRUD Table:** + +```json +{ + "type": "table", + "api": "/api/products", + "columns": [ + { "name": "name", "label": "Product Name" }, + { "name": "price", "label": "Price", "type": "number" }, + { "name": "stock", "label": "Stock", "type": "number" }, + { "name": "active", "label": "Active", "type": "boolean" } + ], + "filters": [ + { "name": "category", "label": "Category", "type": "select", "api": "/api/categories" }, + { "name": "priceRange", "label": "Price Range", "type": "input" } + ], + "selectable": true, + "batchActions": [ + { "label": "Delete Selected", "onClick": { "type": "api", "api": "/api/products/batch-delete", "method": "POST" } }, + { "label": "Export", "onClick": { "type": "download", "url": "/api/products/export" } } + ], + "rowActions": [ + { "label": "Edit", "onClick": { "type": "dialog", "title": "Edit Product", "body": { "type": "form", "fields": [...] } } }, + { "label": "Delete", "onClick": { "type": "api", "api": "/api/products/${id}", "method": "DELETE" } } + ] +} +``` + +## Actions + +Actions define interactive behaviors in ObjectUI. + +### API Request + +Make HTTP requests. + +**Protocol:** + +```typescript +{ + type: 'api', + api: string, // API endpoint + method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', + data?: object | string, // Request body (template supported) + headers?: object, // Request headers + confirm?: string, // Confirmation message + onSuccess?: Action, // Success handler + onError?: Action, // Error handler +} +``` + +**Example:** + +```json +{ + "type": "button", + "label": "Save", + "onClick": { + "type": "api", + "api": "/api/users/${userId}", + "method": "PUT", + "data": "${formData}", + "confirm": "Save changes?", + "onSuccess": { + "type": "toast", + "message": "Saved successfully!" + } + } +} +``` + +### Navigate + +Navigate to a different page. + +**Protocol:** + +```typescript +{ + type: 'navigate', + url: string, // Target URL (template supported) + newTab?: boolean, // Open in new tab + replace?: boolean, // Replace current history entry +} +``` + +**Example:** + +```json +{ + "type": "button", + "label": "View Details", + "onClick": { + "type": "navigate", + "url": "/users/${userId}" + } +} +``` + +### Dialog + +Open a modal dialog. + +**Protocol:** + +```typescript +{ + type: 'dialog', + title?: string, + width?: number | string, + body: Component, + footer?: Component[], + closable?: boolean, +} +``` + +**Example:** + +```json +{ + "type": "button", + "label": "Add User", + "onClick": { + "type": "dialog", + "title": "Create New User", + "width": 600, + "body": { + "type": "form", + "api": "/api/users", + "fields": [...] + } + } +} +``` + +### Toast + +Show notification message. + +**Protocol:** + +```typescript +{ + type: 'toast', + message: string, // Message text (template supported) + variant?: 'success' | 'error' | 'warning' | 'info', + duration?: number, // Auto-close duration (ms) +} +``` + +**Example:** + +```json +{ + "type": "toast", + "message": "Operation completed successfully!", + "variant": "success", + "duration": 3000 +} +``` + +## Complete Examples + +### User Management CRUD + +```json +{ + "type": "page", + "title": "User Management", + "actions": [ + { + "label": "Add User", + "onClick": { + "type": "dialog", + "title": "Create User", + "body": { + "type": "form", + "api": "/api/users", + "method": "POST", + "fields": [ + { "type": "input", "name": "name", "label": "Name", "required": true }, + { "type": "input", "name": "email", "label": "Email", "inputType": "email", "required": true }, + { "type": "select", "name": "role", "label": "Role", "options": [ + { "label": "Admin", "value": "admin" }, + { "label": "User", "value": "user" } + ]} + ], + "onSuccess": [ + { "type": "toast", "message": "User created!" }, + { "type": "reload" } + ] + } + } + } + ], + "body": { + "type": "table", + "api": "/api/users", + "columns": [ + { "name": "name", "label": "Name", "sortable": true }, + { "name": "email", "label": "Email" }, + { "name": "role", "label": "Role" }, + { "name": "createdAt", "label": "Created", "type": "date" } + ], + "rowActions": [ + { + "label": "Edit", + "onClick": { + "type": "dialog", + "title": "Edit User", + "body": { + "type": "form", + "api": "/api/users/${id}", + "method": "PUT", + "initialValues": "${row}", + "fields": [ + { "type": "input", "name": "name", "label": "Name" }, + { "type": "input", "name": "email", "label": "Email" } + ] + } + } + }, + { + "label": "Delete", + "onClick": { + "type": "api", + "api": "/api/users/${id}", + "method": "DELETE", + "confirm": "Delete this user?", + "onSuccess": [ + { "type": "toast", "message": "User deleted" }, + { "type": "reload" } + ] + } + } + ], + "searchable": true, + "pagination": true + } +} +``` + +## Next Steps + +- **[Renderer Usage](./renderer-usage)** - Learn how to integrate these components in your application diff --git a/content/docs/03-objectui/component-spec.zh-CN.mdx b/content/docs/03-objectui/component-spec.zh-CN.mdx new file mode 100644 index 0000000..4b2ef46 --- /dev/null +++ b/content/docs/03-objectui/component-spec.zh-CN.mdx @@ -0,0 +1,787 @@ +--- +title: 组件规范 +description: 所有 ObjectUI 组件的完整协议参考 +--- + +# 组件规范 + +这份全面的参考文档记录了所有 ObjectUI 组件及其 JSON 协议。 + +## 基础组件 + +基础组件是表单和数据输入的基本构建块。 + +### Input 输入框 + +单行文本输入字段。 + +**协议:** + +```typescript +{ + type: 'input', + name: string, // 字段名 + label?: string, // 显示标签 + placeholder?: string, // 占位符文本 + defaultValue?: string, // 默认值 + required?: boolean, // 必填验证 + disabled?: boolean, // 禁用状态 + maxLength?: number, // 最大长度 + minLength?: number, // 最小长度 + pattern?: string, // 正则表达式 + inputType?: 'text' | 'email' | 'password' | 'url' | 'tel' | 'number', + onChange?: Action, // 变化事件处理器 +} +``` + +**示例:** + +```json +{ + "type": "input", + "name": "email", + "label": "邮箱地址", + "placeholder": "user@example.com", + "inputType": "email", + "required": true, + "maxLength": 255 +} +``` + +**高级示例 - 依赖字段:** + +```json +{ + "type": "form", + "fields": [ + { + "type": "input", + "name": "username", + "label": "用户名", + "required": true, + "onChange": { + "type": "api", + "api": "/api/check-username", + "method": "POST", + "data": { "username": "${value}" } + } + } + ] +} +``` + +### Select 下拉选择 + +下拉选择字段。 + +**协议:** + +```typescript +{ + type: 'select', + name: string, + label?: string, + placeholder?: string, + defaultValue?: string | number, + required?: boolean, + disabled?: boolean, + multiple?: boolean, // 允许多选 + searchable?: boolean, // 启用搜索/过滤 + clearable?: boolean, // 显示清除按钮 + options?: Array<{ // 静态选项 + label: string, + value: string | number, + disabled?: boolean + }>, + api?: string, // 动态选项的 API 端点 + optionLabel?: string, // 选项标签的字段名 + optionValue?: string, // 选项值的字段名 + dependsOn?: string, // 字段依赖 + onChange?: Action, +} +``` + +**示例 - 静态选项:** + +```json +{ + "type": "select", + "name": "status", + "label": "状态", + "placeholder": "选择状态", + "required": true, + "options": [ + { "label": "激活", "value": "active" }, + { "label": "未激活", "value": "inactive" }, + { "label": "待审核", "value": "pending" } + ] +} +``` + +**示例 - 从 API 获取动态选项:** + +```json +{ + "type": "select", + "name": "category", + "label": "分类", + "api": "/api/categories", + "optionLabel": "name", + "optionValue": "id", + "searchable": true +} +``` + +**示例 - 级联选择:** + +```json +{ + "type": "form", + "fields": [ + { + "type": "select", + "name": "country", + "label": "国家", + "api": "/api/countries" + }, + { + "type": "select", + "name": "state", + "label": "省/州", + "api": "/api/states?country=${country}", + "dependsOn": "country" + } + ] +} +``` + +### DatePicker 日期选择器 + +日期和时间选择组件。 + +**协议:** + +```typescript +{ + type: 'datepicker', + name: string, + label?: string, + placeholder?: string, + defaultValue?: string | Date, + required?: boolean, + disabled?: boolean, + mode?: 'date' | 'datetime' | 'time' | 'daterange', + format?: string, // 显示格式 (例如 'YYYY-MM-DD') + minDate?: string | Date, // 最小可选日期 + maxDate?: string | Date, // 最大可选日期 + disabledDates?: string[], // 禁用日期数组 + showTime?: boolean, // 显示时间选择器 + onChange?: Action, +} +``` + +**示例 - 简单日期:** + +```json +{ + "type": "datepicker", + "name": "birthday", + "label": "出生日期", + "mode": "date", + "maxDate": "today", + "required": true +} +``` + +**示例 - 日期时间:** + +```json +{ + "type": "datepicker", + "name": "appointment", + "label": "预约时间", + "mode": "datetime", + "format": "YYYY-MM-DD HH:mm", + "minDate": "today", + "showTime": true +} +``` + +**示例 - 日期范围:** + +```json +{ + "type": "datepicker", + "name": "dateRange", + "label": "选择时间段", + "mode": "daterange", + "defaultValue": { + "start": "2024-01-01", + "end": "2024-12-31" + } +} +``` + +### Switch 开关 + +布尔值的切换开关。 + +**协议:** + +```typescript +{ + type: 'switch', + name: string, + label?: string, + defaultValue?: boolean, + disabled?: boolean, + checkedText?: string, // 选中时的文本 + uncheckedText?: string, // 未选中时的文本 + onChange?: Action, +} +``` + +**示例:** + +```json +{ + "type": "switch", + "name": "isActive", + "label": "激活状态", + "defaultValue": true, + "checkedText": "已激活", + "uncheckedText": "未激活" +} +``` + +## 容器组件 + +容器组件用于组织和构建 UI 布局。 + +### Page 页面 + +顶级页面容器,带有标题和操作。 + +**协议:** + +```typescript +{ + type: 'page', + title: string, + description?: string, + icon?: string, + breadcrumbs?: Array<{ + label: string, + url?: string + }>, + actions?: Action[], // 页头操作 + tabs?: Array<{ + label: string, + key: string, + body: Component + }>, + body: Component | Component[], +} +``` + +**示例:** + +```json +{ + "type": "page", + "title": "用户管理", + "description": "管理系统用户和权限", + "breadcrumbs": [ + { "label": "首页", "url": "/" }, + { "label": "用户" } + ], + "actions": [ + { + "label": "添加用户", + "type": "dialog", + "body": { + "type": "form", + "api": "/api/users", + "fields": [...] + } + } + ], + "body": { + "type": "table", + "api": "/api/users" + } +} +``` + +### Grid 网格 + +响应式网格布局。 + +**协议:** + +```typescript +{ + type: 'grid', + columns?: number | { // 列配置 + xs?: number, // 超小设备 + sm?: number, // 小设备 + md?: number, // 中等设备 + lg?: number, // 大设备 + xl?: number // 超大设备 + }, + gap?: number | string, // 项目间距 + items: Component[], +} +``` + +**示例:** + +```json +{ + "type": "grid", + "columns": { "xs": 1, "sm": 2, "lg": 3 }, + "gap": "20px", + "items": [ + { + "type": "card", + "title": "总用户数", + "body": { "type": "text", "value": "${stats.users}" } + }, + { + "type": "card", + "title": "总订单数", + "body": { "type": "text", "value": "${stats.orders}" } + }, + { + "type": "card", + "title": "收入", + "body": { "type": "text", "value": "¥${stats.revenue}" } + } + ] +} +``` + +### Card 卡片 + +带有标题、主体和页脚的卡片容器。 + +**协议:** + +```typescript +{ + type: 'card', + title?: string, + description?: string, + cover?: string, // 封面图片 URL + avatar?: string, // 头像图片 URL + actions?: Action[], + body: Component | Component[], + footer?: Component | Component[], +} +``` + +**示例:** + +```json +{ + "type": "card", + "title": "用户资料", + "avatar": "${user.avatar}", + "body": [ + { "type": "text", "value": "姓名: ${user.name}" }, + { "type": "text", "value": "邮箱: ${user.email}" }, + { "type": "text", "value": "角色: ${user.role}" } + ], + "actions": [ + { "label": "编辑", "onClick": { "type": "navigate", "url": "/users/${user.id}/edit" } }, + { "label": "删除", "onClick": { "type": "api", "api": "/api/users/${user.id}", "method": "DELETE" } } + ] +} +``` + +### Form 表单 + +带有字段和提交处理的表单容器。 + +**协议:** + +```typescript +{ + type: 'form', + title?: string, + api?: string, // 提交端点 + method?: 'POST' | 'PUT' | 'PATCH', + initialValues?: object, // 初始表单值 + fields: FormField[], + layout?: 'horizontal' | 'vertical' | 'inline', + columns?: number, // 多列布局 + submitText?: string, // 提交按钮文本 + resetText?: string, // 重置按钮文本 + showReset?: boolean, // 显示重置按钮 + onSubmit?: Action, + onSuccess?: Action, + onError?: Action, +} +``` + +**示例 - 简单表单:** + +```json +{ + "type": "form", + "title": "创建用户", + "api": "/api/users", + "method": "POST", + "fields": [ + { + "type": "input", + "name": "name", + "label": "全名", + "required": true + }, + { + "type": "input", + "name": "email", + "label": "邮箱", + "inputType": "email", + "required": true + }, + { + "type": "select", + "name": "role", + "label": "角色", + "options": [ + { "label": "管理员", "value": "admin" }, + { "label": "用户", "value": "user" } + ] + } + ], + "submitText": "创建用户", + "onSuccess": { + "type": "toast", + "message": "用户创建成功!" + } +} +``` + +**示例 - 多列表单:** + +```json +{ + "type": "form", + "title": "用户注册", + "columns": 2, + "fields": [ + { "type": "input", "name": "firstName", "label": "名" }, + { "type": "input", "name": "lastName", "label": "姓" }, + { "type": "input", "name": "email", "label": "邮箱", "inputType": "email" }, + { "type": "input", "name": "phone", "label": "电话", "inputType": "tel" }, + { "type": "datepicker", "name": "birthday", "label": "生日" }, + { "type": "select", "name": "country", "label": "国家", "api": "/api/countries" } + ] +} +``` + +### Table 表格 + +带有排序、筛选和分页的数据表格。 + +**协议:** + +```typescript +{ + type: 'table', + api?: string, // 数据端点 + data?: any[], // 静态数据 + columns: Array<{ + name: string, + label: string, + type?: 'text' | 'number' | 'boolean' | 'date' | 'image', + sortable?: boolean, + filterable?: boolean, + width?: number | string, + render?: string, // 自定义渲染模板 + }>, + rowActions?: Action[], // 每行操作 + batchActions?: Action[], // 批量操作 + pagination?: boolean, + pageSize?: number, + searchable?: boolean, // 全局搜索 + filters?: Array<{ + name: string, + label: string, + type: 'input' | 'select' | 'datepicker' + }>, + selectable?: boolean, // 行选择 + expandable?: Component, // 可展开行内容 +} +``` + +**示例 - 基础表格:** + +```json +{ + "type": "table", + "api": "/api/users", + "columns": [ + { "name": "id", "label": "ID", "width": 80 }, + { "name": "name", "label": "姓名", "sortable": true }, + { "name": "email", "label": "邮箱", "sortable": true }, + { "name": "role", "label": "角色", "filterable": true }, + { "name": "createdAt", "label": "创建时间", "type": "date", "sortable": true } + ], + "rowActions": [ + { "label": "编辑", "onClick": { "type": "navigate", "url": "/users/${id}/edit" } }, + { "label": "删除", "onClick": { "type": "api", "api": "/api/users/${id}", "method": "DELETE", "confirm": "删除此用户?" } } + ], + "pagination": true, + "pageSize": 20, + "searchable": true +} +``` + +**示例 - CRUD 表格:** + +```json +{ + "type": "table", + "api": "/api/products", + "columns": [ + { "name": "name", "label": "产品名称" }, + { "name": "price", "label": "价格", "type": "number" }, + { "name": "stock", "label": "库存", "type": "number" }, + { "name": "active", "label": "激活", "type": "boolean" } + ], + "filters": [ + { "name": "category", "label": "分类", "type": "select", "api": "/api/categories" }, + { "name": "priceRange", "label": "价格区间", "type": "input" } + ], + "selectable": true, + "batchActions": [ + { "label": "批量删除", "onClick": { "type": "api", "api": "/api/products/batch-delete", "method": "POST" } }, + { "label": "导出", "onClick": { "type": "download", "url": "/api/products/export" } } + ], + "rowActions": [ + { "label": "编辑", "onClick": { "type": "dialog", "title": "编辑产品", "body": { "type": "form", "fields": [...] } } }, + { "label": "删除", "onClick": { "type": "api", "api": "/api/products/${id}", "method": "DELETE" } } + ] +} +``` + +## 操作(Actions) + +操作定义 ObjectUI 中的交互行为。 + +### API 请求 + +发起 HTTP 请求。 + +**协议:** + +```typescript +{ + type: 'api', + api: string, // API 端点 + method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', + data?: object | string, // 请求体 (支持模板) + headers?: object, // 请求头 + confirm?: string, // 确认消息 + onSuccess?: Action, // 成功处理器 + onError?: Action, // 错误处理器 +} +``` + +**示例:** + +```json +{ + "type": "button", + "label": "保存", + "onClick": { + "type": "api", + "api": "/api/users/${userId}", + "method": "PUT", + "data": "${formData}", + "confirm": "保存更改?", + "onSuccess": { + "type": "toast", + "message": "保存成功!" + } + } +} +``` + +### 导航 + +导航到不同页面。 + +**协议:** + +```typescript +{ + type: 'navigate', + url: string, // 目标 URL (支持模板) + newTab?: boolean, // 在新标签页打开 + replace?: boolean, // 替换当前历史记录 +} +``` + +**示例:** + +```json +{ + "type": "button", + "label": "查看详情", + "onClick": { + "type": "navigate", + "url": "/users/${userId}" + } +} +``` + +### 对话框 + +打开模态对话框。 + +**协议:** + +```typescript +{ + type: 'dialog', + title?: string, + width?: number | string, + body: Component, + footer?: Component[], + closable?: boolean, +} +``` + +**示例:** + +```json +{ + "type": "button", + "label": "添加用户", + "onClick": { + "type": "dialog", + "title": "创建新用户", + "width": 600, + "body": { + "type": "form", + "api": "/api/users", + "fields": [...] + } + } +} +``` + +### 提示消息 + +显示通知消息。 + +**协议:** + +```typescript +{ + type: 'toast', + message: string, // 消息文本 (支持模板) + variant?: 'success' | 'error' | 'warning' | 'info', + duration?: number, // 自动关闭时长 (毫秒) +} +``` + +**示例:** + +```json +{ + "type": "toast", + "message": "操作成功完成!", + "variant": "success", + "duration": 3000 +} +``` + +## 完整示例 + +### 用户管理 CRUD + +```json +{ + "type": "page", + "title": "用户管理", + "actions": [ + { + "label": "添加用户", + "onClick": { + "type": "dialog", + "title": "创建用户", + "body": { + "type": "form", + "api": "/api/users", + "method": "POST", + "fields": [ + { "type": "input", "name": "name", "label": "姓名", "required": true }, + { "type": "input", "name": "email", "label": "邮箱", "inputType": "email", "required": true }, + { "type": "select", "name": "role", "label": "角色", "options": [ + { "label": "管理员", "value": "admin" }, + { "label": "用户", "value": "user" } + ]} + ], + "onSuccess": [ + { "type": "toast", "message": "用户已创建!" }, + { "type": "reload" } + ] + } + } + } + ], + "body": { + "type": "table", + "api": "/api/users", + "columns": [ + { "name": "name", "label": "姓名", "sortable": true }, + { "name": "email", "label": "邮箱" }, + { "name": "role", "label": "角色" }, + { "name": "createdAt", "label": "创建时间", "type": "date" } + ], + "rowActions": [ + { + "label": "编辑", + "onClick": { + "type": "dialog", + "title": "编辑用户", + "body": { + "type": "form", + "api": "/api/users/${id}", + "method": "PUT", + "initialValues": "${row}", + "fields": [ + { "type": "input", "name": "name", "label": "姓名" }, + { "type": "input", "name": "email", "label": "邮箱" } + ] + } + } + }, + { + "label": "删除", + "onClick": { + "type": "api", + "api": "/api/users/${id}", + "method": "DELETE", + "confirm": "删除此用户?", + "onSuccess": [ + { "type": "toast", "message": "用户已删除" }, + { "type": "reload" } + ] + } + } + ], + "searchable": true, + "pagination": true + } +} +``` + +## 下一步 + +- **[渲染器使用](./renderer-usage)** - 了解如何在应用中集成这些组件 diff --git a/content/docs/03-objectui/core-concepts.en.mdx b/content/docs/03-objectui/core-concepts.en.mdx new file mode 100644 index 0000000..c742fbb --- /dev/null +++ b/content/docs/03-objectui/core-concepts.en.mdx @@ -0,0 +1,617 @@ +--- +title: Core Concepts +description: Understanding declarative UI, data-driven rendering, and ObjectUI principles +--- + +# Core Concepts + +This guide explains the fundamental concepts that power ObjectUI's declarative and data-driven architecture. + +## Declarative UI vs Imperative UI + +Understanding the difference between declarative and imperative approaches is key to mastering ObjectUI. + +### Imperative UI + +In traditional imperative UI development, you write code that describes **how** to build and update the interface: + +```tsx +// Imperative approach - describes HOW +function UserProfile() { + const [user, setUser] = useState(null) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + fetch('/api/user') + .then(res => res.json()) + .then(data => { + setUser(data) + setLoading(false) + }) + .catch(err => { + setError(err.message) + setLoading(false) + }) + }, []) + + if (loading) return
Loading...
+ if (error) return
Error: {error}
+ + return ( +
+

{user.name}

+

{user.email}

+ +
+ ) +} +``` + +**Characteristics:** +- You manage state (`useState`, `useEffect`) +- You handle data fetching manually +- You write event handlers +- You control rendering logic +- More code, more maintenance + +### Declarative UI + +In ObjectUI's declarative approach, you describe **what** you want, not how to build it: + +```json +{ + "type": "card", + "title": "${name}", + "api": "/api/user", + "body": [ + { "type": "text", "value": "Email: ${email}" } + ], + "actions": [ + { + "label": "Delete User", + "api": "/api/user", + "method": "DELETE", + "confirm": "Are you sure?", + "onSuccess": { "type": "redirect", "url": "/users" } + } + ] +} +``` + +**Characteristics:** +- No state management code +- Automatic data fetching +- Declarative actions +- Built-in error handling +- Less code, easier maintenance + +### Benefits of Declarative UI + +1. **Simplicity** - Describe what you want, not how to build it +2. **Consistency** - All components follow the same patterns +3. **Maintainability** - Less code means fewer bugs +4. **Flexibility** - UI can be modified without code changes +5. **Portability** - JSON protocols can be stored, versioned, and shared +6. **Dynamic Generation** - UI can be generated programmatically or from database + +## Data-Driven Rendering + +ObjectUI components are **data-driven**, meaning the UI automatically reflects the underlying data. + +### The Data Flow + +``` +┌──────────────┐ +│ Protocol │ → Defines structure and behavior +│ (JSON) │ +└──────┬───────┘ + │ + ▼ +┌──────────────┐ +│ Data │ → Provides content +│ (API/Props)│ +└──────┬───────┘ + │ + ▼ +┌──────────────┐ +│ Rendered UI │ → Automatic rendering +│ │ +└──────────────┘ +``` + +### Data Sources + +ObjectUI components can receive data from multiple sources: + +#### 1. API Endpoints + +Fetch data automatically from APIs: + +```json +{ + "type": "table", + "api": "/api/users", + "columns": [ + { "name": "name", "label": "Name" }, + { "name": "email", "label": "Email" } + ] +} +``` + +The component: +- Fetches data from `/api/users` on mount +- Handles loading states +- Displays errors if request fails +- Re-fetches on parameter changes + +#### 2. Static Data + +Provide data directly in the protocol: + +```json +{ + "type": "select", + "name": "priority", + "label": "Priority", + "options": [ + { "label": "High", "value": "high" }, + { "label": "Medium", "value": "medium" }, + { "label": "Low", "value": "low" } + ] +} +``` + +#### 3. Context Data + +Access data from parent components: + +```json +{ + "type": "form", + "api": "/api/users/${userId}", + "fields": [ + { "name": "name", "type": "input" }, + { "name": "email", "type": "input" } + ] +} +``` + +`${userId}` is resolved from the context (URL params, parent state, etc.) + +### Reactive Updates + +When data changes, the UI automatically updates: + +```json +{ + "type": "card", + "api": "/api/stats", + "refresh": 5000, + "body": [ + { "type": "text", "value": "Active Users: ${activeUsers}" }, + { "type": "text", "value": "Total Orders: ${totalOrders}" } + ] +} +``` + +This card: +- Fetches initial data from `/api/stats` +- Automatically refreshes every 5 seconds +- Updates displayed values when data changes + +## Protocol Structure + +Every ObjectUI component follows a consistent protocol structure: + +### Basic Protocol + +```typescript +{ + // Component type (required) + type: 'input' | 'select' | 'table' | 'form' | 'page' | ..., + + // Common properties + name?: string, // Field name for forms + label?: string, // Display label + visible?: boolean, // Visibility control + disabled?: boolean, // Disabled state + + // Data properties + value?: any, // Static value + defaultValue?: any, // Default value + api?: string, // API endpoint for data + + // Event handlers + onChange?: Action, // Change handler + onClick?: Action, // Click handler + + // Styling + className?: string, // CSS class + style?: object, // Inline styles +} +``` + +### Component-Specific Properties + +Each component type has additional specific properties: + +```json +{ + "type": "input", + "placeholder": "Enter text...", + "maxLength": 100 +} +``` + +```json +{ + "type": "table", + "columns": [...], + "pagination": true, + "pageSize": 20 +} +``` + +## Template Expressions + +ObjectUI supports template expressions for dynamic values: + +### Simple Variable + +```json +{ + "type": "text", + "value": "Hello, ${userName}!" +} +``` + +### Nested Properties + +```json +{ + "type": "text", + "value": "${user.profile.firstName} ${user.profile.lastName}" +} +``` + +### Conditional Display + +```json +{ + "type": "badge", + "value": "${status}", + "visible": "${status === 'active'}" +} +``` + +### Array Access + +```json +{ + "type": "text", + "value": "First item: ${items[0].name}" +} +``` + +## Actions + +Actions define interactive behavior in ObjectUI: + +### Action Types + +#### 1. API Request + +Make HTTP requests: + +```json +{ + "type": "button", + "label": "Submit", + "onClick": { + "type": "api", + "api": "/api/submit", + "method": "POST", + "data": "${formData}" + } +} +``` + +#### 2. Navigation + +Navigate to different pages: + +```json +{ + "type": "button", + "label": "View Details", + "onClick": { + "type": "navigate", + "url": "/users/${id}" + } +} +``` + +#### 3. Dialog + +Open dialogs or modals: + +```json +{ + "type": "button", + "label": "Edit", + "onClick": { + "type": "dialog", + "title": "Edit User", + "body": { + "type": "form", + "fields": [...] + } + } +} +``` + +#### 4. Toast/Message + +Show notifications: + +```json +{ + "type": "button", + "label": "Save", + "onClick": { + "type": "toast", + "message": "Saved successfully!", + "variant": "success" + } +} +``` + +#### 5. Set State + +Update component state: + +```json +{ + "type": "button", + "label": "Toggle", + "onClick": { + "type": "setState", + "key": "isExpanded", + "value": "${!isExpanded}" + } +} +``` + +### Action Chains + +Execute multiple actions in sequence: + +```json +{ + "type": "button", + "label": "Delete", + "onClick": [ + { + "type": "api", + "api": "/api/users/${id}", + "method": "DELETE" + }, + { + "type": "toast", + "message": "User deleted" + }, + { + "type": "navigate", + "url": "/users" + } + ] +} +``` + +### Conditional Actions + +Execute actions based on conditions: + +```json +{ + "type": "button", + "label": "Submit", + "onClick": { + "type": "api", + "api": "/api/submit", + "method": "POST", + "onSuccess": { + "type": "toast", + "message": "Success!" + }, + "onError": { + "type": "toast", + "message": "Error: ${error}", + "variant": "error" + } + } +} +``` + +## Component Lifecycle + +ObjectUI components have a predictable lifecycle: + +### Mount + +1. **Parse protocol** - Validate and process the JSON protocol +2. **Resolve data** - Fetch data from API or use provided values +3. **Initialize state** - Set up internal component state +4. **Render** - Generate the UI + +### Update + +1. **Detect changes** - Monitor data, props, or state changes +2. **Re-evaluate expressions** - Update template expressions +3. **Re-render** - Update the UI with new values + +### Unmount + +1. **Cleanup** - Cancel pending requests, clear timers +2. **Remove** - Remove component from DOM + +## Component Composition + +Build complex UIs by composing simple components: + +### Nested Components + +```json +{ + "type": "page", + "title": "Dashboard", + "body": { + "type": "grid", + "columns": 3, + "items": [ + { + "type": "card", + "title": "Users", + "body": { + "type": "text", + "value": "${userCount}" + } + }, + { + "type": "card", + "title": "Orders", + "body": { + "type": "text", + "value": "${orderCount}" + } + }, + { + "type": "card", + "title": "Revenue", + "body": { + "type": "text", + "value": "$${revenue}" + } + } + ] + } +} +``` + +### Reusable Components + +Define reusable component templates: + +```typescript +// Define template +const userCard = (user) => ({ + type: 'card', + title: user.name, + body: [ + { type: 'text', value: `Email: ${user.email}` }, + { type: 'text', value: `Role: ${user.role}` } + ] +}) + +// Use template +const protocol = { + type: 'grid', + columns: 2, + items: users.map(userCard) +} +``` + +## Best Practices + +### 1. Keep Protocols Simple + +Prefer simple, focused components over complex nested structures: + +```json +// Good - Simple and focused +{ + "type": "form", + "api": "/api/users", + "fields": [...] +} + +// Avoid - Too complex +{ + "type": "page", + "body": { + "type": "grid", + "items": [ + { + "type": "card", + "body": { + "type": "form", + // Deeply nested... + } + } + ] + } +} +``` + +### 2. Use Template Expressions Wisely + +Keep expressions simple and readable: + +```json +// Good +{ "value": "${user.name}" } + +// Avoid - Too complex +{ "value": "${users.filter(u => u.active).map(u => u.name).join(', ')}" } +``` + +### 3. Leverage Actions + +Use actions for all interactions: + +```json +{ + "type": "button", + "label": "Delete", + "onClick": { + "type": "api", + "api": "/api/delete", + "confirm": "Are you sure?" + } +} +``` + +### 4. Provide Feedback + +Always provide user feedback for actions: + +```json +{ + "onClick": { + "type": "api", + "api": "/api/submit", + "onSuccess": { + "type": "toast", + "message": "Saved successfully!" + }, + "onError": { + "type": "toast", + "message": "Failed to save", + "variant": "error" + } + } +} +``` + +## Next Steps + +Now that you understand the core concepts, explore: + +- **[Component Spec](./component-spec)** - Complete component protocol reference +- **[Renderer Usage](./renderer-usage)** - Integrate ObjectUI in your application diff --git a/content/docs/03-objectui/core-concepts.zh-CN.mdx b/content/docs/03-objectui/core-concepts.zh-CN.mdx new file mode 100644 index 0000000..e5b771e --- /dev/null +++ b/content/docs/03-objectui/core-concepts.zh-CN.mdx @@ -0,0 +1,617 @@ +--- +title: 核心概念 +description: 理解声明式 UI、数据驱动渲染和 ObjectUI 原则 +--- + +# 核心概念 + +本指南解释了支撑 ObjectUI 声明式和数据驱动架构的基本概念。 + +## 声明式 UI vs 命令式 UI + +理解声明式和命令式方法之间的区别是掌握 ObjectUI 的关键。 + +### 命令式 UI + +在传统的命令式 UI 开发中,您编写代码描述**如何**构建和更新界面: + +```tsx +// 命令式方法 - 描述如何做 +function UserProfile() { + const [user, setUser] = useState(null) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + fetch('/api/user') + .then(res => res.json()) + .then(data => { + setUser(data) + setLoading(false) + }) + .catch(err => { + setError(err.message) + setLoading(false) + }) + }, []) + + if (loading) return
加载中...
+ if (error) return
错误: {error}
+ + return ( +
+

{user.name}

+

{user.email}

+ +
+ ) +} +``` + +**特点:** +- 您管理状态 (`useState`, `useEffect`) +- 您手动处理数据获取 +- 您编写事件处理器 +- 您控制渲染逻辑 +- 更多代码,更多维护 + +### 声明式 UI + +在 ObjectUI 的声明式方法中,您描述**想要什么**,而不是如何构建: + +```json +{ + "type": "card", + "title": "${name}", + "api": "/api/user", + "body": [ + { "type": "text", "value": "邮箱: ${email}" } + ], + "actions": [ + { + "label": "删除用户", + "api": "/api/user", + "method": "DELETE", + "confirm": "确定要删除吗?", + "onSuccess": { "type": "redirect", "url": "/users" } + } + ] +} +``` + +**特点:** +- 无状态管理代码 +- 自动数据获取 +- 声明式操作 +- 内置错误处理 +- 更少代码,更易维护 + +### 声明式 UI 的优势 + +1. **简单性** - 描述想要什么,而不是如何构建 +2. **一致性** - 所有组件遵循相同的模式 +3. **可维护性** - 更少的代码意味着更少的 bug +4. **灵活性** - UI 可以在不改变代码的情况下修改 +5. **可移植性** - JSON 协议可以存储、版本化和共享 +6. **动态生成** - UI 可以通过编程或从数据库生成 + +## 数据驱动渲染 + +ObjectUI 组件是**数据驱动**的,这意味着 UI 自动反映底层数据。 + +### 数据流 + +``` +┌──────────────┐ +│ 协议 │ → 定义结构和行为 +│ (JSON) │ +└──────┬───────┘ + │ + ▼ +┌──────────────┐ +│ 数据 │ → 提供内容 +│ (API/Props) │ +└──────┬───────┘ + │ + ▼ +┌──────────────┐ +│ 渲染的 UI │ → 自动渲染 +│ │ +└──────────────┘ +``` + +### 数据源 + +ObjectUI 组件可以从多个源接收数据: + +#### 1. API 端点 + +自动从 API 获取数据: + +```json +{ + "type": "table", + "api": "/api/users", + "columns": [ + { "name": "name", "label": "姓名" }, + { "name": "email", "label": "邮箱" } + ] +} +``` + +组件会: +- 挂载时从 `/api/users` 获取数据 +- 处理加载状态 +- 请求失败时显示错误 +- 参数变化时重新获取 + +#### 2. 静态数据 + +直接在协议中提供数据: + +```json +{ + "type": "select", + "name": "priority", + "label": "优先级", + "options": [ + { "label": "高", "value": "high" }, + { "label": "中", "value": "medium" }, + { "label": "低", "value": "low" } + ] +} +``` + +#### 3. 上下文数据 + +从父组件访问数据: + +```json +{ + "type": "form", + "api": "/api/users/${userId}", + "fields": [ + { "name": "name", "type": "input" }, + { "name": "email", "type": "input" } + ] +} +``` + +`${userId}` 从上下文中解析(URL 参数、父状态等) + +### 响应式更新 + +当数据变化时,UI 自动更新: + +```json +{ + "type": "card", + "api": "/api/stats", + "refresh": 5000, + "body": [ + { "type": "text", "value": "活跃用户: ${activeUsers}" }, + { "type": "text", "value": "总订单: ${totalOrders}" } + ] +} +``` + +这个卡片会: +- 从 `/api/stats` 获取初始数据 +- 每 5 秒自动刷新 +- 数据变化时更新显示值 + +## 协议结构 + +每个 ObjectUI 组件都遵循一致的协议结构: + +### 基本协议 + +```typescript +{ + // 组件类型 (必需) + type: 'input' | 'select' | 'table' | 'form' | 'page' | ..., + + // 通用属性 + name?: string, // 表单字段名 + label?: string, // 显示标签 + visible?: boolean, // 可见性控制 + disabled?: boolean, // 禁用状态 + + // 数据属性 + value?: any, // 静态值 + defaultValue?: any, // 默认值 + api?: string, // 数据的 API 端点 + + // 事件处理器 + onChange?: Action, // 变化处理器 + onClick?: Action, // 点击处理器 + + // 样式 + className?: string, // CSS 类 + style?: object, // 内联样式 +} +``` + +### 组件特定属性 + +每种组件类型都有额外的特定属性: + +```json +{ + "type": "input", + "placeholder": "输入文本...", + "maxLength": 100 +} +``` + +```json +{ + "type": "table", + "columns": [...], + "pagination": true, + "pageSize": 20 +} +``` + +## 模板表达式 + +ObjectUI 支持动态值的模板表达式: + +### 简单变量 + +```json +{ + "type": "text", + "value": "你好, ${userName}!" +} +``` + +### 嵌套属性 + +```json +{ + "type": "text", + "value": "${user.profile.firstName} ${user.profile.lastName}" +} +``` + +### 条件显示 + +```json +{ + "type": "badge", + "value": "${status}", + "visible": "${status === 'active'}" +} +``` + +### 数组访问 + +```json +{ + "type": "text", + "value": "第一项: ${items[0].name}" +} +``` + +## 操作(Actions) + +操作定义 ObjectUI 中的交互行为: + +### 操作类型 + +#### 1. API 请求 + +发起 HTTP 请求: + +```json +{ + "type": "button", + "label": "提交", + "onClick": { + "type": "api", + "api": "/api/submit", + "method": "POST", + "data": "${formData}" + } +} +``` + +#### 2. 导航 + +导航到不同页面: + +```json +{ + "type": "button", + "label": "查看详情", + "onClick": { + "type": "navigate", + "url": "/users/${id}" + } +} +``` + +#### 3. 对话框 + +打开对话框或模态框: + +```json +{ + "type": "button", + "label": "编辑", + "onClick": { + "type": "dialog", + "title": "编辑用户", + "body": { + "type": "form", + "fields": [...] + } + } +} +``` + +#### 4. 提示/消息 + +显示通知: + +```json +{ + "type": "button", + "label": "保存", + "onClick": { + "type": "toast", + "message": "保存成功!", + "variant": "success" + } +} +``` + +#### 5. 设置状态 + +更新组件状态: + +```json +{ + "type": "button", + "label": "切换", + "onClick": { + "type": "setState", + "key": "isExpanded", + "value": "${!isExpanded}" + } +} +``` + +### 操作链 + +按顺序执行多个操作: + +```json +{ + "type": "button", + "label": "删除", + "onClick": [ + { + "type": "api", + "api": "/api/users/${id}", + "method": "DELETE" + }, + { + "type": "toast", + "message": "用户已删除" + }, + { + "type": "navigate", + "url": "/users" + } + ] +} +``` + +### 条件操作 + +基于条件执行操作: + +```json +{ + "type": "button", + "label": "提交", + "onClick": { + "type": "api", + "api": "/api/submit", + "method": "POST", + "onSuccess": { + "type": "toast", + "message": "成功!" + }, + "onError": { + "type": "toast", + "message": "错误: ${error}", + "variant": "error" + } + } +} +``` + +## 组件生命周期 + +ObjectUI 组件有可预测的生命周期: + +### 挂载 + +1. **解析协议** - 验证和处理 JSON 协议 +2. **解析数据** - 从 API 获取数据或使用提供的值 +3. **初始化状态** - 设置内部组件状态 +4. **渲染** - 生成 UI + +### 更新 + +1. **检测变化** - 监控数据、props 或状态变化 +2. **重新评估表达式** - 更新模板表达式 +3. **重新渲染** - 用新值更新 UI + +### 卸载 + +1. **清理** - 取消待处理的请求,清除计时器 +2. **移除** - 从 DOM 移除组件 + +## 组件组合 + +通过组合简单组件构建复杂 UI: + +### 嵌套组件 + +```json +{ + "type": "page", + "title": "仪表盘", + "body": { + "type": "grid", + "columns": 3, + "items": [ + { + "type": "card", + "title": "用户", + "body": { + "type": "text", + "value": "${userCount}" + } + }, + { + "type": "card", + "title": "订单", + "body": { + "type": "text", + "value": "${orderCount}" + } + }, + { + "type": "card", + "title": "收入", + "body": { + "type": "text", + "value": "¥${revenue}" + } + } + ] + } +} +``` + +### 可复用组件 + +定义可复用的组件模板: + +```typescript +// 定义模板 +const userCard = (user) => ({ + type: 'card', + title: user.name, + body: [ + { type: 'text', value: `邮箱: ${user.email}` }, + { type: 'text', value: `角色: ${user.role}` } + ] +}) + +// 使用模板 +const protocol = { + type: 'grid', + columns: 2, + items: users.map(userCard) +} +``` + +## 最佳实践 + +### 1. 保持协议简单 + +偏好简单、专注的组件而不是复杂的嵌套结构: + +```json +// 好 - 简单专注 +{ + "type": "form", + "api": "/api/users", + "fields": [...] +} + +// 避免 - 太复杂 +{ + "type": "page", + "body": { + "type": "grid", + "items": [ + { + "type": "card", + "body": { + "type": "form", + // 深度嵌套... + } + } + ] + } +} +``` + +### 2. 明智使用模板表达式 + +保持表达式简单可读: + +```json +// 好 +{ "value": "${user.name}" } + +// 避免 - 太复杂 +{ "value": "${users.filter(u => u.active).map(u => u.name).join(', ')}" } +``` + +### 3. 利用操作 + +对所有交互使用操作: + +```json +{ + "type": "button", + "label": "删除", + "onClick": { + "type": "api", + "api": "/api/delete", + "confirm": "确定吗?" + } +} +``` + +### 4. 提供反馈 + +始终为操作提供用户反馈: + +```json +{ + "onClick": { + "type": "api", + "api": "/api/submit", + "onSuccess": { + "type": "toast", + "message": "保存成功!" + }, + "onError": { + "type": "toast", + "message": "保存失败", + "variant": "error" + } + } +} +``` + +## 下一步 + +现在您已经理解了核心概念,继续探索: + +- **[组件规范](./component-spec)** - 完整的组件协议参考 +- **[渲染器使用](./renderer-usage)** - 在应用中集成 ObjectUI diff --git a/content/docs/03-objectui/index.en.mdx b/content/docs/03-objectui/index.en.mdx new file mode 100644 index 0000000..962c230 --- /dev/null +++ b/content/docs/03-objectui/index.en.mdx @@ -0,0 +1,335 @@ +--- +title: ObjectUI - The Interface Engine +description: Declarative UI engine with data-driven rendering and protocol-based components +--- + +# ObjectUI - The Interface Engine + +ObjectUI is the presentation layer of ObjectStack, providing a declarative UI engine that renders interfaces from JSON protocols. It transforms UI development from imperative code to data-driven declarations. + +## What is ObjectUI? + +ObjectUI is a **declarative UI engine** that allows you to define user interfaces as JSON protocols instead of writing component code. It provides a complete set of pre-built components that render automatically based on data specifications. + +### Key Capabilities + +- 🎨 **Declarative UI** - Define interfaces with JSON instead of JSX/code +- 🔄 **Data-Driven Rendering** - UI automatically reflects data changes +- 📦 **Component Protocol** - Rich set of pre-built components +- 🎭 **Theme Support** - Customizable styles and themes +- ⚡ **Performance Optimized** - Smart rendering and updates +- 🔌 **Framework Agnostic** - React renderer with others planned + +## How It Works + +ObjectUI operates on a simple principle: **UI as Data**. + +### 1. Define UI Protocol + +Describe your interface using JSON: + +```json +{ + "type": "page", + "title": "Todo List", + "body": { + "type": "form", + "fields": [ + { + "name": "title", + "type": "input", + "label": "Task Name", + "required": true + }, + { + "name": "priority", + "type": "select", + "label": "Priority", + "options": ["High", "Medium", "Low"] + } + ] + } +} +``` + +### 2. Render with ObjectUI + +Pass the protocol to the renderer: + +```tsx +import { ObjectUIRenderer } from '@objectstack/react-renderer' + +function App() { + return +} +``` + +### 3. Automatic UI Generation + +ObjectUI renders a fully functional interface with forms, validation, actions, and state management—all from the JSON protocol. + +## Architecture Overview + +``` +┌─────────────────────────────────────────┐ +│ Application Layer │ +│ (Business Logic) │ +└────────────────┬────────────────────────┘ + │ + │ UI Protocol (JSON) + │ +┌────────────────▼────────────────────────┐ +│ ObjectUI Engine │ +│ ┌────────────┬──────────────────────┐ │ +│ │ Protocol │ Component │ │ +│ │ Parser │ Registry │ │ +│ └────────────┴──────────────────────┘ │ +│ ┌──────────────────────────────────┐ │ +│ │ Render Coordinator │ │ +│ │ (State & Actions) │ │ +│ └──────────────────────────────────┘ │ +└────────────────┬────────────────────────┘ + │ + │ Component Instances + │ +┌────────────────▼────────────────────────┐ +│ React Renderer │ +│ ┌──────┬──────┬──────┬──────┬──────┐ │ +│ │Input │Select│Table │Form │Page │ │ +│ │ │ │ │ │ │ │ +│ └──────┴──────┴──────┴──────┴──────┘ │ +└─────────────────────────────────────────┘ +``` + +## Core Concepts + +### Declarative vs Imperative + +**Imperative UI (Traditional):** +```tsx +// Write code to describe HOW to build UI +function TodoForm() { + const [title, setTitle] = useState('') + const [priority, setPriority] = useState('') + + const handleSubmit = (e) => { + e.preventDefault() + // Submit logic... + } + + return ( +
+ setTitle(e.target.value)} + /> + + +
+ ) +} +``` + +**Declarative UI (ObjectUI):** +```json +{ + "type": "form", + "api": "/api/todos", + "fields": [ + { "name": "title", "type": "input", "required": true }, + { "name": "priority", "type": "select", "options": ["High", "Medium", "Low"] } + ] +} +``` + +The declarative approach: +- ✅ Less code to write and maintain +- ✅ Easier to understand and modify +- ✅ Can be generated, stored, and versioned as data +- ✅ Enables dynamic UI generation + +### Data-Driven Rendering + +ObjectUI components are **data-driven**: + +1. **Protocol defines structure** - JSON describes what to render +2. **Data defines content** - Component fetches or receives data +3. **State triggers updates** - Changes automatically re-render + +```json +{ + "type": "table", + "api": "/api/users", + "columns": [ + { "name": "name", "label": "Name" }, + { "name": "email", "label": "Email" }, + { "name": "status", "label": "Status" } + ] +} +``` + +This table: +- Automatically fetches data from `/api/users` +- Renders columns as specified +- Handles pagination, sorting, filtering +- Updates when data changes + +## Use Cases + +### Admin Panels + +Build complete admin interfaces without writing UI code: + +```json +{ + "type": "page", + "title": "User Management", + "body": { + "type": "crud", + "api": "/api/users", + "columns": [...], + "filters": [...], + "actions": [...] + } +} +``` + +### Dynamic Forms + +Create forms that adapt based on configuration: + +```json +{ + "type": "form", + "api": "/api/submit", + "fields": [ + { "name": "name", "type": "input", "required": true }, + { + "name": "country", + "type": "select", + "api": "/api/countries" + }, + { + "name": "state", + "type": "select", + "api": "/api/states", + "dependsOn": "country" + } + ] +} +``` + +### Low-Code Platforms + +Enable users to build interfaces through configuration: + +```typescript +// Store UI protocol in database +await db.mutation('pages', { + action: 'insert', + data: { + name: 'dashboard', + protocol: { type: 'page', ... } + } +}) + +// Render from database +const page = await db.query('pages', { filters: [['name', '=', 'dashboard']] }) +return +``` + +## What You'll Learn + +In this section, you will discover: + +- ✅ **Core Concepts** - Understanding declarative UI and data-driven rendering +- ✅ **Component Specification** - Complete protocol reference for all components +- ✅ **Renderer Usage** - How to integrate ObjectUI in React applications +- ✅ **Customization** - Theming, styling, and extending components + +## Getting Started + +Ready to dive in? Start with: + +1. **[Core Concepts](./core-concepts)** - Understand declarative UI principles +2. **[Component Spec](./component-spec)** - Learn the component protocols +3. **[Renderer Usage](./renderer-usage)** - Integrate in your application + +## Quick Example + +Here's a complete example to get you started: + +```tsx +import { ObjectUIRenderer } from '@objectstack/react-renderer' + +// 1. Define UI Protocol +const todoApp = { + type: 'page', + title: 'My Todos', + body: { + type: 'grid', + columns: 2, + items: [ + { + type: 'form', + title: 'Add Todo', + api: '/api/todos', + method: 'POST', + fields: [ + { + name: 'title', + type: 'input', + label: 'Task', + required: true + }, + { + name: 'completed', + type: 'switch', + label: 'Completed', + defaultValue: false + } + ], + submitText: 'Add Task' + }, + { + type: 'table', + title: 'Todo List', + api: '/api/todos', + columns: [ + { name: 'title', label: 'Task' }, + { name: 'completed', label: 'Done', type: 'boolean' } + ], + actions: [ + { label: 'Delete', api: '/api/todos/${id}', method: 'DELETE' } + ] + } + ] + } +} + +// 2. Render +function App() { + return +} +``` + +This creates a fully functional todo application with: +- A form to add todos +- A table showing all todos +- Delete actions for each todo +- Automatic data fetching and updates + +## Next Steps + +Begin your ObjectUI journey: + +- **New to declarative UI?** Start with [Core Concepts](./core-concepts) +- **Need component reference?** Jump to [Component Spec](./component-spec) +- **Ready to build?** Check [Renderer Usage](./renderer-usage) diff --git a/content/docs/03-objectui/index.zh-CN.mdx b/content/docs/03-objectui/index.zh-CN.mdx new file mode 100644 index 0000000..ec5a93c --- /dev/null +++ b/content/docs/03-objectui/index.zh-CN.mdx @@ -0,0 +1,335 @@ +--- +title: ObjectUI - 界面引擎 +description: 声明式 UI 引擎,提供数据驱动渲染和基于协议的组件 +--- + +# ObjectUI - 界面引擎 + +ObjectUI 是 ObjectStack 的表现层,提供声明式 UI 引擎,通过 JSON 协议渲染界面。它将 UI 开发从命令式代码转变为数据驱动的声明。 + +## 什么是 ObjectUI? + +ObjectUI 是一个**声明式 UI 引擎**,允许您使用 JSON 协议定义用户界面,而不是编写组件代码。它提供了一套完整的预构建组件,可以根据数据规范自动渲染。 + +### 核心能力 + +- 🎨 **声明式 UI** - 使用 JSON 而非 JSX/代码定义界面 +- 🔄 **数据驱动渲染** - UI 自动反映数据变化 +- 📦 **组件协议** - 丰富的预构建组件集 +- 🎭 **主题支持** - 可自定义的样式和主题 +- ⚡ **性能优化** - 智能渲染和更新 +- 🔌 **框架无关** - React 渲染器,其他框架即将支持 + +## 工作原理 + +ObjectUI 基于一个简单的原则:**UI 即数据**。 + +### 1. 定义 UI 协议 + +使用 JSON 描述您的界面: + +```json +{ + "type": "page", + "title": "待办事项列表", + "body": { + "type": "form", + "fields": [ + { + "name": "title", + "type": "input", + "label": "任务名称", + "required": true + }, + { + "name": "priority", + "type": "select", + "label": "优先级", + "options": ["高", "中", "低"] + } + ] + } +} +``` + +### 2. 使用 ObjectUI 渲染 + +将协议传递给渲染器: + +```tsx +import { ObjectUIRenderer } from '@objectstack/react-renderer' + +function App() { + return +} +``` + +### 3. 自动生成 UI + +ObjectUI 渲染出一个功能完整的界面,包含表单、验证、操作和状态管理——全部来自 JSON 协议。 + +## 架构概览 + +``` +┌─────────────────────────────────────────┐ +│ 应用层 │ +│ (业务逻辑) │ +└────────────────┬────────────────────────┘ + │ + │ UI 协议 (JSON) + │ +┌────────────────▼────────────────────────┐ +│ ObjectUI 引擎 │ +│ ┌────────────┬──────────────────────┐ │ +│ │ 协议 │ 组件 │ │ +│ │ 解析器 │ 注册表 │ │ +│ └────────────┴──────────────────────┘ │ +│ ┌──────────────────────────────────┐ │ +│ │ 渲染协调器 │ │ +│ │ (状态与操作) │ │ +│ └──────────────────────────────────┘ │ +└────────────────┬────────────────────────┘ + │ + │ 组件实例 + │ +┌────────────────▼────────────────────────┐ +│ React 渲染器 │ +│ ┌──────┬──────┬──────┬──────┬──────┐ │ +│ │Input │Select│Table │Form │Page │ │ +│ │ │ │ │ │ │ │ +│ └──────┴──────┴──────┴──────┴──────┘ │ +└─────────────────────────────────────────┘ +``` + +## 核心概念 + +### 声明式 vs 命令式 + +**命令式 UI (传统方式):** +```tsx +// 编写代码描述如何构建 UI +function TodoForm() { + const [title, setTitle] = useState('') + const [priority, setPriority] = useState('') + + const handleSubmit = (e) => { + e.preventDefault() + // 提交逻辑... + } + + return ( +
+ setTitle(e.target.value)} + /> + + +
+ ) +} +``` + +**声明式 UI (ObjectUI):** +```json +{ + "type": "form", + "api": "/api/todos", + "fields": [ + { "name": "title", "type": "input", "required": true }, + { "name": "priority", "type": "select", "options": ["高", "中", "低"] } + ] +} +``` + +声明式方法的优势: +- ✅ 更少的代码编写和维护 +- ✅ 更容易理解和修改 +- ✅ 可以作为数据生成、存储和版本控制 +- ✅ 支持动态 UI 生成 + +### 数据驱动渲染 + +ObjectUI 组件是**数据驱动**的: + +1. **协议定义结构** - JSON 描述要渲染什么 +2. **数据定义内容** - 组件获取或接收数据 +3. **状态触发更新** - 变化自动重新渲染 + +```json +{ + "type": "table", + "api": "/api/users", + "columns": [ + { "name": "name", "label": "姓名" }, + { "name": "email", "label": "邮箱" }, + { "name": "status", "label": "状态" } + ] +} +``` + +这个表格: +- 自动从 `/api/users` 获取数据 +- 按指定列渲染 +- 处理分页、排序、筛选 +- 数据变化时自动更新 + +## 使用场景 + +### 管理后台 + +无需编写 UI 代码即可构建完整的管理界面: + +```json +{ + "type": "page", + "title": "用户管理", + "body": { + "type": "crud", + "api": "/api/users", + "columns": [...], + "filters": [...], + "actions": [...] + } +} +``` + +### 动态表单 + +创建基于配置自适应的表单: + +```json +{ + "type": "form", + "api": "/api/submit", + "fields": [ + { "name": "name", "type": "input", "required": true }, + { + "name": "country", + "type": "select", + "api": "/api/countries" + }, + { + "name": "state", + "type": "select", + "api": "/api/states", + "dependsOn": "country" + } + ] +} +``` + +### 低代码平台 + +让用户通过配置构建界面: + +```typescript +// 将 UI 协议存储到数据库 +await db.mutation('pages', { + action: 'insert', + data: { + name: 'dashboard', + protocol: { type: 'page', ... } + } +}) + +// 从数据库渲染 +const page = await db.query('pages', { filters: [['name', '=', 'dashboard']] }) +return +``` + +## 您将学到什么 + +在本章节中,您将了解: + +- ✅ **核心概念** - 理解声明式 UI 和数据驱动渲染 +- ✅ **组件规范** - 所有组件的完整协议参考 +- ✅ **渲染器使用** - 如何在 React 应用中集成 ObjectUI +- ✅ **自定义** - 主题、样式和扩展组件 + +## 开始学习 + +准备好深入了解了吗?从这里开始: + +1. **[核心概念](./core-concepts)** - 理解声明式 UI 原则 +2. **[组件规范](./component-spec)** - 学习组件协议 +3. **[渲染器使用](./renderer-usage)** - 在应用中集成 + +## 快速示例 + +这里有一个完整的示例帮您入门: + +```tsx +import { ObjectUIRenderer } from '@objectstack/react-renderer' + +// 1. 定义 UI 协议 +const todoApp = { + type: 'page', + title: '我的待办事项', + body: { + type: 'grid', + columns: 2, + items: [ + { + type: 'form', + title: '添加待办', + api: '/api/todos', + method: 'POST', + fields: [ + { + name: 'title', + type: 'input', + label: '任务', + required: true + }, + { + name: 'completed', + type: 'switch', + label: '已完成', + defaultValue: false + } + ], + submitText: '添加任务' + }, + { + type: 'table', + title: '待办列表', + api: '/api/todos', + columns: [ + { name: 'title', label: '任务' }, + { name: 'completed', label: '完成', type: 'boolean' } + ], + actions: [ + { label: '删除', api: '/api/todos/${id}', method: 'DELETE' } + ] + } + ] + } +} + +// 2. 渲染 +function App() { + return +} +``` + +这创建了一个功能完整的待办应用,包含: +- 添加待办的表单 +- 显示所有待办的表格 +- 每个待办的删除操作 +- 自动数据获取和更新 + +## 下一步 + +开始您的 ObjectUI 之旅: + +- **初次接触声明式 UI?** 从[核心概念](./core-concepts)开始 +- **需要组件参考?** 跳转到[组件规范](./component-spec) +- **准备开始构建?** 查看[渲染器使用](./renderer-usage) diff --git a/content/docs/03-objectui/meta.en.json b/content/docs/03-objectui/meta.en.json new file mode 100644 index 0000000..8d9deb8 --- /dev/null +++ b/content/docs/03-objectui/meta.en.json @@ -0,0 +1,9 @@ +{ + "title": "ObjectUI", + "pages": [ + "index", + "core-concepts", + "component-spec", + "renderer-usage" + ] +} diff --git a/content/docs/03-objectui/meta.zh-CN.json b/content/docs/03-objectui/meta.zh-CN.json new file mode 100644 index 0000000..8d9deb8 --- /dev/null +++ b/content/docs/03-objectui/meta.zh-CN.json @@ -0,0 +1,9 @@ +{ + "title": "ObjectUI", + "pages": [ + "index", + "core-concepts", + "component-spec", + "renderer-usage" + ] +} diff --git a/content/docs/03-objectui/renderer-usage.en.mdx b/content/docs/03-objectui/renderer-usage.en.mdx new file mode 100644 index 0000000..3b5ba0a --- /dev/null +++ b/content/docs/03-objectui/renderer-usage.en.mdx @@ -0,0 +1,732 @@ +--- +title: Renderer Usage +description: Integrating ObjectUI React renderer in your applications +--- + +# Renderer Usage + +Learn how to integrate the ObjectUI renderer into your React applications, customize styles, and implement advanced features. + +## Installation + +Install the ObjectUI React renderer package: + +```bash +npm install @objectstack/react-renderer +``` + +Or with yarn: + +```bash +yarn add @objectstack/react-renderer +``` + +Or with pnpm: + +```bash +pnpm add @objectstack/react-renderer +``` + +## Basic Usage + +### Simple Integration + +The simplest way to use ObjectUI is to render a protocol directly: + +```tsx +import { ObjectUIRenderer } from '@objectstack/react-renderer' + +const protocol = { + type: 'page', + title: 'My App', + body: { + type: 'card', + title: 'Welcome', + body: { + type: 'text', + value: 'Hello, ObjectUI!' + } + } +} + +function App() { + return +} + +export default App +``` + +### With Data Context + +Pass data to the renderer via context: + +```tsx +import { ObjectUIRenderer } from '@objectstack/react-renderer' + +const protocol = { + type: 'card', + title: '${user.name}', + body: [ + { type: 'text', value: 'Email: ${user.email}' }, + { type: 'text', value: 'Role: ${user.role}' } + ] +} + +function UserProfile({ userId }) { + const [user, setUser] = useState(null) + + useEffect(() => { + fetch(`/api/users/${userId}`) + .then(res => res.json()) + .then(setUser) + }, [userId]) + + if (!user) return
Loading...
+ + return ( + + ) +} +``` + +### Dynamic Protocol + +Load protocol dynamically from API or database: + +```tsx +import { ObjectUIRenderer } from '@objectstack/react-renderer' + +function DynamicPage({ pageId }) { + const [protocol, setProtocol] = useState(null) + + useEffect(() => { + // Fetch protocol from API + fetch(`/api/pages/${pageId}`) + .then(res => res.json()) + .then(data => setProtocol(data.protocol)) + }, [pageId]) + + if (!protocol) return
Loading...
+ + return +} +``` + +## Configuration + +### Global Configuration + +Configure the renderer globally with default settings: + +```tsx +import { ObjectUIProvider } from '@objectstack/react-renderer' + +function App() { + return ( + { + console.error('ObjectUI Error:', error) + } + }} + > + + + ) +} +``` + +### Configuration Options + +```typescript +interface ObjectUIConfig { + // API Configuration + apiBaseUrl?: string // Base URL for API requests + headers?: Record // Default headers for requests + + // Localization + locale?: string // Locale for date/number formatting + messages?: Record // Custom i18n messages + + // Theme + theme?: 'light' | 'dark' | 'auto' + + // Error Handling + errorHandler?: (error: Error) => void + + // Custom Components + components?: Record + + // Custom Actions + actions?: Record +} +``` + +## Theming and Styling + +### Using Built-in Themes + +ObjectUI comes with built-in themes: + +```tsx +import { ObjectUIProvider } from '@objectstack/react-renderer' +import '@objectstack/react-renderer/themes/light.css' + +function App() { + return ( + + + + ) +} +``` + +Available themes: +- `light` - Light theme +- `dark` - Dark theme +- `compact` - Compact spacing +- `comfortable` - Comfortable spacing + +### Custom Theme + +Create a custom theme by overriding CSS variables: + +```css +/* custom-theme.css */ +:root { + /* Primary Colors */ + --objectui-primary: #1976d2; + --objectui-primary-hover: #1565c0; + --objectui-primary-active: #0d47a1; + + /* Text Colors */ + --objectui-text-primary: #212121; + --objectui-text-secondary: #757575; + + /* Background Colors */ + --objectui-bg-primary: #ffffff; + --objectui-bg-secondary: #f5f5f5; + + /* Border Colors */ + --objectui-border-color: #e0e0e0; + --objectui-border-radius: 4px; + + /* Spacing */ + --objectui-spacing-xs: 4px; + --objectui-spacing-sm: 8px; + --objectui-spacing-md: 16px; + --objectui-spacing-lg: 24px; + --objectui-spacing-xl: 32px; + + /* Typography */ + --objectui-font-family: 'Roboto', sans-serif; + --objectui-font-size-sm: 12px; + --objectui-font-size-md: 14px; + --objectui-font-size-lg: 16px; + --objectui-font-size-xl: 20px; +} +``` + +Import your custom theme: + +```tsx +import '@objectstack/react-renderer/dist/style.css' +import './custom-theme.css' +``` + +### Component-Level Styling + +Apply styles to individual components via protocol: + +```json +{ + "type": "card", + "className": "custom-card", + "style": { + "backgroundColor": "#f5f5f5", + "padding": "24px", + "borderRadius": "8px" + }, + "body": { + "type": "text", + "value": "Styled card" + } +} +``` + +### CSS Modules + +Use CSS modules for scoped styles: + +```tsx +// UserCard.module.css +.card { + background: #f5f5f5; + padding: 20px; + border-radius: 8px; +} + +.title { + font-size: 18px; + font-weight: bold; + color: #333; +} +``` + +```tsx +import { ObjectUIRenderer } from '@objectstack/react-renderer' +import styles from './UserCard.module.css' + +const protocol = { + type: 'card', + className: styles.card, + title: 'User Profile', + body: { + type: 'text', + className: styles.title, + value: '${user.name}' + } +} + +function UserCard({ user }) { + return +} +``` + +## Custom Components + +Extend ObjectUI with custom components: + +### Registering Custom Components + +```tsx +import { ObjectUIProvider, registerComponent } from '@objectstack/react-renderer' + +// Define custom component +function CustomBanner({ title, message, variant }) { + return ( +
+

{title}

+

{message}

+
+ ) +} + +// Register component +registerComponent('customBanner', CustomBanner) + +// Use in protocol +const protocol = { + type: 'page', + body: { + type: 'customBanner', + title: 'Welcome', + message: 'This is a custom component', + variant: 'info' + } +} +``` + +### Custom Component with Actions + +```tsx +import { useObjectUIAction } from '@objectstack/react-renderer' + +function CustomButton({ label, onClick }) { + const executeAction = useObjectUIAction() + + const handleClick = () => { + executeAction(onClick) + } + + return ( + + ) +} + +registerComponent('customButton', CustomButton) +``` + +## Custom Actions + +Implement custom action handlers: + +```tsx +import { ObjectUIProvider, registerAction } from '@objectstack/react-renderer' + +// Register custom action +registerAction('sendEmail', async (action, context) => { + const { to, subject, body } = action + + await fetch('/api/send-email', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ to, subject, body }) + }) + + return { success: true } +}) + +// Use in protocol +const protocol = { + type: 'button', + label: 'Send Email', + onClick: { + type: 'sendEmail', + to: '${user.email}', + subject: 'Welcome', + body: 'Welcome to our platform!' + } +} +``` + +## API Integration + +### Global API Configuration + +Configure API settings globally: + +```tsx +import { ObjectUIProvider } from '@objectstack/react-renderer' + +function App() { + return ( + { + // Add custom headers, modify URL, etc. + return config + }, + // Transform response before using + responseTransform: (response) => { + // Extract data, handle errors, etc. + return response.data + } + }} + > + + + ) +} +``` + +### Custom Fetch Handler + +Implement custom fetch logic: + +```tsx +import { ObjectUIProvider } from '@objectstack/react-renderer' + +const customFetch = async (url, options) => { + // Add authentication + const token = localStorage.getItem('token') + const headers = { + ...options?.headers, + 'Authorization': `Bearer ${token}` + } + + // Make request + const response = await fetch(url, { ...options, headers }) + + // Handle errors + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`) + } + + return response.json() +} + +function App() { + return ( + + + + ) +} +``` + +## State Management + +### Local State + +Components manage their own state automatically: + +```json +{ + "type": "form", + "fields": [ + { + "type": "input", + "name": "search", + "placeholder": "Search...", + "onChange": { + "type": "setState", + "key": "searchQuery", + "value": "${value}" + } + }, + { + "type": "table", + "api": "/api/items?search=${searchQuery}", + "columns": [...] + } + ] +} +``` + +### Shared State + +Share state across components: + +```tsx +import { ObjectUIProvider, useObjectUIState } from '@objectstack/react-renderer' + +function App() { + const [globalState, setGlobalState] = useState({ + user: null, + settings: {} + }) + + return ( + + + + ) +} +``` + +## Error Handling + +### Global Error Handler + +Handle errors globally: + +```tsx +import { ObjectUIProvider } from '@objectstack/react-renderer' + +function App() { + const handleError = (error, context) => { + console.error('ObjectUI Error:', error) + + // Log to error tracking service + errorTracker.log(error, context) + + // Show user-friendly message + toast.error('Something went wrong. Please try again.') + } + + return ( + + + + ) +} +``` + +### Component-Level Error Handling + +```json +{ + "type": "table", + "api": "/api/users", + "onError": { + "type": "toast", + "message": "Failed to load users", + "variant": "error" + }, + "fallback": { + "type": "text", + "value": "Unable to load data. Please try again later." + } +} +``` + +## Performance Optimization + +### Lazy Loading + +Load components on demand: + +```tsx +import { lazy, Suspense } from 'react' +import { ObjectUIRenderer } from '@objectstack/react-renderer' + +const LazyComponent = lazy(() => import('./HeavyComponent')) + +const protocol = { + type: 'page', + body: { + type: 'lazy', + component: LazyComponent, + fallback: { type: 'text', value: 'Loading...' } + } +} +``` + +### Memoization + +Memoize expensive computations: + +```tsx +import { useMemo } from 'react' +import { ObjectUIRenderer } from '@objectstack/react-renderer' + +function OptimizedPage({ data }) { + const protocol = useMemo(() => ({ + type: 'table', + data: data, + columns: [...] + }), [data]) + + return +} +``` + +## Best Practices + +### 1. Protocol Organization + +Keep protocols organized and maintainable: + +```tsx +// protocols/user-management.ts +export const userTableProtocol = { + type: 'table', + api: '/api/users', + columns: [...] +} + +export const userFormProtocol = { + type: 'form', + api: '/api/users', + fields: [...] +} + +// pages/UserManagement.tsx +import { userTableProtocol } from './protocols/user-management' +``` + +### 2. Type Safety + +Use TypeScript for type-safe protocols: + +```typescript +import { Protocol } from '@objectstack/react-renderer' + +const protocol: Protocol = { + type: 'page', + title: 'Users', + body: { + type: 'table', + api: '/api/users' + } +} +``` + +### 3. Reusable Templates + +Create reusable protocol templates: + +```typescript +// templates/crud-page.ts +export const createCRUDPage = ( + title: string, + api: string, + columns: Column[], + fields: Field[] +) => ({ + type: 'page', + title, + actions: [ + { + label: `Add ${title}`, + onClick: { + type: 'dialog', + title: `Create ${title}`, + body: { + type: 'form', + api, + fields + } + } + } + ], + body: { + type: 'table', + api, + columns, + rowActions: [ + { label: 'Edit', onClick: {...} }, + { label: 'Delete', onClick: {...} } + ] + } +}) + +// Usage +const userPage = createCRUDPage('User', '/api/users', userColumns, userFields) +``` + +### 4. Environment-Specific Configuration + +Use environment variables: + +```tsx +import { ObjectUIProvider } from '@objectstack/react-renderer' + +const config = { + apiBaseUrl: process.env.REACT_APP_API_URL, + theme: process.env.REACT_APP_THEME || 'light' +} + +function App() { + return ( + + + + ) +} +``` + +## Troubleshooting + +### Common Issues + +**Issue: Components not rendering** +- Check protocol syntax and structure +- Verify component types are registered +- Check console for errors + +**Issue: API requests failing** +- Verify `apiBaseUrl` configuration +- Check network requests in DevTools +- Ensure proper authentication headers + +**Issue: Styles not applying** +- Import ObjectUI CSS: `import '@objectstack/react-renderer/dist/style.css'` +- Check CSS specificity +- Verify theme configuration + +## Next Steps + +- Explore [Component Spec](./component-spec) for detailed component reference +- Check out [Core Concepts](./core-concepts) for architectural understanding +- Join the community for support and examples diff --git a/content/docs/03-objectui/renderer-usage.zh-CN.mdx b/content/docs/03-objectui/renderer-usage.zh-CN.mdx new file mode 100644 index 0000000..26fe1dd --- /dev/null +++ b/content/docs/03-objectui/renderer-usage.zh-CN.mdx @@ -0,0 +1,732 @@ +--- +title: 渲染器使用 +description: 在应用中集成 ObjectUI React 渲染器 +--- + +# 渲染器使用 + +学习如何将 ObjectUI 渲染器集成到 React 应用中、自定义样式以及实现高级功能。 + +## 安装 + +安装 ObjectUI React 渲染器包: + +```bash +npm install @objectstack/react-renderer +``` + +或使用 yarn: + +```bash +yarn add @objectstack/react-renderer +``` + +或使用 pnpm: + +```bash +pnpm add @objectstack/react-renderer +``` + +## 基础用法 + +### 简单集成 + +使用 ObjectUI 最简单的方式是直接渲染协议: + +```tsx +import { ObjectUIRenderer } from '@objectstack/react-renderer' + +const protocol = { + type: 'page', + title: '我的应用', + body: { + type: 'card', + title: '欢迎', + body: { + type: 'text', + value: '你好, ObjectUI!' + } + } +} + +function App() { + return +} + +export default App +``` + +### 使用数据上下文 + +通过上下文向渲染器传递数据: + +```tsx +import { ObjectUIRenderer } from '@objectstack/react-renderer' + +const protocol = { + type: 'card', + title: '${user.name}', + body: [ + { type: 'text', value: '邮箱: ${user.email}' }, + { type: 'text', value: '角色: ${user.role}' } + ] +} + +function UserProfile({ userId }) { + const [user, setUser] = useState(null) + + useEffect(() => { + fetch(`/api/users/${userId}`) + .then(res => res.json()) + .then(setUser) + }, [userId]) + + if (!user) return
加载中...
+ + return ( + + ) +} +``` + +### 动态协议 + +从 API 或数据库动态加载协议: + +```tsx +import { ObjectUIRenderer } from '@objectstack/react-renderer' + +function DynamicPage({ pageId }) { + const [protocol, setProtocol] = useState(null) + + useEffect(() => { + // 从 API 获取协议 + fetch(`/api/pages/${pageId}`) + .then(res => res.json()) + .then(data => setProtocol(data.protocol)) + }, [pageId]) + + if (!protocol) return
加载中...
+ + return +} +``` + +## 配置 + +### 全局配置 + +使用默认设置全局配置渲染器: + +```tsx +import { ObjectUIProvider } from '@objectstack/react-renderer' + +function App() { + return ( + { + console.error('ObjectUI 错误:', error) + } + }} + > + + + ) +} +``` + +### 配置选项 + +```typescript +interface ObjectUIConfig { + // API 配置 + apiBaseUrl?: string // API 请求的基础 URL + headers?: Record // 请求的默认头部 + + // 本地化 + locale?: string // 日期/数字格式化的语言环境 + messages?: Record // 自定义 i18n 消息 + + // 主题 + theme?: 'light' | 'dark' | 'auto' + + // 错误处理 + errorHandler?: (error: Error) => void + + // 自定义组件 + components?: Record + + // 自定义操作 + actions?: Record +} +``` + +## 主题和样式 + +### 使用内置主题 + +ObjectUI 内置了多个主题: + +```tsx +import { ObjectUIProvider } from '@objectstack/react-renderer' +import '@objectstack/react-renderer/themes/light.css' + +function App() { + return ( + + + + ) +} +``` + +可用主题: +- `light` - 浅色主题 +- `dark` - 深色主题 +- `compact` - 紧凑间距 +- `comfortable` - 舒适间距 + +### 自定义主题 + +通过覆盖 CSS 变量创建自定义主题: + +```css +/* custom-theme.css */ +:root { + /* 主色 */ + --objectui-primary: #1976d2; + --objectui-primary-hover: #1565c0; + --objectui-primary-active: #0d47a1; + + /* 文本颜色 */ + --objectui-text-primary: #212121; + --objectui-text-secondary: #757575; + + /* 背景颜色 */ + --objectui-bg-primary: #ffffff; + --objectui-bg-secondary: #f5f5f5; + + /* 边框颜色 */ + --objectui-border-color: #e0e0e0; + --objectui-border-radius: 4px; + + /* 间距 */ + --objectui-spacing-xs: 4px; + --objectui-spacing-sm: 8px; + --objectui-spacing-md: 16px; + --objectui-spacing-lg: 24px; + --objectui-spacing-xl: 32px; + + /* 字体 */ + --objectui-font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; + --objectui-font-size-sm: 12px; + --objectui-font-size-md: 14px; + --objectui-font-size-lg: 16px; + --objectui-font-size-xl: 20px; +} +``` + +导入自定义主题: + +```tsx +import '@objectstack/react-renderer/dist/style.css' +import './custom-theme.css' +``` + +### 组件级样式 + +通过协议为单个组件应用样式: + +```json +{ + "type": "card", + "className": "custom-card", + "style": { + "backgroundColor": "#f5f5f5", + "padding": "24px", + "borderRadius": "8px" + }, + "body": { + "type": "text", + "value": "样式化的卡片" + } +} +``` + +### CSS 模块 + +使用 CSS 模块实现作用域样式: + +```tsx +// UserCard.module.css +.card { + background: #f5f5f5; + padding: 20px; + border-radius: 8px; +} + +.title { + font-size: 18px; + font-weight: bold; + color: #333; +} +``` + +```tsx +import { ObjectUIRenderer } from '@objectstack/react-renderer' +import styles from './UserCard.module.css' + +const protocol = { + type: 'card', + className: styles.card, + title: '用户资料', + body: { + type: 'text', + className: styles.title, + value: '${user.name}' + } +} + +function UserCard({ user }) { + return +} +``` + +## 自定义组件 + +使用自定义组件扩展 ObjectUI: + +### 注册自定义组件 + +```tsx +import { ObjectUIProvider, registerComponent } from '@objectstack/react-renderer' + +// 定义自定义组件 +function CustomBanner({ title, message, variant }) { + return ( +
+

{title}

+

{message}

+
+ ) +} + +// 注册组件 +registerComponent('customBanner', CustomBanner) + +// 在协议中使用 +const protocol = { + type: 'page', + body: { + type: 'customBanner', + title: '欢迎', + message: '这是一个自定义组件', + variant: 'info' + } +} +``` + +### 带操作的自定义组件 + +```tsx +import { useObjectUIAction } from '@objectstack/react-renderer' + +function CustomButton({ label, onClick }) { + const executeAction = useObjectUIAction() + + const handleClick = () => { + executeAction(onClick) + } + + return ( + + ) +} + +registerComponent('customButton', CustomButton) +``` + +## 自定义操作 + +实现自定义操作处理器: + +```tsx +import { ObjectUIProvider, registerAction } from '@objectstack/react-renderer' + +// 注册自定义操作 +registerAction('sendEmail', async (action, context) => { + const { to, subject, body } = action + + await fetch('/api/send-email', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ to, subject, body }) + }) + + return { success: true } +}) + +// 在协议中使用 +const protocol = { + type: 'button', + label: '发送邮件', + onClick: { + type: 'sendEmail', + to: '${user.email}', + subject: '欢迎', + body: '欢迎使用我们的平台!' + } +} +``` + +## API 集成 + +### 全局 API 配置 + +全局配置 API 设置: + +```tsx +import { ObjectUIProvider } from '@objectstack/react-renderer' + +function App() { + return ( + { + // 添加自定义头部、修改 URL 等 + return config + }, + // 使用前转换响应 + responseTransform: (response) => { + // 提取数据、处理错误等 + return response.data + } + }} + > + + + ) +} +``` + +### 自定义 Fetch 处理器 + +实现自定义 fetch 逻辑: + +```tsx +import { ObjectUIProvider } from '@objectstack/react-renderer' + +const customFetch = async (url, options) => { + // 添加认证 + const token = localStorage.getItem('token') + const headers = { + ...options?.headers, + 'Authorization': `Bearer ${token}` + } + + // 发起请求 + const response = await fetch(url, { ...options, headers }) + + // 处理错误 + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`) + } + + return response.json() +} + +function App() { + return ( + + + + ) +} +``` + +## 状态管理 + +### 本地状态 + +组件自动管理自己的状态: + +```json +{ + "type": "form", + "fields": [ + { + "type": "input", + "name": "search", + "placeholder": "搜索...", + "onChange": { + "type": "setState", + "key": "searchQuery", + "value": "${value}" + } + }, + { + "type": "table", + "api": "/api/items?search=${searchQuery}", + "columns": [...] + } + ] +} +``` + +### 共享状态 + +在组件间共享状态: + +```tsx +import { ObjectUIProvider, useObjectUIState } from '@objectstack/react-renderer' + +function App() { + const [globalState, setGlobalState] = useState({ + user: null, + settings: {} + }) + + return ( + + + + ) +} +``` + +## 错误处理 + +### 全局错误处理器 + +全局处理错误: + +```tsx +import { ObjectUIProvider } from '@objectstack/react-renderer' + +function App() { + const handleError = (error, context) => { + console.error('ObjectUI 错误:', error) + + // 记录到错误跟踪服务 + errorTracker.log(error, context) + + // 显示用户友好的消息 + toast.error('出错了,请重试。') + } + + return ( + + + + ) +} +``` + +### 组件级错误处理 + +```json +{ + "type": "table", + "api": "/api/users", + "onError": { + "type": "toast", + "message": "加载用户失败", + "variant": "error" + }, + "fallback": { + "type": "text", + "value": "无法加载数据,请稍后重试。" + } +} +``` + +## 性能优化 + +### 懒加载 + +按需加载组件: + +```tsx +import { lazy, Suspense } from 'react' +import { ObjectUIRenderer } from '@objectstack/react-renderer' + +const LazyComponent = lazy(() => import('./HeavyComponent')) + +const protocol = { + type: 'page', + body: { + type: 'lazy', + component: LazyComponent, + fallback: { type: 'text', value: '加载中...' } + } +} +``` + +### 记忆化 + +记忆化昂贵的计算: + +```tsx +import { useMemo } from 'react' +import { ObjectUIRenderer } from '@objectstack/react-renderer' + +function OptimizedPage({ data }) { + const protocol = useMemo(() => ({ + type: 'table', + data: data, + columns: [...] + }), [data]) + + return +} +``` + +## 最佳实践 + +### 1. 协议组织 + +保持协议组织有序且易于维护: + +```tsx +// protocols/user-management.ts +export const userTableProtocol = { + type: 'table', + api: '/api/users', + columns: [...] +} + +export const userFormProtocol = { + type: 'form', + api: '/api/users', + fields: [...] +} + +// pages/UserManagement.tsx +import { userTableProtocol } from './protocols/user-management' +``` + +### 2. 类型安全 + +使用 TypeScript 实现类型安全的协议: + +```typescript +import { Protocol } from '@objectstack/react-renderer' + +const protocol: Protocol = { + type: 'page', + title: '用户', + body: { + type: 'table', + api: '/api/users' + } +} +``` + +### 3. 可复用模板 + +创建可复用的协议模板: + +```typescript +// templates/crud-page.ts +export const createCRUDPage = ( + title: string, + api: string, + columns: Column[], + fields: Field[] +) => ({ + type: 'page', + title, + actions: [ + { + label: `添加${title}`, + onClick: { + type: 'dialog', + title: `创建${title}`, + body: { + type: 'form', + api, + fields + } + } + } + ], + body: { + type: 'table', + api, + columns, + rowActions: [ + { label: '编辑', onClick: {...} }, + { label: '删除', onClick: {...} } + ] + } +}) + +// 使用 +const userPage = createCRUDPage('用户', '/api/users', userColumns, userFields) +``` + +### 4. 环境特定配置 + +使用环境变量: + +```tsx +import { ObjectUIProvider } from '@objectstack/react-renderer' + +const config = { + apiBaseUrl: process.env.REACT_APP_API_URL, + theme: process.env.REACT_APP_THEME || 'light' +} + +function App() { + return ( + + + + ) +} +``` + +## 故障排除 + +### 常见问题 + +**问题: 组件不渲染** +- 检查协议语法和结构 +- 验证组件类型已注册 +- 检查控制台错误 + +**问题: API 请求失败** +- 验证 `apiBaseUrl` 配置 +- 在开发者工具中检查网络请求 +- 确保认证头部正确 + +**问题: 样式未应用** +- 导入 ObjectUI CSS: `import '@objectstack/react-renderer/dist/style.css'` +- 检查 CSS 优先级 +- 验证主题配置 + +## 下一步 + +- 查看[组件规范](./component-spec)了解详细的组件参考 +- 查看[核心概念](./core-concepts)了解架构理解 +- 加入社区获取支持和示例 diff --git a/content/docs/04-objectos/deployment.en.mdx b/content/docs/04-objectos/deployment.en.mdx new file mode 100644 index 0000000..95a3578 --- /dev/null +++ b/content/docs/04-objectos/deployment.en.mdx @@ -0,0 +1,645 @@ +--- +title: Deployment & Operations +description: Deploy ObjectOS in standalone, server, and multi-tenant modes +--- + +# Deployment & Operations + +ObjectOS supports flexible deployment options to match your application needs: from single-file standalone executables to containerized multi-tenant SaaS platforms. + +## Deployment Modes + +ObjectOS can be deployed in three primary modes: + +### 1. Standalone Mode + +Single-file executable with embedded database for Local-First applications. + +### 2. Server Mode + +Traditional server deployment with external database for team collaboration. + +### 3. Multi-Tenant Mode + +SaaS-ready deployment with complete tenant isolation. + +## Standalone Mode (.oos Files) + +Standalone mode packages your application as a single executable `.oos` file with an embedded SQLite database. + +### Creating a Standalone Application + +```typescript +// app.ts - Your application definition +import { ObjectOS } from '@objectstack/os' + +const app = new ObjectOS({ + mode: 'standalone', + + // Embedded storage + storage: { + type: 'sqlite', + embedded: true // Database bundled in .oos file + }, + + // Your schema + schema: { + objects: { + tasks: { + fields: { + name: { type: 'text', required: true }, + completed: { type: 'boolean', defaultValue: false } + } + } + } + }, + + // Your pages + pages: { + home: { + type: 'list', + object: 'tasks' + } + } +}) + +export default app +``` + +### Building the .oos File + +```bash +# Install ObjectOS CLI +npm install -g @objectstack/cli + +# Build standalone executable +objectos build app.ts --output myapp.oos + +# Output: myapp.oos (single executable file) +``` + +### Running the .oos File + +```bash +# Make executable (Linux/Mac) +chmod +x myapp.oos + +# Run the application +./myapp.oos + +# Application starts on http://localhost:3000 +# Data stored in ~/.myapp/data.db by default +``` + +### .oos File Configuration + +Users can configure the .oos file via environment variables: + +```bash +# Custom data directory +DATA_DIR=~/Documents/myapp ./myapp.oos + +# Custom port +PORT=8080 ./myapp.oos + +# Enable sync +SYNC_ENABLED=true SYNC_URL=https://sync.example.com ./myapp.oos +``` + +### Distribution + +Distribute your application as a single file: + +```bash +# Cross-platform builds +objectos build app.ts --platform linux --output myapp-linux.oos +objectos build app.ts --platform macos --output myapp-macos.oos +objectos build app.ts --platform windows --output myapp.exe + +# Users just download and run - no installation needed! +``` + +## Server Mode (Docker Deployment) + +Server mode deploys ObjectOS as a containerized service with an external database. + +### Basic Docker Deployment + +Create a `Dockerfile`: + +```dockerfile +FROM node:18-alpine + +# Install dependencies +WORKDIR /app +COPY package*.json ./ +RUN npm ci --production + +# Copy application +COPY . . + +# Build application +RUN npm run build + +# Expose port +EXPOSE 3000 + +# Start application +CMD ["npm", "start"] +``` + +Create `docker-compose.yml`: + +```yaml +version: '3.8' + +services: + app: + build: . + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - DATABASE_URL=postgresql://postgres:password@db:5432/myapp + - SESSION_SECRET=${SESSION_SECRET} + depends_on: + - db + restart: unless-stopped + + db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=myapp + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=password + volumes: + - db-data:/var/lib/postgresql/data + restart: unless-stopped + +volumes: + db-data: +``` + +### Environment Variables + +Configure your application via environment variables: + +```bash +# Database connection +DATABASE_URL=postgresql://user:pass@host:5432/dbname + +# Server settings +PORT=3000 +HOST=0.0.0.0 +NODE_ENV=production + +# Authentication +SESSION_SECRET=your-secret-key-min-32-chars +JWT_SECRET=your-jwt-secret-key + +# OAuth (optional) +GOOGLE_CLIENT_ID=your-google-client-id +GOOGLE_CLIENT_SECRET=your-google-client-secret +GITHUB_CLIENT_ID=your-github-client-id +GITHUB_CLIENT_SECRET=your-github-client-secret + +# Email (optional) +SMTP_HOST=smtp.example.com +SMTP_PORT=587 +SMTP_USER=your-smtp-user +SMTP_PASS=your-smtp-password + +# Storage (optional - for file uploads) +S3_BUCKET=your-bucket +S3_REGION=us-east-1 +S3_ACCESS_KEY=your-access-key +S3_SECRET_KEY=your-secret-key + +# Logging +LOG_LEVEL=info # debug, info, warn, error +``` + +### Deploy with Docker Compose + +```bash +# Start services +docker-compose up -d + +# View logs +docker-compose logs -f app + +# Stop services +docker-compose down + +# Update application +docker-compose pull +docker-compose up -d --build +``` + +### Production Deployment + +For production environments, add these configurations: + +```yaml +version: '3.8' + +services: + app: + build: . + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - DATABASE_URL=${DATABASE_URL} + - SESSION_SECRET=${SESSION_SECRET} + deploy: + replicas: 3 # Multiple instances + restart_policy: + condition: on-failure + max_attempts: 3 + resources: + limits: + cpus: '1' + memory: 1G + reservations: + cpus: '0.5' + memory: 512M + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=${DB_NAME} + - POSTGRES_USER=${DB_USER} + - POSTGRES_PASSWORD=${DB_PASSWORD} + volumes: + - db-data:/var/lib/postgresql/data + - ./backups:/backups + deploy: + resources: + limits: + cpus: '2' + memory: 2G + + nginx: + image: nginx:alpine + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + - ./certs:/etc/nginx/certs:ro + depends_on: + - app + +volumes: + db-data: +``` + +### Nginx Configuration + +Create `nginx.conf` for reverse proxy: + +```nginx +events { + worker_connections 1024; +} + +http { + upstream app { + least_conn; + server app:3000; + } + + server { + listen 80; + server_name example.com; + + # Redirect HTTP to HTTPS + return 301 https://$server_name$request_uri; + } + + server { + listen 443 ssl http2; + server_name example.com; + + # SSL certificates + ssl_certificate /etc/nginx/certs/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/privkey.pem; + + # Security headers + add_header Strict-Transport-Security "max-age=31536000" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + + # Proxy settings + location / { + proxy_pass http://app; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + } + + # WebSocket support + location /ws { + proxy_pass http://app; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + } +} +``` + +## Multi-Tenant SaaS Configuration + +Deploy ObjectOS as a multi-tenant SaaS platform with complete tenant isolation. + +### Multi-Tenant Architecture + +ObjectOS supports two multi-tenant strategies: + +#### 1. Virtual City Strategy (Recommended) + +Uses ObjectQL's Virtual City mechanism for tenant isolation: + +```typescript +const app = new ObjectOS({ + mode: 'multi-tenant', + + // Multi-tenant configuration + multiTenant: { + enabled: true, + strategy: 'virtualCity', // Use Virtual City + isolation: 'strict', // Strict tenant isolation + + // Tenant identification + identifier: { + type: 'subdomain', // tenant1.app.com, tenant2.app.com + // Or: type: 'header', header: 'X-Tenant-ID' + // Or: type: 'path', pattern: '/:tenantId/*' + } + }, + + // Shared database + storage: { + type: 'postgresql', + url: process.env.DATABASE_URL + } +}) +``` + +#### 2. Database-Per-Tenant Strategy + +Separate database for each tenant: + +```typescript +const app = new ObjectOS({ + mode: 'multi-tenant', + + multiTenant: { + enabled: true, + strategy: 'database', // Separate databases + + // Database routing + databaseResolver: async (tenantId) => { + return { + type: 'postgresql', + url: `postgresql://user:pass@host:5432/tenant_${tenantId}` + } + } + } +}) +``` + +### Environment Variables for Multi-Tenant + +```bash +# Multi-tenant mode +MULTI_TENANT=true +TENANT_STRATEGY=virtualCity # or 'database' +TENANT_IDENTIFIER=subdomain # 'subdomain', 'header', or 'path' + +# Database +DATABASE_URL=postgresql://user:pass@host:5432/saas_app + +# Tenant management +TENANT_AUTO_CREATE=false # Don't auto-create tenants +TENANT_ADMIN_EMAIL=admin@example.com +``` + +### Docker Compose for Multi-Tenant + +```yaml +version: '3.8' + +services: + app: + build: . + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - MULTI_TENANT=true + - TENANT_STRATEGY=virtualCity + - TENANT_IDENTIFIER=subdomain + - DATABASE_URL=postgresql://postgres:password@db:5432/saas_app + - SESSION_SECRET=${SESSION_SECRET} + depends_on: + - db + - redis + deploy: + replicas: 3 + + db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=saas_app + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=password + volumes: + - db-data:/var/lib/postgresql/data + + redis: + image: redis:7-alpine + volumes: + - redis-data:/data + +volumes: + db-data: + redis-data: +``` + +### Tenant Management + +```typescript +// Create a new tenant +const tenant = await app.tenants.create({ + id: 'acme-corp', + name: 'Acme Corporation', + domain: 'acme.myapp.com', + plan: 'enterprise', + settings: { + maxUsers: 100, + maxStorage: '100GB' + } +}) + +// List all tenants +const tenants = await app.tenants.list() + +// Get tenant by ID +const tenant = await app.tenants.get('acme-corp') + +// Update tenant +await app.tenants.update('acme-corp', { + plan: 'enterprise-plus', + settings: { + maxUsers: 500 + } +}) + +// Delete tenant (with data) +await app.tenants.delete('acme-corp', { + deleteData: true // Permanently delete tenant data +}) +``` + +## Database Migrations + +Manage schema changes across deployments: + +```bash +# Generate migration +objectos migrate create add-priority-field + +# Apply migrations +objectos migrate up + +# Rollback migrations +objectos migrate down + +# Check migration status +objectos migrate status +``` + +Migration file example: + +```typescript +// migrations/20240115_add_priority_field.ts +export async function up(db) { + await db.addField('tasks', { + priority: { + type: 'select', + options: ['low', 'medium', 'high'], + defaultValue: 'medium' + } + }) +} + +export async function down(db) { + await db.removeField('tasks', 'priority') +} +``` + +## Monitoring & Logging + +### Application Logging + +```typescript +const app = new ObjectOS({ + logging: { + level: 'info', // debug, info, warn, error + format: 'json', // json, text + outputs: [ + { type: 'console' }, + { type: 'file', path: './logs/app.log' }, + { + type: 'cloudwatch', + group: '/aws/objectos/myapp', + stream: 'production' + } + ] + } +}) +``` + +### Health Checks + +```typescript +// Built-in health check endpoint +// GET /health + +{ + "status": "healthy", + "database": "connected", + "uptime": 86400, + "memory": { + "used": "256MB", + "total": "1GB" + } +} +``` + +### Metrics + +```typescript +const app = new ObjectOS({ + metrics: { + enabled: true, + endpoint: '/metrics', // Prometheus metrics + include: [ + 'http_requests', + 'db_queries', + 'cache_hits', + 'active_users' + ] + } +}) +``` + +## Backup & Recovery + +### Automated Backups + +```bash +# Setup automated backups (PostgreSQL) +# Using pg_dump in cron + +0 2 * * * docker exec db pg_dump -U postgres myapp > /backups/myapp-$(date +\%Y\%m\%d).sql + +# Restore from backup +docker exec -i db psql -U postgres myapp < /backups/myapp-20240115.sql +``` + +### ObjectOS Backup API + +```typescript +// Create backup +await app.backup.create({ + path: './backups/myapp-backup.db', + includeFiles: true +}) + +// Restore from backup +await app.backup.restore({ + path: './backups/myapp-backup.db' +}) +``` + +## Next Steps + +You now know how to deploy ObjectOS in various configurations: + +- **[Platform Architecture](./platform-architecture)** - Review platform architecture +- **[Identity & Access](./identity-access)** - Configure security and permissions diff --git a/content/docs/04-objectos/deployment.zh-CN.mdx b/content/docs/04-objectos/deployment.zh-CN.mdx new file mode 100644 index 0000000..c787a8a --- /dev/null +++ b/content/docs/04-objectos/deployment.zh-CN.mdx @@ -0,0 +1,645 @@ +--- +title: 部署与运维 +description: 以独立模式、服务器模式和多租户模式部署 ObjectOS +--- + +# 部署与运维 + +ObjectOS 支持灵活的部署选项以匹配您的应用需求:从单文件独立可执行程序到容器化的多租户 SaaS 平台。 + +## 部署模式 + +ObjectOS 可以以三种主要模式部署: + +### 1. 独立模式 + +带嵌入式数据库的单文件可执行程序,用于本地优先应用。 + +### 2. 服务器模式 + +传统的服务器部署,使用外部数据库,用于团队协作。 + +### 3. 多租户模式 + +SaaS 就绪的部署,具有完整的租户隔离。 + +## 独立模式(.oos 文件) + +独立模式将您的应用打包为一个带嵌入式 SQLite 数据库的可执行 `.oos` 文件。 + +### 创建独立应用 + +```typescript +// app.ts - 您的应用定义 +import { ObjectOS } from '@objectstack/os' + +const app = new ObjectOS({ + mode: 'standalone', + + // 嵌入式存储 + storage: { + type: 'sqlite', + embedded: true // 数据库打包在 .oos 文件中 + }, + + // 您的 schema + schema: { + objects: { + tasks: { + fields: { + name: { type: 'text', required: true }, + completed: { type: 'boolean', defaultValue: false } + } + } + } + }, + + // 您的页面 + pages: { + home: { + type: 'list', + object: 'tasks' + } + } +}) + +export default app +``` + +### 构建 .oos 文件 + +```bash +# 安装 ObjectOS CLI +npm install -g @objectstack/cli + +# 构建独立可执行文件 +objectos build app.ts --output myapp.oos + +# 输出: myapp.oos (单个可执行文件) +``` + +### 运行 .oos 文件 + +```bash +# 添加可执行权限 (Linux/Mac) +chmod +x myapp.oos + +# 运行应用 +./myapp.oos + +# 应用启动在 http://localhost:3000 +# 数据默认存储在 ~/.myapp/data.db +``` + +### .oos 文件配置 + +用户可以通过环境变量配置 .oos 文件: + +```bash +# 自定义数据目录 +DATA_DIR=~/Documents/myapp ./myapp.oos + +# 自定义端口 +PORT=8080 ./myapp.oos + +# 启用同步 +SYNC_ENABLED=true SYNC_URL=https://sync.example.com ./myapp.oos +``` + +### 分发 + +将您的应用作为单个文件分发: + +```bash +# 跨平台构建 +objectos build app.ts --platform linux --output myapp-linux.oos +objectos build app.ts --platform macos --output myapp-macos.oos +objectos build app.ts --platform windows --output myapp.exe + +# 用户只需下载并运行 - 无需安装! +``` + +## 服务器模式(Docker 部署) + +服务器模式将 ObjectOS 部署为带外部数据库的容器化服务。 + +### 基本 Docker 部署 + +创建 `Dockerfile`: + +```dockerfile +FROM node:18-alpine + +# 安装依赖 +WORKDIR /app +COPY package*.json ./ +RUN npm ci --production + +# 复制应用 +COPY . . + +# 构建应用 +RUN npm run build + +# 暴露端口 +EXPOSE 3000 + +# 启动应用 +CMD ["npm", "start"] +``` + +创建 `docker-compose.yml`: + +```yaml +version: '3.8' + +services: + app: + build: . + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - DATABASE_URL=postgresql://postgres:password@db:5432/myapp + - SESSION_SECRET=${SESSION_SECRET} + depends_on: + - db + restart: unless-stopped + + db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=myapp + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=password + volumes: + - db-data:/var/lib/postgresql/data + restart: unless-stopped + +volumes: + db-data: +``` + +### 环境变量 + +通过环境变量配置您的应用: + +```bash +# 数据库连接 +DATABASE_URL=postgresql://user:pass@host:5432/dbname + +# 服务器设置 +PORT=3000 +HOST=0.0.0.0 +NODE_ENV=production + +# 认证 +SESSION_SECRET=your-secret-key-min-32-chars +JWT_SECRET=your-jwt-secret-key + +# OAuth (可选) +GOOGLE_CLIENT_ID=your-google-client-id +GOOGLE_CLIENT_SECRET=your-google-client-secret +GITHUB_CLIENT_ID=your-github-client-id +GITHUB_CLIENT_SECRET=your-github-client-secret + +# 邮件 (可选) +SMTP_HOST=smtp.example.com +SMTP_PORT=587 +SMTP_USER=your-smtp-user +SMTP_PASS=your-smtp-password + +# 存储 (可选 - 用于文件上传) +S3_BUCKET=your-bucket +S3_REGION=us-east-1 +S3_ACCESS_KEY=your-access-key +S3_SECRET_KEY=your-secret-key + +# 日志 +LOG_LEVEL=info # debug, info, warn, error +``` + +### 使用 Docker Compose 部署 + +```bash +# 启动服务 +docker-compose up -d + +# 查看日志 +docker-compose logs -f app + +# 停止服务 +docker-compose down + +# 更新应用 +docker-compose pull +docker-compose up -d --build +``` + +### 生产环境部署 + +对于生产环境,添加这些配置: + +```yaml +version: '3.8' + +services: + app: + build: . + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - DATABASE_URL=${DATABASE_URL} + - SESSION_SECRET=${SESSION_SECRET} + deploy: + replicas: 3 # 多个实例 + restart_policy: + condition: on-failure + max_attempts: 3 + resources: + limits: + cpus: '1' + memory: 1G + reservations: + cpus: '0.5' + memory: 512M + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=${DB_NAME} + - POSTGRES_USER=${DB_USER} + - POSTGRES_PASSWORD=${DB_PASSWORD} + volumes: + - db-data:/var/lib/postgresql/data + - ./backups:/backups + deploy: + resources: + limits: + cpus: '2' + memory: 2G + + nginx: + image: nginx:alpine + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + - ./certs:/etc/nginx/certs:ro + depends_on: + - app + +volumes: + db-data: +``` + +### Nginx 配置 + +创建 `nginx.conf` 作为反向代理: + +```nginx +events { + worker_connections 1024; +} + +http { + upstream app { + least_conn; + server app:3000; + } + + server { + listen 80; + server_name example.com; + + # 重定向 HTTP 到 HTTPS + return 301 https://$server_name$request_uri; + } + + server { + listen 443 ssl http2; + server_name example.com; + + # SSL 证书 + ssl_certificate /etc/nginx/certs/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/privkey.pem; + + # 安全头 + add_header Strict-Transport-Security "max-age=31536000" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + + # 代理设置 + location / { + proxy_pass http://app; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + } + + # WebSocket 支持 + location /ws { + proxy_pass http://app; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + } +} +``` + +## 多租户 SaaS 配置 + +将 ObjectOS 部署为具有完整租户隔离的多租户 SaaS 平台。 + +### 多租户架构 + +ObjectOS 支持两种多租户策略: + +#### 1. Virtual City 策略(推荐) + +使用 ObjectQL 的 Virtual City 机制实现租户隔离: + +```typescript +const app = new ObjectOS({ + mode: 'multi-tenant', + + // 多租户配置 + multiTenant: { + enabled: true, + strategy: 'virtualCity', // 使用 Virtual City + isolation: 'strict', // 严格租户隔离 + + // 租户识别 + identifier: { + type: 'subdomain', // tenant1.app.com, tenant2.app.com + // 或: type: 'header', header: 'X-Tenant-ID' + // 或: type: 'path', pattern: '/:tenantId/*' + } + }, + + // 共享数据库 + storage: { + type: 'postgresql', + url: process.env.DATABASE_URL + } +}) +``` + +#### 2. 每租户独立数据库策略 + +每个租户使用独立数据库: + +```typescript +const app = new ObjectOS({ + mode: 'multi-tenant', + + multiTenant: { + enabled: true, + strategy: 'database', // 独立数据库 + + // 数据库路由 + databaseResolver: async (tenantId) => { + return { + type: 'postgresql', + url: `postgresql://user:pass@host:5432/tenant_${tenantId}` + } + } + } +}) +``` + +### 多租户环境变量 + +```bash +# 多租户模式 +MULTI_TENANT=true +TENANT_STRATEGY=virtualCity # 或 'database' +TENANT_IDENTIFIER=subdomain # 'subdomain', 'header', 或 'path' + +# 数据库 +DATABASE_URL=postgresql://user:pass@host:5432/saas_app + +# 租户管理 +TENANT_AUTO_CREATE=false # 不自动创建租户 +TENANT_ADMIN_EMAIL=admin@example.com +``` + +### 多租户 Docker Compose + +```yaml +version: '3.8' + +services: + app: + build: . + ports: + - "3000:3000" + environment: + - NODE_ENV=production + - MULTI_TENANT=true + - TENANT_STRATEGY=virtualCity + - TENANT_IDENTIFIER=subdomain + - DATABASE_URL=postgresql://postgres:password@db:5432/saas_app + - SESSION_SECRET=${SESSION_SECRET} + depends_on: + - db + - redis + deploy: + replicas: 3 + + db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=saas_app + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=password + volumes: + - db-data:/var/lib/postgresql/data + + redis: + image: redis:7-alpine + volumes: + - redis-data:/data + +volumes: + db-data: + redis-data: +``` + +### 租户管理 + +```typescript +// 创建新租户 +const tenant = await app.tenants.create({ + id: 'acme-corp', + name: 'Acme Corporation', + domain: 'acme.myapp.com', + plan: 'enterprise', + settings: { + maxUsers: 100, + maxStorage: '100GB' + } +}) + +// 列出所有租户 +const tenants = await app.tenants.list() + +// 通过 ID 获取租户 +const tenant = await app.tenants.get('acme-corp') + +// 更新租户 +await app.tenants.update('acme-corp', { + plan: 'enterprise-plus', + settings: { + maxUsers: 500 + } +}) + +// 删除租户(含数据) +await app.tenants.delete('acme-corp', { + deleteData: true // 永久删除租户数据 +}) +``` + +## 数据库迁移 + +跨部署管理 schema 变更: + +```bash +# 生成迁移 +objectos migrate create add-priority-field + +# 应用迁移 +objectos migrate up + +# 回滚迁移 +objectos migrate down + +# 检查迁移状态 +objectos migrate status +``` + +迁移文件示例: + +```typescript +// migrations/20240115_add_priority_field.ts +export async function up(db) { + await db.addField('tasks', { + priority: { + type: 'select', + options: ['low', 'medium', 'high'], + defaultValue: 'medium' + } + }) +} + +export async function down(db) { + await db.removeField('tasks', 'priority') +} +``` + +## 监控与日志 + +### 应用日志 + +```typescript +const app = new ObjectOS({ + logging: { + level: 'info', // debug, info, warn, error + format: 'json', // json, text + outputs: [ + { type: 'console' }, + { type: 'file', path: './logs/app.log' }, + { + type: 'cloudwatch', + group: '/aws/objectos/myapp', + stream: 'production' + } + ] + } +}) +``` + +### 健康检查 + +```typescript +// 内置健康检查端点 +// GET /health + +{ + "status": "healthy", + "database": "connected", + "uptime": 86400, + "memory": { + "used": "256MB", + "total": "1GB" + } +} +``` + +### 指标 + +```typescript +const app = new ObjectOS({ + metrics: { + enabled: true, + endpoint: '/metrics', // Prometheus 指标 + include: [ + 'http_requests', + 'db_queries', + 'cache_hits', + 'active_users' + ] + } +}) +``` + +## 备份与恢复 + +### 自动备份 + +```bash +# 设置自动备份 (PostgreSQL) +# 在 cron 中使用 pg_dump + +0 2 * * * docker exec db pg_dump -U postgres myapp > /backups/myapp-$(date +\%Y\%m\%d).sql + +# 从备份恢复 +docker exec -i db psql -U postgres myapp < /backups/myapp-20240115.sql +``` + +### ObjectOS 备份 API + +```typescript +// 创建备份 +await app.backup.create({ + path: './backups/myapp-backup.db', + includeFiles: true +}) + +// 从备份恢复 +await app.backup.restore({ + path: './backups/myapp-backup.db' +}) +``` + +## 下一步 + +您现在了解了如何以各种配置部署 ObjectOS: + +- **[平台架构](./platform-architecture)** - 回顾平台架构 +- **[身份与访问](./identity-access)** - 配置安全和权限 diff --git a/content/docs/04-objectos/identity-access.en.mdx b/content/docs/04-objectos/identity-access.en.mdx new file mode 100644 index 0000000..6630d96 --- /dev/null +++ b/content/docs/04-objectos/identity-access.en.mdx @@ -0,0 +1,566 @@ +--- +title: Identity & Access Management +description: User system, RBAC model, and field-level security in ObjectOS +--- + +# Identity & Access Management + +ObjectOS provides a comprehensive identity and access management system with built-in user management, role-based access control (RBAC), and field-level security (FLS). This enables you to build secure applications with fine-grained permissions. + +## User System + +ObjectOS includes a built-in user system based on the `_users` and `_roles` system objects. + +### The `_users` Object + +Every ObjectOS application has a system object `_users` that stores user information: + +```typescript +// Built-in _users schema (system object) +{ + _users: { + fields: { + username: { type: 'text', required: true, unique: true }, + email: { type: 'email', required: true, unique: true }, + password: { type: 'password' }, // Hashed automatically + name: { type: 'text' }, + profile: { type: 'lookup', reference_to: '_profiles' }, + roles: { type: 'lookup', reference_to: '_roles', multiple: true }, + active: { type: 'boolean', defaultValue: true }, + last_login: { type: 'datetime' }, + created: { type: 'datetime' }, + modified: { type: 'datetime' } + } + } +} +``` + +### User Authentication + +ObjectOS supports multiple authentication methods: + +```typescript +const app = new ObjectOS({ + auth: { + enabled: true, + providers: { + // Email/Password authentication + email: { + enabled: true, + requireVerification: true, + passwordPolicy: { + minLength: 8, + requireUppercase: true, + requireNumbers: true, + requireSpecialChars: false + } + }, + + // OAuth providers + oauth: { + google: { + clientId: process.env.GOOGLE_CLIENT_ID, + clientSecret: process.env.GOOGLE_CLIENT_SECRET + }, + github: { + clientId: process.env.GITHUB_CLIENT_ID, + clientSecret: process.env.GITHUB_CLIENT_SECRET + } + }, + + // SSO/SAML + saml: { + enabled: false, + entryPoint: 'https://sso.example.com', + cert: process.env.SAML_CERT + } + }, + + // Session configuration + session: { + secret: process.env.SESSION_SECRET, + maxAge: 86400000, // 24 hours + secure: true, + sameSite: 'strict' + } + } +}) +``` + +### User Registration + +```typescript +// Register a new user +const newUser = await app.auth.register({ + username: 'john_doe', + email: 'john@example.com', + password: 'SecurePass123', + name: 'John Doe' +}) + +// User is automatically created in _users object +// Password is hashed using bcrypt +``` + +### User Login + +```typescript +// Email/password login +const session = await app.auth.login({ + email: 'john@example.com', + password: 'SecurePass123' +}) + +// Returns session token +console.log(session.token) +console.log(session.user) +``` + +## Role-Based Access Control (RBAC) + +ObjectOS implements a flexible RBAC system using the `_roles` object. + +### The `_roles` Object + +```typescript +// Built-in _roles schema (system object) +{ + _roles: { + fields: { + name: { type: 'text', required: true, unique: true }, + label: { type: 'text' }, + description: { type: 'text' }, + permissions: { type: 'json' }, // Permission definitions + users: { type: 'lookup', reference_to: '_users', multiple: true }, + created: { type: 'datetime' }, + modified: { type: 'datetime' } + } + } +} +``` + +### Creating Roles + +```typescript +// Create an Admin role +await app.mutation('_roles', { + action: 'insert', + data: { + name: 'admin', + label: 'Administrator', + description: 'Full system access', + permissions: { + // Object-level permissions + objects: { + '*': { // All objects + allowCreate: true, + allowRead: true, + allowEdit: true, + allowDelete: true + } + }, + // System permissions + system: { + manageUsers: true, + manageRoles: true, + viewAuditLog: true + } + } + } +}) + +// Create a User role +await app.mutation('_roles', { + action: 'insert', + data: { + name: 'user', + label: 'Standard User', + description: 'Basic access', + permissions: { + objects: { + tasks: { + allowCreate: true, + allowRead: true, + allowEdit: "owner eq $user.id", // Can only edit own tasks + allowDelete: "owner eq $user.id" + }, + projects: { + allowCreate: false, + allowRead: true, + allowEdit: false, + allowDelete: false + } + } + } + } +}) +``` + +### Assigning Roles to Users + +```typescript +// Assign role to user +await app.mutation('_users', { + action: 'update', + _id: userId, + data: { + roles: [adminRoleId, userRoleId] + } +}) + +// Users can have multiple roles +// Permissions are merged (most permissive wins) +``` + +### Permission Schema + +ObjectOS supports rich permission expressions: + +```typescript +{ + permissions: { + objects: { + // Specific object + contacts: { + // Boolean permissions + allowCreate: true, + allowRead: true, + + // Conditional permissions (filter expression) + allowEdit: "owner eq $user.id or assigned_to eq $user.id", + allowDelete: "owner eq $user.id", + + // Field-level permissions (see FLS section) + fieldPermissions: { + salary: { + allowRead: "$user.role eq 'hr'", + allowEdit: "$user.role eq 'hr'" + } + } + }, + + // Wildcard for all objects + '*': { + allowRead: true + } + }, + + // System-level permissions + system: { + manageUsers: false, + manageRoles: false, + viewAuditLog: false, + exportData: false + } + } +} +``` + +## Field-Level Security (FLS) + +Field-Level Security allows you to hide or protect sensitive fields based on user permissions. + +### Defining Field-Level Security + +Define FLS in the schema: + +```typescript +const schema = { + objects: { + employees: { + fields: { + name: { type: 'text' }, + email: { type: 'email' }, + department: { type: 'text' }, + + // Sensitive field + salary: { + type: 'number', + security: { + read: "$user.role eq 'hr' or $user.role eq 'admin'", + edit: "$user.role eq 'hr'" + } + }, + + // Highly sensitive field + ssn: { + type: 'text', + security: { + read: "$user.role eq 'admin'", + edit: false // Never editable + } + } + } + } + } +} +``` + +### FLS Behavior + +When FLS is applied: + +```typescript +// HR user queries employees +const employees = await app.query('employees', {}, { user: hrUser }) + +// Result includes salary field: +// [ +// { name: 'John', email: 'john@ex.com', salary: 50000, ... } +// ] + +// Regular user queries employees +const employees = await app.query('employees', {}, { user: regularUser }) + +// Result EXCLUDES salary field: +// [ +// { name: 'John', email: 'john@ex.com', ... } +// ] +// Field is completely hidden, not just null +``` + +### FLS in Role Permissions + +You can also define FLS in role permissions: + +```typescript +await app.mutation('_roles', { + action: 'insert', + data: { + name: 'sales', + label: 'Sales Team', + permissions: { + objects: { + customers: { + allowRead: true, + allowEdit: "owner eq $user.id", + + // Field-level permissions + fieldPermissions: { + credit_card: { + allowRead: false, // Never see credit cards + allowEdit: false + }, + internal_notes: { + allowRead: "$user.department eq 'sales'", + allowEdit: "$user.role eq 'sales_manager'" + } + } + } + } + } + } +}) +``` + +## Permission Evaluation + +ObjectOS evaluates permissions in the following order: + +### 1. System Object Protection + +```typescript +// System objects (_users, _roles, etc.) have default protection +// Only admins can access by default +``` + +### 2. Object-Level Permissions + +```typescript +// Check if user can perform action on object +if (!hasPermission(user, 'contacts', 'read')) { + throw new PermissionError('Cannot read contacts') +} +``` + +### 3. Record-Level Permissions + +```typescript +// Filter records based on conditional permissions +const filters = buildPermissionFilters(user, 'contacts', 'read') +// e.g., [['owner', 'eq', user.id], 'or', ['public', 'eq', true]] +``` + +### 4. Field-Level Security + +```typescript +// Remove fields user cannot access +const visibleFields = filterFieldsByPermissions(user, 'contacts', fields) +``` + +### Permission Context Variables + +Available in permission expressions: + +```typescript +// Permission expression context +{ + $user: { + id: 'user_id', + username: 'john_doe', + email: 'john@example.com', + role: 'admin', // Primary role + roles: ['admin', 'user'], // All roles + department: 'engineering', + // ... custom user fields + }, + + $record: { + // Current record being accessed + owner: 'record_owner_id', + status: 'active', + // ... all record fields + } +} + +// Example permission expressions: +"owner eq $user.id" // User owns record +"$user.role eq 'admin'" // User is admin +"status eq 'published' or owner eq $user.id" // Published or owned +"department eq $user.department" // Same department +``` + +## Advanced Scenarios + +### Hierarchical Roles + +```typescript +// Define role hierarchy +const roles = [ + { + name: 'admin', + inherits: [] // Top level + }, + { + name: 'manager', + inherits: ['user'] // Has all user permissions plus more + }, + { + name: 'user', + inherits: [] + } +] +``` + +### Dynamic Permissions + +```typescript +// Compute permissions at runtime +const schema = { + objects: { + documents: { + permission_set: { + user_permission: { + allowRead: async (user, record) => { + // Custom logic + if (record.public) return true + if (record.owner === user.id) return true + + // Check team membership + const team = await app.query('teams', { + filters: [ + ['members', 'contains', user.id], + ['documents', 'contains', record.id] + ] + }) + + return team.length > 0 + } + } + } + } + } +} +``` + +### Audit Logging + +```typescript +const app = new ObjectOS({ + audit: { + enabled: true, + logLevel: 'all', // 'all', 'mutations', 'sensitive' + logFields: true, // Log field-level changes + retention: 90 // Days to retain logs + } +}) + +// Query audit log +const auditLog = await app.query('_audit_log', { + filters: [ + ['user_id', 'eq', userId], + ['action', 'eq', 'delete'] + ], + sort: 'created desc' +}) +``` + +## Security Best Practices + +### 1. Principle of Least Privilege + +```typescript +// Start with minimal permissions +// Grant additional permissions as needed +permissions: { + objects: { + '*': { + allowRead: false, + allowCreate: false, + allowEdit: false, + allowDelete: false + }, + // Only grant what's needed + tasks: { + allowRead: true, + allowCreate: true + } + } +} +``` + +### 2. Protect Sensitive Fields + +```typescript +// Always use FLS for sensitive data +{ + password: { + type: 'password', + security: { + read: false, // Never readable + edit: false // Only via special API + } + }, + api_key: { + type: 'text', + security: { + read: "$user.id eq $record.owner", + edit: "$user.id eq $record.owner" + } + } +} +``` + +### 3. Regular Permission Audits + +```typescript +// Review role permissions regularly +const roles = await app.query('_roles', {}) + +for (const role of roles) { + console.log(`Role: ${role.name}`) + console.log(JSON.stringify(role.permissions, null, 2)) + + // Check for overly permissive settings + if (role.permissions.objects['*']?.allowDelete) { + console.warn('⚠️ Role has wildcard delete permission') + } +} +``` + +## Next Steps + +Now that you understand identity and access management: + +- **[Deployment & Operations](./deployment)** - Learn how to deploy ObjectOS applications +- **[Platform Architecture](./platform-architecture)** - Review the platform architecture diff --git a/content/docs/04-objectos/identity-access.zh-CN.mdx b/content/docs/04-objectos/identity-access.zh-CN.mdx new file mode 100644 index 0000000..4305b31 --- /dev/null +++ b/content/docs/04-objectos/identity-access.zh-CN.mdx @@ -0,0 +1,566 @@ +--- +title: 身份与访问管理 +description: ObjectOS 中的用户系统、RBAC 模型和字段级安全 +--- + +# 身份与访问管理 + +ObjectOS 提供全面的身份与访问管理系统,包括内置的用户管理、基于角色的访问控制(RBAC)和字段级安全(FLS)。这使您能够构建具有细粒度权限的安全应用。 + +## 用户系统 + +ObjectOS 包含基于 `_users` 和 `_roles` 系统对象的内置用户系统。 + +### `_users` 对象 + +每个 ObjectOS 应用都有一个系统对象 `_users` 用于存储用户信息: + +```typescript +// 内置 _users schema(系统对象) +{ + _users: { + fields: { + username: { type: 'text', required: true, unique: true }, + email: { type: 'email', required: true, unique: true }, + password: { type: 'password' }, // 自动加密 + name: { type: 'text' }, + profile: { type: 'lookup', reference_to: '_profiles' }, + roles: { type: 'lookup', reference_to: '_roles', multiple: true }, + active: { type: 'boolean', defaultValue: true }, + last_login: { type: 'datetime' }, + created: { type: 'datetime' }, + modified: { type: 'datetime' } + } + } +} +``` + +### 用户认证 + +ObjectOS 支持多种认证方式: + +```typescript +const app = new ObjectOS({ + auth: { + enabled: true, + providers: { + // 邮箱/密码认证 + email: { + enabled: true, + requireVerification: true, + passwordPolicy: { + minLength: 8, + requireUppercase: true, + requireNumbers: true, + requireSpecialChars: false + } + }, + + // OAuth 提供商 + oauth: { + google: { + clientId: process.env.GOOGLE_CLIENT_ID, + clientSecret: process.env.GOOGLE_CLIENT_SECRET + }, + github: { + clientId: process.env.GITHUB_CLIENT_ID, + clientSecret: process.env.GITHUB_CLIENT_SECRET + } + }, + + // SSO/SAML + saml: { + enabled: false, + entryPoint: 'https://sso.example.com', + cert: process.env.SAML_CERT + } + }, + + // Session 配置 + session: { + secret: process.env.SESSION_SECRET, + maxAge: 86400000, // 24 小时 + secure: true, + sameSite: 'strict' + } + } +}) +``` + +### 用户注册 + +```typescript +// 注册新用户 +const newUser = await app.auth.register({ + username: 'john_doe', + email: 'john@example.com', + password: 'SecurePass123', + name: 'John Doe' +}) + +// 用户自动在 _users 对象中创建 +// 密码使用 bcrypt 加密 +``` + +### 用户登录 + +```typescript +// 邮箱/密码登录 +const session = await app.auth.login({ + email: 'john@example.com', + password: 'SecurePass123' +}) + +// 返回 session token +console.log(session.token) +console.log(session.user) +``` + +## 基于角色的访问控制(RBAC) + +ObjectOS 使用 `_roles` 对象实现灵活的 RBAC 系统。 + +### `_roles` 对象 + +```typescript +// 内置 _roles schema(系统对象) +{ + _roles: { + fields: { + name: { type: 'text', required: true, unique: true }, + label: { type: 'text' }, + description: { type: 'text' }, + permissions: { type: 'json' }, // 权限定义 + users: { type: 'lookup', reference_to: '_users', multiple: true }, + created: { type: 'datetime' }, + modified: { type: 'datetime' } + } + } +} +``` + +### 创建角色 + +```typescript +// 创建管理员角色 +await app.mutation('_roles', { + action: 'insert', + data: { + name: 'admin', + label: '管理员', + description: '完整系统访问权限', + permissions: { + // 对象级权限 + objects: { + '*': { // 所有对象 + allowCreate: true, + allowRead: true, + allowEdit: true, + allowDelete: true + } + }, + // 系统权限 + system: { + manageUsers: true, + manageRoles: true, + viewAuditLog: true + } + } + } +}) + +// 创建普通用户角色 +await app.mutation('_roles', { + action: 'insert', + data: { + name: 'user', + label: '普通用户', + description: '基本访问权限', + permissions: { + objects: { + tasks: { + allowCreate: true, + allowRead: true, + allowEdit: "owner eq $user.id", // 只能编辑自己的任务 + allowDelete: "owner eq $user.id" + }, + projects: { + allowCreate: false, + allowRead: true, + allowEdit: false, + allowDelete: false + } + } + } + } +}) +``` + +### 为用户分配角色 + +```typescript +// 为用户分配角色 +await app.mutation('_users', { + action: 'update', + _id: userId, + data: { + roles: [adminRoleId, userRoleId] + } +}) + +// 用户可以拥有多个角色 +// 权限会合并(最宽松的权限生效) +``` + +### 权限 Schema + +ObjectOS 支持丰富的权限表达式: + +```typescript +{ + permissions: { + objects: { + // 特定对象 + contacts: { + // 布尔权限 + allowCreate: true, + allowRead: true, + + // 条件权限(过滤表达式) + allowEdit: "owner eq $user.id or assigned_to eq $user.id", + allowDelete: "owner eq $user.id", + + // 字段级权限(见 FLS 部分) + fieldPermissions: { + salary: { + allowRead: "$user.role eq 'hr'", + allowEdit: "$user.role eq 'hr'" + } + } + }, + + // 所有对象的通配符 + '*': { + allowRead: true + } + }, + + // 系统级权限 + system: { + manageUsers: false, + manageRoles: false, + viewAuditLog: false, + exportData: false + } + } +} +``` + +## 字段级安全(FLS) + +字段级安全允许您根据用户权限隐藏或保护敏感字段。 + +### 定义字段级安全 + +在 schema 中定义 FLS: + +```typescript +const schema = { + objects: { + employees: { + fields: { + name: { type: 'text' }, + email: { type: 'email' }, + department: { type: 'text' }, + + // 敏感字段 + salary: { + type: 'number', + security: { + read: "$user.role eq 'hr' or $user.role eq 'admin'", + edit: "$user.role eq 'hr'" + } + }, + + // 高度敏感字段 + ssn: { + type: 'text', + security: { + read: "$user.role eq 'admin'", + edit: false // 永不可编辑 + } + } + } + } + } +} +``` + +### FLS 行为 + +当应用 FLS 时: + +```typescript +// HR 用户查询员工 +const employees = await app.query('employees', {}, { user: hrUser }) + +// 结果包含 salary 字段: +// [ +// { name: 'John', email: 'john@ex.com', salary: 50000, ... } +// ] + +// 普通用户查询员工 +const employees = await app.query('employees', {}, { user: regularUser }) + +// 结果不包含 salary 字段: +// [ +// { name: 'John', email: 'john@ex.com', ... } +// ] +// 字段完全隐藏,而不只是为 null +``` + +### 角色权限中的 FLS + +您也可以在角色权限中定义 FLS: + +```typescript +await app.mutation('_roles', { + action: 'insert', + data: { + name: 'sales', + label: '销售团队', + permissions: { + objects: { + customers: { + allowRead: true, + allowEdit: "owner eq $user.id", + + // 字段级权限 + fieldPermissions: { + credit_card: { + allowRead: false, // 永远看不到信用卡 + allowEdit: false + }, + internal_notes: { + allowRead: "$user.department eq 'sales'", + allowEdit: "$user.role eq 'sales_manager'" + } + } + } + } + } + } +}) +``` + +## 权限评估 + +ObjectOS 按以下顺序评估权限: + +### 1. 系统对象保护 + +```typescript +// 系统对象(_users、_roles 等)有默认保护 +// 默认只有管理员可以访问 +``` + +### 2. 对象级权限 + +```typescript +// 检查用户是否可以对对象执行操作 +if (!hasPermission(user, 'contacts', 'read')) { + throw new PermissionError('无法读取联系人') +} +``` + +### 3. 记录级权限 + +```typescript +// 基于条件权限过滤记录 +const filters = buildPermissionFilters(user, 'contacts', 'read') +// 例如: [['owner', 'eq', user.id], 'or', ['public', 'eq', true]] +``` + +### 4. 字段级安全 + +```typescript +// 移除用户无法访问的字段 +const visibleFields = filterFieldsByPermissions(user, 'contacts', fields) +``` + +### 权限上下文变量 + +权限表达式中可用的变量: + +```typescript +// 权限表达式上下文 +{ + $user: { + id: 'user_id', + username: 'john_doe', + email: 'john@example.com', + role: 'admin', // 主要角色 + roles: ['admin', 'user'], // 所有角色 + department: 'engineering', + // ... 自定义用户字段 + }, + + $record: { + // 当前访问的记录 + owner: 'record_owner_id', + status: 'active', + // ... 所有记录字段 + } +} + +// 权限表达式示例: +"owner eq $user.id" // 用户拥有记录 +"$user.role eq 'admin'" // 用户是管理员 +"status eq 'published' or owner eq $user.id" // 已发布或拥有 +"department eq $user.department" // 同部门 +``` + +## 高级场景 + +### 分层角色 + +```typescript +// 定义角色层次 +const roles = [ + { + name: 'admin', + inherits: [] // 顶层 + }, + { + name: 'manager', + inherits: ['user'] // 拥有所有用户权限加更多 + }, + { + name: 'user', + inherits: [] + } +] +``` + +### 动态权限 + +```typescript +// 运行时计算权限 +const schema = { + objects: { + documents: { + permission_set: { + user_permission: { + allowRead: async (user, record) => { + // 自定义逻辑 + if (record.public) return true + if (record.owner === user.id) return true + + // 检查团队成员 + const team = await app.query('teams', { + filters: [ + ['members', 'contains', user.id], + ['documents', 'contains', record.id] + ] + }) + + return team.length > 0 + } + } + } + } + } +} +``` + +### 审计日志 + +```typescript +const app = new ObjectOS({ + audit: { + enabled: true, + logLevel: 'all', // 'all', 'mutations', 'sensitive' + logFields: true, // 记录字段级变更 + retention: 90 // 保留日志天数 + } +}) + +// 查询审计日志 +const auditLog = await app.query('_audit_log', { + filters: [ + ['user_id', 'eq', userId], + ['action', 'eq', 'delete'] + ], + sort: 'created desc' +}) +``` + +## 安全最佳实践 + +### 1. 最小权限原则 + +```typescript +// 从最小权限开始 +// 根据需要授予额外权限 +permissions: { + objects: { + '*': { + allowRead: false, + allowCreate: false, + allowEdit: false, + allowDelete: false + }, + // 只授予需要的权限 + tasks: { + allowRead: true, + allowCreate: true + } + } +} +``` + +### 2. 保护敏感字段 + +```typescript +// 始终对敏感数据使用 FLS +{ + password: { + type: 'password', + security: { + read: false, // 永不可读 + edit: false // 只能通过特殊 API + } + }, + api_key: { + type: 'text', + security: { + read: "$user.id eq $record.owner", + edit: "$user.id eq $record.owner" + } + } +} +``` + +### 3. 定期权限审计 + +```typescript +// 定期审查角色权限 +const roles = await app.query('_roles', {}) + +for (const role of roles) { + console.log(`角色: ${role.name}`) + console.log(JSON.stringify(role.permissions, null, 2)) + + // 检查过度宽松的设置 + if (role.permissions.objects['*']?.allowDelete) { + console.warn('⚠️ 角色拥有通配符删除权限') + } +} +``` + +## 下一步 + +现在您已经理解了身份与访问管理: + +- **[部署与运维](./deployment)** - 了解如何部署 ObjectOS 应用 +- **[平台架构](./platform-architecture)** - 回顾平台架构 diff --git a/content/docs/04-objectos/index.en.mdx b/content/docs/04-objectos/index.en.mdx new file mode 100644 index 0000000..3563c55 --- /dev/null +++ b/content/docs/04-objectos/index.en.mdx @@ -0,0 +1,227 @@ +--- +title: ObjectOS - The Platform +description: Runtime platform that binds data and UI together with Local-First architecture +--- + +# ObjectOS - The Platform + +ObjectOS is the runtime platform that brings ObjectQL (data) and ObjectUI (interface) together, providing a complete application environment with identity management, access control, and flexible deployment options. + +## What is ObjectOS? + +ObjectOS is the **runtime platform** that orchestrates the entire ObjectStack. It binds the data layer (ObjectQL) with the presentation layer (ObjectUI) while providing essential platform services like user management, security, and deployment capabilities. + +### Key Capabilities + +- 🔗 **QL-UI Binding** - Seamlessly connects data engine with UI framework +- 🏠 **Local-First Architecture** - Run applications entirely on local devices +- 👥 **Identity & Access Management** - Built-in user system with RBAC +- 🔒 **Field-Level Security** - Fine-grained data access control +- 🚀 **Flexible Deployment** - Standalone .oos files or Docker containers +- 🏢 **Multi-Tenant Ready** - SaaS-ready with tenant isolation + +## How It Works + +ObjectOS operates as the runtime environment that: + +### 1. Binds Data and UI + +ObjectOS creates a bidirectional bridge between ObjectQL and ObjectUI: + +```typescript +// ObjectOS automatically wires up data and UI +const app = new ObjectOS({ + schema: './schema', // ObjectQL schemas + pages: './pages', // ObjectUI components + storage: './data' // Local-First storage +}) + +await app.start() +``` + +### 2. Manages Platform Services + +Provides essential platform capabilities: + +- **User System** - Authentication and user profiles in `_users` object +- **Role Management** - Flexible role definitions in `_roles` object +- **Permission Control** - RBAC with field-level security +- **Data Isolation** - Multi-tenant data segregation + +### 3. Enables Flexible Deployment + +Deploy applications in multiple modes: + +```bash +# Standalone mode - single .oos file +./myapp.oos + +# Server mode - Docker container +docker run -p 3000:3000 myapp:latest + +# Multi-tenant SaaS mode +docker run -e MULTI_TENANT=true myapp:latest +``` + +## Platform Architecture + +``` +┌─────────────────────────────────────────────────────┐ +│ Application Layer │ +│ (Your Business Logic) │ +└──────────────────────┬──────────────────────────────┘ + │ +┌──────────────────────▼──────────────────────────────┐ +│ ObjectOS Platform │ +│ ┌──────────────────────────────────────────────┐ │ +│ │ Identity & Access Layer │ │ +│ │ ┌────────┬────────┬──────────────────┐ │ │ +│ │ │ Users │ Roles │ Permissions (FLS)│ │ │ +│ │ └────────┴────────┴──────────────────┘ │ │ +│ └──────────────────────────────────────────────┘ │ +│ ┌──────────────────────────────────────────────┐ │ +│ │ QL-UI Binding Layer │ │ +│ │ ┌──────────────────┬──────────────────┐ │ │ +│ │ │ ObjectUI │ ObjectQL │ │ │ +│ │ │ (Presentation) │ (Data Engine) │ │ │ +│ │ └──────────────────┴──────────────────┘ │ │ +│ └──────────────────────────────────────────────┘ │ +│ ┌──────────────────────────────────────────────┐ │ +│ │ Storage Abstraction │ │ +│ │ ┌─────────┬──────────┬──────────────────┐ │ │ +│ │ │ SQLite │ MySQL │ PostgreSQL │ │ │ +│ │ │ (Local) │ (Server) │ (Server) │ │ │ +│ │ └─────────┴──────────┴──────────────────┘ │ │ +│ └──────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────┘ +``` + +## Use Cases + +### Personal Productivity Apps + +Build Local-First applications that run entirely offline: + +```typescript +const app = new ObjectOS({ + mode: 'standalone', + storage: '~/.myapp/data.db' +}) +``` + +### Team Collaboration Tools + +Deploy server-based applications with user management: + +```typescript +const app = new ObjectOS({ + mode: 'server', + database: 'mysql://localhost/myapp', + auth: { + providers: ['email', 'oauth'] + } +}) +``` + +### Multi-Tenant SaaS Platforms + +Build SaaS applications with complete tenant isolation: + +```typescript +const app = new ObjectOS({ + mode: 'multi-tenant', + tenantStrategy: 'virtualCity', + database: 'postgresql://localhost/saas' +}) +``` + +## What You'll Learn + +In this section, you will discover: + +- ✅ **Platform Architecture** - How ObjectOS binds QL and UI together +- ✅ **Identity & Access** - User system, RBAC, and field-level security +- ✅ **Deployment & Operations** - Standalone, server, and multi-tenant modes +- ✅ **Best Practices** - Security configurations and optimization techniques + +## Getting Started + +Ready to dive in? Start with: + +1. **[Platform Architecture](./platform-architecture)** - Understand how ObjectOS works +2. **[Identity & Access](./identity-access)** - Set up users and permissions +3. **[Deployment](./deployment)** - Deploy your application + +## Quick Example + +Here's a complete example to get you started: + +```typescript +import { ObjectOS } from '@objectstack/os' + +// 1. Initialize ObjectOS with schema and UI +const app = new ObjectOS({ + mode: 'standalone', + storage: './myapp.db', + schema: { + objects: { + tasks: { + fields: { + name: { type: 'text', required: true }, + owner: { type: 'lookup', reference_to: '_users' } + }, + permission_set: { + user_permission: { + allowCreate: true, + allowRead: true, + allowEdit: "owner eq $user.id", + allowDelete: "owner eq $user.id" + } + } + } + } + }, + pages: { + home: { + type: 'list', + object: 'tasks' + } + } +}) + +// 2. Start the application +await app.start() + +console.log('ObjectOS running at http://localhost:3000') +``` + +## Local-First Architecture + +ObjectOS is designed with Local-First principles: + +- **Offline-First** - Applications work without network connectivity +- **User Ownership** - Data stays on user's device in standalone mode +- **Sync When Ready** - Optional synchronization with cloud services +- **Fast Performance** - SQLite-powered local storage + +```typescript +// Local-First configuration +const app = new ObjectOS({ + mode: 'standalone', + storage: './local-data.db', + sync: { + enabled: false, // Fully offline + // Or enable sync: + // remote: 'https://sync.example.com', + // strategy: 'manual' + } +}) +``` + +## Next Steps + +Begin your ObjectOS journey: + +- **New to ObjectOS?** Start with [Platform Architecture](./platform-architecture) +- **Setting up security?** Jump to [Identity & Access](./identity-access) +- **Ready to deploy?** Check [Deployment & Operations](./deployment) diff --git a/content/docs/04-objectos/index.zh-CN.mdx b/content/docs/04-objectos/index.zh-CN.mdx new file mode 100644 index 0000000..4a67272 --- /dev/null +++ b/content/docs/04-objectos/index.zh-CN.mdx @@ -0,0 +1,227 @@ +--- +title: ObjectOS - 运行时与平台 +description: 将数据与界面绑定在一起的运行时平台,实现本地优先架构 +--- + +# ObjectOS - 运行时与平台 + +ObjectOS 是将 ObjectQL(数据)和 ObjectUI(界面)整合在一起的运行时平台,提供完整的应用环境,包括身份管理、访问控制和灵活的部署选项。 + +## 什么是 ObjectOS? + +ObjectOS 是**运行时平台**,它编排整个 ObjectStack。它将数据层(ObjectQL)与展示层(ObjectUI)绑定,同时提供用户管理、安全和部署等关键平台服务。 + +### 核心能力 + +- 🔗 **QL-UI 绑定** - 无缝连接数据引擎与 UI 框架 +- 🏠 **本地优先架构** - 应用完全在本地设备上运行 +- 👥 **身份与访问管理** - 内置用户系统和 RBAC +- 🔒 **字段级安全** - 细粒度的数据访问控制 +- 🚀 **灵活部署** - 独立 .oos 文件或 Docker 容器 +- 🏢 **多租户就绪** - 支持 SaaS 场景的租户隔离 + +## 工作原理 + +ObjectOS 作为运行时环境提供以下能力: + +### 1. 绑定数据与界面 + +ObjectOS 在 ObjectQL 和 ObjectUI 之间创建双向桥接: + +```typescript +// ObjectOS 自动连接数据和界面 +const app = new ObjectOS({ + schema: './schema', // ObjectQL schemas + pages: './pages', // ObjectUI 组件 + storage: './data' // 本地优先存储 +}) + +await app.start() +``` + +### 2. 管理平台服务 + +提供核心平台能力: + +- **用户系统** - `_users` 对象中的认证和用户资料 +- **角色管理** - `_roles` 对象中的灵活角色定义 +- **权限控制** - 支持字段级安全的 RBAC +- **数据隔离** - 多租户数据隔离 + +### 3. 支持灵活部署 + +以多种模式部署应用: + +```bash +# 独立模式 - 单个 .oos 文件 +./myapp.oos + +# 服务器模式 - Docker 容器 +docker run -p 3000:3000 myapp:latest + +# 多租户 SaaS 模式 +docker run -e MULTI_TENANT=true myapp:latest +``` + +## 平台架构 + +``` +┌─────────────────────────────────────────────────────┐ +│ 应用层 │ +│ (您的业务逻辑) │ +└──────────────────────┬──────────────────────────────┘ + │ +┌──────────────────────▼──────────────────────────────┐ +│ ObjectOS 平台 │ +│ ┌──────────────────────────────────────────────┐ │ +│ │ 身份与访问层 │ │ +│ │ ┌────────┬────────┬──────────────────┐ │ │ +│ │ │ 用户 │ 角色 │ 权限(FLS) │ │ │ +│ │ └────────┴────────┴──────────────────┘ │ │ +│ └──────────────────────────────────────────────┘ │ +│ ┌──────────────────────────────────────────────┐ │ +│ │ QL-UI 绑定层 │ │ +│ │ ┌──────────────────┬──────────────────┐ │ │ +│ │ │ ObjectUI │ ObjectQL │ │ │ +│ │ │ (展示层) │ (数据引擎) │ │ │ +│ │ └──────────────────┴──────────────────┘ │ │ +│ └──────────────────────────────────────────────┘ │ +│ ┌──────────────────────────────────────────────┐ │ +│ │ 存储抽象层 │ │ +│ │ ┌─────────┬──────────┬──────────────────┐ │ │ +│ │ │ SQLite │ MySQL │ PostgreSQL │ │ │ +│ │ │ (本地) │ (服务器) │ (服务器) │ │ │ +│ │ └─────────┴──────────┴──────────────────┘ │ │ +│ └──────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────┘ +``` + +## 使用场景 + +### 个人生产力应用 + +构建完全离线运行的本地优先应用: + +```typescript +const app = new ObjectOS({ + mode: 'standalone', + storage: '~/.myapp/data.db' +}) +``` + +### 团队协作工具 + +部署带用户管理的服务器应用: + +```typescript +const app = new ObjectOS({ + mode: 'server', + database: 'mysql://localhost/myapp', + auth: { + providers: ['email', 'oauth'] + } +}) +``` + +### 多租户 SaaS 平台 + +构建具有完整租户隔离的 SaaS 应用: + +```typescript +const app = new ObjectOS({ + mode: 'multi-tenant', + tenantStrategy: 'virtualCity', + database: 'postgresql://localhost/saas' +}) +``` + +## 您将学到什么 + +在本章节中,您将了解: + +- ✅ **平台架构** - ObjectOS 如何绑定 QL 和 UI +- ✅ **身份与访问** - 用户系统、RBAC 和字段级安全 +- ✅ **部署与运维** - 独立模式、服务器模式和多租户模式 +- ✅ **最佳实践** - 安全配置和优化技巧 + +## 开始学习 + +准备好深入了解了吗?从这里开始: + +1. **[平台架构](./platform-architecture)** - 理解 ObjectOS 的工作原理 +2. **[身份与访问](./identity-access)** - 设置用户和权限 +3. **[部署与运维](./deployment)** - 部署您的应用 + +## 快速示例 + +这里有一个完整的示例帮您入门: + +```typescript +import { ObjectOS } from '@objectstack/os' + +// 1. 使用 schema 和 UI 初始化 ObjectOS +const app = new ObjectOS({ + mode: 'standalone', + storage: './myapp.db', + schema: { + objects: { + tasks: { + fields: { + name: { type: 'text', required: true }, + owner: { type: 'lookup', reference_to: '_users' } + }, + permission_set: { + user_permission: { + allowCreate: true, + allowRead: true, + allowEdit: "owner eq $user.id", + allowDelete: "owner eq $user.id" + } + } + } + } + }, + pages: { + home: { + type: 'list', + object: 'tasks' + } + } +}) + +// 2. 启动应用 +await app.start() + +console.log('ObjectOS 运行在 http://localhost:3000') +``` + +## 本地优先架构 + +ObjectOS 采用本地优先原则设计: + +- **离线优先** - 应用无需网络连接即可工作 +- **用户拥有数据** - 独立模式下数据保存在用户设备上 +- **按需同步** - 可选的云服务同步 +- **快速性能** - SQLite 驱动的本地存储 + +```typescript +// 本地优先配置 +const app = new ObjectOS({ + mode: 'standalone', + storage: './local-data.db', + sync: { + enabled: false, // 完全离线 + // 或启用同步: + // remote: 'https://sync.example.com', + // strategy: 'manual' + } +}) +``` + +## 下一步 + +开始您的 ObjectOS 之旅: + +- **初次接触 ObjectOS?** 从[平台架构](./platform-architecture)开始 +- **设置安全?** 跳转到[身份与访问](./identity-access) +- **准备部署?** 查看[部署与运维](./deployment) diff --git a/content/docs/04-objectos/meta.en.json b/content/docs/04-objectos/meta.en.json new file mode 100644 index 0000000..0702a28 --- /dev/null +++ b/content/docs/04-objectos/meta.en.json @@ -0,0 +1,9 @@ +{ + "title": "ObjectOS", + "pages": [ + "index", + "platform-architecture", + "identity-access", + "deployment" + ] +} diff --git a/content/docs/04-objectos/meta.zh-CN.json b/content/docs/04-objectos/meta.zh-CN.json new file mode 100644 index 0000000..0702a28 --- /dev/null +++ b/content/docs/04-objectos/meta.zh-CN.json @@ -0,0 +1,9 @@ +{ + "title": "ObjectOS", + "pages": [ + "index", + "platform-architecture", + "identity-access", + "deployment" + ] +} diff --git a/content/docs/04-objectos/platform-architecture.en.mdx b/content/docs/04-objectos/platform-architecture.en.mdx new file mode 100644 index 0000000..4e8d8ac --- /dev/null +++ b/content/docs/04-objectos/platform-architecture.en.mdx @@ -0,0 +1,426 @@ +--- +title: Platform Architecture +description: How ObjectOS binds ObjectQL and ObjectUI together with Local-First implementation +--- + +# Platform Architecture + +ObjectOS serves as the runtime platform that orchestrates ObjectQL (data engine) and ObjectUI (interface framework) into a cohesive application environment. This page explains how ObjectOS binds these components together and implements Local-First architecture. + +## Architecture Overview + +ObjectOS is structured in three primary layers: + +``` +┌─────────────────────────────────────────────────────┐ +│ Application Layer │ +│ ┌──────────────┐ ┌──────────────┐ │ +│ │ Business │ │ Custom │ │ +│ │ Logic │ │ Components │ │ +│ └──────────────┘ └──────────────┘ │ +└────────────────────┬────────────────────────────────┘ + │ +┌────────────────────▼────────────────────────────────┐ +│ ObjectOS Platform │ +│ ┌──────────────────────────────────────────────┐ │ +│ │ Runtime Binding Layer │ │ +│ │ ┌────────────────┬────────────────────────┐ │ │ +│ │ │ Schema-UI │ Data-Component │ │ │ +│ │ │ Mapping │ Synchronization │ │ │ +│ │ └────────────────┴────────────────────────┘ │ │ +│ └──────────────────────────────────────────────┘ │ +│ ┌──────────────────────────────────────────────┐ │ +│ │ Platform Services Layer │ │ +│ │ ┌───────┬───────┬─────────┬──────────────┐ │ │ +│ │ │ Auth │ RBAC │ FLS │ Multi-tenant │ │ │ +│ │ └───────┴───────┴─────────┴──────────────┘ │ │ +│ └──────────────────────────────────────────────┘ │ +│ ┌────────────┬────────────────────┐ │ +│ │ ObjectUI │ ObjectQL │ │ +│ │ Components │ Data Engine │ │ +│ └────────────┴────────────────────┘ │ +└─────────────────────────────────────────────────────┘ +``` + +## QL-UI Binding Mechanism + +ObjectOS automatically connects ObjectQL schemas with ObjectUI components through a declarative binding system. + +### Schema-Driven UI Generation + +When you define an object schema, ObjectOS automatically generates appropriate UI components: + +```typescript +// Schema definition (ObjectQL) +const schema = { + objects: { + contacts: { + label: 'Contacts', + fields: { + name: { + type: 'text', + label: 'Name', + required: true + }, + email: { + type: 'email', + label: 'Email' + }, + company: { + type: 'lookup', + reference_to: 'companies', + label: 'Company' + }, + status: { + type: 'select', + options: ['active', 'inactive'], + label: 'Status' + } + } + } + } +} + +// ObjectOS automatically generates: +// - Form components with proper inputs +// - List views with sortable columns +// - Detail views with formatted fields +// - Lookup selectors for relationships +``` + +### Component-to-Data Binding + +ObjectUI components automatically bind to ObjectQL data: + +```typescript +// Page definition (ObjectUI) +const pages = { + contacts: { + type: 'list', + object: 'contacts', // Auto-binds to schema + fields: ['name', 'email', 'company', 'status'], + filters: { + status: 'active' + } + }, + contactDetail: { + type: 'detail', + object: 'contacts', // Auto-binds to schema + layout: [ + { field: 'name' }, + { field: 'email' }, + { field: 'company' }, // Renders lookup selector + { field: 'status' } // Renders dropdown + ] + } +} +``` + +### Real-Time Data Synchronization + +ObjectOS maintains bidirectional sync between UI and data: + +```typescript +// When user edits a field in UI: +// 1. ObjectUI captures the change +// 2. ObjectOS validates against schema +// 3. ObjectQL persists to database +// 4. ObjectOS broadcasts update +// 5. All UI components refresh automatically + +const app = new ObjectOS({ + schema, + pages, + reactivity: { + enabled: true, // Enable real-time sync + debounce: 300, // Debounce updates (ms) + optimistic: true // Optimistic UI updates + } +}) +``` + +## Local-First Implementation + +ObjectOS implements Local-First architecture principles, making applications work seamlessly offline while supporting optional cloud synchronization. + +### Storage Architecture + +``` +┌─────────────────────────────────────────────────┐ +│ Application Runtime │ +└────────────────┬────────────────────────────────┘ + │ +┌────────────────▼────────────────────────────────┐ +│ ObjectOS Storage Layer │ +│ ┌──────────────────────────────────────────┐ │ +│ │ Local Storage (SQLite) │ │ +│ │ ┌─────────┬──────────┬───────────────┐ │ │ +│ │ │ App Data│ User Data│ System Data │ │ │ +│ │ └─────────┴──────────┴───────────────┘ │ │ +│ └──────────────────────────────────────────┘ │ +│ ┌──────────────────────────────────────────┐ │ +│ │ Sync Engine (Optional) │ │ +│ │ ┌────────────┬──────────────────────┐ │ │ +│ │ │ Change Log │ Conflict Resolution │ │ │ +│ │ └────────────┴──────────────────────┘ │ │ +│ └──────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────┘ + │ + │ (Optional sync) + │ +┌────────────────▼────────────────────────────────┐ +│ Remote Server (Optional) │ +└─────────────────────────────────────────────────┘ +``` + +### Standalone Mode (Pure Local-First) + +In standalone mode, everything runs locally: + +```typescript +const app = new ObjectOS({ + mode: 'standalone', + storage: { + type: 'sqlite', + path: './myapp.db' // Local SQLite file + }, + offline: { + enabled: true, // Full offline support + persistQueries: true // Cache queries locally + } +}) + +// Application works 100% offline: +// - All data stored locally +// - No network required +// - Instant performance +// - User owns their data +``` + +### Hybrid Mode (Local-First + Sync) + +Applications can sync with remote servers when online: + +```typescript +const app = new ObjectOS({ + mode: 'hybrid', + storage: { + type: 'sqlite', + path: './myapp.db' + }, + sync: { + enabled: true, + remote: 'https://api.example.com', + strategy: 'eventual', // Eventual consistency + interval: 60000, // Sync every 60s when online + conflictResolution: 'last-write-wins' + } +}) + +// Benefits: +// - Works offline first +// - Syncs when network available +// - Handles conflicts automatically +// - Fast local operations +``` + +### Data Persistence + +ObjectOS ensures data durability: + +```typescript +const app = new ObjectOS({ + storage: { + type: 'sqlite', + path: './myapp.db', + persistence: { + wal: true, // Write-Ahead Logging + autoCheckpoint: 1000, // Checkpoint every 1000 pages + backup: { + enabled: true, + interval: 3600000, // Hourly backups + retention: 7 // Keep 7 backups + } + } + } +}) +``` + +## Runtime Lifecycle + +Understanding the ObjectOS lifecycle helps optimize application behavior: + +### Initialization Phase + +```typescript +const app = new ObjectOS({ /* config */ }) + +// Phase 1: Initialize core services +await app.initialize() +// - Load configuration +// - Setup storage layer +// - Initialize ObjectQL engine +// - Register schemas + +// Phase 2: Start platform services +await app.start() +// - Initialize auth system +// - Load user permissions +// - Start UI runtime +// - Bind components to data +// - Begin sync (if configured) + +console.log('App ready at', app.url) +``` + +### Request Handling + +```typescript +// Incoming request flow: +Request → ObjectOS Router + ↓ + Auth Middleware (verify user) + ↓ + Permission Check (RBAC + FLS) + ↓ + ObjectQL Query (with filters) + ↓ + ObjectUI Render (with data) + ↓ + Response +``` + +### Shutdown Phase + +```typescript +// Graceful shutdown +process.on('SIGTERM', async () => { + await app.shutdown({ + // Complete pending writes + flushWrites: true, + // Checkpoint WAL + checkpoint: true, + // Close connections + closeConnections: true, + // Timeout in ms + timeout: 5000 + }) +}) +``` + +## Configuration Options + +Complete ObjectOS configuration reference: + +```typescript +const app = new ObjectOS({ + // Deployment mode + mode: 'standalone' | 'server' | 'multi-tenant', + + // Storage configuration + storage: { + type: 'sqlite' | 'mysql' | 'postgresql', + path: './data.db', // SQLite path + url: 'mysql://...', // Or connection URL + pool: { + min: 2, + max: 10 + } + }, + + // Schema definition + schema: { + objects: { /* ... */ } + }, + + // UI pages + pages: { + /* ... */ + }, + + // Authentication + auth: { + enabled: true, + providers: ['email', 'oauth'], + session: { + secret: 'your-secret', + maxAge: 86400000 + } + }, + + // Sync configuration (Local-First) + sync: { + enabled: false, + remote: 'https://api.example.com', + strategy: 'eventual' | 'manual', + interval: 60000, + conflictResolution: 'last-write-wins' | 'manual' + }, + + // Server settings + server: { + port: 3000, + host: '0.0.0.0' + }, + + // Multi-tenant settings + multiTenant: { + enabled: false, + strategy: 'virtualCity' | 'database', + isolation: 'strict' | 'shared-schema' + } +}) +``` + +## Performance Optimization + +ObjectOS includes several optimization strategies: + +### Query Optimization + +```typescript +const app = new ObjectOS({ + performance: { + // Enable query result caching + queryCache: { + enabled: true, + maxSize: 100, // Cache 100 queries + ttl: 60000 // 60s TTL + }, + + // Optimize Virtual Column Index (SQLite) + virtualColumnIndex: true, + + // Batch mutations + batchMutations: { + enabled: true, + maxSize: 100, + flushInterval: 1000 + } + } +}) +``` + +### UI Rendering Optimization + +```typescript +const pages = { + largeList: { + type: 'list', + object: 'records', + performance: { + virtualScroll: true, // Render only visible rows + pageSize: 50, // Load 50 at a time + lazyLoad: true, // Load on scroll + memoizeRows: true // Cache rendered rows + } + } +} +``` + +## Next Steps + +Now that you understand the platform architecture: + +- **[Identity & Access](./identity-access)** - Learn about user management and RBAC +- **[Deployment](./deployment)** - Deploy ObjectOS applications diff --git a/content/docs/04-objectos/platform-architecture.zh-CN.mdx b/content/docs/04-objectos/platform-architecture.zh-CN.mdx new file mode 100644 index 0000000..6e49c55 --- /dev/null +++ b/content/docs/04-objectos/platform-architecture.zh-CN.mdx @@ -0,0 +1,425 @@ +--- +title: 平台架构 +description: ObjectOS 如何将 ObjectQL 和 ObjectUI 绑定在一起并实现本地优先架构 +--- + +# 平台架构 + +ObjectOS 作为运行时平台,将 ObjectQL(数据引擎)和 ObjectUI(界面框架)编排成一个完整的应用环境。本页面解释 ObjectOS 如何绑定这些组件并实现本地优先架构。 + +## 架构概览 + +ObjectOS 分为三个主要层次: + +``` +┌─────────────────────────────────────────────────────┐ +│ 应用层 │ +│ ┌──────────────┐ ┌──────────────┐ │ +│ │ 业务逻辑 │ │ 自定义组件 │ │ +│ └──────────────┘ └──────────────┘ │ +└────────────────────┬────────────────────────────────┘ + │ +┌────────────────────▼────────────────────────────────┐ +│ ObjectOS 平台 │ +│ ┌──────────────────────────────────────────────┐ │ +│ │ 运行时绑定层 │ │ +│ │ ┌────────────────┬────────────────────────┐ │ │ +│ │ │ Schema-UI │ 数据-组件 │ │ │ +│ │ │ 映射 │ 同步 │ │ │ +│ │ └────────────────┴────────────────────────┘ │ │ +│ └──────────────────────────────────────────────┘ │ +│ ┌──────────────────────────────────────────────┐ │ +│ │ 平台服务层 │ │ +│ │ ┌───────┬───────┬─────────┬──────────────┐ │ │ +│ │ │ 认证 │ RBAC │ FLS │ 多租户 │ │ │ +│ │ └───────┴───────┴─────────┴──────────────┘ │ │ +│ └──────────────────────────────────────────────┘ │ +│ ┌────────────┬────────────────────┐ │ +│ │ ObjectUI │ ObjectQL │ │ +│ │ 组件 │ 数据引擎 │ │ +│ └────────────┴────────────────────┘ │ +└─────────────────────────────────────────────────────┘ +``` + +## QL-UI 绑定机制 + +ObjectOS 通过声明式绑定系统自动连接 ObjectQL schema 和 ObjectUI 组件。 + +### Schema 驱动的 UI 生成 + +当您定义对象 schema 时,ObjectOS 自动生成相应的 UI 组件: + +```typescript +// Schema 定义 (ObjectQL) +const schema = { + objects: { + contacts: { + label: '联系人', + fields: { + name: { + type: 'text', + label: '姓名', + required: true + }, + email: { + type: 'email', + label: '邮箱' + }, + company: { + type: 'lookup', + reference_to: 'companies', + label: '公司' + }, + status: { + type: 'select', + options: ['active', 'inactive'], + label: '状态' + } + } + } + } +} + +// ObjectOS 自动生成: +// - 带有适当输入控件的表单组件 +// - 可排序列的列表视图 +// - 格式化字段的详情视图 +// - 关联关系的查找选择器 +``` + +### 组件到数据的绑定 + +ObjectUI 组件自动绑定到 ObjectQL 数据: + +```typescript +// 页面定义 (ObjectUI) +const pages = { + contacts: { + type: 'list', + object: 'contacts', // 自动绑定到 schema + fields: ['name', 'email', 'company', 'status'], + filters: { + status: 'active' + } + }, + contactDetail: { + type: 'detail', + object: 'contacts', // 自动绑定到 schema + layout: [ + { field: 'name' }, + { field: 'email' }, + { field: 'company' }, // 渲染查找选择器 + { field: 'status' } // 渲染下拉框 + ] + } +} +``` + +### 实时数据同步 + +ObjectOS 在 UI 和数据之间保持双向同步: + +```typescript +// 当用户在 UI 中编辑字段时: +// 1. ObjectUI 捕获变更 +// 2. ObjectOS 根据 schema 进行验证 +// 3. ObjectQL 持久化到数据库 +// 4. ObjectOS 广播更新 +// 5. 所有 UI 组件自动刷新 + +const app = new ObjectOS({ + schema, + pages, + reactivity: { + enabled: true, // 启用实时同步 + debounce: 300, // 防抖更新 (毫秒) + optimistic: true // 乐观 UI 更新 + } +}) +``` + +## 本地优先实现 + +ObjectOS 实现了本地优先架构原则,使应用可以无缝离线工作,同时支持可选的云同步。 + +### 存储架构 + +``` +┌─────────────────────────────────────────────────┐ +│ 应用运行时 │ +└────────────────┬────────────────────────────────┘ + │ +┌────────────────▼────────────────────────────────┐ +│ ObjectOS 存储层 │ +│ ┌──────────────────────────────────────────┐ │ +│ │ 本地存储 (SQLite) │ │ +│ │ ┌─────────┬──────────┬───────────────┐ │ │ +│ │ │ 应用数据│ 用户数据 │ 系统数据 │ │ │ +│ │ └─────────┴──────────┴───────────────┘ │ │ +│ └──────────────────────────────────────────┘ │ +│ ┌──────────────────────────────────────────┐ │ +│ │ 同步引擎(可选) │ │ +│ │ ┌────────────┬──────────────────────┐ │ │ +│ │ │ 变更日志 │ 冲突解决 │ │ │ +│ │ └────────────┴──────────────────────┘ │ │ +│ └──────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────┘ + │ + │ (可选同步) + │ +┌────────────────▼────────────────────────────────┐ +│ 远程服务器(可选) │ +└─────────────────────────────────────────────────┘ +``` + +### 独立模式(纯本地优先) + +在独立模式下,所有内容都在本地运行: + +```typescript +const app = new ObjectOS({ + mode: 'standalone', + storage: { + type: 'sqlite', + path: './myapp.db' // 本地 SQLite 文件 + }, + offline: { + enabled: true, // 完整离线支持 + persistQueries: true // 本地缓存查询 + } +}) + +// 应用 100% 离线工作: +// - 所有数据存储在本地 +// - 无需网络连接 +// - 即时性能 +// - 用户拥有数据 +``` + +### 混合模式(本地优先 + 同步) + +应用可以在在线时与远程服务器同步: + +```typescript +const app = new ObjectOS({ + mode: 'hybrid', + storage: { + type: 'sqlite', + path: './myapp.db' + }, + sync: { + enabled: true, + remote: 'https://api.example.com', + strategy: 'eventual', // 最终一致性 + interval: 60000, // 在线时每 60 秒同步 + conflictResolution: 'last-write-wins' + } +}) + +// 优势: +// - 离线优先工作 +// - 网络可用时同步 +// - 自动处理冲突 +// - 快速的本地操作 +``` + +### 数据持久化 + +ObjectOS 确保数据持久性: + +```typescript +const app = new ObjectOS({ + storage: { + type: 'sqlite', + path: './myapp.db', + persistence: { + wal: true, // 预写日志 + autoCheckpoint: 1000, // 每 1000 页检查点 + backup: { + enabled: true, + interval: 3600000, // 每小时备份 + retention: 7 // 保留 7 个备份 + } + } + } +}) +``` + +## 运行时生命周期 + +理解 ObjectOS 生命周期有助于优化应用行为: + +### 初始化阶段 + +```typescript +const app = new ObjectOS({ /* config */ }) + +// 阶段 1: 初始化核心服务 +await app.initialize() +// - 加载配置 +// - 设置存储层 +// - 初始化 ObjectQL 引擎 +// - 注册 schemas + +// 阶段 2: 启动平台服务 +await app.start() +// - 初始化认证系统 +// - 加载用户权限 +// - 启动 UI 运行时 +// - 绑定组件到数据 +// - 开始同步(如果配置) + +console.log('应用就绪于', app.url) +``` + +### 请求处理 + +```typescript +// 传入请求流程: +请求 → ObjectOS 路由器 + ↓ + 认证中间件(验证用户) + ↓ + 权限检查(RBAC + FLS) + ↓ + ObjectQL 查询(带过滤器) + ↓ + ObjectUI 渲染(带数据) + ↓ + 响应 +``` + +### 关闭阶段 + +```typescript +// 优雅关闭 +process.on('SIGTERM', async () => { + await app.shutdown({ + // 完成待处理写入 + flushWrites: true, + // 检查点 WAL + checkpoint: true, + // 关闭连接 + closeConnections: true, + // 超时时间(毫秒) + timeout: 5000 + }) +}) +``` + +## 配置选项 + +完整的 ObjectOS 配置参考: + +```typescript +const app = new ObjectOS({ + // 部署模式 + mode: 'standalone' | 'server' | 'multi-tenant', + + // 存储配置 + storage: { + type: 'sqlite' | 'mysql' | 'postgresql', + path: './data.db', // SQLite 路径 + url: 'mysql://...', // 或连接 URL + pool: { + min: 2, + max: 10 + } + }, + + // Schema 定义 + schema: { + objects: { /* ... */ } + }, + + // UI 页面 + pages: { + /* ... */ + }, + + // 认证 + auth: { + enabled: true, + providers: ['email', 'oauth'], + session: { + secret: 'your-secret', + maxAge: 86400000 + } + }, + + // 同步配置(本地优先) + sync: { + enabled: false, + remote: 'https://api.example.com', + strategy: 'eventual' | 'manual', + interval: 60000, + conflictResolution: 'last-write-wins' | 'manual' + }, + + // 服务器设置 + server: { + port: 3000, + host: '0.0.0.0' + }, + + // 多租户设置 + multiTenant: { + enabled: false, + strategy: 'virtualCity' | 'database', + isolation: 'strict' | 'shared-schema' + } +}) +``` + +## 性能优化 + +ObjectOS 包含多种优化策略: + +### 查询优化 + +```typescript +const app = new ObjectOS({ + performance: { + // 启用查询结果缓存 + queryCache: { + enabled: true, + maxSize: 100, // 缓存 100 个查询 + ttl: 60000 // 60 秒 TTL + }, + + // 优化虚拟列索引(SQLite) + virtualColumnIndex: true, + + // 批量变更 + batchMutations: { + enabled: true, + maxSize: 100, + flushInterval: 1000 + } + } +}) +``` + +### UI 渲染优化 + +```typescript +const pages = { + largeList: { + type: 'list', + object: 'records', + performance: { + virtualScroll: true, // 只渲染可见行 + pageSize: 50, // 一次加载 50 条 + lazyLoad: true, // 滚动时加载 + memoizeRows: true // 缓存已渲染行 + } + } +} +``` + +## 下一步 + +现在您已经理解了平台架构: + +- **[身份与访问](./identity-access)** - 了解用户管理和 RBAC +- **[部署与运维](./deployment)** - 部署 ObjectOS 应用 diff --git a/content/docs/meta.en.json b/content/docs/meta.en.json index 8ff445c..9c9f2cf 100644 --- a/content/docs/meta.en.json +++ b/content/docs/meta.en.json @@ -1,4 +1,19 @@ { "title": "Documentation", - "pages": ["index", "getting-started", "architecture", "objectui", "objectql", "objectos", "i18n-guide"] + "pages": [ + "index", + "00-intro", + "01-quickstart", + "02-objectql", + "03-objectui", + "04-objectos", + "---", + "getting-started", + "architecture", + "objectui", + "objectql", + "objectos", + "---", + "i18n-guide" + ] } diff --git a/content/docs/meta.zh-CN.json b/content/docs/meta.zh-CN.json index 4ccca52..d86780a 100644 --- a/content/docs/meta.zh-CN.json +++ b/content/docs/meta.zh-CN.json @@ -1,4 +1,19 @@ { "title": "文档", - "pages": ["index", "getting-started", "architecture", "objectui", "objectql", "objectos", "i18n-guide"] + "pages": [ + "index", + "00-intro", + "01-quickstart", + "02-objectql", + "03-objectui", + "04-objectos", + "---", + "getting-started", + "architecture", + "objectui", + "objectql", + "objectos", + "---", + "i18n-guide" + ] }