add code download route
This commit is contained in:
parent
480e16d070
commit
dddc4b4081
6 changed files with 131 additions and 2 deletions
40
Cargo.lock
generated
40
Cargo.lock
generated
|
@ -299,6 +299,16 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstr"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.17.0"
|
version = "3.17.0"
|
||||||
|
@ -1118,6 +1128,19 @@ version = "0.31.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "globset"
|
||||||
|
version = "0.4.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"bstr",
|
||||||
|
"log",
|
||||||
|
"regex-automata 0.4.9",
|
||||||
|
"regex-syntax 0.8.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.4.10"
|
version = "0.4.10"
|
||||||
|
@ -1447,6 +1470,22 @@ dependencies = [
|
||||||
"icu_properties",
|
"icu_properties",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ignore"
|
||||||
|
version = "0.4.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"globset",
|
||||||
|
"log",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata 0.4.9",
|
||||||
|
"same-file",
|
||||||
|
"walkdir",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.9.0"
|
version = "2.9.0"
|
||||||
|
@ -1747,6 +1786,7 @@ dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"clap",
|
"clap",
|
||||||
"futures",
|
"futures",
|
||||||
|
"ignore",
|
||||||
"lopdf",
|
"lopdf",
|
||||||
"quick-xml",
|
"quick-xml",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
|
|
@ -29,3 +29,7 @@ utoipa-swagger-ui = { version = "9.0.2", features = ["axum", "vendored"] }
|
||||||
uuid = { version = "1.17.0", features = ["v4"] }
|
uuid = { version = "1.17.0", features = ["v4"] }
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
zip = "4.2.0"
|
zip = "4.2.0"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
ignore = "0.4.23"
|
||||||
|
zip = { version = "4.2.0", default-features = false, features = ["deflate"] }
|
||||||
|
|
46
build.rs
Normal file
46
build.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
use std::{
|
||||||
|
env,
|
||||||
|
fs::File,
|
||||||
|
io::{Read, Write},
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
|
use ignore::Walk;
|
||||||
|
use zip::{CompressionMethod, write::SimpleFileOptions};
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let out_dir = env::var("OUT_DIR")?;
|
||||||
|
let src_dir = ".";
|
||||||
|
|
||||||
|
let zip_path = Path::new(&out_dir).join("archive.zip");
|
||||||
|
let zip_file = File::create(zip_path)?;
|
||||||
|
|
||||||
|
let walkdir = Walk::new(src_dir);
|
||||||
|
let it = walkdir.into_iter();
|
||||||
|
|
||||||
|
let mut zip = zip::ZipWriter::new(zip_file);
|
||||||
|
|
||||||
|
let options = SimpleFileOptions::default()
|
||||||
|
.compression_method(CompressionMethod::Deflated)
|
||||||
|
.unix_permissions(0o755);
|
||||||
|
|
||||||
|
for entry in it {
|
||||||
|
let entry = entry?;
|
||||||
|
let path = entry.path();
|
||||||
|
let name = path.strip_prefix(Path::new(src_dir))?;
|
||||||
|
|
||||||
|
if path.is_file() {
|
||||||
|
zip.start_file(name.to_str().unwrap(), options)?;
|
||||||
|
let mut f = File::open(path)?;
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
f.read_to_end(&mut buffer)?;
|
||||||
|
zip.write_all(&buffer)?;
|
||||||
|
} else if !name.as_os_str().is_empty() {
|
||||||
|
zip.add_directory(name.to_str().unwrap(), options)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zip.finish()?;
|
||||||
|
println!("cargo:rerun-if-changed={}", src_dir);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ use utoipa_swagger_ui::SwaggerUi;
|
||||||
|
|
||||||
use crate::{storage::Postgres, text_encoder::TextEncoder, tokenize::Tokenizer};
|
use crate::{storage::Postgres, text_encoder::TextEncoder, tokenize::Tokenizer};
|
||||||
|
|
||||||
|
pub mod code;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod query;
|
pub mod query;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
|
|
38
src/api/code.rs
Normal file
38
src/api/code.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use axum::{
|
||||||
|
http::{StatusCode, header},
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::TAG;
|
||||||
|
|
||||||
|
const SOURCE_ARCHIVE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/archive.zip"));
|
||||||
|
|
||||||
|
/// Serve the source code archive as a downloadable zip file.
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/code",
|
||||||
|
tag = TAG,
|
||||||
|
responses(
|
||||||
|
(status = 200,
|
||||||
|
description = "Source code archive as zip file",
|
||||||
|
content_type = "application/zip",
|
||||||
|
headers(
|
||||||
|
("content-disposition" = String, description = "Attachment filename"),
|
||||||
|
("content-length" = String, description = "File size in bytes")
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn route() -> Result<Response, StatusCode> {
|
||||||
|
let content_length = SOURCE_ARCHIVE.len().to_string();
|
||||||
|
let headers = [
|
||||||
|
(header::CONTENT_TYPE, "application/zip"),
|
||||||
|
(
|
||||||
|
header::CONTENT_DISPOSITION,
|
||||||
|
"attachment; filename=\"source.zip\"",
|
||||||
|
),
|
||||||
|
(header::CONTENT_LENGTH, content_length.as_str()),
|
||||||
|
];
|
||||||
|
|
||||||
|
Ok((headers, SOURCE_ARCHIVE).into_response())
|
||||||
|
}
|
|
@ -6,13 +6,13 @@ use tower_http::trace::TraceLayer;
|
||||||
use utoipa_axum::{router::OpenApiRouter, routes};
|
use utoipa_axum::{router::OpenApiRouter, routes};
|
||||||
|
|
||||||
use super::state::AppState;
|
use super::state::AppState;
|
||||||
use crate::api::query;
|
use crate::api::{code, query};
|
||||||
|
|
||||||
/// Create the main API router with all endpoints and middleware.
|
/// Create the main API router with all endpoints and middleware.
|
||||||
pub fn router(state: AppState) -> OpenApiRouter {
|
pub fn router(state: AppState) -> OpenApiRouter {
|
||||||
let store = Arc::new(state);
|
let store = Arc::new(state);
|
||||||
OpenApiRouter::new()
|
OpenApiRouter::new()
|
||||||
.routes(routes!(query::route))
|
.routes(routes!(query::route, code::route))
|
||||||
.layer(TraceLayer::new_for_http())
|
.layer(TraceLayer::new_for_http())
|
||||||
.with_state(store)
|
.with_state(store)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue