mirror of
https://github.com/sergi0g/cup.git
synced 2025-11-12 23:23:48 -05:00
Compare commits
9 Commits
v3.2.0-alp
...
v3.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e26f941c59 | ||
|
|
c411fc4bad | ||
|
|
e965380133 | ||
|
|
ef849b624f | ||
|
|
8db7e2e12b | ||
|
|
54e1998032 | ||
|
|
9f142ab81c | ||
|
|
ffd4d6267c | ||
|
|
242029db22 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -355,7 +355,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cup"
|
||||
version = "3.2.0-alpha.1"
|
||||
version = "3.2.0"
|
||||
dependencies = [
|
||||
"bollard",
|
||||
"chrono",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cup"
|
||||
version = "3.2.0-alpha.1"
|
||||
version = "3.2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -23,7 +23,7 @@ _If you like this project and/or use Cup, please consider starring the project
|
||||
|
||||
- Extremely fast. Cup takes full advantage of your CPU and is hightly optimized, resulting in lightning fast speed. On my Raspberry Pi 5, it took 3.7 seconds for 58 images!
|
||||
- Supports most registries, including Docker Hub, ghcr.io, Quay, lscr.io and even Gitea (or derivatives)
|
||||
- Doesn't exhaust any rate limits. This is the original reason I created Cup. It was inspired by [What's up docker?](https://github.com/getwud/wud) which would always use it up.
|
||||
- Doesn't exhaust any rate limits. This is the original reason I created Cup. I feel that this feature is especially relevant now with [Docker Hub reducing its pull limits for unauthenticated users](https://docs.docker.com/docker-hub/usage/).
|
||||
- Beautiful CLI and web interface for checking on your containers any time.
|
||||
- The binary is tiny! At the time of writing it's just 5.4 MB. No more pulling 100+ MB docker images for a such a simple program.
|
||||
- JSON output for both the CLI and web interface so you can connect Cup to integrations. It's easy to parse and makes webhooks and pretty dashboards simple to set up!
|
||||
@@ -56,7 +56,7 @@ For more information, check the [docs](https://cup.sergi0g.dev/docs/contributing
|
||||
|
||||
## Support
|
||||
|
||||
If you have any questions about Cup, feel free to ask in the [discussions](https://github.com/sergi0g/cup/discussions)!
|
||||
If you have any questions about Cup, feel free to ask in the [discussions](https://github.com/sergi0g/cup/discussions)! You can also join our [discord server](https://discord.gg/jmh5ctzwNG).
|
||||
|
||||
If you find a bug, or want to propose a feature, search for it in the [issues](https://github.com/sergi0g/cup/issues). If there isn't already an open issue, please open one.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Cup can automatically refresh the results when running in server mode. Simply ad
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"refresh_interval": "0 0,30 * 0 0" // Check twice an hour
|
||||
"refresh_interval": "0 0,30 * * * *" // Check twice an hour
|
||||
// Other options
|
||||
}
|
||||
```
|
||||
|
||||
@@ -49,7 +49,7 @@ pub async fn get_images_from_docker_daemon(
|
||||
Some(service_spec) => match &service_spec.task_template {
|
||||
Some(task_spec) => match &task_spec.container_spec {
|
||||
Some(container_spec) => match &container_spec.image {
|
||||
Some(image) => Image::from_inspect_data(image),
|
||||
Some(image) => Image::from_inspect_data(ctx, image),
|
||||
None => None,
|
||||
},
|
||||
None => None,
|
||||
@@ -75,7 +75,7 @@ pub async fn get_images_from_docker_daemon(
|
||||
.collect();
|
||||
inspects
|
||||
.iter()
|
||||
.filter_map(|inspect| Image::from_inspect_data(inspect.clone()))
|
||||
.filter_map(|inspect| Image::from_inspect_data(ctx, inspect.clone()))
|
||||
.collect()
|
||||
}
|
||||
None => {
|
||||
@@ -87,7 +87,7 @@ pub async fn get_images_from_docker_daemon(
|
||||
};
|
||||
images
|
||||
.iter()
|
||||
.filter_map(|image| Image::from_inspect_data(image.clone()))
|
||||
.filter_map(|image| Image::from_inspect_data(ctx, image.clone()))
|
||||
.collect::<Vec<Image>>()
|
||||
}
|
||||
};
|
||||
|
||||
@@ -205,7 +205,7 @@ pub async fn get_latest_tag(
|
||||
}
|
||||
}
|
||||
}
|
||||
None => unreachable!("{:?}", tags),
|
||||
None => error!("Image {} has no remote version tags! Local tag: {}", image.reference, image.parts.tag),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ use xitca_web::{
|
||||
use crate::{
|
||||
check::get_updates,
|
||||
config::Theme,
|
||||
error,
|
||||
structs::update::Update,
|
||||
utils::{
|
||||
json::{to_full_json, to_simple_json},
|
||||
@@ -55,13 +56,24 @@ pub async fn serve(port: &u16, ctx: &Context) -> std::io::Result<()> {
|
||||
if let Some(interval) = &ctx.config.refresh_interval {
|
||||
scheduler
|
||||
.add(
|
||||
Job::new_async(interval, move |_uuid, _lock| {
|
||||
match Job::new_async(interval, move |_uuid, _lock| {
|
||||
let data_copy = data_copy.clone();
|
||||
Box::pin(async move {
|
||||
data_copy.lock().await.refresh().await;
|
||||
})
|
||||
})
|
||||
.unwrap(),
|
||||
}) {
|
||||
Ok(job) => job,
|
||||
Err(e) => match e {
|
||||
tokio_cron_scheduler::JobSchedulerError::ParseSchedule => error!(
|
||||
"Failed to parse cron schedule: {}. Please ensure it is valid!",
|
||||
interval
|
||||
),
|
||||
e => error!(
|
||||
"An unexpected error occured while scheduling automatic refresh: {}",
|
||||
e
|
||||
),
|
||||
},
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -79,12 +91,16 @@ pub async fn serve(port: &u16, ctx: &Context) -> std::io::Result<()> {
|
||||
.at("/", get(handler_service(_static)))
|
||||
.at("/*", get(handler_service(_static)));
|
||||
}
|
||||
app_builder
|
||||
match app_builder
|
||||
.enclosed_fn(logger)
|
||||
.serve()
|
||||
.bind(format!("0.0.0.0:{}", port))?
|
||||
.run()
|
||||
.wait()
|
||||
.bind(format!("0.0.0.0:{}", port))
|
||||
{
|
||||
Ok(r) => r,
|
||||
Err(_) => error!("Failed to bind to port {}. Is it in use?", port),
|
||||
}
|
||||
.run()
|
||||
.wait()
|
||||
}
|
||||
|
||||
async fn _static(data: StateRef<'_, Arc<Mutex<ServerData>>>, path: PathRef<'_>) -> WebResponse {
|
||||
|
||||
@@ -44,7 +44,7 @@ pub struct Image {
|
||||
|
||||
impl Image {
|
||||
/// Creates and populates the fields of an Image object based on the ImageSummary from the Docker daemon
|
||||
pub fn from_inspect_data<T: InspectData>(image: T) -> Option<Self> {
|
||||
pub fn from_inspect_data<T: InspectData>(ctx: &Context, image: T) -> Option<Self> {
|
||||
let tags = image.tags().unwrap();
|
||||
let digests = image.digests().unwrap();
|
||||
if !tags.is_empty() && !digests.is_empty() {
|
||||
@@ -56,7 +56,18 @@ impl Image {
|
||||
let version_tag = Version::from_tag(&tag);
|
||||
let local_digests = digests
|
||||
.iter()
|
||||
.map(|digest| digest.split('@').collect::<Vec<&str>>()[1].to_string())
|
||||
.filter_map(
|
||||
|digest| match digest.split('@').collect::<Vec<&str>>().get(1) {
|
||||
Some(digest) => Some(digest.to_string()),
|
||||
None => {
|
||||
ctx.logger.warn(format!(
|
||||
"Ignoring invalid digest {} for image {}!",
|
||||
digest, reference
|
||||
));
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
Some(Self {
|
||||
reference,
|
||||
|
||||
@@ -17,8 +17,10 @@ pub fn parse_www_authenticate(www_auth: &str) -> String {
|
||||
.fold(String::new(), |acc, (key, value)| {
|
||||
if *key == "realm" {
|
||||
acc.to_owned() + value.as_escaped() + "?"
|
||||
} else {
|
||||
} else if value.unescaped_len() != 0 {
|
||||
format!("{}&{}={}", acc, key, value.as_escaped())
|
||||
} else {
|
||||
acc
|
||||
}
|
||||
})
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user