feat: add deployable alpha silo

This commit is contained in:
2026-07-11 00:25:45 +08:00
parent 44557da499
commit 9e954790dc
57 changed files with 2792 additions and 400 deletions
@@ -0,0 +1,30 @@
-- ADR-0024 / Issue 44: Feishu provider-local user identifiers are scoped by
-- the Organization's stable customer application connection. Existing legacy
-- User.feishuOpenId values are deliberately not guessed into this table.
CREATE TABLE "FeishuUserIdentity" (
"id" TEXT NOT NULL,
"connectionId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"openId" TEXT NOT NULL,
"unionId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "FeishuUserIdentity_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "FeishuUserIdentity_connectionId_openId_key"
ON "FeishuUserIdentity"("connectionId", "openId");
CREATE UNIQUE INDEX "FeishuUserIdentity_connectionId_unionId_key"
ON "FeishuUserIdentity"("connectionId", "unionId");
CREATE UNIQUE INDEX "FeishuUserIdentity_connectionId_userId_key"
ON "FeishuUserIdentity"("connectionId", "userId");
CREATE INDEX "FeishuUserIdentity_userId_idx"
ON "FeishuUserIdentity"("userId");
ALTER TABLE "FeishuUserIdentity"
ADD CONSTRAINT "FeishuUserIdentity_connectionId_fkey"
FOREIGN KEY ("connectionId") REFERENCES "OrganizationFeishuApplicationConnection"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "FeishuUserIdentity"
ADD CONSTRAINT "FeishuUserIdentity_userId_fkey"
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+28 -3
View File
@@ -89,7 +89,9 @@ model OrganizationProjectSettings {
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
}
/// A Feishu user known to the Hub. principal sub-typology is OPEN (ADR-0004).
/// A person known to the Hub. `feishuOpenId` remains a legacy compatibility
/// key; new customer-app identities live in FeishuUserIdentity and store a
/// connection-scoped opaque USER principal here instead of a raw open_id.
model User {
id String @id @default(cuid())
feishuOpenId String @unique
@@ -111,6 +113,7 @@ model User {
auditEntries AuditEntry[] @relation("auditActor")
providerCredentialVersions ProviderCredentialVersion[] @relation("providerCredentialVersionCreator")
feishuCredentialVersions FeishuApplicationCredentialVersion[] @relation("feishuCredentialVersionCreator")
feishuIdentities FeishuUserIdentity[]
}
/// ADR-0024: exactly one customer-owned Feishu application connection may be
@@ -130,10 +133,32 @@ model OrganizationFeishuApplicationConnection {
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
secretVersions FeishuApplicationCredentialVersion[] @relation("feishuCredentialVersions")
activeSecretVersion FeishuApplicationCredentialVersion? @relation("activeFeishuCredentialVersion", fields: [activeSecretVersionId], references: [id], onDelete: Restrict)
userIdentities FeishuUserIdentity[]
@@index([status])
}
/// ADR-0024 / Issue 44: provider-local Feishu identities are never global.
/// The same open_id may identify different people under different customer
/// applications, so all lookup and uniqueness begins with connectionId.
model FeishuUserIdentity {
id String @id @default(cuid())
connectionId String
userId String
openId String
unionId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
connection OrganizationFeishuApplicationConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([connectionId, openId])
@@unique([connectionId, unionId])
@@unique([connectionId, userId])
@@index([userId])
}
/// ADR-0024: all Feishu app material, including provider-local app/bot ids, is
/// inside one immutable authenticated envelope version.
model FeishuApplicationCredentialVersion {
@@ -237,8 +262,8 @@ enum PlatformRole {
}
/// ADR-0019: typed principals for permission grants and actor resolution.
/// USER is identified by Feishu open_id; TEAM by Hub Team.id; Feishu external
/// principals by their Feishu ids.
/// USER and Feishu external principals use connection-scoped opaque ids, never
/// raw provider-local ids; TEAM uses Hub Team.id.
enum PrincipalType {
USER
TEAM