Render DockerHub info for apps published there

This commit is contained in:
Alicia Sykes 2024-03-08 07:37:11 +00:00
parent 3a1ec913fb
commit 508634488a
3 changed files with 263 additions and 0 deletions

View File

@ -0,0 +1,178 @@
---
import type { TemplateResponse } from '@utils/fetch-docker-instructions';
import { formatDate, timeAgo } from '@utils/dates-n-stuff';
interface Props {
docker: TemplateResponse;
}
const { docker } = Astro.props;
const formatBigNumber = (num: number): string => {
if (!num) return 'None';
return num.toLocaleString();
};
---
<div class="docker-info-wrap">
<div class="left">
{ docker.template && (
<h4>Container Info</h4>
<p class="dockerhub-title">
<img src={docker.template.logo} width="16" />
{docker.template.name}
</p>
<p class="dockerhub-description">{docker.template.description}</p>
<span class="tags">
{(docker.template.categories || []).map((category: string) => (
<span class="tag">#{category}</span>
))}
</span>
)}
{docker.dockerHubData && Object.keys(docker.dockerHubData).length > 0 && (
<h4>DockerHub Metrics</h4>
<ul class="table">
<li>
<span class="lbl">Pull Count</span>
<span class="val">{formatBigNumber(docker.dockerHubData.pull_count)}</span>
</li>
<li>
<span class="lbl">Stars</span>
<span class="val">{formatBigNumber(docker.dockerHubData.star_count)}</span>
</li>
<li>
<span class="lbl">Date Created</span>
<span class="val">{formatDate(docker.dockerHubData.date_registered)}</span>
</li>
<li>
<span class="lbl">Last Updated</span>
<span class="val">{timeAgo(docker.dockerHubData.last_updated)}</span>
</li>
</ul>
)}
<h4>View on DockerHub</h4>
{docker.dockerHubData && Object.keys(docker.dockerHubData).length > 0 && (
<a href={`https://hub.docker.com/r/${docker.dockerHubData.user}/${docker.dockerHubData.name}`} target="_blank">
{docker.dockerHubData.user}/{docker.dockerHubData.name}
</a>
)}
{!docker.dockerHubData && docker.template &&
(
<a href={`https://hub.docker.com/r/${docker.template.image}`} target="_blank">
{docker.template.image}
</a>
)
}
</div>
<div class="right">
{docker.usage && (
<h4>Run Command</h4>
<pre>{docker.usage.dockerRunCommand}</pre>
)}
</div>
</div>
<style lang="scss">
.docker-info-wrap {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.left, .right {
width: 50%;
@media (max-width: 768px) {
width: 100%;
}
}
.long-list-data {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
}
.left {
.dockerhub-title, .dockerhub-description {
font-size: 0.9rem;
opacity: 0.8;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
border-left: 2px solid var(--accent-3);
padding-left: 0.5rem;
}
.dockerhub-title {
font-weight: 500;
}
.dockerhub-description {
font-style: italic;
-webkit-line-clamp: 3;
}
.table {
max-width: 15rem;
padding-left: 0;
li {
display: flex;
justify-content: space-between;
.lbl {
font-weight: 500;
}
}
}
}
h4 {
margin: 1rem 0 0 0;
font-size: 1.2rem;
}
p {
margin: 0;
display: flex;
align-items: center;
gap: 0.25rem;
:global(svg) {
width: 1rem;
}
img {
border-radius: var(--curve-sm);
}
}
.tags {
display: flex;
flex-wrap: wrap;
.tag {
margin: 0.1rem;
background: #5f53f440;
border-radius: var(--curve-sm);
padding: 0.05rem 0.2rem;
font-size: 0.8rem;
opacity: 0.8;
}
}
ul {
padding-left: 1rem;
list-style: circle;
max-height: 400px;
overflow: auto;
img {
border-radius: var(--curve-sm);
}
}
pre {
font-family: 'Courier New', Courier, monospace;
background: #cecbf780;
padding: 0.6rem 1rem;
border-radius: var(--curve-sm);
font-size: 0.8rem;
overflow-x: auto;
}
</style>

View File

@ -6,12 +6,14 @@ import Comments from '@components/things/Comments.svelte';
import GitHubDetailedInfo from '@components/things/GitHubDetailedInfo.astro';
import PrivacyPolicyDetails from '@components/things/PrivacyPolicyDetails.astro';
import WebsiteDetailedInfo from '@components/things/WebsiteDetailedInfo.astro';
import DockerDetailedInfo from '@components/things/DockerDetailedInfo.astro';
import DataActions from '@components/things/DataActions.svelte';
import type { AwesomePrivacy, Section, Service } from '../types/Service';
import { fetchData, slugify } from '@utils/fetch-data';
import { fetchGitHubStats } from '@utils/fetch-repo-info';
import { fetchTosdrPrivacy } from '@utils/fetch-privacy-policy';
import { fetchWebsiteInfo } from '@utils/fetch-website-info';
import { fetchDockerData } from '@utils/fetch-docker-instructions';
import { parseMarkdown, formatLink } from '@utils/parse-markdown';
import FontAwesome from '@components/form/FontAwesome.svelte';
import Button from '@components/form/Button.astro';
@ -122,8 +124,11 @@ const ignoredSites = ['github.gom', 'wikipedia.'];
// Fetch detailed data about the services GitHub repo, privacy policy and website
const githubData = github ? await fetchGitHubStats(github) : null;
const privacyData = tosdrId ? await fetchTosdrPrivacy(tosdrId) : null;
const dockerData = await fetchDockerData(name);
const websiteData = (url && !ignoredSites.some(ignoredSite => url.includes(ignoredSite))) ? await fetchWebsiteInfo(url) : null;
console.log(dockerData);
const getApiEndpoint = () => {
return `https://api.awesome-privacy.xyz/${slugify(categoryName)}/${slugify(parentSection.name)}/${slugify(name)}`;
}
@ -237,10 +242,18 @@ const getApiEndpoint = () => {
<WebsiteDetailedInfo url={url} websiteInfo={websiteData} />
</section>
)}
{ dockerData && dockerData.found && (
<section>
<h3>{name} Docker</h3>
<DockerDetailedInfo docker={dockerData} />
</section>
)}
<section>
<h3>{name} Reviews</h3>
<Comments client:visible />
</section>
<section>
<h3>More {parentSection.name}</h3>
<ServiceList

