mirror of
https://codeberg.org/icewind/docker-hub-rss.git
synced 2026-06-03 10:14:07 +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
|
||||
.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"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[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() {
|
||||
println!("Hello, world!");
|
||||
mod hub;
|
||||
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