Skip to content
This repository was archived by the owner on Feb 24, 2025. It is now read-only.

Commit 1af765c

Browse files
committed
feat: add support for i64 type when serializing timestamp
1 parent b1e30f6 commit 1af765c

File tree

9 files changed

+326
-24
lines changed

9 files changed

+326
-24
lines changed

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ b2-client = { version = "0.1.3", features = ["with_surf"] }
1414
base64 = "0.22.0"
1515
bcrypt = "0.15.1"
1616
bson = "2.10.0"
17+
bytes = { version = "1.6.0", features = ["serde"] }
1718
chrono = { version = "0.4.38", features = ["serde"] }
1819
futures = "0.3.30"
1920
hex = { version = "0.4.3", features = ["serde"] }
@@ -40,9 +41,3 @@ lto = true
4041
strip = true
4142
opt-level = 3
4243
codegen-units = 1
43-
44-
[target.x86_64-unknown-linux-gnu]
45-
linker = "x86_64-linux-musl-gcc"
46-
47-
[target.x86_64-pc-windows-gnu]
48-
linker = "x86_64-w64-mingw32-gcc"

src/main.rs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
extern crate chrono;
22
mod database;
3+
mod launch;
34
mod models;
45
mod routers;
5-
mod utils;
6-
mod launch;
76
mod tests;
7+
mod utils;
88
use axum::{
9-
routing::{delete, get, post},
9+
routing::{delete, get, post, put},
1010
Extension, Router,
1111
};
12-
use std::sync::Arc;
13-
use tokio::sync::Mutex;
14-
use serde_json::Value;
1512
use launch::{generate_aes_key, generate_rsa_keypair};
13+
use serde_json::Value;
1614
use socketioxide::{
1715
extract::{AckSender, Bin, Data, SocketRef},
1816
SocketIo,
1917
};
18+
use std::sync::Arc;
19+
use tokio::sync::Mutex;
2020

