Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions frontend/providers/devbox/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ ACCOUNT_URL=
# in dev: postgresql://username:password@127.0.0.1:26257/devboxdb?connection_limit=50&pool_timeout=20
# in prod: postgresql://username:password@cockroachdb-global.cockroach-operator-system:26257/devboxdb?connection_limit=50&pool_timeout=20
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
Expand Down
33 changes: 23 additions & 10 deletions frontend/providers/devbox/deploy/manifests/deploy.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -41,23 +41,34 @@ spec:
env:
- name: DATABASE_URL
value: {{ .databaseUrl }} # -nsealos cm desktop-frontend-config ->database->global + devbox
- name: DATABASE_PROVIDER
value: "{{ default "cockroachdb" .databaseProvider }}"
command:
- sh
- -c
args:
- |-
cd /app/providers/devbox
MIGRATION="20260125094114_add_template_repository_kind"
HAS_MIGRATION=$(psql "$DATABASE_URL" -tAc "SELECT 1 FROM _prisma_migrations WHERE migration_name='${MIGRATION}' AND rolled_back_at IS NULL LIMIT 1;")
if [ "$HAS_MIGRATION" = "1" ]; then
prisma migrate deploy
exit 0
DB_PROVIDER="${DATABASE_PROVIDER:-cockroachdb}"
SCHEMA_PATH="./prisma/cockroach/schema.prisma"
if [ "$DB_PROVIDER" = "postgresql" ] || [ "$DB_PROVIDER" = "postgres" ] || [ "$DB_PROVIDER" = "pg" ]; then
SCHEMA_PATH="./prisma/postgresql/schema.prisma"
HAS_UUID_FN=$(psql "$DATABASE_URL" -tAc "SELECT 1 FROM pg_proc WHERE proname='gen_random_uuid' LIMIT 1;")
if [ "$HAS_UUID_FN" != "1" ]; then
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -c 'CREATE OR REPLACE FUNCTION public.gen_random_uuid() RETURNS uuid LANGUAGE SQL VOLATILE AS $func$ SELECT md5(random()::text || clock_timestamp()::text || txid_current()::text)::uuid; $func$;'
fi
MIGRATION="20260125094114_add_template_repository_kind"
HAS_MIGRATION=$(psql "$DATABASE_URL" -tAc "SELECT 1 FROM _prisma_migrations WHERE migration_name='${MIGRATION}' AND rolled_back_at IS NULL LIMIT 1;")
if [ "$HAS_MIGRATION" = "1" ]; then
prisma migrate deploy --schema "$SCHEMA_PATH"
exit 0
fi
HAS_ENUM=$(psql "$DATABASE_URL" -tAc "SELECT 1 FROM pg_enum e JOIN pg_type t ON t.oid = e.enumtypid WHERE t.typname IN ('TemplateRepositoryKind','template_repository_kind') AND e.enumlabel = 'SERVICE' LIMIT 1;")
if [ "$HAS_ENUM" = "1" ]; then
prisma migrate resolve --schema "$SCHEMA_PATH" --applied "$MIGRATION"
fi
fi
HAS_ENUM=$(psql "$DATABASE_URL" -tAc "SELECT 1 FROM pg_enum e JOIN pg_type t ON t.oid = e.enumtypid WHERE t.typname IN ('TemplateRepositoryKind','template_repository_kind') AND e.enumlabel = 'SERVICE' LIMIT 1;")
if [ "$HAS_ENUM" = "1" ]; then
prisma migrate resolve --applied "$MIGRATION"
fi
prisma migrate deploy
prisma migrate deploy --schema "$SCHEMA_PATH"
containers:
- name: devbox-frontend
env:
Expand Down Expand Up @@ -103,6 +114,8 @@ spec:
value: {{ .regionUid }} # -nsealos cm desktop-frontend-config ->regionUid
- name: DATABASE_URL
value: {{ .databaseUrl }} # -nsealos cm desktop-frontend-config ->database->global + devbox
- name: DATABASE_PROVIDER
value: "{{ default "cockroachdb" .databaseProvider }}"
- name: ENABLE_IMPORT_FEATURE
value: 'false'
- name: ENABLE_WEBIDE_FEATURE
Expand Down
5 changes: 4 additions & 1 deletion frontend/providers/devbox/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
"ts-lint": "tsc --noEmit",
"link-sdk": "rm -rf node_modules/sealos-desktop-sdk && yalc link sealos-desktop-sdk",
"unlink-sdk": "yalc remove --all && pnpm install sealos-desktop-sdk",
"gen-client": "prisma generate --schema ./prisma/schema.prisma",
"gen-client": "prisma generate --schema ./prisma/cockroach/schema.prisma && prisma generate --schema ./prisma/postgresql/schema.prisma",
"migrate:deploy:cockroach": "prisma migrate deploy --schema ./prisma/cockroach/schema.prisma",
"migrate:deploy:postgresql": "prisma migrate deploy --schema ./prisma/postgresql/schema.prisma",
"migrate:resolve:postgresql": "prisma migrate resolve --schema ./prisma/postgresql/schema.prisma",
"postinstall": "pnpm gen-client",
"ui": "pnpm dlx shadcn@latest add"
},
Expand Down
20 changes: 20 additions & 0 deletions frontend/providers/devbox/prisma/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Prisma Layout (No Default Entry)

