diff --git a/README.md b/README.md index 1e23048..76be2b3 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Frontend runs on `http://localhost:3000` by default. > [!IMPORTANT] > Before running features that require cluster access, configure `.env.local` with at least: -> `NEXT_PUBLIC_MOCK_USER`, `SEALOS_DOMAIN`, and related backend endpoints (`DATABASE_URL`, `METRICS_URL`, `ACCOUNT_URL`, `RETAG_SVC_URL`) based on your environment. +> `NEXT_PUBLIC_MOCK_USER`, `SEALOS_DOMAIN`, and related backend endpoints (`DATABASE_URL`, `METRICS_URL`, `ACCOUNT_URL`), plus registry retag settings (`REGISTRY_ADDR`, `REGISTRY_USER`, `REGISTRY_PASSWORD`) based on your environment. ### 2. Run controller diff --git a/v1/frontend/.env.template b/v1/frontend/.env.template index e027447..e6f8e81 100644 --- a/v1/frontend/.env.template +++ b/v1/frontend/.env.template @@ -47,10 +47,11 @@ DATABASE_URL= # database provider for prisma runtime/migration routing # values: cockroachdb (default) | postgresql DATABASE_PROVIDER="cockroachdb" -# url for template retag -# in dev: http://127.0.0.1:8092 -# in prod: http://devbox-service.devbox-system.svc.cluster.local:8092 -RETAG_SVC_URL= +# registry credentials for template image retag +REGISTRY_USER= +REGISTRY_PASSWORD= +# set true only for internal HTTP registries +REGISTRY_INSECURE="false" # privacy document url(for user to create template) PRIVACY_URL_ZH="https://sealos.run/docs/msa/privacy-policy" PRIVACY_URL_EN="https://sealos.io/docs/msa/privacy-policy" diff --git a/v1/frontend/AGENT.md b/v1/frontend/AGENT.md index b11d0f5..870af74 100644 --- a/v1/frontend/AGENT.md +++ b/v1/frontend/AGENT.md @@ -1,21 +1,23 @@ -# Devbox Provider Agent Notes +# Devbox V1 Frontend Agent Notes ## Scope -This directory is the Devbox provider in the Sealos frontend workspace. It owns -the DevBox list, create/edit flow, detail page, template conversion UI, release -and deployment actions, and provider-side API routes under `app/api`. +This directory is the standalone Devbox v1 frontend in the `labring/devbox` +repository. It owns the DevBox list, create/edit flow, detail page, template +conversion UI, release and deployment actions, and frontend-side API routes +under `app/api`. ## Working Rules -- Keep changes scoped to `frontend/providers/devbox` unless the task clearly - crosses a shared package boundary. +- Keep changes scoped to `v1/frontend` unless the task clearly crosses another + Devbox component boundary. - Do not execute database writes or migrations unless the user explicitly asks. - For production or test cloud images, build `linux/amd64` by default. - For 70-cluster work, use `KUBECONFIG=/Users/mlhiter/.kube/70` and namespace `devbox-frontend` unless the user gives a different target. -- The deployed image path uses the monorepo root `frontend/Dockerfile` with - `--build-arg name=devbox --build-arg path=providers/devbox`. +- The image build path in this repository uses `v1/frontend/Dockerfile` with + build context `v1/frontend`; do not use the old Sealos monorepo provider + build arguments here. - The deployment has both `devbox-frontend-init` and `devbox-frontend` containers. Inspect both image tags when verifying a rollout. - Use the Codex in-app Browser for local browser verification. diff --git a/v1/frontend/Dockerfile b/v1/frontend/Dockerfile index a6cb276..cc5ef2d 100644 --- a/v1/frontend/Dockerfile +++ b/v1/frontend/Dockerfile @@ -60,8 +60,10 @@ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static # Next standalone includes prisma schemas/clients but not migrations. # Keep both providers' migrations for initContainer migrate deploy. -COPY --from=builder --chown=nextjs:nodejs /app/prisma/cockroach/migrations ./providers/devbox/prisma/cockroach/migrations -COPY --from=builder --chown=nextjs:nodejs /app/prisma/postgresql/migrations ./providers/devbox/prisma/postgresql/migrations +COPY --from=builder --chown=nextjs:nodejs /app/prisma/cockroach/schema.prisma ./prisma/cockroach/schema.prisma +COPY --from=builder --chown=nextjs:nodejs /app/prisma/cockroach/migrations ./prisma/cockroach/migrations +COPY --from=builder --chown=nextjs:nodejs /app/prisma/postgresql/schema.prisma ./prisma/postgresql/schema.prisma +COPY --from=builder --chown=nextjs:nodejs /app/prisma/postgresql/migrations ./prisma/postgresql/migrations USER nextjs diff --git a/v1/frontend/README.md b/v1/frontend/README.md index 425bc51..bd839f7 100644 --- a/v1/frontend/README.md +++ b/v1/frontend/README.md @@ -1,16 +1,21 @@ # Devbox Frontend Provider -Devbox is the Sealos frontend provider for creating, editing, releasing, and -operating DevBox development environments. It is a Next.js app inside the -Sealos frontend workspace. +Devbox v1 frontend is the Next.js app for creating, editing, releasing, and +operating DevBox development environments. This copy lives in the standalone +`labring/devbox` repository under `v1/frontend`. ## How to dev -1. First,you should refer to `frontend/README.md` ’s `How to dev` part. +1. Install dependencies and start from this directory: + + ```bash + cd /Users/mlhiter/labring/devbox/v1/frontend + pnpm install + ``` 2. Then you should config your env. - 1. Create a new file `.env.local` in `frontend/providers/devbox`. + 1. Create a new file `.env.local` in `v1/frontend`. > `SEALOS_DOMAIN` is anyone website you use in sealos. diff --git a/v1/frontend/app/[lang]/(platform)/(home)/components/List.tsx b/v1/frontend/app/[lang]/(platform)/(home)/components/List.tsx index a140961..be02c41 100644 --- a/v1/frontend/app/[lang]/(platform)/(home)/components/List.tsx +++ b/v1/frontend/app/[lang]/(platform)/(home)/components/List.tsx @@ -29,7 +29,6 @@ import { type HeaderContext, type CellContext } from '@tanstack/react-table'; -import Image from 'next/image'; import dynamic from 'next/dynamic'; import { useTranslations } from 'next-intl'; import { useCallback, useMemo, useState, useEffect, useRef } from 'react'; @@ -63,6 +62,7 @@ import DatePicker from '@/components/DatePicker'; import { Separator } from '@labring/sealos-ui/separator'; import SearchEmpty from './SearchEmpty'; import GPUItem from '@/components/GPUItem'; +import { RuntimeIcon } from '@/components/RuntimeIcon'; const DeleteDevboxDialog = dynamic(() => import('@/components/dialogs/DeleteDevboxDialog')); const EditRemarkDialog = dynamic(() => import('@/components/dialogs/EditRemarkDialog')); @@ -203,23 +203,13 @@ const DevboxList = ({
- {item.template.name} +
- {item.template.name} +

