Render DockerHub info for apps published there
This commit is contained in:
parent
3a1ec913fb
commit
508634488a
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue