1 个不稳定版本
0.1.0 | 2023年9月16日 |
---|
#1558 in 数据库接口
7.5MB
2K SLoC
nestbox
注意
我仍在开发中。我的目标是创建一个最小可行产品。这意味着
- 拥有一个基于Web的GUI(缺失)
- 拥有一个后端,能够
- 根据扫描的QR码显示数据。
- 验证用户身份
- 接受经过身份验证和授权用户对鸟巢的修改
想法
一些观鸟者也在照顾鸟巢。因此,这些鸟巢在冬季通常会定期检查。为了给你一个想法,我们照顾着近400个盒子。如果一个鸟使用鸟巢,人们可以通过建造的巢来区分特定的鸟。然后观鸟者清理盒子,这意味着巢被移除,盒子准备下一次繁殖。通常还有一个列表,记录哪些鸟巢被哪些鸟使用以及哪一年。所以我的意图是给鸟巢清洁工一个工具,因为现在几乎每个人都有智能手机可用。所以关键的想法是
- 每个鸟巢上都有一个QR码标签。
- 经过认证的清洁工现在可以扫描QR码并执行以下操作。
- 为新盒子设置新的地理位置,以便以后可以找到。
- 设置检测到的繁殖(鸟的类型和年份)
- 路过的人也可以扫描QR码并获得以下信息
- 负责该盒子的协会以及如何联系他们。
- 该盒子的历史,过去的繁殖情况。
- 让照顾这些盒子的协会“走出黑暗”,因为他们在做有价值的工作,而许多人甚至不知道它们的存在。
- 我个人觉得在业余时间开发这类应用程序很有趣。任何后端软件都使用Rust编写 - 并非因为我特别擅长Rust,而是因为我喜欢它,我也喜欢学习。
仓库
此仓库包含后端。
数据库模型
一般
尽管MongoDB是一个无模式的数据库,但模型的概念可以很容易地描述。注意,它只是一个骨架,这意味着我只包括我认为至关重要的属性。
uuid
在每条记录中都可以找到一个属性 uuid
。它充当公共可见的主键,因为我认为不应该透露太多关于数据库的信息,而MongoDB对象ID是可猜测的。所以UUID是随机生成的。任何名为"uuid"的集合字段都是公共可访问的键,可以在URL等中使用。它们都是随机生成的类型4 UUID。
模型
在深入了解每个集合的细节之前,这里是一个快速概述各个集合之间的关系。
mandant 1 ----- n users
|
+----- 1 ------ n nestboxes 1 ----- n breeds
|
\ 1 ------------- n geolocation
mandants
mandant是一个观鸟者的协会,如果你愿意的话。他们负责一个定义的地理区域内的一对鸟巢。一个记录现在包含以下属性
- _id: ObjectId - 目前在任何上下文中都不使用。
- uuid: 公共可访问密钥,例如在URL中。例如 e7620353-b6f6-47e9-b543-66af20769145
- name: 关联名称,例如 BirdLife
- website: 大多数这些关联都有自己的网站,例如 https://www.birdwatcher.ch
- email: 显然,例如 [email protected]
users
用户属于一个客户,可以更改属于此客户的任何鸟箱。目前实现了一种基于简单密码的认证。属性
- _id: ObjectId - 目前在任何上下文中都不使用。
- mandant_uuid: 用户属于此客户。
- username: 用户登录名
- uuid: 公共可访问密钥
- lastname
- firstname
- password_hash: 密码的盐化SHA3散列
- salt: 类型4的uuid
sessions
如果用户成功登录,将包含session_key属性的用户的记录副本存储在会话集合中。
- session_key: 类型4的uuid
- session_created_at: 用于mongodb中的TTL的时间戳,设置为86400秒。这意味着数据库在一天后将从集合中删除会话。
会话密钥必须是唯一的。用户同一时间只能有一个会话。会话对象将在86400秒后被删除。
nestboxes
巢箱在概念上表示一个可引用的QR码。在我们的案例中,它将是一个挂在树上的木制巢箱。
- _id: ObjectId
- public: 是否巢箱数据是公开的 - true或false
- uuid: 公共可访问密钥
- mandant_uuid: 巢箱属于此客户
- created_at: ISODate Zulu时间
geolocations
表示巢箱放置位置的地理定位随时间变化。
- _id: ObjectId
- uuid: 地理定位的公共密钥
- nestbox_uuid: 地理定位附加的巢箱
- from_date: 地理定位有效开始,Zulu时间戳
- until_date: 地理定位有效结束,Zulu时间戳
- position: 地理空间类型点
breeds
跟踪所有品种。
- _id: ObjectId
- uuid: 公共密钥
- nestbox_uuid: 在此巢箱中发现的品种
- user_uuid: 发现此品种的用户
- discovery_date: 发现品种的时间戳Zulu时间
- bird_uuid: 根据在箱中找到的巢估计的鸟类
birds
此集合存储了一个客户的所有鸟类。每个客户必须创建自己的鸟类。这种冗余的原因是
- 不同的位置不同的鸟类
- 不同的气候,不同的鸟类
- 不同的海拔,不同的鸟类
- 不同的语言,不同的鸟类
属性包括
- _id: ObjectId
- uuid: 鸟类的公共密钥
- bird: 鸟类的名称
- mandant_uuid: 创建鸟类的客户
Indices
数据库需要执行以下索引。
db.mandants.createIndex({"uuid": 1}, {"unique": true})
db.nestboxes.createIndex({"uuid": 1}, {"unique": true})
db.breeds.createIndex({"uuid": 1}, {"unique": true})
db.breeds.createIndex({"nestbox_uuid": 1})
db.users.createIndex({"uuid": 1}, {"unique": true})
db.users.createIndex({"username": 1}, {"unique": true})
db.geolocations.createIndex({"uuid": 1}, {"unique": true})
db.birds.createIndex({"uuid": 1}, {"unique": true})
db.birds.createIndex({"mandant_uuid": 1})
db.sessions.createIndex({"session_key": 2}, {"unique": true})
db.sessions.createIndex({"session_created_at": 1}, { expireAfterSeconds: 86400 })
db.geolocations.createIndex({"nestbox_uuid": 1})
db.nestboxes.createIndex({"mandant_uuid":1})
Backend
Framework
后端是基于actix-web编写的RESTful服务器。
Start
守护进程需要一个配置文件,如果没有提供配置文件则显示以下消息。
Usage: target/debug/nestboxd -c CONFIG_FILE
Options:
-c, --config CONFIG_FILE
Path to configuration file
配置文件是一个YAML文件,内容如下
mongodb:
uri: mongodb://localhost:27017
database: nestbox
httpserver:
ip: 127.0.0.1
port: "8080"
images:
directory: /some/where
Logging
目前有一个标准日志输出到STDOUT。
[2021-06-03T19:25:32Z INFO actix_server::builder] Starting 4 workers
[2021-06-03T19:25:32Z INFO actix_server::builder] Starting "actix-web-service-127.0.0.1:8080" service on 127.0.0.1:8080
[2021-06-03T19:29:25Z INFO actix_web::middleware::logger] 127.0.0.1:47618 "GET /nestboxes/9915a1ef-edaa-4268-b86c-7e43fe0bbd6b/breeds?page_limit=2&page_number=1 HTTP/1.1" 200 539 "-" "curl/7.68.0" 0.024426
[2021-06-03T19:29:39Z INFO actix_web::middleware::logger] 127.0.0.1:47624 "GET /nestboxes/9915a1ef-edaa-4268-b86c-7e43fe0bbd6b/breeds?page_limit=2&page_number=1 HTTP/1.1" 200 641 "-" "curl/7.68.0" 0.021756
post /login
允许登录。目前,database_bouncycastle对明文密码进行散列。这将被转换为散列,这意味着客户端只传输散列。如果用户登录两次,则旧的会话将被销毁。如果经过身份验证的用户登录失败,则当前会话也将被删除 - 这实际上意味着用户已被注销。
Request
curl \
--header "Content-Type: application/json" \
--request POST \
--data '{"username":"fg_199","password":"secretbird"}' \
http://127.0.0.1:8080/login
Response
{"username":"fg_199","success":true,"session":"28704470-0908-408e-938f-64dd2b7578b9"}
get /birds
Request
必须提供有效的会话。如果是这样,则用户将获得属于用户会话所属客户的鸟类可分页视图。
请注意,提供的鸟类是报告品种时可选择的鸟类。
curl -H "Authorization: Basic 2c91ebd1-800e-4573-8f2b-6ac91c2a407a" http://127.0.0.1:8080/birds?page_limit=2\&page_number=1
Response
{
"documents": [
{
"uuid": "aee03da8-e297-46da-aac2-51f6604558dc",
"bird": "bird_0"
},
{
"uuid":"31a1e34e-7ae3-4b59-9197-c0ad9468fa20",
"bird":"bird_1"
}
],
"counted_documents":150,
"pages":75,
"page_number":1,
"page_limit":2
}
如果没有提供或提供的会话无效,则响应将是
{"error":2,"error_message":"UNAUTHORIZED"}
get /nestboxes/{uuid}/breeds
Request
curl \
-H "Authorization: Basic 28704470-0908-408e-938f-64dd2b7578b9" \
-H "Content-Type: application/json" \
http://127.0.0.1:8080/nestboxes/9915a1ef-edaa-4268-b86c-7e43fe0bbd6b/breeds?page_limit=2\&page_number=1
Response
{
"documents":[
{
"uuid":"0b5cec76-02ac-4c6e-933e-62ebfae3e337",
"nestbox_uuid":"6f25fd00-011a-462f-aa3d-6959e6809017",
"discovery_date":"2021-06-01 18:36:38.989 UTC",
"user_uuid":"071f3391-2c8f-4807-89d8-4b2870228730",
"bird_uuid":"ebe661d6-77ba-4bd1-bae3-9e4e7eb880a6",
"bird":"bird_17"
},
{
"uuid":"320d1b78-3e30-4741-aff2-ce8180dd09fb",
"nestbox_uuid":"6f25fd00-011a-462f-aa3d-6959e6809017",
"discovery_date":"2021-06-01 18:36:38.989 UTC",
"user_uuid":"071f3391-2c8f-4807-89d8-4b2870228730",
"bird_uuid":"afc7ffcf-92aa-4e2a-9c41-92eb300d0281",
"bird":"bird_76"
},
{
"uuid":"aae7236e-74fb-40cb-8502-111ac4f2d984",
"nestbox_uuid":"6f25fd00-011a-462f-aa3d-6959e6809017",
"discovery_date":"2021-06-01 18:36:38.989 UTC",
"user_uuid":"071f3391-2c8f-4807-89d8-4b2870228730",
"bird_uuid":"60e4a52c-cd81-44fc-90bc-b2b578caae08",
"bird":"bird_108"
}
],
"counted_documents":3,
"pages":1,
"page_number":1,
"page_limit":10
}
如果用户未经过身份验证,则响应将不带user_uuid。
{
"documents":[
{
"uuid":"0b5cec76-02ac-4c6e-933e-62ebfae3e337",
"nestbox_uuid":"6f25fd00-011a-462f-aa3d-6959e6809017",
"discovery_date":"2021-06-01 18:36:38.989 UTC",
"bird_uuid":"ebe661d6-77ba-4bd1-bae3-9e4e7eb880a6",
"bird":"bird_17"
},
{
"uuid":"320d1b78-3e30-4741-aff2-ce8180dd09fb",
"nestbox_uuid":"6f25fd00-011a-462f-aa3d-6959e6809017",
"discovery_date":"2021-06-01 18:36:38.989 UTC",
"bird_uuid":"afc7ffcf-92aa-4e2a-9c41-92eb300d0281",
"bird":"bird_76"
},
{
"uuid":"aae7236e-74fb-40cb-8502-111ac4f2d984",
"nestbox_uuid":"6f25fd00-011a-462f-aa3d-6959e6809017",
"discovery_date":"2021-06-01 18:36:38.989 UTC",
"bird_uuid":"60e4a52c-cd81-44fc-90bc-b2b578caae08",
"bird":"bird_108"
}
],
"counted_documents":3,
"pages":1,
"page_number":1,
"page_limit":10
}
post /nestboxes/{uuid}/breeds
Request
curl \
-H "Authorization: Basic b955d5ab-531d-45a5-b610-5b456fa509d9" \
--H "Content-Type: application/json" \
--request POST \
--data '{"bird_uuid": "a4152a25-b734-4748-8a43-2401ed387c65", "bird":"a"}' \
http://127.0.0.1:8080/nestboxes/9973e59f-771d-452f-9a1b-8b4a6d5c4f95/breeds
Response
{"inserted_id":{"$oid":"60bfcc160014769d00e0b88a"}}
post /nestboxes/{uuid}/geolocations
Request
向巢箱添加新的地理位置。如果巢箱被移动(例如,树被砍伐或损坏),则属于这种情况。
curl \
-H "Authorization: Basic 8f42f009-dda8-4448-a2db-f9abb8326b06" \
-H "Content-Type: application/json" \
--request POST \
--data '{ "long":-11.6453, "lat": -47.2345}' \
http://127.0.0.1:8080/nestboxes/787c9399-b10a-44f7-bcc5-251e4414cbb0/geolocations
Response
如果一切顺利
{"inserted_id":"ObjectId(\"60d5a4ed0062a0f1006a1967\")"}
如果用户未正确认证
{"error":2,"error_message":"UNAUTHORIZED"}
或者巢箱属于另一个客户,则用户会话
{"error":1,"error_message":"NESTBOX_OF_OTHER_MANDANT"}
get /nestboxes/{uuid}
Request
检索巢箱。
curl http://127.0.0.1:8080/nestboxes/1bec20fc-5416-4941-b7e4-e15aa26a5c7a
Response
{
"uuid": "5d04fab4-6bdd-4103-b57f-9e231e777790",
"created_at": "2021-06-01 18:36:38.443 +00:00:00",
"images": [
"935206e4bf27eccfa4562f32043ef9435c0a037dfd08811817c187ed618412c1.jpg"
],
"mandant_uuid": "a9ed6720-5e01-417a-9278-9417df4d8aa0",
"mandant_name": "BirdLife 100",
"mandant_website": "https://www.birdwatcher.ch"
}
post /nestboxes/{uuid}/images
Request
允许发布巢箱的图片。
curl -v -H "Authorization: Basic 9e693578-500c-4505-990a-5db978e557c2" -F file=@20211024_170535.jpg http://127.0.0.1:8080/nestboxes/5d04fab4-6bdd-4103-b57f-9e231e777790/images
Response
如果已认证并授权,则返回文件的SHA3哈希值。
< HTTP/1.1 201 Created
< content-length: 86
< content-type: application/json
< date: Thu, 09 Dec 2021 18:03:50 GMT
<
{"file_name":["c9aff3597f2fbc4dd5a22c9c0764c2324c5dd68776a367ac150e6a40bfed6526.jpg"]}
如果未认证或未授权
< HTTP/1.1 401 Unauthorized
< content-length: 42
< content-type: application/json
< date: Thu, 09 Dec 2021 18:06:40 GMT
* HTTP error before end of send, stop sending
<
* Closing connection 0
{"error":2,"error_message":"UNAUTHORIZED"}
依赖关系
~66MB
~1M SLoC