apptank / horusync

移动设备与Laravel应用之间的数据同步

v0.1.8 2024-09-20 13:57 UTC

This package is auto-updated.

Last update: 2024-09-24 16:38:22 UTC


README

Horusync Logo
Build Status Latest Stable Version License

请注意:此库目前处于测试阶段,直到发布版本1.0.0。

Horus Sync

此库提供了一种简单的方法来管理服务器上远程数据库与移动设备上本地数据库之间的数据同步。它通过指定参数和与其他实体之间的关系定义了一组可同步的实体。

功能

  • 数据同步:使定义的实体数据在本地数据库和远程数据库之间同步。
  • 模式迁移:允许检索可同步实体及其关系的模式。
  • 完整性验证:确保同步数据的完整性。
  • 身份验证和权限:定义一个经过身份验证的用户及其与实体关联的权限。
  • 中间件:支持定义用于同步路由的自定义中间件。
  • UUID支持:允许指定是否使用UUID或Int作为实体的主键。
  • 外键支持:支持在可同步实体中定义具有级联删除功能的外键。
  • 用户行为支持:表示用户是否有权限作为其他用户执行操作并访问已授予权限的实体。
  • 支持可同步实体类型:支持根据实体是否可编辑将实体定义为可写或可读。

安装

此库必须与Laravel 11或更高版本一起使用。

使用以下命令使用Composer安装此包

composer require apptank/horusync

用法

所有具有可编辑记录的可同步模型都必须继承自WritableEntitySynchronizable,并定义它们包含的属性。如果您想创建一个只读实体,您应该继承自ReadableEntitySynchronizable

您可以使用Artisan命令为您生成一个可同步模型。您只需要指定模型应存储的路径。以下示例将在App/Models/Sync/MyModelSync中创建一个模型

php artisan horus:entity MyModelSync
namespace App\Models\Sync;

use AppTank\Horus\Core\Entity\SyncParameter;
use AppTank\Horus\Illuminate\Database\WritableEntitySynchronizable;

class MyModel extends WritableEntitySynchronizable
{
    public static function parameters(): array
    {
        return [
            // Define the parameters of the entity
            SyncParameter::createString("name", 1),
            SyncParameter::createInt("age", 1),
            SyncParameter::createPrimaryKeyString("email", 1),
            SyncParameter::createTimestamp("datetime", 1),
            SyncParameter::createBoolean("active", 1),
            SyncParameter::createFloat("price", 1),
            SyncParameter::createRelationOneToMany("children", [ChildModel::class], 1)
        ];
    }

    public static function getEntityName(): string
    {
        return "my_model";
    }

    public static function getVersionNumber(): int
    {
        return 1;
    }
}

如果您想创建一个只读实体,您可以在Artisan命令中添加--readable选项。

php artisan horus:entity MyModelSync --readable

初始化

初始化容器

需要在Laravel的AppServiceProvider中通过传递包含实体层次映射的数组来初始化Horus容器。

  • 您可以可选地定义数据库连接名称以及是否使用UUID或Int作为主键。
class AppServiceProvider extends ServiceProvider
{

    public function register(): void
    {

    }

    public function boot(): void
    {
        Horus::initialize([
            MyModel::class => [ChildModel::class], // Entities Map
            "sync_database", // Connection Name
            false // Uses UUID
        ]);
        
    }

}

运行迁移

初始化Horus容器后,需要运行同步表的迁移。

php artisan migrate

中间件

如果您想实现自定义中间件用于同步路由,您可以在Service Provider中这样做

Horus::setMiddleware([MyMiddleware::class,'throttle:60,1']);

身份验证和权限

以下代码显示了如何配置用户身份验证和与实体关联的权限。使用UserAuth类,您定义了一个经过身份验证的用户以及他们可以访问的实体和相应的权限。

