Merge pull request #18 from hamster1963/new-docker

[TSK1-485]Add docker support
This commit is contained in:
仓鼠 2024-09-22 22:15:08 +08:00 committed by GitHub
commit 176b422d5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 110 additions and 29 deletions

View File

@ -1,5 +1,5 @@
NezhaBaseUrl=http://0.0.0.0:8008 NezhaBaseUrl=http://1.1.1.1:8008
NezhaAuth=5hAY3QX6Nl9B3UOQgB26KdsdS1dsdUdM NezhaAuth=nezha-token
NEXT_PUBLIC_NezhaFetchInterval=5000 NEXT_PUBLIC_NezhaFetchInterval=2000
NEXT_PUBLIC_ShowFlag=true NEXT_PUBLIC_ShowFlag=true
NEXT_PUBLIC_DisableCartoon=true NEXT_PUBLIC_DisableCartoon=false

46
.github/workflows/Deploy.yml vendored Normal file
View File

@ -0,0 +1,46 @@
name: Build and push Docker image
on:
push:
tags:
- "v*"
jobs:
build-and-push:
runs-on: ubuntu-latest
environment: Production
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to AliYun Container Registry
uses: docker/login-action@v3
with:
registry: registry.cn-guangzhou.aliyuncs.com
username: ${{ secrets.ALI_USERNAME }}
password: ${{ secrets.ALI_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: |
hamster1963/nezha-dash
registry.cn-guangzhou.aliyuncs.com/hamster-home/nezha-dash
tags: |
type=raw,value=latest
type=ref,event=tag
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View File

@ -24,9 +24,6 @@ WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/node_modules ./node_modules
COPY . . COPY . .
ARG PROD_ENV=""
# Appends to .env.production
RUN printf "$PROD_ENV" >> .env.production
RUN yarn build RUN yarn build
@ -41,12 +38,10 @@ RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public COPY --from=builder /app/public ./public
COPY --from=builder /app/.env.production ./.env.production
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/.env.example ./.env
USER nextjs USER nextjs

View File

@ -4,10 +4,11 @@ import { ServerApi } from "@/app/types/nezha-api";
import ServerCard from "@/components/ServerCard"; import ServerCard from "@/components/ServerCard";
import { nezhaFetcher } from "@/lib/utils"; import { nezhaFetcher } from "@/lib/utils";
import useSWR from "swr"; import useSWR from "swr";
import getEnv from "@/lib/env-entry";
export default function ServerListClient() { export default function ServerListClient() {
const { data } = useSWR<ServerApi>("/api/server", nezhaFetcher, { const { data } = useSWR<ServerApi>("/api/server", nezhaFetcher, {
refreshInterval: Number(process.env.NEXT_PUBLIC_NezhaFetchInterval) || 2000, refreshInterval: Number(getEnv("NEXT_PUBLIC_NezhaFetchInterval")) || 2000,
}); });
if (!data) return null; if (!data) return null;

View File

@ -7,11 +7,12 @@ import useSWR from "swr";
import { formatBytes, nezhaFetcher } from "@/lib/utils"; import { formatBytes, nezhaFetcher } from "@/lib/utils";
import { Loader } from "@/components/loading/Loader"; import { Loader } from "@/components/loading/Loader";
import { ServerApi } from "@/app/types/nezha-api"; import { ServerApi } from "@/app/types/nezha-api";
import getEnv from "@/lib/env-entry";
export default function ServerOverviewClient() { export default function ServerOverviewClient() {
const { data } = useSWR<ServerApi>("/api/server", nezhaFetcher); const { data } = useSWR<ServerApi>("/api/server", nezhaFetcher);
const disableCartoon = process.env.NEXT_PUBLIC_DisableCartoon === "true"; const disableCartoon = getEnv("NEXT_PUBLIC_DisableCartoon") === "true";
return ( return (
<section className="grid grid-cols-2 gap-4 md:grid-cols-4"> <section className="grid grid-cols-2 gap-4 md:grid-cols-4">

View File

@ -1,11 +1,17 @@
import { GetNezhaData } from "@/lib/serverFetch"; import { GetNezhaData } from "@/lib/serverFetch";
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
export async function GET(_: Request) { export async function GET(_: Request) {
try { try {
const response = await GetNezhaData(); const response = await GetNezhaData();
return NextResponse.json(response, { status: 200 }); return NextResponse.json(response, { status: 200 });
} catch (error) { } catch (error) {
return NextResponse.json({ error: error }, { status: 200 }); console.error(error);
return NextResponse.json(
{ error: "fetch nezha data failed" },
{ status: 400 },
);
} }
} }

View File

@ -6,6 +6,7 @@ import { ThemeProvider } from "next-themes";
import React from "react"; import React from "react";
import { Viewport } from "next"; import { Viewport } from "next";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { PublicEnvScript } from "next-runtime-env";
const fontSans = FontSans({ const fontSans = FontSans({
subsets: ["latin"], subsets: ["latin"],
@ -37,6 +38,9 @@ interface RootLayoutProps {
export default function RootLayout({ children }: RootLayoutProps) { export default function RootLayout({ children }: RootLayoutProps) {
return ( return (
<html lang="en" suppressHydrationWarning> <html lang="en" suppressHydrationWarning>
<head>
<PublicEnvScript />
</head>
<body <body
className={cn( className={cn(
"min-h-screen bg-background font-sans antialiased", "min-h-screen bg-background font-sans antialiased",

BIN
bun.lockb

Binary file not shown.

View File

@ -9,6 +9,7 @@ import {
import { cn, formatNezhaInfo } from "@/lib/utils"; import { cn, formatNezhaInfo } from "@/lib/utils";
import ServerCardPopover from "./ServerCardPopover"; import ServerCardPopover from "./ServerCardPopover";
import getUnicodeFlagIcon from "country-flag-icons/unicode"; import getUnicodeFlagIcon from "country-flag-icons/unicode";
import { env } from "next-runtime-env";
export default function ServerCard({ export default function ServerCard({
serverInfo, serverInfo,
@ -18,7 +19,7 @@ export default function ServerCard({
const { name, country_code, online, cpu, up, down, mem, stg, ...props } = const { name, country_code, online, cpu, up, down, mem, stg, ...props } =
formatNezhaInfo(serverInfo); formatNezhaInfo(serverInfo);
const showFlag = process.env.NEXT_PUBLIC_ShowFlag === "true"; const showFlag = env("NEXT_PUBLIC_ShowFlag") === "true";
return online ? ( return online ? (
<Card <Card

5
docker/.env.example Normal file
View File

@ -0,0 +1,5 @@
NezhaBaseUrl=http://0.0.0.0:8008
NezhaAuth=5hAY3QX6Nl9B3UOQgB26KdsdS1dsdUdM
NEXT_PUBLIC_NezhaFetchInterval=5000
NEXT_PUBLIC_ShowFlag=true
NEXT_PUBLIC_DisableCartoon=true

View File

@ -0,0 +1,9 @@
services:
nezha-dash:
container_name: nezha-dash
image: hamster1963/nezha-dash:latest
volumes:
- ./.env:/app/.env
restart: always
ports:
- "4123:3000"

View File

@ -0,0 +1,9 @@
services:
nezha-dash:
container_name: nezha-dash
image: registry.cn-guangzhou.aliyuncs.com/hamster-home/nezha-dash:latest
volumes:
- ./.env:/app/.env
restart: always
ports:
- "4123:3000"

8
lib/env-entry.ts Normal file
View File

@ -0,0 +1,8 @@
import { env } from "next-runtime-env";
export default function getEnv(key: string) {
if (key.startsWith("NEXT_PUBLIC_")) {
return env(key);
}
return process.env[key];
}

View File

@ -3,22 +3,23 @@
import { NezhaAPI, ServerApi } from "@/app/types/nezha-api"; import { NezhaAPI, ServerApi } from "@/app/types/nezha-api";
import { MakeOptional } from "@/app/types/utils"; import { MakeOptional } from "@/app/types/utils";
import { error } from "console"; import { error } from "console";
import getEnv from "./env-entry";
export async function GetNezhaData() { export async function GetNezhaData() {
if (!process.env.NezhaBaseUrl) { var nezhaBaseUrl = getEnv("NezhaBaseUrl");
if (!nezhaBaseUrl) {
error("NezhaBaseUrl is not set"); error("NezhaBaseUrl is not set");
return; return;
} }
// Remove trailing slash
var nezhaBaseUrl = process.env.NezhaBaseUrl;
if (process.env.NezhaBaseUrl[process.env.NezhaBaseUrl.length - 1] === "/") { // Remove trailing slash
nezhaBaseUrl = process.env.NezhaBaseUrl.slice(0, -1); if (nezhaBaseUrl[nezhaBaseUrl.length - 1] === "/") {
nezhaBaseUrl = nezhaBaseUrl.slice(0, -1);
} }
try { try {
const response = await fetch(nezhaBaseUrl + "/api/v1/server/details", { const response = await fetch(nezhaBaseUrl + "/api/v1/server/details", {
headers: { headers: {
Authorization: process.env.NezhaAuth as string, Authorization: getEnv("NezhaAuth") as string,
}, },
next: { next: {
revalidate: 0, revalidate: 0,

View File

@ -1,11 +1,5 @@
import withPWAInit from "@ducanh2912/next-pwa"; import withPWAInit from "@ducanh2912/next-pwa";
import withBundleAnalyzer from "@next/bundle-analyzer";
const bundleAnalyzer = withBundleAnalyzer({
enabled: process.env.ANALYZE === "true",
});
const withPWA = withPWAInit({ const withPWA = withPWAInit({
dest: "public", dest: "public",
cacheOnFrontEndNav: true, cacheOnFrontEndNav: true,
@ -23,4 +17,4 @@ const nextConfig = {
reactStrictMode: true, reactStrictMode: true,
}; };
export default bundleAnalyzer(withPWA(nextConfig)); export default withPWA(nextConfig);

View File

@ -1,5 +1,5 @@
{ {
"name": "homedash-shadcn", "name": "nezha-dash",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -27,6 +27,7 @@
"lucide-react": "^0.414.0", "lucide-react": "^0.414.0",
"luxon": "^3.5.0", "luxon": "^3.5.0",
"next": "^14.2.13", "next": "^14.2.13",
"next-runtime-env": "^3.2.2",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-device-detect": "^2.2.3", "react-device-detect": "^2.2.3",
@ -46,7 +47,7 @@
"@types/react": "^18.3.8", "@types/react": "^18.3.8",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"eslint": "^9.10.0", "eslint": "^9.11.0",
"eslint-config-next": "^14.2.13", "eslint-config-next": "^14.2.13",
"postcss": "^8.4.47", "postcss": "^8.4.47",
"prettier": "^3.3.3", "prettier": "^3.3.3",