2121
fn on_connect(socket: SocketRef, Data(data): Data<Value>) {
2222
socket.emit("auth", data).ok();
@@ -58,10 +58,24 @@ async fn main() {
5858

5959
// Set up the router
6060
let app = Router::new()
61-
.route("/activity/:id", get(routers::activities::read::read_one))
62-
.route("/activity/:id", delete(routers::activities::remove::remove_activity))
6361
.route("/activity/", get(routers::activities::read::read_all))
64-
.route("/activity/", post(routers::activities::insert::insert_activity))
62+
.route(
63+
"/activity/",
64+
post(routers::activities::insert::insert_activity),
65+
)
66+
.route("/activity/:id", get(routers::activities::read::read_one))
67+
.route(
68+
"/activity/:id/name",
69+
put(routers::activities::update::update_activity_name),
70+
)
71+
.route(
72+
"/activity/:id/description",
73+
put(routers::activities::update::update_activity_description),
74+
)
75+
.route(
76+
"/activity/:id",
77+
delete(routers::activities::remove::remove_activity),
78+
)
6579
.route("/user/auth", post(routers::auth::login))
6680
.layer(Extension(shared_client.clone()));
6781

src/models/activities.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ pub struct Activity {
9494
pub category: Option<SpecialActivityCategory>,
9595
}
9696

97-
fn datetime_or_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
97+
pub fn datetime_or_u64<'de, D>(deserializer: D) -> Result<u64, D::Error>
9898
where
9999
D: Deserializer<'de>,
100100
{
@@ -114,6 +114,13 @@ where
114114
Ok(value)
115115
}
116116

117+
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
118+
where
119+
E: de::Error,
120+
{
121+
Ok(value as u64)
122+
}
123+
117124
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
118125
where
119126
E: de::Error,

src/models/notifications.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use bson::oid::ObjectId;
22
use serde::{Deserialize, Serialize};
3+
use crate::models::activities::datetime_or_u64;
34

45
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
56
#[serde(rename_all = "kebab-case")]
@@ -13,13 +14,15 @@ pub enum NotificationType {
1314
pub struct Notification {
1415
pub _id: ObjectId,
1516
pub title: String,
16-
pub content: String,
17-
pub time: u128,
17+
pub content: Option<String>,
18+
#[serde(deserialize_with = "datetime_or_u64")]
19+
pub time: u64,
1820
#[serde(rename = "type")]
1921
pub notification_type: NotificationType,
2022
pub publisher: ObjectId,
2123
pub receivers: Option<Vec<ObjectId>>,
2224
pub anoymous: bool,
2325
pub global: bool,
24-
pub expire: u128,
26+
#[serde(deserialize_with = "datetime_or_u64")]
27+
pub expire: u64,
2528
}

src/routers/activities/insert.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{models::{
22
activities::{Activity, ActivityStatus, ActivityType}, groups::GroupPermission, response::{ErrorResponse, ResponseStatus, SuccessResponse}
33
}, utils::jwt::UserData};
44
use axum::{
5-
extract::Extension,
5+
extract::{Extension, Path, Query},
66
http::StatusCode,
77
response::{IntoResponse, Json},
88
};
@@ -14,8 +14,10 @@ use tokio::sync::Mutex;
1414
pub async fn insert_activity(
1515
Extension(db): Extension<Arc<Mutex<Database>>>,
1616
user: UserData,
17+
Query(renew_object_id): Query<Option<bool>>,
1718
Json(mut activity): Json<Activity>,
1819
) -> impl IntoResponse {
20+
let renew_object_id = renew_object_id.unwrap_or(true);
1921
let db = db.lock().await;
2022
let collection = db.collection("activities");
2123
if user.perms.contains(&GroupPermission::Admin) {
@@ -55,7 +57,9 @@ pub async fn insert_activity(
5557
}
5658
// Remove the _id field if it exists
5759
let mut activity = activity;
58-
activity._id = ObjectId::new();
60+
if renew_object_id {
61+
activity._id = ObjectId::new();
62+
}
5963
let activity = bson::to_document(&activity);
6064
if let Ok(activity) = activity {
6165
if let Ok(_) = collection.insert_one(activity, None).await {

src/routers/activities/read.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ pub async fn read_all(
107107

108108
pub async fn read_one(
109109
Extension(client): Extension<Arc<Mutex<Database>>>,
110+
_: UserData,
110111
Path(id): Path<String>,
111112
) -> impl IntoResponse {
112113
println!("ID: {:?}", id);
@@ -126,6 +127,7 @@ pub async fn read_one(
126127
let filter = doc! {"_id": id};
127128
let result = collection.find_one(filter, None).await;
128129
if let Err(e) = result {
130+
println!("Failed to read activity: {:?}", e);
129131
let response = ErrorResponse {
130132
status: ResponseStatus::Error,
131133
code: 500,
@@ -137,6 +139,7 @@ pub async fn read_one(
137139
let result: Option<Activity> = result.unwrap();
138140
match result {
139141
Some(document) => {
142+
println!("{:?}", document);
140143
let response: SuccessResponse<Activity, ()> = SuccessResponse {
141144
status: ResponseStatus::Success,
142145
code: 200,
@@ -147,6 +150,7 @@ pub async fn read_one(
147150
(StatusCode::OK, Json(response))
148151
}
149152
None => {
153+
println!("Activity not found");
150154
let response = ErrorResponse {
151155
status: ResponseStatus::Error,
152156
code: 404,

src/routers/activities/update.rs

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
use crate::{models::{
2+
activities::{Activity, ActivityStatus}, groups::GroupPermission, response::{ErrorResponse, ResponseStatus, SuccessResponse}
3+
}, utils::jwt::UserData};
4+
use axum::{
5+
extract::{Extension, Path},
6+
http::StatusCode,
7+
response::{IntoResponse, Json},
8+
};
9+
use bson::{doc, oid::ObjectId};
10+
use mongodb::{Database, Collection};
11+
use std::sync::Arc;
12+
use tokio::sync::Mutex;
13+
use serde::{Deserialize, Serialize};
14+
15+
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
16+
pub struct UpdateActivityName {
17+
pub name: String,
18+
}
19+
20+
pub async fn update_activity_name(
21+
Extension(db): Extension<Arc<Mutex<Database>>>,
22+
user: UserData,
23+
Path(id): Path<String>,
24+
Json(data): Json<UpdateActivityName>,
25+
) -> impl IntoResponse {
26+
let db = db.lock().await;
27+
let collection: Collection<Activity> = db.collection("activities");
28+
let id = ObjectId::parse_str(&id);
29+
if let Err(_) = id {
30+
let response = ErrorResponse {
31+
status: ResponseStatus::Error,
32+
code: 400,
33+
message: "Invalid activity id".to_string(),
34+
};
35+
let response = serde_json::to_string(&response).unwrap();
36+
return (StatusCode::BAD_REQUEST, Json(response));
37+
}
38+
let id = id.unwrap();
39+
let activity = collection.find_one(doc! {"_id": id}, None).await;
40+
if let Err(e) = activity {
41+
let response = ErrorResponse {
42+
status: ResponseStatus::Error,
43+
code: 500,
44+
message: "Failed to find activity: ".to_string() + &e.to_string(),
45+
};
46+
let response = serde_json::to_string(&response).unwrap();
47+
return (StatusCode::INTERNAL_SERVER_ERROR, Json(response));
48+
}
49+
let activity = activity.unwrap();
50+
if let None = activity {
51+
let response = ErrorResponse {
52+
status: ResponseStatus::Error,
53+
code: 404,
54+
message: "Activity not found".to_string(),
55+
};
56+
let response = serde_json::to_string(&response).unwrap();
57+
return (StatusCode::NOT_FOUND, Json(response));
58+
}
59+
let activity = activity.unwrap();
60+
let creator = activity.creator;
61+
if user.perms.contains(&GroupPermission::Admin) || user.perms.contains(&GroupPermission::Department) || id == creator {
62+
let result = collection.update_one(doc! {"_id": id}, doc! {"$set": {"name": data.name}}, None).await;
63+
if let Err(e) = result {
64+
let response = ErrorResponse {
65+
status: ResponseStatus::Error,
66+
code: 500,
67+
message: "Failed to update activity: ".to_string() + &e.to_string(),
68+
};
69+
let response = serde_json::to_string(&response).unwrap();
70+
return (StatusCode::INTERNAL_SERVER_ERROR, Json(response));
71+
}
72+
let response: SuccessResponse<Vec<Activity>, ()> = SuccessResponse {
73+
status: ResponseStatus::Success,
74+
code: 200,
75+
data: vec![],
76+
metadata: None,
77+
};
78+
let response = serde_json::to_string(&response).unwrap();
79+
return (StatusCode::OK, Json(response));
80+
} else {
81+
let response = ErrorResponse {
82+
status: ResponseStatus::Error,
83+
code: 403,
84+
message: "Permission denied".to_string(),
85+
};
86+
let response = serde_json::to_string(&response).unwrap();
87+
return (StatusCode::FORBIDDEN, Json(response));
88+
}
89+
}
90+
91+
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
92+
pub struct UpdateActivityDescription {
93+
pub description: String,
94+
}
95+
96+
pub async fn update_activity_description(
97+
Extension(db): Extension<Arc<Mutex<Database>>>,
98+
user: UserData,
99+
Path(id): Path<String>,
100+
Json(data): Json<UpdateActivityDescription>,
101+
) -> impl IntoResponse {
102+
let db = db.lock().await;
103+
let collection: Collection<Activity> = db.collection("activities");
104+
let id = ObjectId::parse_str(&id);
105+
if let Err(_) = id {
106+
let response = ErrorResponse {
107+
status: ResponseStatus::Error,
108+
code: 400,
109+
message: "Invalid activity id".to_string(),
110+
};
111+
let response = serde_json::to_string(&response).unwrap();
112+
return (StatusCode::BAD_REQUEST, Json(response));
113+
}
114+
let id = id.unwrap();
115+
let activity = collection.find_one(doc! {"_id": id}, None).await;
116+
if let Err(e) = activity {
117+
let response = ErrorResponse {
118+
status: ResponseStatus::Error,
119+
code: 500,
120+
message: "Failed to find activity: ".to_string() + &e.to_string(),
121+
};
122+
let response = serde_json::to_string(&response).unwrap();
123+
return (StatusCode::INTERNAL_SERVER_ERROR, Json(response));
124+
}
125+
let activity = activity.unwrap();
126+
if let None = activity {
127+
let response = ErrorResponse {
128+
status: ResponseStatus::Error,
129+
code: 404,
130+
message: "Activity not found".to_string(),
131+
};
132+
let response = serde_json::to_string(&response).unwrap();
133+
return (StatusCode::NOT_FOUND, Json(response));
134+
}
135+
let activity = activity.unwrap();
136+
let creator = activity.creator;
137+
if user.perms.contains(&GroupPermission::Admin) || user.perms.contains(&GroupPermission::Department) || id == creator {
138+
let result = collection.update_one(doc! {"_id": id}, doc! {"$set": {"description": data.description}}, None).await;
139+
if let Err(e) = result {
140+
let response = ErrorResponse {
141+
status: ResponseStatus::Error,
142+
code: 500,
143+
message: "Failed to update activity: ".to_string() + &e.to_string(),
144+
};
145+
let response = serde_json::to_string(&response).unwrap();
146+
return (StatusCode::INTERNAL_SERVER_ERROR, Json(response));
147+
}
148+
let response: SuccessResponse<Vec<Activity>, ()> = SuccessResponse {
149+
status: ResponseStatus::Success,
150+
code: 200,
151+
data: vec![],
152+
metadata: None,
153+
};
154+
let response = serde_json::to_string(&response).unwrap();
155+
return (StatusCode::OK, Json(response));
156+
} else {
157+
let response = ErrorResponse {
158+
status: ResponseStatus::Error,
159+
code: 403,
160+
message: "Permission denied".to_string(),
161+
};
162+
let response = serde_json::to_string(&response).unwrap();
163+
return (StatusCode::FORBIDDEN, Json(response));
164+
}
165+
}
166+
167+
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
168+
pub struct UpdateActivityStatus {
169+
status: ActivityStatus,
170+
}

0 commit comments

Comments
 (0)