To avoid accidental misuse, this repo intentionally does **not** keep:

- `prisma/schema.prisma`
- `prisma/migrations/*`

Always use an explicit schema:

- CockroachDB
- schema: `prisma/cockroach/schema.prisma`
- deploy: `pnpm migrate:deploy:cockroach`
- PostgreSQL
- schema: `prisma/postgresql/schema.prisma`
- deploy: `pnpm migrate:deploy:postgresql`

Runtime client routing is controlled by `DATABASE_PROVIDER`:

- `cockroachdb` (default)
- `postgresql`
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
generator globalClient {
provider = "prisma-client-js"
output = "./generated/client"
output = "../generated/client"
binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
-- CreateEnum
CREATE TYPE "TemplateRepositoryKind" AS ENUM ('FRAMEWORK', 'OS', 'LANGUAGE', 'CUSTOM');

-- Ensure gen_random_uuid() exists on PostgreSQL variants (e.g. KingbaseES)
CREATE OR REPLACE FUNCTION public.gen_random_uuid()
RETURNS uuid
LANGUAGE SQL
VOLATILE
AS $func$
SELECT md5(random()::text || clock_timestamp()::text || txid_current()::text)::uuid;
$func$;

-- CreateTable
CREATE TABLE "User" (
"uid" UUID NOT NULL DEFAULT gen_random_uuid(),
"regionUid" TEXT NOT NULL,
"namespaceId" TEXT NOT NULL,
"deletedAt" TIMESTAMPTZ(3),
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
"isDeleted" BOOL DEFAULT false,

CONSTRAINT "User_pkey" PRIMARY KEY ("uid")
);

-- CreateTable
CREATE TABLE "Organization" (
"uid" UUID NOT NULL DEFAULT gen_random_uuid(),
"id" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
"deletedAt" TIMESTAMPTZ(3),
"isDeleted" BOOL DEFAULT false,
"name" TEXT NOT NULL,

CONSTRAINT "Organization_pkey" PRIMARY KEY ("uid")
);

-- CreateTable
CREATE TABLE "UserOrganization" (
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
"userUid" UUID NOT NULL,
"organizationUid" UUID NOT NULL,

CONSTRAINT "UserOrganization_pkey" PRIMARY KEY ("organizationUid","userUid")
);

-- CreateTable
CREATE TABLE "TemplateRepository" (
"uid" UUID NOT NULL DEFAULT gen_random_uuid(),
"deletedAt" TIMESTAMPTZ(3),
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"kind" "TemplateRepositoryKind" NOT NULL,
"organizationUid" TEXT NOT NULL,
"isPublic" BOOL NOT NULL DEFAULT false,
"iconId" TEXT,
"isDeleted" BOOL DEFAULT false,

CONSTRAINT "TemplateRepository_pkey" PRIMARY KEY ("uid")
);

-- CreateTable
CREATE TABLE "Template" (
"uid" UUID NOT NULL DEFAULT gen_random_uuid(),
"name" TEXT NOT NULL,
"templateRepositoryUid" TEXT NOT NULL,
"devboxReleaseImage" TEXT,
"image" TEXT NOT NULL,
"config" TEXT NOT NULL,
"deletedAt" TIMESTAMPTZ(3),
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
"parentUid" UUID,
"isDeleted" BOOL DEFAULT false,

CONSTRAINT "Template_pkey" PRIMARY KEY ("uid")
);

-- CreateTable
CREATE TABLE "Tag" (
"uid" UUID NOT NULL DEFAULT gen_random_uuid(),
"name" TEXT NOT NULL,
"zhName" TEXT,
"enName" TEXT,

CONSTRAINT "Tag_pkey" PRIMARY KEY ("uid")
);

-- CreateTable
CREATE TABLE "TemplateRepositoryTag" (
"templateRepositoryUid" UUID NOT NULL,
"tagUid" UUID NOT NULL,

CONSTRAINT "TemplateRepositoryTag_pkey" PRIMARY KEY ("templateRepositoryUid","tagUid")
);

-- CreateIndex
CREATE UNIQUE INDEX "User_isDeleted_regionUid_namespaceId_key" ON "User"("isDeleted", "regionUid", "namespaceId");

-- CreateIndex
CREATE UNIQUE INDEX "Organization_id_key" ON "Organization"("id");

-- CreateIndex
CREATE INDEX "UserOrganization_userUid_idx" ON "UserOrganization"("userUid");

-- CreateIndex
CREATE INDEX "UserOrganization_createdAt_idx" ON "UserOrganization"("createdAt");

-- CreateIndex
CREATE INDEX "TemplateRepository_isDeleted_isPublic_idx" ON "TemplateRepository"("isDeleted", "isPublic");

-- CreateIndex
CREATE UNIQUE INDEX "TemplateRepository_isDeleted_name_key" ON "TemplateRepository"("isDeleted", "name");

-- CreateIndex
CREATE UNIQUE INDEX "Template_isDeleted_templateRepositoryUid_name_key" ON "Template"("isDeleted", "templateRepositoryUid", "name");

-- CreateIndex
CREATE INDEX "TemplateRepositoryTag_tagUid_idx" ON "TemplateRepositoryTag"("tagUid");
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- CreateEnum
CREATE TYPE "TagType" AS ENUM ('PROGRAMMING_LANGUAGE', 'USE_CASE', 'OFFICIAL_CONTENT');

-- AlterTable
ALTER TABLE "Tag" ADD COLUMN "type" "TagType" NOT NULL DEFAULT 'OFFICIAL_CONTENT';
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-- upgrade region
-- DropIndex
DROP INDEX "TemplateRepository_isDeleted_name_key";

-- add regionUid column
ALTER TABLE "TemplateRepository"
ADD COLUMN "regionUid" TEXT NOT NULL default '00000000-0000-0000-0000-000000000000';
ALTER TABLE "TemplateRepository"
ALTER COLUMN "regionUid" DROP DEFAULT;

-- 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 public."TemplateRepository" alter column "organizationUid" type uuid using "organizationUid"::uuid;
-- AlterTable
DROP INDEX "Template_isDeleted_templateRepositoryUid_name_key";
ALTER TABLE "Template" ALTER COLUMN "templateRepositoryUid" type uuid using "templateRepositoryUid"::uuid;
CREATE UNIQUE INDEX "Template_isDeleted_templateRepositoryUid_name_key" ON "Template" ("isDeleted", "templateRepositoryUid", "name");
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- Add usage count field to TemplateRepository
ALTER TABLE "TemplateRepository" ADD COLUMN "usageCount" INT4 NOT NULL DEFAULT 0;

-- Create index on usageCount for better query performance
CREATE INDEX "TemplateRepository_isDeleted_usageCount_idx" ON "TemplateRepository"("isDeleted", "usageCount");
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterEnum
ALTER TYPE "TemplateRepositoryKind" ADD VALUE 'SERVICE';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"
128 changes: 128 additions & 0 deletions frontend/providers/devbox/prisma/postgresql/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
generator globalClient {
provider = "prisma-client-js"
output = "../generated/postgresql-client"
binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
relationMode = "prisma"
}

model User {
uid String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
regionUid String
namespaceId String
deletedAt DateTime? @db.Timestamptz(3)
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
isDeleted Boolean? @default(false)
userOrganizations UserOrganization[]

@@unique([isDeleted, regionUid, namespaceId])
}

model Organization {
uid String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
id String @unique //nanoid
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
deletedAt DateTime? @db.Timestamptz(3)
isDeleted Boolean? @default(false)
name String
userOrganizations UserOrganization[]
templateRepositories TemplateRepository[]
}

model UserOrganization {
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
userUid String @db.Uuid
organizationUid String @db.Uuid
organization Organization @relation(fields: [organizationUid], references: [uid])
user User @relation(fields: [userUid], references: [uid])
// role OrganizationRole // rbac

@@id([organizationUid, userUid])
@@index([userUid])
@@index([createdAt])
}

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
// hzh.hub.sealos.run/orgNanoid/templateRepositoryName:templateName
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[]
usageCount Int @default(0)

@@unique([isDeleted, regionUid, name])
@@index([isDeleted, isPublic])
@@index([isDeleted, createdAt])
@@index([isDeleted, usageCount])
}

