mirror of
https://github.com/sergi0g/cup.git
synced 2025-11-13 07:33: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]]
|
[[package]]
|
||||||
name = "cup"
|
name = "cup"
|
||||||
version = "3.2.0-alpha.1"
|
version = "3.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bollard",
|
"bollard",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cup"
|
name = "cup"
|
||||||
version = "3.2.0-alpha.1"
|
version = "3.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[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!
|
- 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)
|
- 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.
|
- 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.
|
- 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!
|
- 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
|
## 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.
|
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
|
```jsonc
|
||||||
{
|
{
|
||||||
"refresh_interval": "0 0,30 * 0 0" // Check twice an hour
|
"refresh_interval": "0 0,30 * * * *" // Check twice an hour
|
||||||
// Other options
|
// Other options
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ pub async fn get_images_from_docker_daemon(
|
|||||||
Some(service_spec) => match &service_spec.task_template {
|
Some(service_spec) => match &service_spec.task_template {
|
||||||
Some(task_spec) => match &task_spec.container_spec {
|
Some(task_spec) => match &task_spec.container_spec {
|
||||||
Some(container_spec) => match &container_spec.image {
|
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,
|
||||||
},
|
},
|
||||||
None => None,
|
None => None,
|
||||||
@@ -75,7 +75,7 @@ pub async fn get_images_from_docker_daemon(
|
|||||||
.collect();
|
.collect();
|
||||||
inspects
|
inspects
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|inspect| Image::from_inspect_data(inspect.clone()))
|
.filter_map(|inspect| Image::from_inspect_data(ctx, inspect.clone()))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@@ -87,7 +87,7 @@ pub async fn get_images_from_docker_daemon(
|
|||||||
};
|
};
|
||||||
images
|
images
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|image| Image::from_inspect_data(image.clone()))
|
.filter_map(|image| Image::from_inspect_data(ctx, image.clone()))
|
||||||
.collect::<Vec<Image>>()
|
.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::{
|
use crate::{
|
||||||
check::get_updates,
|
check::get_updates,
|
||||||
config::Theme,
|
config::Theme,
|
||||||
|
error,
|
||||||
structs::update::Update,
|
structs::update::Update,
|
||||||
utils::{
|
utils::{
|
||||||
json::{to_full_json, to_simple_json},
|
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 {
|
if let Some(interval) = &ctx.config.refresh_interval {
|
||||||
scheduler
|
scheduler
|
||||||
.add(
|
.add(
|
||||||
Job::new_async(interval, move |_uuid, _lock| {
|
match Job::new_async(interval, move |_uuid, _lock| {
|
||||||
let data_copy = data_copy.clone();
|
let data_copy = data_copy.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
data_copy.lock().await.refresh().await;
|
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
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -79,10 +91,14 @@ pub async fn serve(port: &u16, ctx: &Context) -> std::io::Result<()> {
|
|||||||
.at("/", get(handler_service(_static)))
|
.at("/", get(handler_service(_static)))
|
||||||
.at("/*", get(handler_service(_static)));
|
.at("/*", get(handler_service(_static)));
|
||||||
}
|
}
|
||||||
app_builder
|
match app_builder
|
||||||
.enclosed_fn(logger)
|
.enclosed_fn(logger)
|
||||||
.serve()
|
.serve()
|
||||||
.bind(format!("0.0.0.0:{}", port))?
|
.bind(format!("0.0.0.0:{}", port))
|
||||||
|
{
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(_) => error!("Failed to bind to port {}. Is it in use?", port),
|
||||||
|
}
|
||||||
.run()
|
.run()
|
||||||
.wait()
|
.wait()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ pub struct Image {
|
|||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
/// Creates and populates the fields of an Image object based on the ImageSummary from the Docker daemon
|
/// 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 tags = image.tags().unwrap();
|
||||||
let digests = image.digests().unwrap();
|
let digests = image.digests().unwrap();
|
||||||
if !tags.is_empty() && !digests.is_empty() {
|
if !tags.is_empty() && !digests.is_empty() {
|
||||||
@@ -56,7 +56,18 @@ impl Image {
|
|||||||
let version_tag = Version::from_tag(&tag);
|
let version_tag = Version::from_tag(&tag);
|
||||||
let local_digests = digests
|
let local_digests = digests
|
||||||
.iter()
|
.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();
|
.collect();
|
||||||
Some(Self {
|
Some(Self {
|
||||||
reference,
|
reference,
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ pub fn parse_www_authenticate(www_auth: &str) -> String {
|
|||||||
.fold(String::new(), |acc, (key, value)| {
|
.fold(String::new(), |acc, (key, value)| {
|
||||||
if *key == "realm" {
|
if *key == "realm" {
|
||||||
acc.to_owned() + value.as_escaped() + "?"
|
acc.to_owned() + value.as_escaped() + "?"
|
||||||
} else {
|
} else if value.unescaped_len() != 0 {
|
||||||
format!("{}&{}={}", acc, key, value.as_escaped())
|
format!("{}&{}={}", acc, key, value.as_escaped())
|
||||||
|
} else {
|
||||||
|
acc
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user