diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f36a657 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +node_modules +.next +.git +.gitignore +*.md +.env* +!.env.example +scripts/_ssh* +scripts/*.py +.cursor +npm-debug.log* +Dockerfile* +docker-compose*.yml +.dockerignore diff --git a/.gitignore b/.gitignore index 927a854..95c344a 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,9 @@ yarn-error.log* !.env.example # mirrored firmwares (run npm run sync:firmware for public deploy) -/firmware/ +# Keep empty dir in git via .gitkeep when needed for Docker COPY +/firmware/** +!/firmware/.gitkeep # vercel .vercel diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6250314 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,36 @@ +# CYD Flasher — production image (Next.js standalone) +FROM node:20-bookworm-slim AS deps +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm install + +FROM node:20-bookworm-slim AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . +RUN mkdir -p public +ENV NEXT_TELEMETRY_DISABLED=1 +ARG NEXT_PUBLIC_SITE_URL=https://cyd.smallzhiyuns.com +ENV NEXT_PUBLIC_SITE_URL=$NEXT_PUBLIC_SITE_URL +RUN npm run build + +FROM node:20-bookworm-slim AS runner +WORKDIR /app +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 +ENV PORT=3000 +ENV HOSTNAME=0.0.0.0 +ENV NEXT_PUBLIC_SITE_URL=https://cyd.smallzhiyuns.com +ENV CYD_FIRMWARE_ROOT=/app/firmware + +RUN addgroup --system --gid 1001 nodejs \ + && adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static +COPY --chown=nextjs:nodejs firmware ./firmware + +USER nextjs +EXPOSE 3000 +CMD ["node", "server.js"] diff --git a/README.md b/README.md index 608819b..849022f 100644 --- a/README.md +++ b/README.md @@ -73,19 +73,26 @@ Create `meta.json` beside the `.bin` files: Without `meta.json`, the catalog still works from `VERSION.txt`. -## Public deploy +## Public deploy (Docker + Caddy) -Web Serial requires **HTTPS** (or localhost). +Production: **https://cyd.smallzhiyuns.com** +Server layout: `/home/dsx2016/docker/cydweb` + Caddy reverse proxy `127.0.0.1:3020`. ```powershell npm run sync:firmware +# On server: +cd /home/dsx2016/docker/cydweb +docker compose up -d --build +# Reload Caddy after Caddyfile changes: +cd /home/dsx2016/docker/caddy && docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile ``` ```env -NEXT_PUBLIC_SITE_URL=https://your-domain.example +NEXT_PUBLIC_SITE_URL=https://cyd.smallzhiyuns.com +CYD_FIRMWARE_ROOT=/app/firmware ``` -Then `npm run build && npm start` (or any Next.js host). `firmware/` mirror is gitignored. +Web Serial requires **HTTPS** (or localhost). ## Scripts diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..08e0c05 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +services: + cydweb: + build: + context: . + args: + NEXT_PUBLIC_SITE_URL: https://cyd.smallzhiyuns.com + image: cydweb:latest + container_name: cydweb + restart: unless-stopped + ports: + - "127.0.0.1:3020:3000" + environment: + - NODE_ENV=production + - NEXT_PUBLIC_SITE_URL=https://cyd.smallzhiyuns.com + - CYD_FIRMWARE_ROOT=/app/firmware + volumes: + # Live-update firmwares without rebuild (host path populated by sync) + - ./firmware:/app/firmware:ro diff --git a/firmware/.gitkeep b/firmware/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/next.config.ts b/next.config.ts index 2abcfdf..7b4cd9b 100644 --- a/next.config.ts +++ b/next.config.ts @@ -3,6 +3,8 @@ import createNextIntlPlugin from "next-intl/plugin"; const withNextIntl = createNextIntlPlugin("./i18n/request.ts"); -const nextConfig: NextConfig = {}; +const nextConfig: NextConfig = { + output: "standalone", +}; export default withNextIntl(nextConfig); diff --git a/package-lock.json b/package-lock.json index 5f31e1b..17b5a26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,18 @@ { "name": "cydweb", - "version": "0.1.0", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cydweb", - "version": "0.1.0", + "version": "1.0.0", "dependencies": { "@radix-ui/react-radio-group": "^1.5.1", "@radix-ui/react-select": "^2.4.1", "@radix-ui/react-separator": "^1.2.0", "@radix-ui/react-slot": "^1.5.0", + "@swc/helpers": "0.5.23", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^1.59.0", @@ -2341,9 +2342,9 @@ "license": "Apache-2.0" }, "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.0" @@ -6352,6 +6353,15 @@ } } }, + "node_modules/next/node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", diff --git a/package.json b/package.json index 7acb691..fae426a 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@radix-ui/react-select": "^2.4.1", "@radix-ui/react-separator": "^1.2.0", "@radix-ui/react-slot": "^1.5.0", + "@swc/helpers": "0.5.23", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^1.59.0", diff --git a/public/.gitkeep b/public/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/public/.gitkeep @@ -0,0 +1 @@ +