Horus::getInstance()->setUserAuthenticated(
 new UserAuth(
   "07a35af0-7317-41e4-99a3-e3583099aff2", // User Id Authenticated
   [ // Array of Entities Granted
   new EntityGranted(
   "971785f7-0f01-46cd-a3ce-af9ce6273d3d", // User Owner Id
   "animal", // Entity Name
    "9135e859-b053-4cfb-b701-d5f240b0aab1", // Entity Id
    // Set the permissions for the entity
   , new AccessLevel(Permission::READ, Permission::CREATE)),
    // User Acting As
   new UserAuth("b253a0e8-027b-463c-b87a-b18f09c99ddd")
   ]
 )
);

路由

迁移模式

为可同步实体返回迁移模式。它指示每个实体的属性及其关系。它还包括实体的当前版本和每个属性的当前版本,以确定客户端数据库是否需要迁移到新版本。

URL: GET

/sync/migration

响应

点击此处查看响应
[
  {
    "entity": "parent_fake_entity",
    "attributes": [
      {
        "name": "id",
        "version": 1,
        "type": "primary_key_string",
        "nullable": false
      },
      {
        "name": "sync_owner_id",
        "version": 1,
        "type": "string",
        "nullable": false
      },
      {
        "name": "sync_hash",
        "version": 1,
        "type": "string",
        "nullable": false
      },
      {
        "name": "sync_created_at",
        "version": 1,
        "type": "timestamp",
        "nullable": false
      },
      {
        "name": "sync_updated_at",
        "version": 1,
        "type": "timestamp",
        "nullable": false
      },
      {
        "name": "name",
        "version": 1,
        "type": "string",
        "nullable": false
      },
      {
        "name": "color",
        "version": 2,
        "type": "timestamp",
        "nullable": false
      },
      {
        "name": "value_nullable",
        "version": 2,
        "type": "string",
        "nullable": true
      },
      {
        "name": "children",
        "version": 2,
        "type": "relation_one_to_many",
        "nullable": false,
        "related": [
          {
            "entity": "child_fake_entity",
            "attributes": [
              {
                "name": "id",
                "version": 1,
                "type": "primary_key_string",
                "nullable": false
              },
              {
                "name": "sync_owner_id",
                "version": 1,
                "type": "string",
                "nullable": false
              },
              {
                "name": "sync_hash",
                "version": 1,
                "type": "string",
                "nullable": false
              },
              {
                "name": "sync_created_at",
                "version": 1,
                "type": "timestamp",
                "nullable": false
              },
              {
                "name": "sync_updated_at",
                "version": 1,
                "type": "timestamp",
                "nullable": false
              },
              {
                "name": "primary_int_value",
                "version": 5,
                "type": "primary_key_integer",
                "nullable": false
              },
              {
                "name": "primary_string_value",
                "version": 5,
                "type": "primary_key_string",
                "nullable": false
              },
              {
                "name": "int_value",
                "version": 5,
                "type": "int",
                "nullable": true
              },
              {
                "name": "float_value",
                "version": 5,
                "type": "float",
                "nullable": false
              },
              {
                "name": "string_value",
                "version": 5,
                "type": "string",
                "nullable": false
              },
              {
                "name": "boolean_value",
                "version": 5,
                "type": "boolean",
                "nullable": false
              },
              {
                "name": "timestamp_value",
                "version": 5,
                "type": "timestamp",
                "nullable": false
              },
              {
                "name": "parent_id",
                "version": 5,
                "type": "string",
                "nullable": false
              }
            ],
            "current_version": 5
          }
        ]
      }
    ],
    "current_version": 2
  }
]

发送要同步的数据

此端点接收要数据库中执行的操作数组。每个操作必须指定操作类型、所引用的实体、要修改的数据以及执行操作的时间。

URL: POST

/sync/queue/actions 

示例请求数据

