mirror of
https://github.com/uetchy/namae.git
synced 2025-03-16 12:10:32 +09:00
feat: support Docker Hub
This commit is contained in:
parent
09f2755410
commit
ad6052c29a
1
.gitignore
vendored
1
.gitignore
vendored
@ -99,3 +99,4 @@ typings/
|
|||||||
|
|
||||||
.now
|
.now
|
||||||
.vercel
|
.vercel
|
||||||
|
.sentryclirc
|
||||||
|
@ -6,7 +6,9 @@ export default async function handler(
|
|||||||
req: VercelRequest,
|
req: VercelRequest,
|
||||||
res: VercelResponse
|
res: VercelResponse
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { query } = req.query;
|
const { query, existIf = '404' } = req.query;
|
||||||
|
|
||||||
|
const availableStatus = (existIf as string).split(',').map((s) => s.trim());
|
||||||
|
|
||||||
if (!query || typeof query !== 'string') {
|
if (!query || typeof query !== 'string') {
|
||||||
return sendError(res, new Error('no query given'));
|
return sendError(res, new Error('no query given'));
|
||||||
@ -18,7 +20,7 @@ export default async function handler(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`https://${query}`);
|
const response = await fetch(`https://${query}`);
|
||||||
const availability = response.status === 404;
|
const availability = availableStatus.includes(response.status.toString());
|
||||||
send(res, { availability });
|
send(res, { availability });
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if ((err as any).code === 'ENOTFOUND') {
|
if ((err as any).code === 'ENOTFOUND') {
|
||||||
|
@ -51,7 +51,8 @@
|
|||||||
"youtube": "YouTube",
|
"youtube": "YouTube",
|
||||||
"hexpm": "Hex",
|
"hexpm": "Hex",
|
||||||
"fly": "Fly.io",
|
"fly": "Fly.io",
|
||||||
"productHunt": "Product Hunt"
|
"productHunt": "Product Hunt",
|
||||||
|
"docker": "Docker Hub"
|
||||||
},
|
},
|
||||||
"showMore": "show more",
|
"showMore": "show more",
|
||||||
"try": "How about this?",
|
"try": "How about this?",
|
||||||
|
@ -3,29 +3,31 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { DiHeroku } from 'react-icons/di';
|
import { DiHeroku } from 'react-icons/di';
|
||||||
import {
|
import {
|
||||||
FaAws,
|
FaAws,
|
||||||
|
FaCloudflare,
|
||||||
|
FaDocker,
|
||||||
|
FaFirefoxBrowser,
|
||||||
|
FaFly,
|
||||||
FaGithub,
|
FaGithub,
|
||||||
FaGithubAlt,
|
FaGithubAlt,
|
||||||
FaGitlab,
|
FaGitlab,
|
||||||
// FaInstagram,
|
// FaInstagram,
|
||||||
FaJsSquare,
|
FaJsSquare,
|
||||||
|
FaProductHunt,
|
||||||
FaPython,
|
FaPython,
|
||||||
FaReddit,
|
FaReddit,
|
||||||
FaSlack,
|
FaSlack,
|
||||||
FaTwitter,
|
FaTwitter,
|
||||||
FaCloudflare,
|
|
||||||
FaFirefoxBrowser,
|
|
||||||
FaYoutube,
|
FaYoutube,
|
||||||
FaProductHunt,
|
|
||||||
FaFly,
|
|
||||||
} from 'react-icons/fa';
|
} from 'react-icons/fa';
|
||||||
import { IoIosBeer, IoMdAppstore } from 'react-icons/io';
|
import { IoIosBeer } from 'react-icons/io';
|
||||||
import { MdDomain } from 'react-icons/md';
|
import { MdDomain } from 'react-icons/md';
|
||||||
import { RiBuilding2Fill, RiChromeFill, RiNpmjsFill } from 'react-icons/ri';
|
import { RiBuilding2Fill, RiChromeFill, RiNpmjsFill } from 'react-icons/ri';
|
||||||
import { SiDeno, SiElixir } from 'react-icons/si';
|
|
||||||
import {
|
import {
|
||||||
SiAppstore,
|
SiAppstore,
|
||||||
SiArchlinux,
|
SiArchlinux,
|
||||||
SiDebian,
|
SiDebian,
|
||||||
|
SiDeno,
|
||||||
|
SiElixir,
|
||||||
SiFirebase,
|
SiFirebase,
|
||||||
SiRubygems,
|
SiRubygems,
|
||||||
SiRust,
|
SiRust,
|
||||||
@ -41,37 +43,38 @@ const supportedProviders: Record<string, React.ReactNode> = {
|
|||||||
domains: <MdDomain />,
|
domains: <MdDomain />,
|
||||||
github: <FaGithub />,
|
github: <FaGithub />,
|
||||||
gitlab: <FaGitlab />,
|
gitlab: <FaGitlab />,
|
||||||
|
slack: <FaSlack />,
|
||||||
|
productHunt: <FaProductHunt />,
|
||||||
|
githubSearch: <FaGithubAlt />,
|
||||||
|
nta: <RiBuilding2Fill />,
|
||||||
twitter: <FaTwitter />,
|
twitter: <FaTwitter />,
|
||||||
|
reddit: <FaReddit />,
|
||||||
youtube: <FaYoutube />,
|
youtube: <FaYoutube />,
|
||||||
|
docker: <FaDocker />,
|
||||||
homebrew: <IoIosBeer />,
|
homebrew: <IoIosBeer />,
|
||||||
|
archlinux: <SiArchlinux />,
|
||||||
|
debian: <SiDebian />,
|
||||||
|
ubuntu: <SiUbuntu />,
|
||||||
npm: <RiNpmjsFill />,
|
npm: <RiNpmjsFill />,
|
||||||
rust: <SiRust />,
|
|
||||||
pypi: <FaPython />,
|
pypi: <FaPython />,
|
||||||
|
rust: <SiRust />,
|
||||||
rubygems: <SiRubygems />,
|
rubygems: <SiRubygems />,
|
||||||
hexpm: <SiElixir />,
|
hexpm: <SiElixir />,
|
||||||
ocaml: <OcamlIcon />,
|
ocaml: <OcamlIcon />,
|
||||||
archlinux: <SiArchlinux />,
|
|
||||||
ubuntu: <SiUbuntu />,
|
|
||||||
debian: <SiDebian />,
|
|
||||||
reddit: <FaReddit />,
|
|
||||||
// instagram: <FaInstagram />,
|
// instagram: <FaInstagram />,
|
||||||
slack: <FaSlack />,
|
|
||||||
fly: <FaFly />,
|
fly: <FaFly />,
|
||||||
heroku: <DiHeroku />,
|
|
||||||
now: <NowIcon />,
|
now: <NowIcon />,
|
||||||
|
heroku: <DiHeroku />,
|
||||||
netlify: <NetlifyIcon />,
|
netlify: <NetlifyIcon />,
|
||||||
cloudflare: <FaCloudflare />,
|
cloudflare: <FaCloudflare />,
|
||||||
s3: <FaAws />,
|
s3: <FaAws />,
|
||||||
firebase: <SiFirebase />,
|
firebase: <SiFirebase />,
|
||||||
jsorg: <FaJsSquare />,
|
jsorg: <FaJsSquare />,
|
||||||
modland: <SiDeno />,
|
modland: <SiDeno />,
|
||||||
productHunt: <FaProductHunt />,
|
|
||||||
githubSearch: <FaGithubAlt />,
|
|
||||||
appStore: <SiAppstore />,
|
appStore: <SiAppstore />,
|
||||||
// playStore: <IoMdAppstore />,
|
// playStore: <IoMdAppstore />,
|
||||||
firefoxAddons: <FaFirefoxBrowser />,
|
firefoxAddons: <FaFirefoxBrowser />,
|
||||||
chromeWebStore: <RiChromeFill />,
|
chromeWebStore: <RiChromeFill />,
|
||||||
nta: <RiBuilding2Fill />,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Welcome: React.FC = () => {
|
const Welcome: React.FC = () => {
|
||||||
|
@ -83,6 +83,7 @@ class NotFoundError extends Error {
|
|||||||
export const DedicatedAvailability: React.FC<{
|
export const DedicatedAvailability: React.FC<{
|
||||||
name: string;
|
name: string;
|
||||||
query?: string;
|
query?: string;
|
||||||
|
args?: Record<string, string>;
|
||||||
message?: string;
|
message?: string;
|
||||||
messageIfTaken?: string;
|
messageIfTaken?: string;
|
||||||
service: string;
|
service: string;
|
||||||
@ -94,6 +95,7 @@ export const DedicatedAvailability: React.FC<{
|
|||||||
}> = ({
|
}> = ({
|
||||||
name,
|
name,
|
||||||
query = undefined,
|
query = undefined,
|
||||||
|
args = {},
|
||||||
message = '',
|
message = '',
|
||||||
messageIfTaken = undefined,
|
messageIfTaken = undefined,
|
||||||
service,
|
service,
|
||||||
@ -105,7 +107,8 @@ export const DedicatedAvailability: React.FC<{
|
|||||||
}) => {
|
}) => {
|
||||||
const increaseCounter = useStoreActions((actions) => actions.stats.add);
|
const increaseCounter = useStoreActions((actions) => actions.stats.add);
|
||||||
const response = useFetch(
|
const response = useFetch(
|
||||||
`/api/services/${service}/${encodeURIComponent(query || name)}`
|
`/api/services/${service}/${encodeURIComponent(query || name)}` +
|
||||||
|
new URLSearchParams(args).toString()
|
||||||
) as Response;
|
) as Response;
|
||||||
|
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
|
@ -6,6 +6,7 @@ import AppStoreCard from './providers/AppStore';
|
|||||||
import ChromeWebStoreCard from './providers/ChromeWebStore';
|
import ChromeWebStoreCard from './providers/ChromeWebStore';
|
||||||
import CloudflareCard from './providers/Cloudflare';
|
import CloudflareCard from './providers/Cloudflare';
|
||||||
import CratesioCard from './providers/Cratesio';
|
import CratesioCard from './providers/Cratesio';
|
||||||
|
import DockerCard from './providers/Docker';
|
||||||
import DomainCard from './providers/Domains';
|
import DomainCard from './providers/Domains';
|
||||||
import FirebaseCard from './providers/Firebase';
|
import FirebaseCard from './providers/Firebase';
|
||||||
import FirefoxAddonsCard from './providers/FirefoxAddons';
|
import FirefoxAddonsCard from './providers/FirefoxAddons';
|
||||||
@ -23,7 +24,7 @@ import NetlifyCard from './providers/Netlify';
|
|||||||
import NpmCard from './providers/Npm';
|
import NpmCard from './providers/Npm';
|
||||||
import NtaCard from './providers/Nta';
|
import NtaCard from './providers/Nta';
|
||||||
import OcamlCard from './providers/Ocaml';
|
import OcamlCard from './providers/Ocaml';
|
||||||
import PlayStoreCard from './providers/PlayStore';
|
// import PlayStoreCard from './providers/PlayStore';
|
||||||
import ProductHuntCard from './providers/ProductHunt';
|
import ProductHuntCard from './providers/ProductHunt';
|
||||||
import PypiCard from './providers/PyPI';
|
import PypiCard from './providers/PyPI';
|
||||||
import RubyGemsCard from './providers/RubyGems';
|
import RubyGemsCard from './providers/RubyGems';
|
||||||
@ -64,6 +65,7 @@ const Index: React.FC<{ query: string }> = ({ query }) => {
|
|||||||
<Section>
|
<Section>
|
||||||
<Title>{t('section.package')}</Title>
|
<Title>{t('section.package')}</Title>
|
||||||
<Cards>
|
<Cards>
|
||||||
|
<DockerCard query={query} />
|
||||||
<HomebrewCard query={query} />
|
<HomebrewCard query={query} />
|
||||||
<LinuxCard query={query} />
|
<LinuxCard query={query} />
|
||||||
<NpmCard query={query} />
|
<NpmCard query={query} />
|
||||||
|
33
src/components/cards/providers/Docker.tsx
Normal file
33
src/components/cards/providers/Docker.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { FaDocker } from 'react-icons/fa';
|
||||||
|
import { normalize } from '../../../util/text';
|
||||||
|
|
||||||
|
import { Card, DedicatedAvailability, Repeater } from '../core';
|
||||||
|
|
||||||
|
const DockerCard: React.FC<{ query: string }> = ({ query }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const normalizedQuery = normalize(query, { allowUnderscore: false });
|
||||||
|
const lowerCase = normalizedQuery.toLowerCase();
|
||||||
|
|
||||||
|
const names = [lowerCase];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card title={t('providers.docker')}>
|
||||||
|
<Repeater items={names}>
|
||||||
|
{(name) => (
|
||||||
|
<DedicatedAvailability
|
||||||
|
name={name}
|
||||||
|
query={`hub.docker.com/v2/orgs/${name}`}
|
||||||
|
service="existence"
|
||||||
|
message="Go to Docker Hub"
|
||||||
|
link={`https://hub.docker.com/orgs/${name}`}
|
||||||
|
icon={<FaDocker />}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Repeater>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DockerCard;
|
@ -5,7 +5,7 @@ import XHR from 'i18next-xhr-backend';
|
|||||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||||
import { initReactI18next } from 'react-i18next';
|
import { initReactI18next } from 'react-i18next';
|
||||||
|
|
||||||
const TRANSLATION_VERSION = '19';
|
const TRANSLATION_VERSION = '20';
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
.use(Backend)
|
.use(Backend)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user