feat: ✨ download package
This commit is contained in:
parent
1ebdf8be7d
commit
9c64df93fd
9 changed files with 168 additions and 127 deletions
|
@ -5,9 +5,11 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = "0.7.7"
|
axum = "0.7.7"
|
||||||
|
futures = "0.3.31"
|
||||||
serde = { version = "1.0.210", features = ["derive"] }
|
serde = { version = "1.0.210", features = ["derive"] }
|
||||||
serde_json = "1.0.128"
|
serde_json = "1.0.128"
|
||||||
sha3 = "0.10.8"
|
sha3 = "0.10.8"
|
||||||
surrealdb = "2.0.4"
|
surrealdb = "2.0.4"
|
||||||
tokio = { version = "1.40.0", features = ["full"] }
|
tokio = { version = "1.40.0", features = ["full"] }
|
||||||
|
tokio-util = { version = "0.7.12", features = ["io"] }
|
||||||
tower-http = { version = "0.6.1", features = ["cors"] }
|
tower-http = { version = "0.6.1", features = ["cors"] }
|
||||||
|
|
|
@ -6,7 +6,7 @@ use surrealdb::{
|
||||||
};
|
};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
use crate::package::Package;
|
use crate::package::package::Package;
|
||||||
|
|
||||||
static DB: LazyLock<Surreal<Client>> = LazyLock::new(Surreal::init);
|
static DB: LazyLock<Surreal<Client>> = LazyLock::new(Surreal::init);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ pub mod database;
|
||||||
pub mod http;
|
pub mod http;
|
||||||
pub mod package;
|
pub mod package;
|
||||||
pub mod routing;
|
pub mod routing;
|
||||||
pub mod utils;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AppState {}
|
pub struct AppState {}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use tokio::net::TcpListener;
|
||||||
async fn main() {
|
async fn main() {
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
|
|
||||||
let listener = TcpListener::bind("127.0.0.1:2345").await.unwrap();
|
let listener = TcpListener::bind("192.168.1.2:2345").await.unwrap();
|
||||||
database::establish_connection().await.unwrap();
|
database::establish_connection().await.unwrap();
|
||||||
let app_state = AppState {};
|
let app_state = AppState {};
|
||||||
let router = routing::route(axum::extract::State(app_state)).await;
|
let router = routing::route(axum::extract::State(app_state)).await;
|
||||||
|
|
114
src/package.rs
114
src/package.rs
|
@ -1,112 +1,2 @@
|
||||||
use std::fmt::Display;
|
pub mod package;
|
||||||
|
pub mod utils;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use surrealdb::sql::Datetime;
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Package {
|
|
||||||
name: String,
|
|
||||||
publisher: Publisher,
|
|
||||||
version: Version,
|
|
||||||
size: u64,
|
|
||||||
hash: String,
|
|
||||||
publish_date_time: Datetime,
|
|
||||||
last_update_date_time: Datetime,
|
|
||||||
location: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Package {
|
|
||||||
pub fn new(name: String, publisher: Publisher, version: Version) -> Self {
|
|
||||||
Self {
|
|
||||||
name,
|
|
||||||
publisher,
|
|
||||||
version,
|
|
||||||
size: 0,
|
|
||||||
hash: String::default(),
|
|
||||||
publish_date_time: Datetime::default(),
|
|
||||||
last_update_date_time: Datetime::default(),
|
|
||||||
location: String::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn get_name(&self) -> String {
|
|
||||||
self.name.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_publisher_name(&self) -> String {
|
|
||||||
self.publisher.get_name()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_version(&self) -> String {
|
|
||||||
self.version.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_size(&self) -> u64 {
|
|
||||||
self.size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_hash(&self) -> String {
|
|
||||||
self.hash.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_publish_date_time(&self) -> String {
|
|
||||||
self.publish_date_time.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_last_update_date_time(&self) -> String {
|
|
||||||
self.last_update_date_time.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_location(&self) -> String {
|
|
||||||
self.location.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Version {
|
|
||||||
first: u8,
|
|
||||||
second: u8,
|
|
||||||
third: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Version {
|
|
||||||
fn new(first: u8, second: u8, third: u8) -> Self {
|
|
||||||
Version {
|
|
||||||
first,
|
|
||||||
second,
|
|
||||||
third,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, first: u8, second: u8, third: u8) -> &Self {
|
|
||||||
self.first = first;
|
|
||||||
self.second = second;
|
|
||||||
self.third = third;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Version {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}.{}.{}", self.first, self.second, self.third)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Publisher {
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Publisher {
|
|
||||||
fn new(name: String) -> Self {
|
|
||||||
Publisher { name }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_name(&self) -> String {
|
|
||||||
self.name.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, name: String) -> &Self {
|
|
||||||
self.name = name;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
121
src/package/package.rs
Normal file
121
src/package/package.rs
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use surrealdb::sql::Datetime;
|
||||||
|
use tokio::fs::File;
|
||||||
|
use tokio_util::io::ReaderStream;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Package {
|
||||||
|
name: String,
|
||||||
|
publisher: Publisher,
|
||||||
|
version: Version,
|
||||||
|
size: u64,
|
||||||
|
hash: String,
|
||||||
|
publish_date_time: Datetime,
|
||||||
|
last_update_date_time: Datetime,
|
||||||
|
location: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Package {
|
||||||
|
pub fn new(name: String, publisher: Publisher, version: Version) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
publisher,
|
||||||
|
version,
|
||||||
|
size: 0,
|
||||||
|
hash: String::default(),
|
||||||
|
publish_date_time: Datetime::default(),
|
||||||
|
last_update_date_time: Datetime::default(),
|
||||||
|
location: String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_name(&self) -> String {
|
||||||
|
self.name.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_publisher_name(&self) -> String {
|
||||||
|
self.publisher.get_name()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_version(&self) -> String {
|
||||||
|
self.version.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_size(&self) -> u64 {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_hash(&self) -> String {
|
||||||
|
self.hash.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_publish_date_time(&self) -> String {
|
||||||
|
self.publish_date_time.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_last_update_date_time(&self) -> String {
|
||||||
|
self.last_update_date_time.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_location(&self) -> String {
|
||||||
|
self.location.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn serve(&self) -> Option<ReaderStream<File>> {
|
||||||
|
match File::open(self.get_location()).await {
|
||||||
|
Ok(package_file) => Some(ReaderStream::new(package_file)),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Version {
|
||||||
|
first: u8,
|
||||||
|
second: u8,
|
||||||
|
third: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Version {
|
||||||
|
fn new(first: u8, second: u8, third: u8) -> Self {
|
||||||
|
Version {
|
||||||
|
first,
|
||||||
|
second,
|
||||||
|
third,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, first: u8, second: u8, third: u8) -> &Self {
|
||||||
|
self.first = first;
|
||||||
|
self.second = second;
|
||||||
|
self.third = third;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Version {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}.{}.{}", self.first, self.second, self.third)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Publisher {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Publisher {
|
||||||
|
fn new(name: String) -> Self {
|
||||||
|
Publisher { name }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_name(&self) -> String {
|
||||||
|
self.name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, name: String) -> &Self {
|
||||||
|
self.name = name;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
21
src/package/utils.rs
Normal file
21
src/package/utils.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
use crate::{database, routing};
|
||||||
|
|
||||||
|
use super::package::Package;
|
||||||
|
|
||||||
|
pub async fn create_package(package: routing::Package) -> Option<Package> {
|
||||||
|
let package = Package::new(package.name, package.publisher, package.version);
|
||||||
|
database::create_package(package).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read_package(package_name: String) -> Option<Package> {
|
||||||
|
database::read_package(package_name).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_package(package_name: String, package: routing::Package) -> Option<Package> {
|
||||||
|
let package = Package::new(package.name, package.publisher, package.version);
|
||||||
|
database::update_package(package_name, package).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_package(package_name: String) -> Option<Package> {
|
||||||
|
database::delete_package(package_name).await
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use axum::{
|
use axum::{
|
||||||
|
body::Body,
|
||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
|
@ -10,15 +11,15 @@ use tower_http::cors::CorsLayer;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
database,
|
database,
|
||||||
package::{self, Publisher, Version},
|
package::package::{Publisher, Version},
|
||||||
AppState,
|
AppState,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
struct Package {
|
pub struct Package {
|
||||||
name: String,
|
pub name: String,
|
||||||
publisher: Publisher,
|
pub publisher: Publisher,
|
||||||
version: Version,
|
pub version: Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn route(State(app_state): State<AppState>) -> Router {
|
pub async fn route(State(app_state): State<AppState>) -> Router {
|
||||||
|
@ -28,6 +29,7 @@ pub async fn route(State(app_state): State<AppState>) -> Router {
|
||||||
.route("/package/:package_name", get(read_package))
|
.route("/package/:package_name", get(read_package))
|
||||||
.route("/package/:package_name", patch(update_package))
|
.route("/package/:package_name", patch(update_package))
|
||||||
.route("/package/:package_name", delete(delete_package))
|
.route("/package/:package_name", delete(delete_package))
|
||||||
|
.route("/package/download/:package_name", get(download_package))
|
||||||
.layer(CorsLayer::permissive())
|
.layer(CorsLayer::permissive())
|
||||||
.with_state(app_state)
|
.with_state(app_state)
|
||||||
}
|
}
|
||||||
|
@ -46,15 +48,14 @@ async fn alive() -> impl IntoResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_package(Json(package): Json<Package>) -> impl IntoResponse {
|
async fn create_package(Json(package): Json<Package>) -> impl IntoResponse {
|
||||||
let package = package::Package::new(package.name, package.publisher, package.version);
|
match crate::package::utils::create_package(package).await {
|
||||||
match database::create_package(package).await {
|
|
||||||
Some(package) => (StatusCode::CREATED, Json(serde_json::json!(package))),
|
Some(package) => (StatusCode::CREATED, Json(serde_json::json!(package))),
|
||||||
None => (StatusCode::BAD_REQUEST, Json(serde_json::json!(""))),
|
None => (StatusCode::BAD_REQUEST, Json(serde_json::json!(""))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read_package(Path(package_name): Path<String>) -> impl IntoResponse {
|
async fn read_package(Path(package_name): Path<String>) -> impl IntoResponse {
|
||||||
match database::read_package(package_name).await {
|
match crate::package::utils::read_package(package_name).await {
|
||||||
Some(package) => (StatusCode::OK, Json(serde_json::json!(package))),
|
Some(package) => (StatusCode::OK, Json(serde_json::json!(package))),
|
||||||
None => (StatusCode::BAD_REQUEST, Json(serde_json::json!(""))),
|
None => (StatusCode::BAD_REQUEST, Json(serde_json::json!(""))),
|
||||||
}
|
}
|
||||||
|
@ -64,16 +65,24 @@ async fn update_package(
|
||||||
Path(package_name): Path<String>,
|
Path(package_name): Path<String>,
|
||||||
Json(package): Json<Package>,
|
Json(package): Json<Package>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
let package = package::Package::new(package.name, package.publisher, package.version);
|
match crate::package::utils::update_package(package_name, package).await {
|
||||||
match database::update_package(package_name, package).await {
|
|
||||||
Some(package) => (StatusCode::ACCEPTED, Json(serde_json::json!(package))),
|
Some(package) => (StatusCode::ACCEPTED, Json(serde_json::json!(package))),
|
||||||
None => (StatusCode::BAD_REQUEST, Json(serde_json::json!(""))),
|
None => (StatusCode::BAD_REQUEST, Json(serde_json::json!(""))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete_package(Path(package_name): Path<String>) -> impl IntoResponse {
|
async fn delete_package(Path(package_name): Path<String>) -> impl IntoResponse {
|
||||||
match database::delete_package(package_name).await {
|
match crate::package::utils::delete_package(package_name).await {
|
||||||
Some(package) => (StatusCode::NO_CONTENT, Json(serde_json::json!(package))),
|
Some(package) => (StatusCode::NO_CONTENT, Json(serde_json::json!(package))),
|
||||||
None => (StatusCode::BAD_REQUEST, Json(serde_json::json!(""))),
|
None => (StatusCode::BAD_REQUEST, Json(serde_json::json!(""))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn download_package(Path(package_name): Path<String>) -> impl IntoResponse {
|
||||||
|
if let Some(package) = crate::package::utils::read_package(package_name).await {
|
||||||
|
if let Some(package_file_stream) = package.serve().await {
|
||||||
|
return (StatusCode::OK, Body::from_stream(package_file_stream));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(StatusCode::BAD_REQUEST, Body::empty())
|
||||||
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue