[ 'user' => [ 'name' => 'User', 'type' => 'text', 'required' => true, 'exampleValue' => 'rssbridge', ], 'repo' => [ 'name' => 'Repository', 'type' => 'text', 'required' => true, 'exampleValue' => 'rss-bridge', ], 'filter' => [ 'name' => 'Filter tag', 'type' => 'text', 'required' => false, 'exampleValue' => 'latest', ] ], 'Official Image' => [ 'repo' => [ 'name' => 'Repository', 'type' => 'text', 'required' => true, 'exampleValue' => 'postgres', ], 'filter' => [ 'name' => 'Filter tag', 'type' => 'text', 'required' => false, 'exampleValue' => 'alpine3.17', ] ] ]; const CACHE_TIMEOUT = 3600; // 1 hour private $apiURL = 'https://hub.docker.com/v2/repositories/'; private $imageUrlRegex = '/hub\.docker\.com\/r\/([\w]+)\/([\w-]+)\/?/'; private $officialImageUrlRegex = '/hub\.docker\.com\/_\/([\w-]+)\/?/'; public function detectParameters($url) { $params = []; // user submitted image if (preg_match($this->imageUrlRegex, $url, $matches)) { $params['context'] = 'User Submitted Image'; $params['user'] = $matches[1]; $params['repo'] = $matches[2]; return $params; } // official image if (preg_match($this->officialImageUrlRegex, $url, $matches)) { $params['context'] = 'Official Image'; $params['repo'] = $matches[1]; return $params; } return null; } public function collectData() { $json = getContents($this->getApiUrl()); $data = json_decode($json, false); foreach ($data->results as $result) { $item = []; $lastPushed = date('Y-m-d H:i:s', strtotime($result->tag_last_pushed)); $item['title'] = $result->name; $item['uid'] = $result->id; $item['uri'] = $this->getTagUrl($result->name); $item['author'] = $result->last_updater_username; $item['timestamp'] = $result->tag_last_pushed; $item['content'] = <<Tag

{$result->name}

Last pushed

{$lastPushed}

Images
{$this->getImagesTable($result)} EOD; $this->items[] = $item; } } public function getURI() { $uri = parent::getURI(); if ($this->queriedContext === 'Official Image') { $uri = self::URI . '/_/' . $this->getRepo(); } if ($this->queriedContext === 'User Submitted Image') { $uri = '/r/' . $this->getRepo(); } if ($this->getInput('filter')) { $uri .= '/tags/?&page=1&name=' . $this->getInput('filter'); } return $uri; } public function getName() { if ($this->getInput('repo')) { $name = $this->getRepo(); if ($this->getInput('filter')) { $name .= ':' . $this->getInput('filter'); } return $name . ' - Docker Hub'; } return parent::getName(); } private function getRepo() { if ($this->queriedContext === 'Official Image') { return $this->getInput('repo'); } return $this->getInput('user') . '/' . $this->getInput('repo'); } private function getApiUrl() { $url = ''; if ($this->queriedContext === 'Official Image') { $url = $this->apiURL . 'library/' . $this->getRepo() . '/tags/?page_size=25&page=1'; } if ($this->queriedContext === 'User Submitted Image') { $url = $this->apiURL . $this->getRepo() . '/tags/?page_size=25&page=1'; } if ($this->getInput('filter')) { $url .= '&name=' . $this->getInput('filter'); } return $url; } private function getLayerUrl($name, $digest) { if ($this->queriedContext === 'Official Image') { return self::URI . '/layers/' . $this->getRepo() . '/library/' . $this->getRepo() . '/' . $name . '/images/' . $digest; } return self::URI . '/layers/' . $this->getRepo() . '/' . $name . '/images/' . $digest; } private function getTagUrl($name) { $url = ''; if ($this->queriedContext === 'Official Image') { $url = self::URI . '/_/' . $this->getRepo(); } if ($this->queriedContext === 'User Submitted Image') { $url = self::URI . '/r/' . $this->getRepo(); } return $url . '/tags/?&name=' . $name; } private function getImagesTable($result) { $data = ''; foreach ($result->images as $image) { $layersUrl = $this->getLayerUrl($result->name, $image->digest); $id = $this->getShortDigestId($image->digest); $size = format_bytes($image->size); $data .= << {$id} {$image->os}/{$image->architecture} {$size} EOD; } return << Digest OS/architecture Compressed Size {$data} EOD; } private function getShortDigestId($digest) { $parts = explode(':', $digest); return substr($parts[1], 0, 12); } }