mirror of
https://github.com/hamster1963/nezha-dash.git
synced 2025-04-24 21:10:45 +08:00
Merge pull request #18 from hamster1963/new-docker
[TSK1-485]Add docker support
This commit is contained in:
commit
176b422d5b
@ -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
46
.github/workflows/Deploy.yml
vendored
Normal 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 }}
|
@ -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
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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">
|
||||||
|
@ -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 },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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",
|
||||||
|
@ -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
5
docker/.env.example
Normal 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
|
9
docker/docker-compose.yml
Normal file
9
docker/docker-compose.yml
Normal 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"
|
9
docker/docker-compose.yml.cn
Normal file
9
docker/docker-compose.yml.cn
Normal 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
8
lib/env-entry.ts
Normal 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];
|
||||||
|
}
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
@ -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",
|
||||||
|
Loading…
Reference in New Issue
Block a user