Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
9 changes: 5 additions & 4 deletions v1/frontend/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
18 changes: 10 additions & 8 deletions v1/frontend/AGENT.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
6 changes: 4 additions & 2 deletions v1/frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
15 changes: 10 additions & 5 deletions v1/frontend/README.md
Original file line number Diff line number Diff line change
@@ -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.

Expand Down
39 changes: 19 additions & 20 deletions v1/frontend/app/[lang]/(platform)/(home)/components/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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'));
Expand Down Expand Up @@ -203,23 +203,13 @@ const DevboxList = ({
<Tooltip>
<TooltipTrigger asChild>
<div className="flex h-8 min-w-8 items-center justify-center rounded-lg border-[0.5px] border-zinc-200 bg-zinc-50">
<Image
width={21}
height={21}
alt={item.template.name}
src={`/images/runtime/${iconId}.svg`}
/>
<RuntimeIcon iconId={iconId} alt={item.template.name} />
</div>
</TooltipTrigger>
<TooltipContent side="bottom" align="start" sideOffset={1}>
<div className="flex items-center gap-3">
<div className="flex h-8 w-8 items-center justify-center rounded-lg border-[0.5px] border-zinc-200 bg-zinc-50">
<Image
width={21}
height={21}
alt={item.template.name}
src={`/images/runtime/${iconId}.svg`}
/>
<RuntimeIcon iconId={iconId} alt={item.template.name} />
</div>
<div className="flex flex-col">
<p className="text-sm/5 font-medium">{iconId}</p>
Expand Down Expand Up @@ -672,8 +662,8 @@ const DevboxList = ({
return (
<>
{/* table */}
<div className="flex h-full w-full flex-col justify-between">
<div className="flex h-full flex-col gap-3 overflow-x-auto">
<div className="flex min-h-0 w-full flex-1 flex-col justify-between gap-3">
<div className="flex flex-col gap-3 overflow-x-auto">
{/* table header */}
<div className="flex h-10 min-w-[1350px] items-center rounded-lg border-[0.5px] bg-white px-6 py-1 text-sm/5 text-zinc-500 shadow-[0px_2px_8px_-2px_rgba(0,0,0,0.08)]">
{table.getFlatHeaders().map((header) => (
Expand Down Expand Up @@ -707,11 +697,20 @@ const DevboxList = ({
</div>
{/* pagination */}
{table.getRowModel().rows.length > 0 && (
<Pagination
currentPage={table.getState().pagination.pageIndex + 1}
totalPages={table.getPageCount()}
onPageChange={(page) => table.setPageIndex(page - 1)}
/>
<div className="flex items-center justify-between gap-2.5 pt-2 text-sm/5 text-zinc-500">
<span>{t('Total') + ': ' + table.getFilteredRowModel().rows.length}</span>
<div className="flex items-center gap-3">
<Pagination
currentPage={table.getState().pagination.pageIndex + 1}
totalPages={table.getPageCount()}
onPageChange={(page) => table.setPageIndex(page - 1)}
/>
<div className="flex items-center gap-1">
<span className="text-zinc-900">{table.getState().pagination.pageSize}</span>/
<span>{t('Page')}</span>
</div>
</div>
</div>
)}
</div>

Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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
});
Expand Down Expand Up @@ -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: {
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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
});
Expand Down Expand Up @@ -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
Expand Down
48 changes: 48 additions & 0 deletions v1/frontend/components/RuntimeIcon.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Image
width={width}
height={height}
alt={alt}
src={imgSrc}
className={className}
priority={priority}
onError={() => setImgSrc(fallbackRuntimeIcon)}
onLoad={onLoad}
/>
);
};
3 changes: 3 additions & 0 deletions v1/frontend/deploy/Kubefile
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
10 changes: 7 additions & 3 deletions v1/frontend/deploy/manifests/deploy.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
12 changes: 6 additions & 6 deletions v1/frontend/docs/references.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading