Compare commits
3 Commits
0cd3a6c4ae
...
076449b9f9
Author | SHA1 | Date | |
---|---|---|---|
076449b9f9 | |||
00f9926016 | |||
cdadd234d5 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2591,7 +2591,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sr_download"
|
||||
version = "1.1.1"
|
||||
version = "1.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
|
102
pages.md
Normal file
102
pages.md
Normal file
@ -0,0 +1,102 @@
|
||||
# sr-download 提供的网络 api
|
||||
|
||||
## 设置
|
||||
|
||||
要想设置这部分内容
|
||||
|
||||
请编辑 `config.toml` 文件
|
||||
|
||||
```toml
|
||||
# 这个部分
|
||||
[serve]
|
||||
# 服务的地址和端口
|
||||
host_with_port = "0.0.0.0:10002"
|
||||
# 数据库最大连接数
|
||||
db_max_connect = 10
|
||||
# 是否启用 serve 模式
|
||||
enable = true
|
||||
```
|
||||
|
||||
## 页面
|
||||
|
||||
`/dashboard`
|
||||
|
||||
展示当前信息
|
||||
|
||||
## API
|
||||
|
||||
### GET `/last/data`
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "ok",
|
||||
"data": {
|
||||
"save_id": 1322273,
|
||||
"save_type": "save",
|
||||
"len": 2955,
|
||||
"blake_hash": "1e327361ae30604f7828f3e1a0987098a61a16df0ce830352237e60c9db434fe"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GET `/last/save`
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "ok",
|
||||
"data": {
|
||||
"save_id": 1322273,
|
||||
"len": 2955,
|
||||
"blake_hash": "1e327361ae30604f7828f3e1a0987098a61a16df0ce830352237e60c9db434fe"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GET `/last/ship`
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "ok",
|
||||
"data": {
|
||||
"save_id": 1322271,
|
||||
"len": 13721,
|
||||
"blake_hash": "79c97ca4fe9fa982209e58d1e11df6ebf22cf2e96a2fc8cc48f9316982e6d7d5"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GET `/info/:id`
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "ok",
|
||||
"data": {
|
||||
"save_id": 1322271,
|
||||
"save_type": "ship",
|
||||
"len": 13721,
|
||||
"blake_hash": "79c97ca4fe9fa982209e58d1e11df6ebf22cf2e96a2fc8cc48f9316982e6d7d5"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GET `/download/:id`
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "ok",
|
||||
"data": {
|
||||
"info": {
|
||||
"save_id": 1322271,
|
||||
"save_type": "ship",
|
||||
"len": 13721,
|
||||
"blake_hash": "79c97ca4fe9fa982209e58d1e11df6ebf22cf2e96a2fc8cc48f9316982e6d7d5"
|
||||
},
|
||||
"raw_data": "<Ship version=\"1\" liftedOff ..."
|
||||
}
|
||||
}
|
||||
```
|
34
readme.md
34
readme.md
@ -10,39 +10,7 @@ Rewritten in Rust !
|
||||
|
||||
现在支持提供 api 了
|
||||
|
||||
- `GET /last_data` 获取最新的数据信息
|
||||
- 返回范例:
|
||||
|
||||
```json
|
||||
{
|
||||
"save_id": 1322269,
|
||||
"save_type": "save",
|
||||
"len": 3404,
|
||||
"blake_hash": "0b4758dbda98fea0ab6ad58fd589ccc7bb14c29ab8b22e6e49b670db8fec8da9"
|
||||
}
|
||||
```
|
||||
|
||||
- `GET /last_save` 获取最新的存档信息
|
||||
- 返回范例:
|
||||
|
||||
```json
|
||||
{
|
||||
"save_id": 1322269,
|
||||
"len": 3404,
|
||||
"blake_hash": "0b4758dbda98fea0ab6ad58fd589ccc7bb14c29ab8b22e6e49b670db8fec8da9"
|
||||
}
|
||||
```
|
||||
|
||||
- `GET /last_ship` 获取最新的船只信息
|
||||
- 返回范例:
|
||||
|
||||
```json
|
||||
{
|
||||
"save_id": 1322267,
|
||||
"len": 38967,
|
||||
"blake_hash": "9474267203155e5cf31e0e7e34ec014773f8f89c78d262f5bd57b6e27fdc25b2"
|
||||
}
|
||||
```
|
||||
具体 API 请参考 [这个页面](./pages.md)
|
||||
|
||||
## V1
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "sr_download"
|
||||
version = "1.1.1"
|
||||
version = "1.2.0"
|
||||
edition = "2021"
|
||||
default-run = "sr_download"
|
||||
|
||||
|
208
sr_download/src/info.html
Normal file
208
sr_download/src/info.html
Normal file
@ -0,0 +1,208 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cn">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
|
||||
<title>sr-download 信息页面</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #F5F5F5FF;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.box {
|
||||
width: 80%;
|
||||
padding: 40px;
|
||||
border: 2px solid #000;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.box:nth-child(1) {
|
||||
background-color: #FFE0B2;
|
||||
/* 淡橙色 */
|
||||
}
|
||||
|
||||
.box:nth-child(2) {
|
||||
background-color: #C8E6C9;
|
||||
/* 淡绿色 */
|
||||
}
|
||||
|
||||
.box2 {
|
||||
width: 90%;
|
||||
padding: 20px;
|
||||
border: 2px solid #000;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.box2:nth-child(1) {
|
||||
background-color: #BBDEFB;
|
||||
/* 淡蓝色 */
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 40px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.monospace {
|
||||
font-family: monospace;
|
||||
/* 启用等宽字体 */
|
||||
border: 1px solid #000;
|
||||
/* 添加边框 */
|
||||
padding: 2px;
|
||||
/* 添加内边距 */
|
||||
background-color: #343942ba;
|
||||
/* 浅灰色背景 */
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
height: 40px;
|
||||
/* 设置行间距 */
|
||||
}
|
||||
|
||||
.input-section {
|
||||
flex-basis: 50%;
|
||||
/* 占满宽度 */
|
||||
padding: 20px;
|
||||
border: 2px solid #000;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
background-color: #FFCDD2;
|
||||
/* 淡蓝色 */
|
||||
}
|
||||
|
||||
.input-section input {
|
||||
padding: 10px;
|
||||
font-size: 18px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.input-section button {
|
||||
padding: 10px 20px;
|
||||
font-size: 18px;
|
||||
background-color: #b3ffe8;
|
||||
/* 淡蓝色 */
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.input-section button:hover {
|
||||
background-color: #90CAF9;
|
||||
/* 深蓝色 */
|
||||
}
|
||||
|
||||
.result-display {
|
||||
flex-basis: 100%;
|
||||
/* 占满宽度 */
|
||||
padding: 20px;
|
||||
border: 2px solid #000;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
background-color: #E1BEE7;
|
||||
/* 淡紫色 */
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="spacer"></div>
|
||||
<div class="container">
|
||||
<div class="box2">
|
||||
<div class="title">最新数据</div>
|
||||
<div>最大 id: |MAX_ID|</div>
|
||||
<div>类型: |MAX_SAVE_TYPE|</div>
|
||||
<div>长度: |MAX_LEN|</div>
|
||||
<div>blake hash: <span class="monospace">|MAX_HASH|</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div class="container">
|
||||
<div class="box">
|
||||
<div class="title">最新飞船</div>
|
||||
<div>最大飞船 id: |MAX_SHIP_ID|</div>
|
||||
<div>长度: |MAX_SHIP_LEN|</div>
|
||||
<div>blake hash: <span class="monospace">|MAX_SHIP_HASH|</span></div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="title">最新存档</div>
|
||||
<div>最大存档 id: |MAX_SAVE_ID|</div>
|
||||
<div>长度: |MAX_SAVE_LEN|</div>
|
||||
<div>blake hash: <span class="monospace">|MAX_SAVE_HASH|</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div class="container">
|
||||
<div class="input-section">
|
||||
<input type="number" id="dataId" placeholder="输入ID">
|
||||
<button onclick="fetchData()">获取数据</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div class="container">
|
||||
<div class="result-display">
|
||||
<div class="title">请求结果</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function fetchData() {
|
||||
// 获取输入框中的 ID
|
||||
const dataId = document.getElementById('dataId').value;
|
||||
if (!dataId) {
|
||||
alert('请输入 ID');
|
||||
return;
|
||||
}
|
||||
if (dataId < 76858) {
|
||||
alert('ID 不能小于 76858 (这个是目前最小的 ID)');
|
||||
return;
|
||||
}
|
||||
// 发送请求
|
||||
fetch(`/info/${dataId}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// 获取结果显示区域
|
||||
const resultDisplay = document.querySelector('.result-display');
|
||||
// 清空结果显示区域
|
||||
resultDisplay.innerHTML = '';
|
||||
// 创建结果显示区域的元素
|
||||
const resultTitle = document.createElement('div');
|
||||
resultTitle.classList.add('title');
|
||||
resultTitle.innerText = '请求结果';
|
||||
resultDisplay.appendChild(resultTitle);
|
||||
// 先判断数据拿没拿到
|
||||
if (data["code"] != 200) {
|
||||
// 没拿到
|
||||
const resultContent = document.createElement('div');
|
||||
resultContent.innerText = data["msg"];
|
||||
resultDisplay.appendChild(resultContent);
|
||||
} else {
|
||||
// 拿到了
|
||||
// 创建结果显示区域的元素
|
||||
const resultContent = document.createElement('div');
|
||||
const inner_data = data["data"];
|
||||
// 添加数据
|
||||
resultContent.innerHTML = `<div>id: ${inner_data["save_id"]}</div>
|
||||
<div>类型: ${inner_data["save_type"]}</div>
|
||||
<div>长度: ${inner_data["len"]}</div>
|
||||
<div>blake hash: <span class="monospace">${inner_data["blake_hash"]}</span></div>`;
|
||||
resultDisplay.appendChild(resultContent);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,10 +1,60 @@
|
||||
use axum::{extract::State, routing::get, Json, Router};
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
response::{Html, IntoResponse},
|
||||
routing::get,
|
||||
Json, Router,
|
||||
};
|
||||
use sea_orm::{ActiveEnum, DatabaseConnection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::db_part;
|
||||
use migration::SaveId;
|
||||
|
||||
pub mod traits;
|
||||
|
||||
use traits::FromDb;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct WebResponse<T> {
|
||||
pub code: u32,
|
||||
pub msg: String,
|
||||
pub data: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> WebResponse<T> {
|
||||
pub fn new(data: Option<T>) -> Self {
|
||||
match data {
|
||||
Some(data) => Self::new_normal(data),
|
||||
None => Self::new_missing("internal error?".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_normal(data: T) -> Self {
|
||||
Self {
|
||||
code: StatusCode::OK.as_u16() as u32,
|
||||
msg: "ok".to_string(),
|
||||
data: Some(data),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_missing(msg: String) -> Self {
|
||||
Self {
|
||||
code: StatusCode::NOT_FOUND.as_u16() as u32,
|
||||
msg,
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_error(status: StatusCode, msg: String) -> Self {
|
||||
Self {
|
||||
code: status.as_u16() as u32,
|
||||
msg,
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 最后一个数据的信息
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct LastData {
|
||||
@ -15,8 +65,7 @@ pub struct LastData {
|
||||
}
|
||||
|
||||
impl LastData {
|
||||
pub async fn from_db(db: &DatabaseConnection) -> Option<Self> {
|
||||
let id = db_part::find_max_id(db).await;
|
||||
pub async fn from_db_by_id(db: &DatabaseConnection, id: SaveId) -> Option<Self> {
|
||||
let data = db_part::get_raw_data(id, db).await?;
|
||||
Some(Self {
|
||||
save_id: data.save_id,
|
||||
@ -35,17 +84,6 @@ pub struct LastSave {
|
||||
pub blake_hash: String,
|
||||
}
|
||||
|
||||
impl LastSave {
|
||||
pub async fn from_db(db: &DatabaseConnection) -> Option<Self> {
|
||||
let data = db_part::find_max_save(db).await?;
|
||||
Some(Self {
|
||||
save_id: data.save_id,
|
||||
len: data.len,
|
||||
blake_hash: data.blake_hash,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// 最后一个飞船的信息
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct LastShip {
|
||||
@ -54,27 +92,135 @@ pub struct LastShip {
|
||||
pub blake_hash: String,
|
||||
}
|
||||
|
||||
impl LastShip {
|
||||
pub async fn from_db(db: &DatabaseConnection) -> Option<Self> {
|
||||
let data = db_part::find_max_ship(db).await?;
|
||||
/// 实际信息
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct RawData {
|
||||
pub info: LastData,
|
||||
pub raw_data: String,
|
||||
}
|
||||
|
||||
impl RawData {
|
||||
pub async fn from_db_by_id(db: &DatabaseConnection, id: SaveId) -> Option<Self> {
|
||||
let data = db_part::get_raw_data(id, db).await?;
|
||||
Some(Self {
|
||||
info: LastData {
|
||||
save_id: data.save_id,
|
||||
save_type: data.save_type.to_value().to_string(),
|
||||
len: data.len,
|
||||
blake_hash: data.blake_hash,
|
||||
},
|
||||
raw_data: data.text?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_last_data(State(db): State<DatabaseConnection>) -> Json<Option<LastData>> {
|
||||
Json(LastData::from_db(&db).await)
|
||||
async fn get_last_data(State(db): State<DatabaseConnection>) -> Json<WebResponse<LastData>> {
|
||||
Json(WebResponse::new(LastData::from_db(&db).await))
|
||||
}
|
||||
|
||||
async fn get_last_save(State(db): State<DatabaseConnection>) -> Json<Option<LastSave>> {
|
||||
Json(LastSave::from_db(&db).await)
|
||||
async fn get_last_save(State(db): State<DatabaseConnection>) -> Json<WebResponse<LastSave>> {
|
||||
Json(WebResponse::new(LastSave::from_db(&db).await))
|
||||
}
|
||||
|
||||
async fn get_last_ship(State(db): State<DatabaseConnection>) -> Json<Option<LastShip>> {
|
||||
Json(LastShip::from_db(&db).await)
|
||||
async fn get_last_ship(State(db): State<DatabaseConnection>) -> Json<WebResponse<LastShip>> {
|
||||
Json(WebResponse::new(LastShip::from_db(&db).await))
|
||||
}
|
||||
|
||||
async fn get_data_info_by_id(
|
||||
State(db): State<DatabaseConnection>,
|
||||
Path(raw_id): Path<String>,
|
||||
) -> Json<WebResponse<LastData>> {
|
||||
match raw_id.parse::<SaveId>() {
|
||||
Ok(id) => match LastData::from_db_by_id(&db, id).await {
|
||||
Some(data) => Json(WebResponse::new_normal(data)),
|
||||
None => Json(WebResponse::new_missing("data not found".to_string())),
|
||||
},
|
||||
Err(e) => Json(WebResponse::new_error(
|
||||
StatusCode::BAD_REQUEST,
|
||||
format!("id parse error: {:?}", e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_data_by_id(
|
||||
State(db): State<DatabaseConnection>,
|
||||
Path(raw_id): Path<String>,
|
||||
) -> Json<WebResponse<RawData>> {
|
||||
match raw_id.parse::<SaveId>() {
|
||||
Ok(id) => match RawData::from_db_by_id(&db, id).await {
|
||||
Some(data) => Json(WebResponse::new_normal(data)),
|
||||
None => Json(WebResponse::new_missing("data not found".to_string())),
|
||||
},
|
||||
Err(e) => Json(WebResponse::new_error(
|
||||
StatusCode::BAD_REQUEST,
|
||||
format!("id parse error: {:?}", e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn jump_to_info(Path(path): Path<String>) -> impl IntoResponse {
|
||||
// html jump
|
||||
(
|
||||
StatusCode::MOVED_PERMANENTLY,
|
||||
Html(format!(
|
||||
"<h1>Jumping from {} to /dashboard</h1><script>location.href='/dashboard'</script>",
|
||||
path
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
/// 下面这段话是用于喂给 GitHub Copilot 让他帮我生成一个好用的 info 页面的 prompt
|
||||
/// 页面背景 F5F5F5FF
|
||||
/// 页面标题为 "sr-download 信息页面"
|
||||
/// 页面内容为三个白色框, 横向排列
|
||||
/// 里面分别是最大 id, 最大飞船 id, 最大存档 id 的信息展示
|
||||
/// 框内文字居中,字体大小 24px, 字体为浏览器默认给的
|
||||
/// 最大 id 部分的文字为 "最大 id: |MAX_ID|\n存档类型: |MAX_SAVE_TYPE|\n长度: |MAX_LEN|\nblake hash: |MAX_HASH|"
|
||||
/// 最大飞船 id 部分展示相关信息, 存档 id 部分同理, 用 相关 |xxx| 作为占位符
|
||||
/// 同时展示 长度, blake hash
|
||||
/// 两个部分分别会展示三行字
|
||||
/// 三个框之间的间距为 20px, 宽度为 80%, 高度为 100%
|
||||
/// 框上面分别是 "最新数据" "最新飞船" "最新存档" 的标题
|
||||
const INFO_PAGE: &str = include_str!("info.html");
|
||||
|
||||
async fn dashboard_page(State(db): State<DatabaseConnection>) -> Html<String> {
|
||||
let max_id = db_part::find_max_id(&db).await;
|
||||
let max_id_data = db_part::get_raw_data(max_id, &db).await.unwrap();
|
||||
let max_ship = db_part::find_max_ship(&db).await;
|
||||
let max_save = db_part::find_max_save(&db).await;
|
||||
|
||||
let mut page_content = INFO_PAGE
|
||||
.replace("|MAX_ID|", &max_id_data.save_id.to_string())
|
||||
.replace(
|
||||
"|MAX_SAVE_TYPE|",
|
||||
&max_id_data.save_type.to_value().to_string(),
|
||||
)
|
||||
.replace("|MAX_LEN|", &max_id_data.len.to_string())
|
||||
.replace("|MAX_HASH|", &max_id_data.blake_hash);
|
||||
if let Some(max_ship) = max_ship {
|
||||
page_content = page_content
|
||||
.replace("|MAX_SHIP_ID|", &max_ship.save_id.to_string())
|
||||
.replace("|MAX_SHIP_LEN|", &max_ship.len.to_string())
|
||||
.replace("|MAX_SHIP_HASH|", &max_ship.blake_hash);
|
||||
} else {
|
||||
page_content = page_content
|
||||
.replace("|MAX_SHIP_ID|", "not found")
|
||||
.replace("|MAX_SHIP_LEN|", "not found")
|
||||
.replace("|MAX_SHIP_HASH|", "not found");
|
||||
}
|
||||
if let Some(max_save) = max_save {
|
||||
page_content = page_content
|
||||
.replace("|MAX_SAVE_ID|", &max_save.save_id.to_string())
|
||||
.replace("|MAX_SAVE_LEN|", &max_save.len.to_string())
|
||||
.replace("|MAX_SAVE_HASH|", &max_save.blake_hash);
|
||||
} else {
|
||||
page_content = page_content
|
||||
.replace("|MAX_SAVE_ID|", "not found")
|
||||
.replace("|MAX_SAVE_LEN|", "not found")
|
||||
.replace("|MAX_SAVE_HASH|", "not found");
|
||||
}
|
||||
|
||||
Html(page_content)
|
||||
}
|
||||
|
||||
pub async fn web_main() -> anyhow::Result<()> {
|
||||
@ -83,12 +229,23 @@ pub async fn web_main() -> anyhow::Result<()> {
|
||||
let listener = tokio::net::TcpListener::bind(conf.serve.host_with_port.clone()).await?;
|
||||
let db = db_part::connect_server(&conf).await?;
|
||||
let app = Router::new()
|
||||
// get /last_data
|
||||
.route("/last_data", get(get_last_data))
|
||||
// get /last_save
|
||||
.route("/last_save", get(get_last_save))
|
||||
// get /last_ship
|
||||
.route("/last_ship", get(get_last_ship))
|
||||
// 获取最后一个数据
|
||||
.route("/last/data", get(get_last_data).post(get_last_data))
|
||||
// 获取最后一个存档
|
||||
.route("/last/save", get(get_last_save).post(get_last_save))
|
||||
// 获取最后一个飞船
|
||||
.route("/last/ship", get(get_last_ship).post(get_last_ship))
|
||||
// 获取指定 id 的数据(也有可能返回 not found)
|
||||
.route(
|
||||
"/info/:id",
|
||||
get(get_data_info_by_id).post(get_data_info_by_id),
|
||||
)
|
||||
// 获取下载指定 id 的数据
|
||||
.route("/download/:id", get(get_data_by_id).post(get_data_by_id))
|
||||
// info 页面
|
||||
.route("/dashboard", get(dashboard_page).post(dashboard_page))
|
||||
// 其他所有路径, 直接跳转到 info 页面
|
||||
.route("/*path", get(jump_to_info).post(jump_to_info))
|
||||
// db
|
||||
.with_state(db);
|
||||
|
||||
|
45
sr_download/src/serve/traits.rs
Normal file
45
sr_download/src/serve/traits.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use sea_orm::{ActiveEnum, DatabaseConnection};
|
||||
|
||||
use super::{LastData, LastSave, LastShip};
|
||||
use crate::db_part;
|
||||
|
||||
pub trait FromDb {
|
||||
async fn from_db(db: &DatabaseConnection) -> Option<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl FromDb for LastData {
|
||||
async fn from_db(db: &DatabaseConnection) -> Option<Self> {
|
||||
let id = db_part::find_max_id(db).await;
|
||||
let data = db_part::get_raw_data(id, db).await?;
|
||||
Some(Self {
|
||||
save_id: data.save_id,
|
||||
save_type: data.save_type.to_value().to_string(),
|
||||
len: data.len,
|
||||
blake_hash: data.blake_hash,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FromDb for LastSave {
|
||||
async fn from_db(db: &DatabaseConnection) -> Option<Self> {
|
||||
let data = db_part::find_max_save(db).await?;
|
||||
Some(Self {
|
||||
save_id: data.save_id,
|
||||
len: data.len,
|
||||
blake_hash: data.blake_hash,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FromDb for LastShip {
|
||||
async fn from_db(db: &DatabaseConnection) -> Option<Self> {
|
||||
let data = db_part::find_max_ship(db).await?;
|
||||
Some(Self {
|
||||
save_id: data.save_id,
|
||||
len: data.len,
|
||||
blake_hash: data.blake_hash,
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user