├── .nvmrc ├── .npmrc ├── .eslintrc.json ├── .vscode ├── extensions.json └── settings.json ├── app ├── favicon.ico ├── loading.tsx ├── mdx-components.tsx ├── [lang] │ ├── docs │ │ └── page.tsx │ ├── page.tsx │ ├── layout.tsx │ ├── detail │ │ └── [...slug] │ │ │ └── page.tsx │ └── search │ │ └── [...slug] │ │ └── page.tsx ├── actions.ts ├── globals.css ├── layout.tsx └── not-found.tsx ├── components ├── ImageDetail │ ├── shell-viewer.tsx │ ├── image-tag-detail.tsx │ ├── image-tag-list.tsx │ ├── index.tsx │ └── image-command.tsx ├── ui │ ├── aspect-ratio.tsx │ ├── skeleton.tsx │ ├── collapsible.tsx │ ├── index.ts │ ├── label.tsx │ ├── textarea.tsx │ ├── separator.tsx │ ├── progress.tsx │ ├── input.tsx │ ├── toaster.tsx │ ├── sonner.tsx │ ├── index.client.ts │ ├── checkbox.tsx │ ├── slider.tsx │ ├── switch.tsx │ ├── badge.tsx │ ├── tooltip.tsx │ ├── hover-card.tsx │ ├── popover.tsx │ ├── avatar.tsx │ ├── toggle.tsx │ ├── radio-group.tsx │ ├── alert.tsx │ ├── scroll-area.tsx │ ├── resizable.tsx │ ├── toggle-group.tsx │ ├── button.tsx │ ├── tabs.tsx │ ├── card.tsx │ ├── accordion.tsx │ ├── input-otp.tsx │ ├── calendar.tsx │ ├── breadcrumb.tsx │ ├── pagination.tsx │ ├── table.tsx │ ├── drawer.tsx │ ├── dialog.tsx │ ├── use-toast.ts │ ├── sheet.tsx │ ├── form.tsx │ ├── alert-dialog.tsx │ ├── toast.tsx │ ├── command.tsx │ ├── navigation-menu.tsx │ ├── select.tsx │ ├── carousel.tsx │ └── context-menu.tsx ├── LangLink.tsx ├── ThemeProvider.tsx ├── tailwind-indicator.tsx ├── Logo.tsx ├── ClientFormAction.tsx ├── Footer.tsx ├── ModeToggle.tsx ├── DataTable │ ├── data-table-view-options.tsx │ ├── data-table-toolbar.tsx │ ├── data-table-row-actions.tsx │ ├── data-table-column-header.tsx │ ├── data-table-pagination.tsx │ ├── index.tsx │ ├── columns.tsx │ └── data-table-faceted-filter.tsx ├── Header.tsx └── LinkCommand.tsx ├── public ├── docker-mirrors.png ├── docker-mirrors-example.mp4 └── docker_mirrors_logo_and_text.png ├── i18n ├── index.ts ├── utils │ ├── i18n.ts │ └── middleware.ts ├── components │ ├── Intl.tsx │ └── IntlProvider.tsx └── locales │ ├── zh.json │ └── en.json ├── postcss.config.mjs ├── .env ├── lib ├── utils.ts ├── hl.ts └── images.ts ├── constants ├── site.ts └── images.ts ├── .editorconfig ├── fly.toml ├── components.json ├── .prettierrc ├── docker-compose.yml ├── middleware.ts ├── .github ├── workflows │ ├── fly.yml │ └── deploy.yml └── FUNDING.yml ├── .dockerignore ├── .gitignore ├── tsconfig.json ├── LICENSE-MIT ├── Dockerfile ├── next.config.mjs ├── scripts └── components.mjs ├── types └── image.ts ├── tailwind.config.ts ├── blog └── 2024-06-25.md ├── README.CN.md ├── README.md └── package.json /.nvmrc: -------------------------------------------------------------------------------- 1 | 20.14.0 -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmmirror.com -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["esbenp.prettier-vscode"] 3 | } 4 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker-mirrors/website/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /components/ImageDetail/shell-viewer.tsx: -------------------------------------------------------------------------------- 1 | export function ShellViewer() { 2 | return '' 3 | } 4 | -------------------------------------------------------------------------------- /public/docker-mirrors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker-mirrors/website/HEAD/public/docker-mirrors.png -------------------------------------------------------------------------------- /public/docker-mirrors-example.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker-mirrors/website/HEAD/public/docker-mirrors-example.mp4 -------------------------------------------------------------------------------- /public/docker_mirrors_logo_and_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docker-mirrors/website/HEAD/public/docker_mirrors_logo_and_text.png -------------------------------------------------------------------------------- /i18n/index.ts: -------------------------------------------------------------------------------- 1 | export * from './utils/i18n' 2 | export * from './utils/middleware' 3 | 4 | export * from './components/IntlProvider' 5 | export * from './components/Intl' 6 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | DOCK_HUB_HOST=https://hub.docker.com 2 | GITHUB_HOST=https://api.github.com 3 | DOCKER_REPLY_REGISTRY=registry.dockermirror.com 4 | NEXT_PUBLIC_DOCKER_REPLY_REGISTRY=$DOCKER_REPLY_REGISTRY -------------------------------------------------------------------------------- /app/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from '@/components/ui/skeleton' 2 | 3 | export default function Loading() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /app/mdx-components.tsx: -------------------------------------------------------------------------------- 1 | import type { MDXComponents } from 'mdx/types'; 2 | 3 | export function useMDXComponents(components: MDXComponents): MDXComponents { 4 | return { 5 | ...components, 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /components/ui/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" 4 | 5 | const AspectRatio = AspectRatioPrimitive.Root 6 | 7 | export { AspectRatio } 8 | -------------------------------------------------------------------------------- /constants/site.ts: -------------------------------------------------------------------------------- 1 | export const site = { 2 | name: 'Docker Mirror', 3 | description: 'Fast server with docker images', 4 | links: { 5 | github: 'https://github.com/docker-mirrors/website', 6 | org: 'https://github.com/docker-mirrors', 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /constants/images.ts: -------------------------------------------------------------------------------- 1 | import { ImageType } from '@/types/image' 2 | 3 | export const IMAGETYPE: { label: ImageType; value: ImageType }[] = [ 4 | { label: 'image', value: 'image' }, 5 | { label: 'community', value: 'community' }, 6 | { label: 'store', value: 'store' } 7 | ] 8 | -------------------------------------------------------------------------------- /components/ImageDetail/image-tag-detail.tsx: -------------------------------------------------------------------------------- 1 | import { ImageDetailForTag } from '@/types/image' 2 | 3 | export type ImageTagDetailProps = { 4 | tag?: ImageDetailForTag 5 | } 6 | 7 | export function ImageTagDetail(props: ImageTagDetailProps) { 8 | return 'ImageTagDetail' 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /i18n/utils/i18n.ts: -------------------------------------------------------------------------------- 1 | const locales = { 2 | en: () => import('@/i18n/locales/en.json').then((module) => module.default), 3 | zh: () => import('@/i18n/locales/zh.json').then((module) => module.default) 4 | } 5 | 6 | export type Locale = keyof typeof locales 7 | 8 | export const getLocale = async (locale: Locale) => locales[locale]() 9 | -------------------------------------------------------------------------------- /components/LangLink.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | import { useParams } from 'next/navigation'; 3 | import { ComponentProps } from 'react'; 4 | 5 | export function LangLink({ href, ...props }: ComponentProps) { 6 | const params = useParams(); 7 | return ; 8 | } 9 | -------------------------------------------------------------------------------- /i18n/components/Intl.tsx: -------------------------------------------------------------------------------- 1 | import { useIntl } from './IntlProvider' 2 | 3 | export type IntlProps = { 4 | children?: React.ReactNode 5 | locale: string 6 | } 7 | 8 | export const Intl = ({ children, locale }: IntlProps) => { 9 | const [, { formatMessage }] = useIntl() 10 | 11 | return <>{formatMessage(locale, children)} 12 | } 13 | -------------------------------------------------------------------------------- /components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ) 13 | } 14 | 15 | export { Skeleton } 16 | -------------------------------------------------------------------------------- /fly.toml: -------------------------------------------------------------------------------- 1 | app = "dockermirror-website" 2 | build = { } 3 | primary_region = "hkg" 4 | 5 | [http_service] 6 | auto_start_machines = true 7 | auto_stop_machines = true 8 | force_https = true 9 | internal_port = 3_000 10 | min_machines_running = 0 11 | processes = [ "app" ] 12 | 13 | [[vm]] 14 | cpu_kind = "shared" 15 | cpus = 1 16 | memory = "1gb" 17 | -------------------------------------------------------------------------------- /components/ThemeProvider.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import * as React from 'react' 3 | import { ThemeProvider as NextThemesProvider } from 'next-themes' 4 | import type { ThemeProviderProps } from 'next-themes/dist/types' 5 | 6 | export const ThemeProvider: React.FC = (props) => { 7 | return 8 | } 9 | 10 | export default ThemeProvider 11 | -------------------------------------------------------------------------------- /components/ui/collapsible.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible" 4 | 5 | const Collapsible = CollapsiblePrimitive.Root 6 | 7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger 8 | 9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent 10 | 11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent } 12 | -------------------------------------------------------------------------------- /components/ui/index.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | 3 | export * from './alert' 4 | export * from './badge' 5 | export * from './breadcrumb' 6 | export * from './button' 7 | export * from './card' 8 | export * from './form' 9 | export * from './input' 10 | export * from './navigation-menu' 11 | export * from './pagination' 12 | export * from './skeleton' 13 | export * from './table' 14 | export * from './textarea' -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "proseWrap": "never", 6 | "overrides": [ 7 | { "files": ".prettierrc", "options": { "parser": "json" } }, 8 | { 9 | "files": "*.md", 10 | "options": { 11 | "proseWrap": "preserve" 12 | } 13 | } 14 | ], 15 | "plugins": ["prettier-plugin-organize-imports", "prettier-plugin-packagejson"] 16 | } 17 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | website: 5 | container_name: website 6 | build: 7 | context: . 8 | dockerfile: Dockerfile 9 | ports: 10 | - "3000:3000" 11 | networks: 12 | - caddy 13 | 14 | registry: 15 | container_name: registry 16 | image: registry:2 17 | ports: 18 | - "5000:5000" 19 | networks: 20 | - caddy 21 | 22 | networks: 23 | caddy: 24 | external: true -------------------------------------------------------------------------------- /app/[lang]/docs/page.tsx: -------------------------------------------------------------------------------- 1 | import { md } from '@/lib/hl'; 2 | 3 | const mdxs = JSON.parse(process.env.mdxs ?? ''); 4 | 5 | export const runtime = 'edge'; 6 | 7 | export default function DocPage({ 8 | params: { lang }, 9 | }: { 10 | params: { lang: string }; 11 | }) { 12 | return ( 13 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /app/[lang]/page.tsx: -------------------------------------------------------------------------------- 1 | export const runtime = 'edge'; 2 | const mdxs = JSON.parse(process.env.mdxs ?? ''); 3 | import { md } from '@/lib/hl'; 4 | 5 | export default async function Home({ 6 | params: { lang }, 7 | }: { 8 | params: { lang: string }; 9 | }) { 10 | console.log(mdxs); 11 | 12 | return ( 13 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /middleware.ts: -------------------------------------------------------------------------------- 1 | import { NextMiddleware, MiddlewareConfig, NextResponse } from 'next/server' 2 | import { redirectLocale } from '@/i18n/utils/middleware' 3 | 4 | export const middleware: NextMiddleware = (request, event) => { 5 | // locale 6 | return redirectLocale(request, event) 7 | } 8 | 9 | export const config: MiddlewareConfig = { 10 | matcher: [ 11 | // Skip all internal paths (_next) 12 | '/((?!_next).*)' 13 | // Optional: only run on root (/) URL 14 | // '/' 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/fly.yml: -------------------------------------------------------------------------------- 1 | name: Fly Deploy 2 | on: 3 | push: 4 | branches: 5 | - main # change to main if needed 6 | jobs: 7 | deploy: 8 | name: Deploy app 9 | runs-on: ubuntu-latest 10 | concurrency: deploy-group # optional: ensure only one action runs at a time 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: superfly/flyctl-actions/setup-flyctl@master 14 | - run: flyctl deploy --remote-only 15 | env: 16 | FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} 17 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | .idea 23 | 24 | # debug 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | 29 | # local env files 30 | .env*.local 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | -------------------------------------------------------------------------------- /lib/hl.ts: -------------------------------------------------------------------------------- 1 | import hljs from 'highlight.js'; 2 | import MarkdownIt from 'markdown-it'; 3 | 4 | export const md = MarkdownIt({ 5 | html: true, 6 | linkify: true, 7 | typographer: true, 8 | highlight: function (str, lang) { 9 | if (lang && hljs.getLanguage(lang)) { 10 | try { 11 | return hljs.highlight(str, { language: lang, ignoreIllegals: true }) 12 | .value; 13 | } catch (__) {} 14 | } 15 | 16 | return ''; // use external default escaping 17 | }, 18 | }).use(require('markdown-it-task-lists')); 19 | 20 | export async function hl(code: string) { 21 | return md.render(code); 22 | } 23 | -------------------------------------------------------------------------------- /app/[lang]/layout.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import type { RootLayoutProps } from '@/app/layout' 3 | import { IntlProvider } from '@/i18n' 4 | import { Header } from '@/components/Header' 5 | import { Footer } from '@/components/Footer' 6 | import { ScrollArea } from '@/components/ui/scroll-area' 7 | 8 | export default function RootLayout({ children, params }: RootLayoutProps) { 9 | return ( 10 | 11 |
12 | 13 |
{children}
14 |
15 |