{iconId}

@@ -672,8 +662,8 @@ const DevboxList = ({ return ( <> {/* table */} -
-
+
+
{/* table header */}
{table.getFlatHeaders().map((header) => ( @@ -707,11 +697,20 @@ const DevboxList = ({
{/* pagination */} {table.getRowModel().rows.length > 0 && ( - table.setPageIndex(page - 1)} - /> +
+ {t('Total') + ': ' + table.getFilteredRowModel().rows.length} +
+ table.setPageIndex(page - 1)} + /> +
+ {table.getState().pagination.pageSize}/ + {t('Page')} +
+
+
)}
diff --git a/v1/frontend/app/api/templateRepository/withTemplate/create/route.ts b/v1/frontend/app/api/templateRepository/withTemplate/create/route.ts index e7267de..3efdf26 100644 --- a/v1/frontend/app/api/templateRepository/withTemplate/create/route.ts +++ b/v1/frontend/app/api/templateRepository/withTemplate/create/route.ts @@ -1,10 +1,10 @@ import { TagType, TemplateRepositoryKind } from '@/prisma/generated/client'; import { authSessionWithJWT } from '@/services/backend/auth'; import { getK8s } from '@/services/backend/kubernetes'; +import { retagImage } from '@/services/backend/registry-retag'; import { jsonRes } from '@/services/backend/response'; import { devboxDB } from '@/services/db/init'; import { ERROR_ENUM } from '@/services/error'; -import { retagSvcClient } from '@/services/retag'; import { KBDevboxReleaseType, KBDevboxTypeV2 } from '@/types/k8s'; import { getRegionUid } from '@/utils/env'; import { mergeTemplateDefaults } from '@/utils/templateConfig'; @@ -24,7 +24,7 @@ export async function POST(req: NextRequest) { }); } const query = createTemplateRepositorySchema.parse(queryRaw); - const { kubeConfig, payload, token } = await authSessionWithJWT(headerList); + const { kubeConfig, payload } = await authSessionWithJWT(headerList); const { namespace, k8sCustomObjects } = await getK8s({ kubeconfig: kubeConfig }); @@ -97,16 +97,7 @@ export async function POST(req: NextRequest) { original: originalImage, target: targetImage }; - const retagResult = await retagSvcClient.post('/tag', retagbody, { - headers: { - Authorization: token - } - }); - if (retagResult.status !== 200) { - console.log('retagResult', retagResult); - throw Error('retag failed'); - } - // invoke retag service !todo + await retagImage(retagbody.original, retagbody.target); // suported deleted because devbox instance of deleted template const origionalTemplate = await devboxDB.template.findUnique({ where: { diff --git a/v1/frontend/app/api/templateRepository/withTemplate/update/route.ts b/v1/frontend/app/api/templateRepository/withTemplate/update/route.ts index 20eaab6..fae29fc 100644 --- a/v1/frontend/app/api/templateRepository/withTemplate/update/route.ts +++ b/v1/frontend/app/api/templateRepository/withTemplate/update/route.ts @@ -1,10 +1,10 @@ import { TagType } from '@/prisma/generated/client'; import { authSessionWithJWT } from '@/services/backend/auth'; import { getK8s } from '@/services/backend/kubernetes'; +import { retagImage } from '@/services/backend/registry-retag'; import { jsonRes } from '@/services/backend/response'; import { devboxDB } from '@/services/db/init'; import { ERROR_ENUM } from '@/services/error'; -import { retagSvcClient } from '@/services/retag'; import { KBDevboxReleaseType, KBDevboxTypeV2 } from '@/types/k8s'; import { getRegionUid } from '@/utils/env'; import { mergeTemplateDefaults } from '@/utils/templateConfig'; @@ -26,7 +26,7 @@ export async function POST(req: NextRequest) { }); } const query = updateTemplateSchema.parse(queryRaw); - const { kubeConfig, payload, token } = await authSessionWithJWT(headerList); + const { kubeConfig, payload } = await authSessionWithJWT(headerList); const { namespace, k8sCustomObjects } = await getK8s({ kubeconfig: kubeConfig }); @@ -107,14 +107,7 @@ export async function POST(req: NextRequest) { original: originalImage, target: tagretImage }; - const retagResult = await retagSvcClient.post('/tag', retagbody, { - headers: { - Authorization: token - } - }); - if (retagResult.status !== 200) { - throw Error('retag failed'); - } + await retagImage(retagbody.original, retagbody.target); const officialTagList = await devboxDB.tag.findMany({ where: { type: TagType.OFFICIAL_CONTENT diff --git a/v1/frontend/components/RuntimeIcon.tsx b/v1/frontend/components/RuntimeIcon.tsx new file mode 100644 index 0000000..2436f69 --- /dev/null +++ b/v1/frontend/components/RuntimeIcon.tsx @@ -0,0 +1,48 @@ +'use client'; + +import Image from 'next/image'; +import { useEffect, useState } from 'react'; + +interface RuntimeIconProps { + iconId: string | null; + alt: string; + width?: number; + height?: number; + className?: string; + priority?: boolean; + onLoad?: () => void; +} + +const fallbackRuntimeIcon = '/images/runtime/custom.svg'; + +const getRuntimeIconSrc = (iconId: string | null) => + iconId ? `/images/runtime/${iconId}.svg` : fallbackRuntimeIcon; + +export const RuntimeIcon = ({ + iconId, + alt, + width = 21, + height = 21, + className, + priority, + onLoad +}: RuntimeIconProps) => { + const [imgSrc, setImgSrc] = useState(getRuntimeIconSrc(iconId)); + + useEffect(() => { + setImgSrc(getRuntimeIconSrc(iconId)); + }, [iconId]); + + return ( + {alt} setImgSrc(fallbackRuntimeIcon)} + onLoad={onLoad} + /> + ); +}; diff --git a/v1/frontend/deploy/Kubefile b/v1/frontend/deploy/Kubefile index b572988..59ed057 100644 --- a/v1/frontend/deploy/Kubefile +++ b/v1/frontend/deploy/Kubefile @@ -9,5 +9,8 @@ ENV cloudDomain="127.0.0.1.nip.io" ENV cloudPort="" ENV certSecretName="wildcard-cert" ENV registryAddr="sealos.hub:5000" +ENV registryUser="" +ENV registryPassword="" +ENV registryInsecure="true" CMD ["kubectl apply -f manifests"] diff --git a/v1/frontend/deploy/manifests/deploy.yaml.tmpl b/v1/frontend/deploy/manifests/deploy.yaml.tmpl index 46850dc..09712a3 100644 --- a/v1/frontend/deploy/manifests/deploy.yaml.tmpl +++ b/v1/frontend/deploy/manifests/deploy.yaml.tmpl @@ -48,7 +48,7 @@ spec: - -c args: - |- - cd /app/providers/devbox + cd /app DB_PROVIDER="${DATABASE_PROVIDER:-cockroachdb}" SCHEMA_PATH="./prisma/cockroach/schema.prisma" if [ "$DB_PROVIDER" = "postgresql" ] || [ "$DB_PROVIDER" = "postgres" ] || [ "$DB_PROVIDER" = "pg" ]; then @@ -88,6 +88,12 @@ spec: value: wildcard-cert - name: REGISTRY_ADDR value: {{ .registryAddr }} + - name: REGISTRY_USER + value: {{ default "" .registryUser }} + - name: REGISTRY_PASSWORD + value: {{ default "" .registryPassword }} + - name: REGISTRY_INSECURE + value: "{{ default "false" .registryInsecure }}" - name: DEVBOX_AFFINITY_ENABLE value: 'true' - name: MONITOR_URL @@ -106,8 +112,6 @@ spec: value: 'false' - name: PRIVACY_URL value: https://sealos.run/docs/msa/privacy-policy - - name: RETAG_SVC_URL - value: http://devbox-service.devbox-system.svc.cluster.local:8092 - name: JWT_SECRET value: {{ .jwtSecret }} # -nsealos cm desktop-frontend-config ->jwt->Internal - name: REGION_UID diff --git a/v1/frontend/docs/references.md b/v1/frontend/docs/references.md index 752f920..041d4ca 100644 --- a/v1/frontend/docs/references.md +++ b/v1/frontend/docs/references.md @@ -2,12 +2,12 @@ ## Internal References -- `frontend/README.md` - frontend workspace setup. -- `frontend/providers/devbox/prisma/README.md` - Prisma schema layout. -- `frontend/providers/devbox/.env.template` - environment variable template. -- `frontend/providers/devbox/deploy/manifests/deploy.yaml.tmpl` - deployment and - migration container shape. -- `frontend/providers/devbox/deploy/manifests/ingress.yaml.tmpl` - frontend and +- `README.md` - local frontend setup. +- `prisma/README.md` - Prisma schema layout. +- `.env.template` - environment variable template. +- `deploy/manifests/deploy.yaml.tmpl` - deployment and migration container + shape. +- `deploy/manifests/ingress.yaml.tmpl` - frontend and domain-challenge ingress shape. ## Issue References diff --git a/v1/frontend/docs/runbook.md b/v1/frontend/docs/runbook.md index 90ea771..6189c78 100644 --- a/v1/frontend/docs/runbook.md +++ b/v1/frontend/docs/runbook.md @@ -2,11 +2,10 @@ ## Local Setup -Start from the frontend workspace setup in `frontend/README.md`, then create a -provider env file: +Start from the v1 frontend directory, then create a local env file: ```bash -cd /Users/mlhiter/labring/sealos/frontend/providers/devbox +cd /Users/mlhiter/labring/devbox/v1/frontend cp .env.template .env.local ``` @@ -17,6 +16,7 @@ NEXT_PUBLIC_MOCK_USER='' SEALOS_DOMAIN='192.168.10.70.nip.io' INGRESS_DOMAIN='192.168.10.70.nip.io' REGISTRY_ADDR='hub.192.168.10.70.nip.io' +REGISTRY_INSECURE='true' JWT_SECRET='' REGION_UID='' DATABASE_URL='