
于是我自己的提问箱就是 https://ask.[data deleted]!
源代码在这里 #/boxy ?(给大家表演如何用一个项目气死你写前端和 Rust 的朋友(x
回答是人工的,并不是人工智能?(不过感觉加上人工智障能的话似乎也蛮好玩诶!
然后因为是模仿 peing 或者 sarahah 那样的匿名的提问箱,所以这个截图里就差不多是后台了~既然说是匿名的话,那就是真匿名~除了记录了提问时间,IP 只用来做了速度限制,没扔数据库。Question 在 MongoDB 里的模型则是
{
"_id" : ObjectId("5e0a0ca0001cd1ea00876f2e"),
"question" : "What's this?",
"question_time" : NumberLong(1577716896),
"answer" : "It's a question box!",
"answer_time" : NumberLong(1577716906),
"id" : NumberLong(0)
}
箱子的 owner 登录之后,直接点击答案的部分就进入编辑 / 回答模式,然后也可以删除提问什么的~
代码里倒是 Web 和纯 API 方式都实现了~然后回答之后自动生成 Twitter 卡片什么的还没有做_(:3」∠)_
再从技术层面上来说的话,这个项目用 Rust 上的 Hyper 作为 HTTP 服务器,然后数据库使用了 MongoDB,考虑到是作为单一用户的提问箱,因此用户名和密码是需要写在 boxy.json 里的~当然放进数据库里的是密码加盐后再 HMAC + SHA512 过的
stored_password = HMACSHA512(password + password_salt, password_salt)
{
"_id" : ObjectId("5e09a250004b7da70096e3dc"),
"user" : "ryza",
"password" : "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
Web 界面和后端做了分离,我自己写了一个简单的~因此就直接手写了,没有用到现在前端流行的 Vue 或者 Angular 之类的框架。访问后端的 API 也是直接用的 jQuery 的 AJAX 模块。
登录之后会返回一个 token,包含了有效期,默认是 365 天,保存在了浏览器的 localStorage 里。登出的时候会自动清掉,但不是关闭页面 / 浏览器自动清除,所以在公用电脑上要记得登出~
那么下面是目前有的一些 API~ 以 http://localhost:5534 为例子!
1. 登录
API: http://localhost:5534/api/v1/token
Method: POST
{
"type": "new",
"username": "ryza",
"password": HMACSHA512(HMACSHA512("password" + password_salt, password_salt) + time, token_salt),
"time": 1577716906,
}
在密码正确,且送达时 API Backend 的时间与计算 HMACSHA512 的时间相差 3 秒以内时就没问题。通过验证时可能的返回值如下~正确 status 肯定是 0,发生错误时 status 的值为相应的 HTTP 状态码,并且会有 reason 告知原因(后面所有的 API 均如此
{
"status": 0,
"token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"expired_time": 1597716906,
}
在发生错误时则可能是
{
"status": 403,
"reason": "Access denied"
}
2. 提问题
API: http://localhost:5534/api/v1/question
Method: POST
{
"question": "How to send questions?"
}
因为是匿名提问箱,所以提问不需要别的东西啦~只要有 question 就行~成功时会将对应的 Question ID 返回回来~
{
"status": 0,
"id": 233
}
3. 回答问题
API: http://localhost:5534/api/v1/answer/{:id}
Method: POST
{
"answer": "Type anything you want to ask in the textarea above(*^3^)",
"token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
}
需要注意这里的 API 的 URL,最后的 {:id} 替换成要回答的问题的 Question ID,比如要回答 ID 为 233 的问题的话,就是
http://localhost:5534/api/v1/answer/233
成功时只会返回 status 并且值为 0.
4. 删除问题
API: http://localhost:5534/api/v1/question/{:id}
Method: DELETE
{
"token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
成功时只会返回 status 并且值为 0. 失败的话就参阅 reason 啦~
5. 获取某个问题的详情
API: http://localhost:5534/api/v1/question/{:id}
Method: GET
如果有 {:id} 对应的问题的话,将返回
{
"status": 0,
"question": {
"id": 233,
"question_time": 1577718525,
"question": "How to send questions?",
"answer": "Type anything you want to ask in the textarea above(*^3^)",
"answer_time": 1577718525
}
}
6. 获取从某一 ID 之前的最多 30 个问题
API: http://localhost:5534/api/v1/question/from/{:id}
Method: GET
假如有 [1, 2, ..., 45] 个 问题的话,要拿到 33 之前(不包含 33)的 30 个问题则是
API: http://localhost:5534/api/v1/question/from/33
(如果中间有的问题被删掉了,则数满 30 个为止,或者没有更多在 33 之前的问题了)
{
"status": 0,
"questions": [
{
"id": 3,
"question_time": 1577718523,
"question": "How to build my own boxy?",
"answer": "It's too long to answer here >_< But you can visit tis GitHub for usage ➜ #/boxy",
"answer_time": 1577718556
},
{
"id": 2,
"question_time": 1577718522,
"question": "How to send questions?",
"answer": "Type anything you want to ask in the textarea above(*^3^)",
"answer_time": 1577718532
},
{
"id": 1,
"question_time": 1577718448,
"question": "What's this?",
"answer": "It's a question box!",
"answer_time": 1577718458
}
]
}
7. 获取新的 Token
其实这个就是登录啦~
API: http://localhost:5534/api/v1/login
Method: POST
{
"type": "new",
"username": "ryza",
"password": HMACSHA512(HMACSHA512("password" + password_salt, password_salt) + time, token_salt),
"time": 1577716906,
}
可能的返回值如下~
{
"status": 0,
"token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"expired_time": 1597716906,
}
8. 注销当前 Token
API: http://localhost:5534/api/v1/login
Method: POST
{
"type": "revoke",
"token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
}
成功时只会返回 status 并且值为 0. 失败的话就参阅 reason 啦~
9. 注销所有 Token
API: http://localhost:5534/api/v1/login
Method: POST
{
"type": "revoke_all",
"token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
}
成功时只会返回 status 并且值为 0. 失败的话就参阅 reason 啦~