点击此处查看示例请求
[
  {
    "action": "DELETE",
    "entity": "parent_fake_entity",
    "data": {
      "id": "3093a07a-543b-336b-9ca8-4c3bf207aeb5"
    },
    "actioned_at": 1124141410
  },
  {
    "action": "UPDATE",
    "entity": "parent_fake_entity",
    "data": {
      "id": "3093a07a-543b-336b-9ca8-4c3bf207aeb5",
      "attributes": {
        "name": "ernser.mazie",
        "color": "Thistle"
      }
    },
    "actioned_at": 1124140410
  },
  {
    "action": "INSERT",
    "entity": "parent_fake_entity",
    "data": {
      "id": "3093a07a-543b-336b-9ca8-4c3bf207aeb5",
      "name": "quitzon.gunnar",
      "color": "DarkGray"
    },
    "actioned_at": 1124139410
  }
]

响应: 201 - 已接受

按时间顺序获取执行的同步操作

URL: GET

/sync/queue/actions 

获取所有实体的同步数据

URL: GET

/sync/data

可选参数

  • after (时间戳:int): 筛选指定日期之后已同步的数据。

获取特定实体的同步数据

URL: GET

/sync/{entity}

可选参数

  • after (时间戳:int): 筛选指定日期之后已同步的数据。
  • ids (数组): 筛选与给定id同步的数据。

获取最后执行的同步操作

URL: GET

/sync/queue/actions/last

获取实体的同步记录的哈希值

URL: GET

/sync/entity/{entity}/hashes

响应

点击此处查看示例响应
[
  {
    "id": "fccd745b-780c-3b14-b657-ad05d86318e0",
    "sync_hash": "26dbdd1c5dff8da8d79747da5aab53b6052beb5247c8f2464ee1b771f818aa81"
  },
  {
    "id": "6de8f02c-ec7a-3df8-ac2e-e3542c5e4693",
    "sync_hash": "06b8ad4f55cc4724beda154ecb140592c08813eac762a531dcc489b360cef633"
  },
  {
    "id": "31af3908-454e-3070-a2f7-a79f12a17c9a",
    "sync_hash": "0de9e664000ee76acdc2c705ca0446b32738ae781b797d3c347e34c547ccae4e"
  }
]

验证同步数据完整性

此端点接收实体及其哈希值的数组,并返回一个数组,其中包含每个实体以及哈希值是否与数据库中实体的哈希值匹配,表示数据完整性正确。

URL: POST

/sync/validate/data

示例请求数据

点击此处查看示例请求
[
  {
    "entity": "parent_fake_entity",
    "hash": "2d971864ba0f14de5ad0be8f5ea6e0bc99685fdbb1f1495a8d8557ffdc572012"
  }
]

示例响应

点击此处查看示例响应
[
  {
    "entity": "parent_fake_entity",
    "hash": {
      "expected": "2d971864ba0f14de5ad0be8f5ea6e0bc99685fdbb1f1495a8d8557ffdc572012",
      "obtained": "2d971864ba0f14de5ad0be8f5ea6e0bc99685fdbb1f1495a8d8557ffdc572012",
      "matched": true
    }
  }
]

验证实体哈希算法

此端点接收属性数组和表示客户端属性哈希的哈希值。服务器执行相同的过程,并将获得的哈希值与客户端接收的哈希值进行比较,以验证哈希算法是否相同。

URL: POST

/sync/validate/hashing

示例请求数据

{
  "data": {
    "z1": "7e998082-a377-3485-b5ed-b225b0c409e9",
    "age": 85,
    "mood": "b176423b-b393-3e21-81de-6b1d58c54c66",
    "date": 1723558663
  },
  "hash": "17ffc52d4338881a6091ee80a4ef08db3901a86bf41636fa431a8ff5de3a6cf8"
}

示例响应

{
  "expected": "17ffc52d4338881a6091ee80a4ef08db3901a86bf41636fa431a8ff5de3a6cf8",
  "obtained": "17ffc52d4338881a6091ee80a4ef08db3901a86bf41636fa431a8ff5de3a6cf8",
  "matched": true
}