DevOS v1 + Phase 2: unified resource model, infra ops, website deployment, AI chat ops

- Monorepo: NestJS API (Fastify) + React web (antd/ProComponents) + shared packages
- PostgreSQL/Prisma (unified resources, relations graph, audit, events, jobs)
- Auth/RBAC/workspace isolation, credential vault (AES-256-GCM)
- Servers/SSH, Docker discovery & lifecycle, Job Center
- Websites & deployments (git provider credential clone, env, health verification)
- Domains/DNS (Cloudflare adapter), certificates (TLS probe), monitoring
- Automation (cron/event/webhook HMAC + notifications), AI Gateway, MCP (29 tools)
- AI chat window (LLM function-calling + rules fallback, SSE streaming, approvals)
- Notes: Vditor WYSIWYG markdown editor, non-blocking autosave
- Tests: 35 unit/security tests; UI audit clean (desktop+mobile, deep links)
This commit is contained in:
phptits
2026-09-10 06:35:55 +00:00
commit 0d6cd37579
209 changed files with 22783 additions and 0 deletions
+734
View File
@@ -0,0 +1,734 @@
// 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])
}