model Template {
uid String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
name String
templateRepositoryUid String @db.Uuid
devboxReleaseImage String?
image String
config String // json
deletedAt DateTime? @db.Timestamptz(3)
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
parentUid String? @db.Uuid
isDeleted Boolean? @default(false)
parent Template? @relation("Template", fields: [parentUid], references: [uid], onDelete: Restrict, onUpdate: Restrict)
children Template[] @relation("Template")

templateRepository TemplateRepository @relation(fields: [templateRepositoryUid], references: [uid])

@@unique([isDeleted, templateRepositoryUid, name])
}

model Tag {
uid String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
type TagType @default(OFFICIAL_CONTENT)
name String
zhName String?
enName String?
templateRepositoryTags TemplateRepositoryTag[]
}

model TemplateRepositoryTag {
templateRepositoryUid String @db.Uuid
tagUid String @db.Uuid
templateRepository TemplateRepository @relation(fields: [templateRepositoryUid], references: [uid])
tag Tag @relation(fields: [tagUid], references: [uid])

@@id([templateRepositoryUid, tagUid])
@@index([tagUid])
}

enum TemplateRepositoryKind {
FRAMEWORK
OS
LANGUAGE
SERVICE
CUSTOM
}

enum TagType {
OFFICIAL_CONTENT
PROGRAMMING_LANGUAGE
USE_CASE
}
Loading
Loading