Skip to content

Commit 5caa86f

Browse files
committed
feat: changes query
1 parent 89e2426 commit 5caa86f

File tree

6 files changed

+135
-6
lines changed

6 files changed

+135
-6
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
CREATE TYPE change_type AS ENUM (
2+
'PACKAGE_VERSION_ADDED',
3+
'PACKAGE_TAG_ADDED'
4+
);
5+
6+
CREATE TABLE changes (
7+
seq BIGSERIAL PRIMARY KEY,
8+
change_type change_type NOT NULL,
9+
package_id VARCHAR(255) NOT NULL,
10+
data TEXT NOT NULL,
11+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
12+
);
13+
14+
-- 创建索引
15+
CREATE INDEX changes_package_id_idx ON changes (package_id);
16+
CREATE INDEX changes_created_at_idx ON changes (created_at);

api/src/api/changes.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use crate::api::ApiError;
2+
use hyper::{Body, Request};
3+
use routerify::Router;
4+
use routerify::prelude::*;
5+
use serde::Serialize;
6+
7+
use crate::{
8+
db::{Change, Database},
9+
util::{self, pagination, ApiResult},
10+
};
11+
12+
13+
#[derive(Serialize)]
14+
pub struct ApiChange {
15+
pub seq: i64,
16+
pub r#type: String,
17+
pub id: String,
18+
pub changes: serde_json::Value,
19+
}
20+
21+
impl From<Change> for ApiChange {
22+
fn from(change: Change) -> Self {
23+
Self {
24+
seq: change.seq,
25+
r#type: change.change_type.to_string(),
26+
id: change.package_id,
27+
changes: serde_json::from_str(&change.data).unwrap(),
28+
}
29+
}
30+
}
31+
32+
pub fn changes_router() -> Router<Body, ApiError> {
33+
Router::builder()
34+
.get("/_changes", util::json(list_changes))
35+
.build()
36+
.unwrap()
37+
}
38+
39+
async fn list_changes(req: Request<Body>) -> ApiResult<Vec<ApiChange>> {
40+
let db = req.data::<Database>().unwrap();
41+
let (start, limit) = pagination(&req);
42+
let changes = db.list_changes(start, limit).await?;
43+
Ok(changes.into_iter().map(ApiChange::from).collect())
44+
}

api/src/api/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ mod scope;
88
mod self_user;
99
mod types;
1010
mod users;
11+
mod changes;
12+
1113

1214
use hyper::Body;
1315
use hyper::Response;
@@ -27,6 +29,7 @@ use self::admin::admin_router;
2729
use self::authorization::authorization_router;
2830
use self::scope::scope_router;
2931
use self::users::users_router;
32+
use self::changes::changes_router;
3033

3134
use crate::util;
3235
use crate::util::CacheDuration;
@@ -43,6 +46,7 @@ pub fn api_router() -> Router<Body, ApiError> {
4346
.middleware(Middleware::pre(util::auth_middleware))
4447
.scope("/admin", admin_router())
4548
.scope("/scopes", scope_router())
49+
.scope("/changes", changes_router())
4650
.scope("/user", self_user_router())
4751
.scope("/users", users_router())
4852
.scope("/authorizations", authorization_router())

api/src/db/database.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,33 @@ impl Database {
5656
.await
5757
}
5858

59+
#[instrument(name = "Database::list_changes", skip(self), err)]
60+
pub async fn list_changes(
61+
&self,
62+
since: i64,
63+
limit: i64,
64+
) -> Result<Vec<Change>> {
65+
sqlx::query_as!(
66+
Change,
67+
r#"
68+
SELECT
69+
seq,
70+
change_type as "change_type: ChangeType",
71+
package_id,
72+
data,
73+
created_at
74+
FROM changes
75+
WHERE seq > $1
76+
ORDER BY seq ASC
77+
LIMIT $2
78+
"#,
79+
since,
80+
limit
81+
)
82+
.fetch_all(&self.pool)
83+
.await
84+
}
85+
5986
#[instrument(name = "Database::get_user_public", skip(self), err)]
6087
pub async fn get_user_public(&self, id: Uuid) -> Result<Option<UserPublic>> {
6188
sqlx::query_as!(

api/src/db/models.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,3 +824,37 @@ impl sqlx::postgres::PgHasArrayType for DownloadKind {
824824
sqlx::postgres::PgTypeInfo::with_name("_download_kind")
825825
}
826826
}
827+
828+
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::Type)]
829+
#[sqlx(type_name = "change_type", rename_all = "SCREAMING_SNAKE_CASE")]
830+
#[serde(rename_all = "snake_case")]
831+
pub enum ChangeType {
832+
833+
PackageVersionAdded,
834+
PackageTagAdded,
835+
}
836+
837+
impl std::fmt::Display for ChangeType {
838+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
839+
match self {
840+
Self::PackageVersionAdded => write!(f, "PACKAGE_VERSION_ADDED"),
841+
Self::PackageTagAdded => write!(f, "PACKAGE_TAG_ADDED"),
842+
}
843+
}
844+
}
845+
846+
#[derive(Debug, Clone)]
847+
pub struct Change {
848+
pub seq: i64,
849+
pub change_type: ChangeType,
850+
pub package_id: String,
851+
pub data: String,
852+
pub created_at: DateTime<Utc>,
853+
}
854+
855+
#[derive(Debug)]
856+
pub struct NewChange<'s> {
857+
pub change_type: ChangeType,
858+
pub package_id: &'s str,
859+
pub data: &'s str,
860+
}

api/src/util.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,13 +283,17 @@ pub fn pagination(req: &Request<Body>) -> (i64, i64) {
283283
.and_then(|page| page.parse::<i64>().ok())
284284
.unwrap_or(100)
285285
.clamp(1, 100);
286-
let page = req
287-
.query("page")
288-
.and_then(|page| page.parse::<i64>().ok())
289-
.unwrap_or(1)
290-
.max(1);
291286

292-
let start = (page * limit) - limit;
287+
let start = if let Some(since) = req.query("since").and_then(|s| s.parse::<i64>().ok()) {
288+
since
289+
} else {
290+
let page = req
291+
.query("page")
292+
.and_then(|page| page.parse::<i64>().ok())
293+
.unwrap_or(1)
294+
.max(1);
295+
(page * limit) - limit
296+
};
293297

294298
(start, limit)
295299
}

0 commit comments

Comments
 (0)