diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..72e9aa4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +Dockerfile +.dockerignore +node_modules +npm-debug.log +README.md +.next +.git \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a009b73 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +FROM node:20-alpine as build-stage + +WORKDIR /app +RUN corepack enable + +COPY .npmrc package.json pnpm-lock.yaml ./ +RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store \ + pnpm install --frozen-lockfile + +COPY . . +RUN pnpm build + +FROM node:20-alpine as production-stage +WORKDIR /srv/ipv6-test + +ENV NODE_ENV production + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=build-stage /app/public ./public + +COPY --from=build-stage --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=build-stage --chown=nextjs:nodejs /app/.next/static ./.next/static + +RUN chown nextjs:nodejs .next + +USER nextjs + +EXPOSE 3000 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1708e01 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,20 @@ +version: "3.9" + +networks: + traefik: + name: traefik + external: true + +services: + ipv6-test: + build: . + container_name: ipv6-test + restart: always + labels: + - traefik.enable=true + - traefik.http.routers.ipv6-test.rule=Host(`ipv6.thehrz.net`) + - traefik.http.routers.ipv6-test.entrypoints=web,websecure + - traefik.http.routers.ipv6-test.tls.certresolver=letsencrypt + - traefik.http.services.ipv6-test.loadbalancer.server.port=3000 + networks: + - traefik diff --git a/next.config.mjs b/next.config.mjs index 4678774..b3e7994 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,4 +1,6 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + output: 'standalone', +} export default nextConfig; diff --git a/package.json b/package.json index 7c83b95..bec6286 100644 --- a/package.json +++ b/package.json @@ -9,18 +9,20 @@ "lint": "next lint" }, "dependencies": { + "next": "14.2.5", "react": "^18", "react-dom": "^18", - "next": "14.2.5" + "theme-change": "^2.5.0" }, "devDependencies": { - "typescript": "^5", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", + "daisyui": "^4.12.10", + "eslint": "^8", + "eslint-config-next": "14.2.5", "postcss": "^8", "tailwindcss": "^3.4.1", - "eslint": "^8", - "eslint-config-next": "14.2.5" + "typescript": "^5" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d6e0c4..495fabc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: react-dom: specifier: ^18 version: 18.3.1(react@18.3.1) + theme-change: + specifier: ^2.5.0 + version: 2.5.0 devDependencies: '@types/node': specifier: ^20 @@ -27,6 +30,9 @@ importers: '@types/react-dom': specifier: ^18 version: 18.3.0 + daisyui: + specifier: ^4.12.10 + version: 4.12.10(postcss@8.4.41) eslint: specifier: ^8 version: 8.57.0 @@ -396,6 +402,9 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + css-selector-tokenizer@0.8.0: + resolution: {integrity: sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -404,6 +413,14 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + culori@3.3.0: + resolution: {integrity: sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + daisyui@4.12.10: + resolution: {integrity: sha512-jp1RAuzbHhGdXmn957Z2XsTZStXGHzFfF0FgIOZj3Wv9sH7OZgLfXTRZNfKVYxltGUOBsG1kbWAdF5SrqjebvA==} + engines: {node: '>=16.9.0'} + damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} @@ -634,6 +651,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fastparse@1.1.2: + resolution: {integrity: sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==} + fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -1404,6 +1424,9 @@ packages: text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + theme-change@2.5.0: + resolution: {integrity: sha512-B/UdsgdHAGhSKHTAQnxg/etN0RaMDpehuJmZIjLMDVJ6DGIliRHGD6pODi1CXLQAN9GV0GSyB3G6yCuK05PkPQ==} + thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -1883,10 +1906,26 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-selector-tokenizer@0.8.0: + dependencies: + cssesc: 3.0.0 + fastparse: 1.1.2 + cssesc@3.0.0: {} csstype@3.1.3: {} + culori@3.3.0: {} + + daisyui@4.12.10(postcss@8.4.41): + dependencies: + css-selector-tokenizer: 0.8.0 + culori: 3.3.0 + picocolors: 1.0.1 + postcss-js: 4.0.1(postcss@8.4.41) + transitivePeerDependencies: + - postcss + damerau-levenshtein@1.0.8: {} data-view-buffer@1.0.1: @@ -2091,7 +2130,7 @@ snapshots: eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) eslint-plugin-react: 7.35.0(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) @@ -2115,7 +2154,7 @@ snapshots: enhanced-resolve: 5.17.1 eslint: 8.57.0 eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.6 is-core-module: 2.15.0 @@ -2137,7 +2176,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -2292,6 +2331,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fastparse@1.1.2: {} + fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -3108,6 +3149,8 @@ snapshots: text-table@0.2.0: {} + theme-change@2.5.0: {} + thenify-all@1.6.0: dependencies: thenify: 3.3.1 diff --git a/src/app/api/myip/route.ts b/src/app/api/myip/route.ts new file mode 100644 index 0000000..188c997 --- /dev/null +++ b/src/app/api/myip/route.ts @@ -0,0 +1,5 @@ +import { NextRequest, NextResponse } from "next/server" + +export async function GET(request: NextRequest) { + return NextResponse.json(request.ip + "") +} \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index 875c01e..3f9ae90 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -2,7 +2,7 @@ @tailwind components; @tailwind utilities; -:root { +/* :root { --foreground-rgb: 0, 0, 0; --background-start-rgb: 214, 219, 220; --background-end-rgb: 255, 255, 255; @@ -30,4 +30,4 @@ body { .text-balance { text-wrap: balance; } -} +} */ diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 3314e47..cf9772b 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,22 +1,30 @@ -import type { Metadata } from "next"; -import { Inter } from "next/font/google"; -import "./globals.css"; +import type { Metadata } from "next" +import { Inter } from "next/font/google" +import "./globals.css" +import Nav from "@/components/Nav" +import Footer from "@/components/Footer" -const inter = Inter({ subsets: ["latin"] }); +const inter = Inter({ subsets: ["latin"] }) export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", -}; + title: "IPv6 测试", + description: "IPv6 测试", +} export default function RootLayout({ children, }: Readonly<{ - children: React.ReactNode; + children: React.ReactNode }>) { return ( - -
{children} + + +
- Get started by editing
- src/app/page.tsx
-
+ 免费IPv6测试服务 +
+- Find in-depth information about Next.js features and API. -
- - - -- Learn about Next.js in an interactive course with quizzes! -
- - - -- Explore starter templates for Next.js. -
- - - -- Instantly deploy your Next.js site to a shareable URL with Vercel. -
- -