mirror of
https://codeberg.org/icewind/docker-hub-rss.git
synced 2026-06-03 18:24:06 +02:00
initial version
This commit is contained in:
parent
a4e383c12c
commit
6f96bd5133
5 changed files with 1783 additions and 4 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
||||||
/target
|
/target
|
||||||
|
.env
|
||||||
1645
Cargo.lock
generated
1645
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
11
Cargo.toml
11
Cargo.toml
|
|
@ -3,6 +3,13 @@ name = "docker-hub-rss"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
tokio = { version = "1", features = ["macros", "rt-multi-thread", "signal"] }
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "json"] }
|
||||||
|
warp = "0.3"
|
||||||
|
serde_json = "1"
|
||||||
|
color-eyre = "0.5"
|
||||||
|
dotenv = "0.15"
|
||||||
|
rss = { version = "1", features = ["builders"] }
|
||||||
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
49
src/hub.rs
Normal file
49
src/hub.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use color_eyre::eyre::WrapErr;
|
||||||
|
use color_eyre::{eyre::ensure, Result};
|
||||||
|
use reqwest::Client;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct Hub {
|
||||||
|
client: Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hub {
|
||||||
|
pub async fn tags(&self, user: &str, repo: &str) -> Result<Vec<HubTag>> {
|
||||||
|
let result = self
|
||||||
|
.client
|
||||||
|
.get(format!(
|
||||||
|
"https://hub.docker.com/v2/repositories/{}/{}/tags",
|
||||||
|
user, repo
|
||||||
|
))
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.wrap_err("error with sending docker hub request")?;
|
||||||
|
ensure!(
|
||||||
|
!result.status().is_client_error(),
|
||||||
|
"error with sending docker hub request"
|
||||||
|
);
|
||||||
|
ensure!(
|
||||||
|
!result.status().is_server_error(),
|
||||||
|
"docker hub request returned an error"
|
||||||
|
);
|
||||||
|
Ok(result
|
||||||
|
.json::<HubTagResponse>()
|
||||||
|
.await
|
||||||
|
.wrap_err("failed to parse hub response")?
|
||||||
|
.results)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct HubTagResponse {
|
||||||
|
results: Vec<HubTag>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct HubTag {
|
||||||
|
pub id: u64,
|
||||||
|
pub last_updated: DateTime<Utc>,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
81
src/main.rs
81
src/main.rs
|
|
@ -1,3 +1,80 @@
|
||||||
fn main() {
|
mod hub;
|
||||||
println!("Hello, world!");
|
use crate::hub::Hub;
|
||||||
|
use color_eyre::{Report, Result};
|
||||||
|
use rss::{Channel, ChannelBuilder, GuidBuilder, ItemBuilder};
|
||||||
|
use warp::reject::Reject;
|
||||||
|
use warp::{Filter, Rejection};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
let port: u16 = dotenv::var("PORT")
|
||||||
|
.map(|port| port.parse())
|
||||||
|
.unwrap_or(Ok(80))?;
|
||||||
|
|
||||||
|
let hub = Hub::default();
|
||||||
|
let hub = warp::any().map(move || hub.clone());
|
||||||
|
|
||||||
|
let feed_route = warp::path!(String / String).and(hub).and_then(feed);
|
||||||
|
let routes = feed_route.clone().or(warp::path!("r" / ..).and(feed_route));
|
||||||
|
|
||||||
|
warp::serve(routes).run(([0, 0, 0, 0], port)).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ReportRejection(Report);
|
||||||
|
|
||||||
|
impl From<Report> for ReportRejection {
|
||||||
|
fn from(report: Report) -> Self {
|
||||||
|
Self(report)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Reject for ReportRejection {}
|
||||||
|
|
||||||
|
async fn feed(user: String, repo: String, hub: Hub) -> Result<impl warp::Reply, Rejection> {
|
||||||
|
feed_inner(user, repo, hub)
|
||||||
|
.await
|
||||||
|
.map_err(warp::reject::custom)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn feed_inner(
|
||||||
|
user: String,
|
||||||
|
repo: String,
|
||||||
|
hub: Hub,
|
||||||
|
) -> Result<impl warp::Reply, ReportRejection> {
|
||||||
|
let repo = repo.trim_end_matches(".atom").to_string();
|
||||||
|
let mut channel: Channel = ChannelBuilder::default()
|
||||||
|
.title(format!("{}/{} | Docker Hub Images", user, repo))
|
||||||
|
.link(format!("https://hub.docker.com/r/{}/{}", user, repo))
|
||||||
|
.description(format!("Image updates for {}/{}", user, repo))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let tags = hub.tags(&user, &repo).await?;
|
||||||
|
|
||||||
|
channel.items = tags
|
||||||
|
.into_iter()
|
||||||
|
.map(|tag| {
|
||||||
|
ItemBuilder::default()
|
||||||
|
.title(format!("{}/{}:{}", user, repo, tag.name))
|
||||||
|
.link(format!(
|
||||||
|
"https://hub.docker.com/r/{}/{}/tags?name={}",
|
||||||
|
user, repo, tag.name
|
||||||
|
))
|
||||||
|
.guid(
|
||||||
|
GuidBuilder::default()
|
||||||
|
.value(format!("{}-{}", tag.id, tag.last_updated.timestamp()))
|
||||||
|
.permalink(false)
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.pub_date(tag.last_updated.to_rfc2822())
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(channel.to_string())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue