Skip to content

Commit 8cdcb49

Browse files
committed
feat: support delete deprecated domain files
1 parent eba898f commit 8cdcb49

File tree

9 files changed

+139
-11
lines changed

9 files changed

+139
-11
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SPA-SERVER
22
It is to provide a static web http server with cache and hot reload.
33

4-
[中文 README](./README_CN.md)
4+
[中文说明](./README_CN.md)
55

66
## Feature
77
- Built with Hyper and Warp, fast and small!

client/src/api.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use crate::Config;
22
use anyhow::anyhow;
33
use reqwest::{header, multipart, StatusCode};
44
use serde_json::Value;
5-
use spa_server::admin_server::request::{DomainWithOptVersionOption, UpdateUploadingStatusOption};
5+
use spa_server::admin_server::request::{
6+
DeleteDomainVersionOption, DomainWithOptVersionOption, UpdateUploadingStatusOption,
7+
};
68
use spa_server::domain_storage::{ShortMetaData, UploadDomainPosition};
79
use std::borrow::Cow;
810
use std::path::PathBuf;
@@ -117,6 +119,22 @@ impl API {
117119
handle!(resp)
118120
}
119121

122+
pub fn remove_files(
123+
&self,
124+
domain: Option<String>,
125+
max_reserve: Option<u32>,
126+
) -> anyhow::Result<()> {
127+
let resp = self
128+
.blocking_client
129+
.post(self.url("files/delete"))
130+
.json(&DeleteDomainVersionOption {
131+
domain,
132+
max_reserve,
133+
})
134+
.send()?;
135+
handle!(resp)
136+
}
137+
120138
pub async fn upload_file<T: Into<Cow<'static, str>>>(
121139
&self,
122140
domain: T,

client/src/commands.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub enum Commands {
1818
domain: String,
1919
version: Option<u32> },
2020
Reload,
21+
Delete {domain: Option<String>, max_reserve:Option<u32>}
2122
}
2223

2324
#[derive(Args, Debug)]

client/src/lib.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,20 @@ fn run_with_commands(commands: CliCommand) -> anyhow::Result<()> {
6969
api.reload_sap_server()?;
7070
success("reload success!");
7171
}
72+
Commands::Delete {
73+
domain,
74+
max_reserve,
75+
} => {
76+
api.remove_files(domain, max_reserve)?;
77+
success("delete success!");
78+
}
7279
};
7380
Ok(())
7481
}
7582

7683
#[cfg(test)]
7784
mod test {
78-
use crate::{run_with_commands, CliCommand};
85+
use crate::{run, run_with_commands, CliCommand};
7986
use clap::Parser;
8087
use std::env;
8188

@@ -115,4 +122,15 @@ mod test {
115122
]));
116123
result.unwrap();
117124
}
125+
#[test]
126+
fn test_delete() {
127+
init_config();
128+
let result = run_with_commands(CliCommand::parse_from(&[
129+
"test",
130+
"delete",
131+
"self.noti.link",
132+
"2",
133+
]));
134+
result.unwrap();
135+
}
118136
}

docs/develop/roadmap.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ There is no real roadmap for v1.3.x now, need users firstly.
77
- [ ] CD: release triggered by new tag
88
- [ ] TEST: test integrate, and add ci to run it
99

10-
### Version v1.2.6
10+
### Version v1.2.6(client:v0.1.4)
1111
- [ ] doc: add spa-server usage scenario and corresponding config File
1212
- [ ] doc: what's version control and file directory layout
1313
- [ ] chore: spa-client command help doc
1414
- [ ] bench: add benchmark
15+
- [x] feat(with client): delete deprecated version to save storage
1516
- [x] feat: Support S3 by docker(backward: release docker timzaak/spa-server:1.2.5-s3)
1617
- [x] deps: bump hocon 0.9, fix size unit config parse
1718
- [x] fix(build): disable generating new tag when build spa-client(js) success

docs/guide/spa-client-command-line.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ spa-client -c $CONFIG_PATH info $OPT_DOMAIN
1616

1717
# reload spa-server, this is used to reload https cert
1818
spa-client -c $CONFIG_PATH reload
19+
20+
# delete deprecated domain files
21+
spa-client -c $CONFIG_PATH delete $OPT_DOMAIN $OPT_MAX_RESERVE
1922
```
2023

2124
There also provides http api to interact with admin server,

docs/guide/spa-server-api.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,16 @@ PATH="/upload/file/path"
104104
curl -X POST "http://$ADMIN_SERVER/file/upload" \
105105
-F "file=@$PATH" -F "domain=$DOMAIN" -F "version=$VERSION" -F "path=$PATH" -H "Authorization: Bearer $TOKEN"
106106
# return status code:200 if success
107+
```
108+
109+
### Delete deprecated domain files
110+
```shell
111+
# keep 2 versions.
112+
MAX_RESERVE = 1
113+
curl -X POST "http://$ADMIN_SERVER/files/delete" \
114+
--data-raw `{
115+
"domain":$DOMAIN,
116+
"max_reserve": $MAX_RESERVE
117+
}`
118+
# return status code:200 if success
107119
```

