Files
phptits 851ccf1d7f v2 platform runtime (docs 22 Phase 2/4/7 + 17): Project/Environment + Config + Storage + Observability
- Project/Environment model with default environments (dev/staging/prod)
- Config Runtime: cascade resolution (platform→org→project→env), secrets via vault
- Storage Runtime: local provider, sha256 dedup, integrity check, file list/upload/delete
- Observability: execution query, per-action summary (success rate, avg, p95), health
- Realtime SSE: workspace event stream endpoint (GET /api/v1/v2/realtime/events)
- Phase 1 acceptance: core.health.check, core.user.create, core.user.list
- Actions: core.project.*, core.config.*, core.file.*, core.user.* (62 total)
2026-09-11 21:11:07 +00:00

831 lines
24 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// DevOS v1 — PostgreSQL + Prisma schema
// 设计原则(04-DATABASE-SCHEMA.md):
// - resources 表承担统一身份/发现/图谱公共字段;
// - 具备大量运行状态的资源使用类型化领域表(servers/websites/...),通过 resourceId 与 resources 一对一关联;
// - 资源关系独立成表(resource_relations),图谱可演进而非堆在一张大表;
// - 凭据密文独立存储,绝不与普通元数据混放。
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// ============ 枚举 ============
enum ResourceType {
SERVER
DOMAIN
WEBSITE
REPOSITORY
CERTIFICATE
DATABASE
CONTAINER
EMAIL_ACCOUNT
PROJECT
TASK
NOTE
DOCUMENT
BACKUP
DEPLOYMENT
MONITOR
CREDENTIAL
AUTOMATION
AI_RUN
}
enum ResourceStatus {
PENDING
ACTIVE
ERROR
OFFLINE
ARCHIVED
UNKNOWN
}
enum WorkspaceRole {
OWNER
ADMIN
MEMBER
VIEWER
}
enum RiskLevel {
LOW
MEDIUM
HIGH
CRITICAL
}
enum ActorType {
USER
SYSTEM
AGENT
MCP
}
enum JobStatus {
PENDING
RUNNING
SUCCEEDED
FAILED
CANCELLED
DEAD
}
enum DeploymentStatus {
QUEUED
RUNNING
SUCCEEDED
FAILED
CANCELLED
}
enum CredentialType {
SSH_PASSWORD
SSH_KEY
API_TOKEN
DNS_PROVIDER
GIT_TOKEN
}
enum ServerAuthType {
PASSWORD
SSH_KEY
}
enum TriggerType {
MANUAL
CRON
EVENT
WEBHOOK
}
enum AiRunStatus {
PLANNING
AWAITING_APPROVAL
RUNNING
SUCCEEDED
FAILED
REJECTED
}
// ============ 身份与工作区 ============
/// # 数据库表 users:平台用户
model User {
/// # 数据库字段 邮箱(登录名,唯一)
id String @id @default(uuid())
email String @unique
/// # 数据库字段 密码哈希(argon2id/scrypt,绝不明文)
passwordHash String
name String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
memberships WorkspaceMember[]
@@map("users")
}
/// # 数据库表 workspaces:租户/安全边界
model Workspace {
id String @id @default(uuid())
name String
slug String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
members WorkspaceMember[]
resources Resource[]
@@map("workspaces")
}
/// # 数据库表 workspace_members:用户-工作区关系与角色
model WorkspaceMember {
id String @id @default(uuid())
workspaceId String
userId String
role WorkspaceRole @default(MEMBER)
createdAt DateTime @default(now())
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([workspaceId, userId])
@@index([userId])
@@map("workspace_members")
}
// ============ 统一资源模型 ============
/// # 数据库表 resources:统一资源身份/发现/图谱公共字段
model Resource {
id String @id @default(uuid())
workspaceId String
type ResourceType
name String
provider String?
status ResourceStatus @default(PENDING)
/// # 数据库字段 扩展元数据(敏感信息禁止写入)
metadata Json @default("{}")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
outgoingRelations ResourceRelation[] @relation("source")
incomingRelations ResourceRelation[] @relation("target")
credential Credential?
server Server?
container Container?
website Website?
domain Domain?
deployment Deployment?
monitor Monitor?
automation Automation?
aiRun AiRun?
gitConnection GitConnection?
repository Repository?
task Task?
note Note?
certificate Certificate?
@@index([workspaceId])
@@index([type, status])
@@index([workspaceId, type])
@@map("resources")
}
/// # 数据库表 resource_relations:资源关系图(有向边)
model ResourceRelation {
id String @id @default(uuid())
workspaceId String
sourceId String
targetId String
/// # 数据库字段 关系类型,如 deployed_on / has_domain / resolves_to / monitors
relationType String
metadata Json @default("{}")
createdAt DateTime @default(now())
source Resource @relation("source", fields: [sourceId], references: [id], onDelete: Cascade)
target Resource @relation("target", fields: [targetId], references: [id], onDelete: Cascade)
@@unique([sourceId, targetId, relationType])
@@index([sourceId])
@@index([targetId])
@@index([workspaceId])
@@map("resource_relations")
}
// ============ 凭据保险库 ============
/// # 数据库表 credentials:加密凭据(信封加密后的密文,禁止记录明文)
model Credential {
id String @id @default(uuid())
workspaceId String
resourceId String? @unique
name String
type CredentialType
provider String?
/// # 数据库字段 AES-256-GCM 密文 JSON(iv/tag/ciphertext,base64)
encryptedData String
metadata Json @default("{}")
lastUsedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resource Resource? @relation(fields: [resourceId], references: [id], onDelete: SetNull)
@@index([workspaceId])
@@map("credentials")
}
// ============ 审计 / 事件 / 任务 ============
/// # 数据库表 audit_logs:审计日志(每个变更必写)
model AuditLog {
id String @id @default(uuid())
workspaceId String
actorType ActorType
actorId String?
action String
resourceType String?
resourceId String?
riskLevel RiskLevel @default(LOW)
status String @default("SUCCESS")
requestId String?
ip String?
metadata Json @default("{}")
createdAt DateTime @default(now())
@@index([workspaceId, createdAt])
@@index([resourceType, resourceId])
@@map("audit_logs")
}
/// # 数据库表 events:领域事件(持久化 + 分发)
model EventRecord {
id String @id @default(uuid())
workspaceId String
eventType String
resourceId String?
payload Json @default("{}")
correlationId String?
createdAt DateTime @default(now())
@@index([eventType, createdAt])
@@index([workspaceId, createdAt])
@@map("events")
}
/// # 数据库表 jobs:异步任务(持久化状态机 + Redis 派发)
model Job {
id String @id @default(uuid())
workspaceId String
type String
status JobStatus @default(PENDING)
payload Json @default("{}")
result Json?
error String?
attempts Int @default(0)
maxAttempts Int @default(3)
runAt DateTime @default(now())
startedAt DateTime?
finishedAt DateTime?
correlationId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status, runAt])
@@index([workspaceId])
@@map("jobs")
}
// ============ 基础设施领域表 ============
/// # 数据库表 servers:受管计算目标(SSH)
model Server {
id String @id @default(uuid())
workspaceId String
resourceId String @unique
name String
host String
port Int @default(22)
username String
authType ServerAuthType @default(SSH_KEY)
provider String @default("ssh")
credentialId String?
osInfo String?
labels Json @default("{}")
lastSeenAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resource Resource @relation(fields: [resourceId], references: [id], onDelete: Cascade)
containers Container[]
websites Website[]
monitors Monitor[]
@@index([workspaceId])
@@map("servers")
}
/// # 数据库表 containers:Docker 容器发现结果
model Container {
id String @id @default(uuid())
workspaceId String
resourceId String @unique
serverId String
containerId String
name String
image String
state String
ports Json @default("{}")
labels Json @default("{}")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resource Resource @relation(fields: [resourceId], references: [id], onDelete: Cascade)
server Server @relation(fields: [serverId], references: [id], onDelete: Cascade)
@@index([serverId])
@@index([workspaceId])
@@map("containers")
}
/// # 数据库表 websites:可部署应用资源
model Website {
id String @id @default(uuid())
workspaceId String
resourceId String @unique
name String
primaryDomain String?
serverId String?
repositoryUrl String?
/// # 数据库字段 关联仓库(Git Provider 凭据克隆;优先于 repositoryUrl)
repositoryId String?
/// # 数据库字段 部署环境:development / staging / production
environment String @default("production")
/// # 数据库字段 部署类型:DOCKER / STATIC / NODE
deployType String @default("DOCKER")
/// # 数据库字段 容器对外端口
port Int @default(80)
healthPath String @default("/")
env Json @default("{}")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resource Resource @relation(fields: [resourceId], references: [id], onDelete: Cascade)
server Server? @relation(fields: [serverId], references: [id], onDelete: SetNull)
deployments Deployment[]
monitors Monitor[]
repository Repository? @relation(fields: [repositoryId], references: [id])
@@index([workspaceId])
@@map("websites")
}
/// # 数据库表 domains:域名与 DNS 提供商绑定
model Domain {
id String @id @default(uuid())
workspaceId String
resourceId String @unique
name String
provider String @default("MANUAL")
credentialId String?
expiresAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resource Resource @relation(fields: [resourceId], references: [id], onDelete: Cascade)
records DomainRecord[]
@@index([workspaceId])
@@map("domains")
}
/// # 数据库表 domain_records:域名记录(手动 provider 时为本地事实源)
model DomainRecord {
id String @id @default(uuid())
workspaceId String
domainId String
type String
name String
value String
ttl Int @default(300)
proxied Boolean @default(false)
providerRecordId String?
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
domain Domain @relation(fields: [domainId], references: [id], onDelete: Cascade)
@@unique([domainId, type, name, value])
@@index([workspaceId])
@@map("domain_records")
}
/// # 数据库表 deployments:部署执行记录
model Deployment {
id String @id @default(uuid())
workspaceId String
resourceId String @unique
websiteId String
status DeploymentStatus @default(QUEUED)
trigger String @default("MANUAL")
progress Int @default(0)
/// # 数据库字段 失败原因(截断存储,不含敏感信息)
error String?
commit String?
/// # 数据库字段 部署环境:development / staging / production
environment String?
/// # 数据库字段 构建策略:DOCKERFILE / STATIC_COPY
buildStrategy String?
/// # 数据库字段 部署策略:DOCKER_CONTAINER / STATIC_DIR
deployStrategy String?
/// # 数据库字段 健康验证证据 {ok,status,latencyMs,checkedAt}
verification Json?
/// # 数据库字段 部署参数快照(分支、镜像 tag 等)
config Json @default("{}")
createdAt DateTime @default(now())
finishedAt DateTime?
resource Resource @relation(fields: [resourceId], references: [id], onDelete: Cascade)
website Website @relation(fields: [websiteId], references: [id], onDelete: Cascade)
logs DeploymentLog[]
@@index([websiteId, createdAt])
@@index([workspaceId, createdAt])
@@map("deployments")
}
/// # 数据库表 deployment_logs:部署日志行(顺序)
model DeploymentLog {
id String @id @default(uuid())
deploymentId String
seq Int
stream String @default("stdout")
line String
createdAt DateTime @default(now())
deployment Deployment @relation(fields: [deploymentId], references: [id], onDelete: Cascade)
@@index([deploymentId, seq])
@@map("deployment_logs")
}
/// # 数据库表 monitors:健康检查配置
model Monitor {
id String @id @default(uuid())
workspaceId String
resourceId String @unique
websiteId String?
serverId String?
name String
/// # 数据库字段 检查类型:HTTP / TCP
kind String @default("HTTP")
target String
intervalSec Int @default(60)
timeoutMs Int @default(10000)
enabled Boolean @default(true)
lastStatus String @default("UNKNOWN")
consecutiveFailures Int @default(0)
lastCheckedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resource Resource @relation(fields: [resourceId], references: [id], onDelete: Cascade)
website Website? @relation(fields: [websiteId], references: [id], onDelete: Cascade)
server Server? @relation(fields: [serverId], references: [id], onDelete: Cascade)
results MonitorResult[]
@@index([enabled, lastCheckedAt])
@@index([workspaceId])
@@map("monitors")
}
/// # 数据库表 monitor_results:监控结果历史
model MonitorResult {
id String @id @default(uuid())
monitorId String
ok Boolean
status String?
latencyMs Int?
error String?
createdAt DateTime @default(now())
monitor Monitor @relation(fields: [monitorId], references: [id], onDelete: Cascade)
@@index([monitorId, createdAt])
@@map("monitor_results")
}
// ============ 自动化 ============
/// # 数据库表 automations:Trigger → Condition → Action 工作流
model Automation {
id String @id @default(uuid())
workspaceId String
resourceId String @unique
name String
enabled Boolean @default(false)
triggerType TriggerType
/// # 数据库字段 触发器配置(cron 表达式 / 事件名 / webhook token)
triggerConfig Json @default("{}")
/// # 数据库字段 条件列表 [{path,op,value}]
conditions Json @default("[]")
/// # 数据库字段 动作列表 [{type,args,timeoutMs,retries}]
actions Json @default("[]")
lastTriggeredAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resource Resource @relation(fields: [resourceId], references: [id], onDelete: Cascade)
runs AutomationRun[]
@@index([workspaceId])
@@index([triggerType, enabled])
@@map("automations")
}
/// # 数据库表 automation_runs:自动化执行历史
model AutomationRun {
id String @id @default(uuid())
automationId String
workspaceId String
status String @default("RUNNING")
trigger Json @default("{}")
stepIndex Int @default(0)
logs Json @default("[]")
error String?
correlationId String?
startedAt DateTime @default(now())
finishedAt DateTime?
automation Automation @relation(fields: [automationId], references: [id], onDelete: Cascade)
@@index([automationId, startedAt])
@@map("automation_runs")
}
// ============ AI Gateway ============
/// # 数据库表 ai_runs:AI 运行(计划→风控→审批→执行→验证)
model AiRun {
id String @id @default(uuid())
workspaceId String
resourceId String @unique
objective String
status AiRunStatus @default(PLANNING)
/// # 数据库字段 来源:UI / CHAT / MCP / AUTOMATION
source String @default("UI")
riskLevel RiskLevel @default(LOW)
/// # 数据库字段 计划步骤 [{tool,args,reason,risk}]
plan Json @default("[]")
/// # 数据库字段 执行历史 [{tool,args,result,ok}]
toolCalls Json @default("[]")
/// # 数据库字段 验证结果 {ok, checks[]}
verification Json?
result Json?
error String?
approvedBy String?
approvedAt DateTime?
correlationId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resource Resource @relation(fields: [resourceId], references: [id], onDelete: Cascade)
@@index([workspaceId, createdAt])
@@map("ai_runs")
}
// ============ Phase 2:Git / 生产力 / 通知 / 证书 ============
/// # 数据库表 git_connections:Git Provider 连接(GitHub/GitLab/Gitea + 凭据引用)
model GitConnection {
id String @id @default(uuid())
workspaceId String
resourceId String @unique
name String
/// # 数据库字段 提供商:GITHUB / GITLAB / GITEA
provider String
/// # 数据库字段 自建实例 API 基址(Gitea/GitLab 自托管时填写)
apiBase String?
credentialId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resource Resource @relation(fields: [resourceId], references: [id], onDelete: Cascade)
repositories Repository[]
@@index([workspaceId])
}
/// # 数据库表 repositories:外部代码仓库聚合(不实现 Git 托管)
model Repository {
id String @id @default(uuid())
workspaceId String
resourceId String @unique
connectionId String?
provider String @default("GENERIC")
/// # 数据库字段 全名 owner/repo
fullName String
/// # 数据库字段 HTTPS 克隆地址(不含凭据)
cloneUrl String
defaultBranch String @default("main")
/// # 数据库字段 private 标记
visibility String @default("private")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resource Resource @relation(fields: [resourceId], references: [id], onDelete: Cascade)
connection GitConnection? @relation(fields: [connectionId], references: [id], onDelete: SetNull)
websites Website[]
@@unique([workspaceId, cloneUrl])
@@index([workspaceId])
}
/// # 数据库表 tasks:工作项(backlog/todo/doing/review/done)
model Task {
id String @id @default(uuid())
workspaceId String
resourceId String @unique
title String
description String @default("")
/// # 数据库字段 状态:BACKLOG / TODO / DOING / REVIEW / DONE
status String @default("TODO")
/// # 数据库字段 优先级:LOW / MEDIUM / HIGH / URGENT
priority String @default("MEDIUM")
labels Json @default("[]")
dueAt DateTime?
/// # 数据库字段 关联项目资源 ID(可空)
projectId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resource Resource @relation(fields: [resourceId], references: [id], onDelete: Cascade)
@@index([workspaceId, status])
@@index([projectId])
}
/// # 数据库表 notes:笔记/知识(markdown 存储)
model Note {
id String @id @default(uuid())
workspaceId String
resourceId String @unique
title String
/// # 数据库字段 正文(HTML,TipTap 富文本)
content String @default("")
tags Json @default("[]")
projectId String?
/// # 数据库字段 置顶
pinned Boolean @default(false)
/// # 数据库字段 文件夹(分组)
folder String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resource Resource @relation(fields: [resourceId], references: [id], onDelete: Cascade)
@@index([workspaceId])
}
/// # 数据库表 notifications:站内通知(自动化/事件投递)
model Notification {
id String @id @default(uuid())
workspaceId String
resourceId String?
title String
body String @default("")
/// # 数据库字段 级别:INFO / WARN / ERROR
level String @default("INFO")
readAt DateTime?
createdAt DateTime @default(now())
@@index([workspaceId, createdAt])
}
/// # 数据库表 certificates:TLS 证书统一模型(探针检测)
model Certificate {
id String @id @default(uuid())
workspaceId String
resourceId String @unique
/// # 数据库字段 证书主域名
domain String
domainId String?
/// # 数据库字段 状态:PENDING / ACTIVE / EXPIRED / ERROR
status String @default("PENDING")
issuer String?
notAfter DateTime?
lastCheckedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resource Resource @relation(fields: [resourceId], references: [id], onDelete: Cascade)
@@index([workspaceId])
}
// ============ v2 Action Runtime ============
/// # 数据库表 devos_action_executions:v2 Action 执行记录(docs 03 §31)
model ActionExecution {
id String @id
actionId String
actorType String
actorId String?
resourceType String?
resourceId String?
status String
input Json?
output Json?
error Json?
requestId String?
rootExecutionId String?
parentExecutionId String?
durationMs Int?
startedAt DateTime?
completedAt DateTime?
createdAt DateTime @default(now())
@@index([actionId, createdAt])
@@index([status])
}
// ============ v2 P0/P1:Project / Environment / Config / Storage ============
/// # 数据库表 devos_projects:项目(组织内的工作与环境分组,docs 22 Phase 2)
model Project {
id String @id @default(uuid())
workspaceId String
name String
slug String
description String @default("")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
environments Environment[]
@@unique([workspaceId, slug])
@@index([workspaceId])
}
/// # 数据库表 devos_environments:项目环境(development/staging/production,docs 22 Phase 2)
model Environment {
id String @id @default(uuid())
projectId String
name String
/// # 数据库字段 环境级别:development / staging / production
level String @default("development")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
@@unique([projectId, name])
}
/// # 数据库表 devos_config_values:配置运行时(docs 22 Phase 4,级联解析)
model ConfigValue {
id String @id @default(uuid())
workspaceId String
/// # 数据库字段 作用域:platform / organization / project / environment
scope String
/// # 数据库字段 作用域对象 ID(platform 为空)
scopeId String?
key String
value String
/// # 数据库字段 是否敏感(读取时脱敏,存储用保险库加密)
isSecret Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([workspaceId, scope, scopeId, key])
@@index([workspaceId, key])
}
/// # 数据库表 devos_files:存储运行时文件记录(docs 15,File Resource)
model FileEntry {
id String @id @default(uuid())
workspaceId String
name String
originalName String
mimeType String
size Int
sha256 String
/// # 数据库字段 存储键(存储桶内路径,不含原始文件名)
storageKey String
/// # 数据库字段 存储提供者:local(后续 S3 兼容)
provider String @default("local")
createdAt DateTime @default(now())
@@index([workspaceId, createdAt])
}