diff --git a/.gitignore b/.gitignore index ff111da..2550469 100644 --- a/.gitignore +++ b/.gitignore @@ -99,3 +99,4 @@ typings/ .now .vercel +.sentryclirc diff --git a/api/services/existence/[query].ts b/api/services/existence/[query].ts index 62ce2ac..f67fcc1 100644 --- a/api/services/existence/[query].ts +++ b/api/services/existence/[query].ts @@ -6,7 +6,9 @@ export default async function handler( req: VercelRequest, res: VercelResponse ): Promise { - 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') { return sendError(res, new Error('no query given')); @@ -18,7 +20,7 @@ export default async function handler( try { const response = await fetch(`https://${query}`); - const availability = response.status === 404; + const availability = availableStatus.includes(response.status.toString()); send(res, { availability }); } catch (err: any) { if ((err as any).code === 'ENOTFOUND') { diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 4582377..4cfd282 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -51,7 +51,8 @@ "youtube": "YouTube", "hexpm": "Hex", "fly": "Fly.io", - "productHunt": "Product Hunt" + "productHunt": "Product Hunt", + "docker": "Docker Hub" }, "showMore": "show more", "try": "How about this?", diff --git a/src/components/Welcome.tsx b/src/components/Welcome.tsx index a0c200d..8f9d8a7 100644 --- a/src/components/Welcome.tsx +++ b/src/components/Welcome.tsx @@ -3,29 +3,31 @@ import { useTranslation } from 'react-i18next'; import { DiHeroku } from 'react-icons/di'; import { FaAws, + FaCloudflare, + FaDocker, + FaFirefoxBrowser, + FaFly, FaGithub, FaGithubAlt, FaGitlab, // FaInstagram, FaJsSquare, + FaProductHunt, FaPython, FaReddit, FaSlack, FaTwitter, - FaCloudflare, - FaFirefoxBrowser, FaYoutube, - FaProductHunt, - FaFly, } from 'react-icons/fa'; -import { IoIosBeer, IoMdAppstore } from 'react-icons/io'; +import { IoIosBeer } from 'react-icons/io'; import { MdDomain } from 'react-icons/md'; import { RiBuilding2Fill, RiChromeFill, RiNpmjsFill } from 'react-icons/ri'; -import { SiDeno, SiElixir } from 'react-icons/si'; import { SiAppstore, SiArchlinux, SiDebian, + SiDeno, + SiElixir, SiFirebase, SiRubygems, SiRust, @@ -41,37 +43,38 @@ const supportedProviders: Record = { domains: , github: , gitlab: , + slack: , + productHunt: , + githubSearch: , + nta: , twitter: , + reddit: , youtube: , + docker: , homebrew: , + archlinux: , + debian: , + ubuntu: , npm: , - rust: , pypi: , + rust: , rubygems: , hexpm: , ocaml: , - archlinux: , - ubuntu: , - debian: , - reddit: , // instagram: , - slack: , fly: , - heroku: , now: , + heroku: , netlify: , cloudflare: , s3: , firebase: , jsorg: , modland: , - productHunt: , - githubSearch: , appStore: , // playStore: , firefoxAddons: , chromeWebStore: , - nta: , }; const Welcome: React.FC = () => { diff --git a/src/components/cards/core.tsx b/src/components/cards/core.tsx index 66867df..4df3bc0 100644 --- a/src/components/cards/core.tsx +++ b/src/components/cards/core.tsx @@ -83,6 +83,7 @@ class NotFoundError extends Error { export const DedicatedAvailability: React.FC<{ name: string; query?: string; + args?: Record; message?: string; messageIfTaken?: string; service: string; @@ -94,6 +95,7 @@ export const DedicatedAvailability: React.FC<{ }> = ({ name, query = undefined, + args = {}, message = '', messageIfTaken = undefined, service, @@ -105,7 +107,8 @@ export const DedicatedAvailability: React.FC<{ }) => { const increaseCounter = useStoreActions((actions) => actions.stats.add); const response = useFetch( - `/api/services/${service}/${encodeURIComponent(query || name)}` + `/api/services/${service}/${encodeURIComponent(query || name)}` + + new URLSearchParams(args).toString() ) as Response; if (response.error) { diff --git a/src/components/cards/index.tsx b/src/components/cards/index.tsx index 5666da8..984a1d7 100644 --- a/src/components/cards/index.tsx +++ b/src/components/cards/index.tsx @@ -6,6 +6,7 @@ import AppStoreCard from './providers/AppStore'; import ChromeWebStoreCard from './providers/ChromeWebStore'; import CloudflareCard from './providers/Cloudflare'; import CratesioCard from './providers/Cratesio'; +import DockerCard from './providers/Docker'; import DomainCard from './providers/Domains'; import FirebaseCard from './providers/Firebase'; import FirefoxAddonsCard from './providers/FirefoxAddons'; @@ -23,7 +24,7 @@ import NetlifyCard from './providers/Netlify'; import NpmCard from './providers/Npm'; import NtaCard from './providers/Nta'; import OcamlCard from './providers/Ocaml'; -import PlayStoreCard from './providers/PlayStore'; +// import PlayStoreCard from './providers/PlayStore'; import ProductHuntCard from './providers/ProductHunt'; import PypiCard from './providers/PyPI'; import RubyGemsCard from './providers/RubyGems'; @@ -64,6 +65,7 @@ const Index: React.FC<{ query: string }> = ({ query }) => {
{t('section.package')} + diff --git a/src/components/cards/providers/Docker.tsx b/src/components/cards/providers/Docker.tsx new file mode 100644 index 0000000..d699b27 --- /dev/null +++ b/src/components/cards/providers/Docker.tsx @@ -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 ( + + + {(name) => ( + } + /> + )} + + + ); +}; + +export default DockerCard; diff --git a/src/util/i18n.ts b/src/util/i18n.ts index fa894f6..656cd8d 100644 --- a/src/util/i18n.ts +++ b/src/util/i18n.ts @@ -5,7 +5,7 @@ import XHR from 'i18next-xhr-backend'; import LanguageDetector from 'i18next-browser-languagedetector'; import { initReactI18next } from 'react-i18next'; -const TRANSLATION_VERSION = '19'; +const TRANSLATION_VERSION = '20'; i18n .use(Backend)