server/src/admin_server.rs

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::admin_server::request::{
2-
DomainWithOptVersionOption, DomainWithVersionOption, GetDomainOption, GetDomainPositionOption,
3-
UpdateUploadingStatusOption,
2+
DeleteDomainVersionOption, DomainWithOptVersionOption, DomainWithVersionOption,
3+
GetDomainOption, GetDomainPositionOption, UpdateUploadingStatusOption,
44
};
55
use crate::config::AdminConfig;
66
use crate::domain_storage::DomainStorage;
@@ -45,7 +45,8 @@ impl AdminServer {
4545
self.update_domain_version()
4646
.or(self.reload_server())
4747
.or(self.change_upload_status())
48-
.or(self.upload_file()),
48+
.or(self.upload_file())
49+
.or(self.remove_domain_version()),
4950
)),
5051
)
5152
}
@@ -150,12 +151,22 @@ impl AdminServer {
150151
.and(warp::query::<DomainWithVersionOption>())
151152
.map(service::get_files_metadata)
152153
}
154+
155+
fn remove_domain_version(
156+
&self,
157+
) -> impl Filter<Extract = (impl warp::Reply,), Error = Rejection> + Clone {
158+
warp::path!("files" / "delete")
159+
.and(with(self.domain_storage.clone()))
160+
.and(warp::query::<DeleteDomainVersionOption>())
161+
.map(service::remove_domain_version)
162+
}
153163
}
154164

155165
pub mod service {
156166
use crate::admin_server::request::{
157-
DomainWithOptVersionOption, DomainWithVersionOption, GetDomainOption,
158-
GetDomainPositionFormat, GetDomainPositionOption, UpdateUploadingStatusOption,
167+
DeleteDomainVersionOption, DomainWithOptVersionOption, DomainWithVersionOption,
168+
GetDomainOption, GetDomainPositionFormat, GetDomainPositionOption,
169+
UpdateUploadingStatusOption,
159170
};
160171
use crate::domain_storage::{DomainStorage, URI_REGEX};
161172
use crate::{AdminConfig, HotReloadManager};
@@ -184,7 +195,7 @@ pub mod service {
184195
return Ok(StatusCode::NOT_FOUND.into_response());
185196
}
186197
}
187-
None => Ok(warp::reply::json(&domain_info).into_response()),
198+
_ => Ok(warp::reply::json(&domain_info).into_response()),
188199
}
189200
}
190201

@@ -344,6 +355,46 @@ pub mod service {
344355
}
345356
}
346357
}
358+
359+
pub(super) fn remove_domain_version(
360+
storage: Arc<DomainStorage>,
361+
query: DeleteDomainVersionOption,
362+
) -> Response {
363+
let domains_info = if let Some(domain) = query.domain {
364+
storage
365+
.get_domain_info_by_domain(&domain)
366+
.map(|v| vec![v])
367+
.unwrap_or(vec![])
368+
} else {
369+
storage.get_domain_info()
370+
};
371+
for info in domains_info {
372+
let delete_versions = if let Some(max_reserve) = query.max_reserve {
373+
if let Some(mut max_version) =
374+
info.current_version
375+
.or(info.versions.iter().max().map(|x| *x))
376+
{
377+
max_version = max_version - max_reserve;
378+
info.versions
379+
.into_iter()
380+
.filter(|v| *v < max_version)
381+
.collect::<Vec<u32>>()
382+
} else {
383+
vec![]
384+
}
385+
} else {
386+
let current_version = info.current_version.unwrap_or(u32::MAX);
387+
info.versions
388+
.into_iter()
389+
.filter(|version| *version != current_version)
390+
.collect::<Vec<u32>>()
391+
};
392+
for version in delete_versions {
393+
let _ = storage.remove_domain_version(&info.domain, Some(version));
394+
}
395+
}
396+
Response::default()
397+
}
347398
}
348399

349400
pub mod request {
@@ -385,12 +436,19 @@ pub mod request {
385436
pub domain: String,
386437
pub version: Option<u32>,
387438
}
439+
388440
#[derive(Deserialize, Serialize)]
389441
pub struct UpdateUploadingStatusOption {
390442
pub domain: String,
391443
pub version: u32,
392444
pub status: UploadingStatus,
393445
}
446+
447+
#[derive(Deserialize, Serialize)]
448+
pub struct DeleteDomainVersionOption {
449+
pub domain: Option<String>,
450+
pub max_reserve: Option<u32>,
451+
}
394452
}
395453

396454
// TODO: the code structure is not friendly with Unit Test, need refactor it.

server/src/domain_storage.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ impl DomainStorage {
191191
}
192192

193193
pub fn get_domain_info_by_domain(&self, domain: &str) -> Option<DomainInfo> {
194-
let versions:Vec<u32> = walkdir::WalkDir::new(&self.prefix.join(domain)).max_depth(1).into_iter().filter_map(|version_entity|{
194+
let versions:Vec<u32> = WalkDir::new(&self.prefix.join(domain)).max_depth(1).into_iter().filter_map(|version_entity|{
195195
let version_entity =version_entity.ok()?;
196196
let version = version_entity.file_name().to_str()?.parse::<u32>().ok()?;
197197
Some(version)
@@ -378,6 +378,23 @@ impl DomainStorage {
378378
}
379379
Ok(())
380380
}
381+
382+
pub fn remove_domain_version(&self, domain:&str, version:Option<u32>) ->anyhow::Result<bool> {
383+
let mut path = self.prefix.join(domain);
384+
if let Some(version) = version {
385+
path = path.join(version.to_string());
386+
if path.exists() {
387+
fs::remove_dir_all(path)?;
388+
return Ok(true);
389+
}
390+
}else {
391+
if path.exists() {
392+
fs::remove_dir_all(path)?;
393+
return Ok(true);
394+
}
395+
}
396+
return Ok(false)
397+
}
381398
}
382399

383400
pub fn md5_file(path: impl AsRef<Path>, byte_buffer: &mut Vec<u8>) -> Option<String> {

0 commit comments

Comments
 (0)