View File

@ -0,0 +1,72 @@
export const fetchDockerData = async (serviceName: string): Promise<TemplateResponse | null> => {
const endpoint = `https://docker-info.as93.workers.dev/${serviceName}`;
try {
return await fetch(endpoint).then((res) => res.json());
} catch (error) {
console.error('Error fetching docker data:', error);
return null;
}
};
interface DockerTemplatePort {
privatePort: number;
publicPort: number;
type: string; // Typically TCP/UDP
}
interface DockerTemplateEnvironmentVariable {
name: string;
default?: string;
description?: string;
}
interface DockerTemplateVolume {
bind: string;
container: string;
readonly?: boolean;
}
interface DockerTemplate {
name?: string;
title: string;
description?: string;
logo?: string;
image: string;
categories?: string[];
ports?: DockerTemplatePort[];
env?: DockerTemplateEnvironmentVariable[];
volumes?: DockerTemplateVolume[];
restart_policy?: string; // Typically "no", "always", "unless-stopped", "on-failure"
}
interface DockerHubData {
user: string;
name: string;
namespace: string;
repository_type: string;
status: number;
description: string;
is_private: boolean;
is_automated: boolean;
can_edit: boolean;
star_count: number;
pull_count: number;
last_updated: string;
date_registered: string;
build_status: string;
}
interface DockerUsage {
dockerRunCommand: string;
dockerComposeFile: string;
}
export interface TemplateResponse {
found: boolean;
error: string | null;
template?: DockerTemplate;
dockerHubData?: DockerHubData;
usage?: DockerUsage;
}