diff --git a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositorySelector/index.tsx b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositorySelector/index.tsx index 77f7078b2a8..a233868741e 100644 --- a/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositorySelector/index.tsx +++ b/frontend/providers/devbox/app/[lang]/(platform)/devbox/create/components/form/BasicConfiguration/TemplateRepositorySelector/index.tsx @@ -1,4 +1,5 @@ import { getTemplateRepository, listOfficialTemplateRepository } from '@/api/template' +import useDriver from '@/hooks/useDriver' import { TemplateRepositoryKind } from '@/prisma/generated/client' import { useDevboxStore } from '@/stores/devbox' import { DevboxEditTypeV2 } from '@/types/devbox' @@ -11,7 +12,6 @@ import { useFormContext } from 'react-hook-form' import Label from '../../Label' import TemplateRepositoryListNav from '../TemplateRepositoryListNav' import TemplateRepositoryItem from './TemplateReposistoryItem' -import useDriver from '@/hooks/useDriver' interface TemplateRepositorySelectorProps { isEdit: boolean @@ -139,8 +139,8 @@ export default function TemplateRepositorySelector({ isEdit }: TemplateRepositor ]) return ( - - + + @@ -171,7 +171,7 @@ export default function TemplateRepositorySelector({ isEdit }: TemplateRepositor ))} - + {/* Framework */} {categorizedData['FRAMEWORK'].length !== 0 && {t('framework')}} diff --git a/frontend/providers/devbox/app/api/auth/init/route.ts b/frontend/providers/devbox/app/api/auth/init/route.ts index 5eac0ea5e06..387be787436 100644 --- a/frontend/providers/devbox/app/api/auth/init/route.ts +++ b/frontend/providers/devbox/app/api/auth/init/route.ts @@ -1,6 +1,7 @@ import { authSessionWithDesktopJWT, generateDevboxToken } from "@/services/backend/auth" import { jsonRes } from "@/services/backend/response" import { devboxDB } from "@/services/db/init" +import { getRegionUid } from "@/utils/env" import { makeOrganizationName } from "@/utils/user" import { NextRequest } from "next/server" @@ -109,9 +110,9 @@ const findOrCreateUser = async (regionUid: string, namespaceId: string) => { }) } export async function POST(req: NextRequest) { - const regionUid = process.env.REGION_UID + const regionUid = getRegionUid() if (!regionUid) { - console.log("REGIONUID is not set") + console.log("REGION_UID is not set") return jsonRes({ code: 500, }) diff --git a/frontend/providers/devbox/app/api/templateRepository/delete/route.ts b/frontend/providers/devbox/app/api/templateRepository/delete/route.ts index 7af91eafdb3..4b9830886bc 100644 --- a/frontend/providers/devbox/app/api/templateRepository/delete/route.ts +++ b/frontend/providers/devbox/app/api/templateRepository/delete/route.ts @@ -1,6 +1,7 @@ import { authSessionWithJWT } from '@/services/backend/auth' import { jsonRes } from '@/services/backend/response' import { devboxDB } from '@/services/db/init' +import { getRegionUid } from '@/utils/env' import { NextRequest } from 'next/server' import { z } from 'zod' @@ -23,11 +24,13 @@ export async function DELETE(req: NextRequest) { const {payload} = await authSessionWithJWT(headerList) const deletedAt = new Date() + const regionUid = getRegionUid() await devboxDB.templateRepository.update({ where: { uid, organizationUid: payload.organizationUid, isDeleted: false, + regionUid }, data: { deletedAt, diff --git a/frontend/providers/devbox/app/api/templateRepository/get/route.ts b/frontend/providers/devbox/app/api/templateRepository/get/route.ts index e0d83c03205..7c00c98b573 100644 --- a/frontend/providers/devbox/app/api/templateRepository/get/route.ts +++ b/frontend/providers/devbox/app/api/templateRepository/get/route.ts @@ -1,6 +1,7 @@ import { authSessionWithJWT } from '@/services/backend/auth' import { jsonRes } from '@/services/backend/response' import { devboxDB } from '@/services/db/init' +import { getRegionUid } from '@/utils/env' import { NextRequest } from 'next/server' import { z } from 'zod' @@ -19,10 +20,12 @@ export async function GET(req: NextRequest) { }) } const uid = uidResult.data + const regionUid = getRegionUid() const templateRepository = await devboxDB.templateRepository.findUnique({ where: { uid, - isDeleted: false + isDeleted: false, + regionUid }, select: { templates: { diff --git a/frontend/providers/devbox/app/api/templateRepository/list/route.ts b/frontend/providers/devbox/app/api/templateRepository/list/route.ts index 3dcd8c999c3..caf88109183 100644 --- a/frontend/providers/devbox/app/api/templateRepository/list/route.ts +++ b/frontend/providers/devbox/app/api/templateRepository/list/route.ts @@ -1,6 +1,7 @@ import { Prisma } from '@/prisma/generated/client' import { jsonRes } from '@/services/backend/response' import { devboxDB } from '@/services/db/init' +import { getRegionUid } from '@/utils/env' import { NextRequest } from 'next/server' import { z } from 'zod' export const dynamic = 'force-dynamic' @@ -11,45 +12,36 @@ export async function GET(req: NextRequest) { const search = searchParams.get('search') || '' const page = z.number().int().positive().safeParse(Number(searchParams.get('page'))).data || 1 const pageSize = z.number().int().min(1).safeParse(Number(searchParams.get('pageSize'))).data || 30 - const dbquery: Prisma.TemplateRepositoryWhereInput = { - - ...(tags && tags.length > 0 - ? { - AND: tags.map((tag) => ({ - templateRepositoryTags: { - some: { - tagUid: tag - } - } - })) - } - : {}), - ...(search && search.length > 0 - ? { - name: { - contains: search - } - } - : {}) - } const [templateRepositoryList, totalItems] = await devboxDB.$transaction(async tx => { - const validRepoIds = await tx.template.findMany({ - where: { - isDeleted: false, - }, - select: { - templateRepositoryUid: true - }, - distinct: ['templateRepositoryUid'] - }) + const regionUid = getRegionUid() const where: Prisma.TemplateRepositoryWhereInput = { - uid: { - in: validRepoIds.map(r => r.templateRepositoryUid) + templates: { + some: { + isDeleted: false + } }, isPublic: true, isDeleted: false, - ...dbquery + regionUid, + ...(tags && tags.length > 0 + ? { + AND: tags.map((tag) => ({ + templateRepositoryTags: { + some: { + tagUid: tag + } + } + })) + } + : {}), + ...(search && search.length > 0 + ? { + name: { + contains: search + } + } + : {}) } const [templateRepositoryList, totalItems] = await Promise.all([ tx.templateRepository.findMany({ @@ -88,7 +80,7 @@ export async function GET(req: NextRequest) { ] }), tx.templateRepository.count({ - where: dbquery, + where, }) ]) return [templateRepositoryList, totalItems] diff --git a/frontend/providers/devbox/app/api/templateRepository/listOfficial/route.ts b/frontend/providers/devbox/app/api/templateRepository/listOfficial/route.ts index 9318d393e3e..c94c53fdaea 100644 --- a/frontend/providers/devbox/app/api/templateRepository/listOfficial/route.ts +++ b/frontend/providers/devbox/app/api/templateRepository/listOfficial/route.ts @@ -1,5 +1,6 @@ import { jsonRes } from '@/services/backend/response' import { devboxDB } from '@/services/db/init' +import { getRegionUid } from '@/utils/env' import { NextRequest } from 'next/server' export const dynamic = 'force-dynamic' @@ -10,12 +11,14 @@ export const GET = async function GET(req: NextRequest) { id: 'labring' } }) - if(!organization) throw Error('organization not found') + if (!organization) throw Error('organization not found') + const regionUid = getRegionUid() const templateRepositoryList = await devboxDB.templateRepository.findMany({ where: { isPublic: true, isDeleted: false, - organizationUid: organization.uid + organizationUid: organization.uid, + regionUid, }, select: { kind: true, diff --git a/frontend/providers/devbox/app/api/templateRepository/template/getConfig/route.ts b/frontend/providers/devbox/app/api/templateRepository/template/getConfig/route.ts index 732bcf8949f..23babfc56e5 100644 --- a/frontend/providers/devbox/app/api/templateRepository/template/getConfig/route.ts +++ b/frontend/providers/devbox/app/api/templateRepository/template/getConfig/route.ts @@ -1,6 +1,7 @@ import { authSessionWithJWT } from '@/services/backend/auth' import { jsonRes } from '@/services/backend/response' import { devboxDB } from '@/services/db/init' +import { getRegionUid } from '@/utils/env' import { NextRequest } from 'next/server' export const dynamic = 'force-dynamic' @@ -32,16 +33,19 @@ export async function GET(req: NextRequest) { isDeleted: true } }, + regionUid: true, isDeleted: true, isPublic: true, } } } }) + const regionUid = getRegionUid() if (!template || !(template.templateRepository.organization.uid === payload.organizationUid || template.templateRepository.isPublic === true - ) + ) || + template.templateRepository.regionUid !== regionUid ) { return jsonRes({ code: 404, diff --git a/frontend/providers/devbox/app/api/templateRepository/template/list/route.ts b/frontend/providers/devbox/app/api/templateRepository/template/list/route.ts index 7c8026b88a7..de9c533c325 100644 --- a/frontend/providers/devbox/app/api/templateRepository/template/list/route.ts +++ b/frontend/providers/devbox/app/api/templateRepository/template/list/route.ts @@ -1,6 +1,7 @@ import { authSessionWithJWT } from '@/services/backend/auth' import { jsonRes } from '@/services/backend/response' import { devboxDB } from '@/services/db/init' +import { getRegionUid } from '@/utils/env' import { NextRequest } from 'next/server' export const dynamic = 'force-dynamic' @@ -18,12 +19,13 @@ export async function GET(req: NextRequest) { const templateRepository = await devboxDB.templateRepository.findUnique({ where: { uid, - isDeleted: false + isDeleted: false, + regionUid: getRegionUid() }, select: { name: true, uid: true, - organization:{ + organization: { select: { uid: true, isDeleted: true @@ -45,18 +47,26 @@ export async function GET(req: NextRequest) { } } }) - if (templateRepository && - !( - (templateRepository.organization.isDeleted === false - && templateRepository.organization.uid === payload.organizationUid) - || templateRepository.isPublic === true - )) { + + if (!templateRepository) { + return jsonRes({ + data: { + templateList: [] + } + }) + } + const templateList = templateRepository.templates + if (!( + (templateRepository.organization.isDeleted === false + && templateRepository.organization.uid === payload.organizationUid) + || templateRepository.isPublic === true + )) { return jsonRes({ - code: 404, - error: 'Template is not found' + data: { + templateList + } }) } - const templateList = templateRepository?.templates || [] return jsonRes({ data: { templateList diff --git a/frontend/providers/devbox/app/api/templateRepository/update/route.ts b/frontend/providers/devbox/app/api/templateRepository/update/route.ts index fe2a2d5d543..33d69f446f1 100644 --- a/frontend/providers/devbox/app/api/templateRepository/update/route.ts +++ b/frontend/providers/devbox/app/api/templateRepository/update/route.ts @@ -2,6 +2,7 @@ import { TagType } from "@/prisma/generated/client" import { authSessionWithJWT } from "@/services/backend/auth" import { jsonRes } from "@/services/backend/response" import { devboxDB } from "@/services/db/init" +import { getRegionUid } from "@/utils/env" import { updateTemplateRepositorySchema } from "@/utils/vaildate" import { NextRequest } from "next/server" import { z } from "zod" @@ -66,7 +67,8 @@ export async function POST(req: NextRequest) { ) const isExist = await devboxDB.templateRepository.findUnique({ where: { - isDeleted_name: { + isDeleted_regionUid_name: { + regionUid: getRegionUid(), name: query.templateRepositoryName, isDeleted: false, } diff --git a/frontend/providers/devbox/app/api/templateRepository/withTemplate/create/route.ts b/frontend/providers/devbox/app/api/templateRepository/withTemplate/create/route.ts index 7e611f4675b..7a7c74114d3 100644 --- a/frontend/providers/devbox/app/api/templateRepository/withTemplate/create/route.ts +++ b/frontend/providers/devbox/app/api/templateRepository/withTemplate/create/route.ts @@ -6,6 +6,7 @@ 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 { createTemplateRepositorySchema } from "@/utils/vaildate" import { NextRequest } from "next/server" import { z } from "zod" @@ -58,7 +59,8 @@ export async function POST(req: NextRequest) { } const isExist = await devboxDB.templateRepository.findUnique({ where: { - isDeleted_name: { + isDeleted_regionUid_name: { + regionUid: getRegionUid(), name: query.templateRepositoryName, isDeleted: false }, @@ -139,6 +141,7 @@ export async function POST(req: NextRequest) { parentUid: devboxBody.spec.templateID, } }, + regionUid: getRegionUid(), organizationUid: payload.organizationUid, iconId: origionalTemplate?.templateRepository.iconId, kind: TemplateRepositoryKind.CUSTOM, diff --git a/frontend/providers/devbox/app/api/templateRepository/withTemplate/update/route.ts b/frontend/providers/devbox/app/api/templateRepository/withTemplate/update/route.ts index 44c00157fc5..3f0275e8a59 100644 --- a/frontend/providers/devbox/app/api/templateRepository/withTemplate/update/route.ts +++ b/frontend/providers/devbox/app/api/templateRepository/withTemplate/update/route.ts @@ -6,6 +6,7 @@ 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 { updateTemplateSchema } from '@/utils/vaildate' import { NextRequest } from 'next/server' import { z } from 'zod' @@ -75,6 +76,7 @@ export async function POST(req: NextRequest) { where: { uid: query.templateRepositoryUid, organizationUid: payload.organizationUid, + regionUid: getRegionUid() }, select: { uid: true, @@ -83,6 +85,7 @@ export async function POST(req: NextRequest) { select: { tagUid: true } + }, templates: true } @@ -130,6 +133,7 @@ export async function POST(req: NextRequest) { await tx.templateRepository.update({ where: { organizationUid: organization.uid, + regionUid: getRegionUid(), uid: templateRepository.uid, }, data: { diff --git a/frontend/providers/devbox/prisma/migrations/20250103095011_region_update/migration.sql b/frontend/providers/devbox/prisma/migrations/20250103095011_region_update/migration.sql new file mode 100644 index 00000000000..2031ff0da3e --- /dev/null +++ b/frontend/providers/devbox/prisma/migrations/20250103095011_region_update/migration.sql @@ -0,0 +1,29 @@ +/* + Warnings: + + - A unique constraint covering the columns `[isDeleted,regionUid,name]` on the table `TemplateRepository` will be added. If there are existing duplicate values, this will fail. + - Added the required column `regionUid` to the `TemplateRepository` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropIndex +DROP INDEX "TemplateRepository_isDeleted_name_key"; + +-- AlterTable +ALTER TABLE "TemplateRepository" ADD COLUMN "regionUid" STRING NOT NULL; + +-- CreateIndex +CREATE INDEX "TemplateRepository_isDeleted_createdAt_idx" ON "TemplateRepository"("isDeleted", "createdAt"); + +-- CreateIndex +CREATE UNIQUE INDEX "TemplateRepository_isDeleted_regionUid_name_key" ON "TemplateRepository"("isDeleted", "regionUid", "name"); + +-- AlterTable +ALTER TABLE "Template" DROP COLUMN "templateRepositoryUid"; +ALTER TABLE "Template" ADD COLUMN "templateRepositoryUid" UUID NOT NULL; + +-- AlterTable +ALTER TABLE "TemplateRepository" DROP COLUMN "organizationUid"; +ALTER TABLE "TemplateRepository" ADD COLUMN "organizationUid" UUID NOT NULL; + +-- CreateIndex +CREATE UNIQUE INDEX "Template_isDeleted_templateRepositoryUid_name_key" ON "Template"("isDeleted", "templateRepositoryUid", "name"); \ No newline at end of file diff --git a/frontend/providers/devbox/prisma/schema.prisma b/frontend/providers/devbox/prisma/schema.prisma index c64a71ebedb..2f4a5c56b2c 100644 --- a/frontend/providers/devbox/prisma/schema.prisma +++ b/frontend/providers/devbox/prisma/schema.prisma @@ -12,7 +12,7 @@ datasource db { model User { uid String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid - regionUid String + regionUid String namespaceId String deletedAt DateTime? @db.Timestamptz(3) createdAt DateTime @default(now()) @db.Timestamptz(3) @@ -50,31 +50,32 @@ model UserOrganization { } model TemplateRepository { - uid String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid - deletedAt DateTime? @db.Timestamptz(3) - createdAt DateTime @default(now()) @db.Timestamptz(3) - updatedAt DateTime @updatedAt @db.Timestamptz(3) - name String + uid String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + deletedAt DateTime? @db.Timestamptz(3) + createdAt DateTime @default(now()) @db.Timestamptz(3) + updatedAt DateTime @updatedAt @db.Timestamptz(3) + name String // hzh.hub.sealos.run/orgNanoid/templateRepositoryName:templateName - description String? - kind TemplateRepositoryKind - organizationUid String - isPublic Boolean @default(false) - templates Template[] - iconId String? - organization Organization @relation(fields: [organizationUid], references: [uid]) - isDeleted Boolean? @default(false) - + description String? + kind TemplateRepositoryKind + organizationUid String @db.Uuid + isPublic Boolean @default(false) + templates Template[] + iconId String? + organization Organization @relation(fields: [organizationUid], references: [uid]) + isDeleted Boolean? @default(false) + regionUid String templateRepositoryTags TemplateRepositoryTag[] - @@unique([isDeleted, name]) + @@unique([isDeleted, regionUid, name]) @@index([isDeleted, isPublic]) + @@index([isDeleted, createdAt]) } model Template { uid String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid name String - templateRepositoryUid String + templateRepositoryUid String @db.Uuid devboxReleaseImage String? image String config String // json @@ -93,7 +94,7 @@ model Template { model Tag { uid String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid - type TagType @default(OFFICIAL_CONTENT) + type TagType @default(OFFICIAL_CONTENT) name String zhName String? enName String? @@ -121,4 +122,4 @@ enum TagType { PROGRAMMING_LANGUAGE USE_CASE OFFICIAL_CONTENT -} \ No newline at end of file +} diff --git a/frontend/providers/devbox/utils/env.ts b/frontend/providers/devbox/utils/env.ts new file mode 100644 index 00000000000..4acd5876183 --- /dev/null +++ b/frontend/providers/devbox/utils/env.ts @@ -0,0 +1 @@ +export const getRegionUid = () => process.env.REGION_UID || '' \ No newline at end of file