mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
chore(server): standardize server db names and columns (#7674)
This commit is contained in:
@@ -0,0 +1,146 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "_data_migrations" ALTER COLUMN "id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "started_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "finished_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "ai_prompts_messages" ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "ai_prompts_metadata" ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "ai_sessions_messages" ALTER COLUMN "id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "session_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "updated_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "ai_sessions_metadata" ALTER COLUMN "id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "user_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "workspace_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "doc_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "deleted_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "parent_session_id" SET DATA TYPE VARCHAR;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "app_runtime_settings" ALTER COLUMN "updated_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "deleted_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "last_updated_by" SET DATA TYPE VARCHAR;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "features" ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "multiple_users_sessions" ALTER COLUMN "id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "expires_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "snapshot_histories"
|
||||||
|
ALTER COLUMN "workspace_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "guid" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "timestamp" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "expired_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "snapshots" ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "updated_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "updates" ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "user_connected_accounts" ALTER COLUMN "id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "user_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "expires_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "updated_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "user_features" ALTER COLUMN "user_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "expired_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "user_invoices" ALTER COLUMN "user_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "updated_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "user_sessions" ALTER COLUMN "id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "session_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "user_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "expires_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "user_stripe_customers" ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "user_subscriptions" ALTER COLUMN "user_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "start" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "end" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "next_bill_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "canceled_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "trial_start" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "trial_end" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "updated_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "users" ALTER COLUMN "name" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "email" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "verification_tokens" ALTER COLUMN "token" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "expiresAt" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "workspace_features" ALTER COLUMN "workspace_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3),
|
||||||
|
ALTER COLUMN "expired_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "workspace_page_user_permissions"
|
||||||
|
ALTER COLUMN "id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "workspace_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "page_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "user_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "workspace_pages" ALTER COLUMN "workspace_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "page_id" SET DATA TYPE VARCHAR;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "workspace_user_permissions" ALTER COLUMN "id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "workspace_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "user_id" SET DATA TYPE VARCHAR,
|
||||||
|
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "workspaces" ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3);
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "accounts";
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "blobs";
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "new_features_waiting_list";
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "optimized_blobs";
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "sessions";
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "user_workspace_permissions";
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "verificationtokens";
|
||||||
@@ -11,18 +11,18 @@ datasource db {
|
|||||||
|
|
||||||
model User {
|
model User {
|
||||||
id String @id @default(uuid()) @db.VarChar
|
id String @id @default(uuid()) @db.VarChar
|
||||||
name String
|
name String @db.VarChar
|
||||||
email String @unique
|
email String @unique @db.VarChar
|
||||||
emailVerifiedAt DateTime? @map("email_verified")
|
emailVerifiedAt DateTime? @map("email_verified") @db.Timestamp(3)
|
||||||
avatarUrl String? @map("avatar_url") @db.VarChar
|
avatarUrl String? @map("avatar_url") @db.VarChar
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
/// Not available if user signed up through OAuth providers
|
/// Not available if user signed up through OAuth providers
|
||||||
password String? @db.VarChar
|
password String? @db.VarChar
|
||||||
/// Indicate whether the user finished the signup progress.
|
/// Indicate whether the user finished the signup progress.
|
||||||
/// for example, the value will be false if user never registered and invited into a workspace by others.
|
/// for example, the value will be false if user never registered and invited into a workspace by others.
|
||||||
registered Boolean @default(true)
|
registered Boolean @default(true)
|
||||||
|
|
||||||
features UserFeatures[]
|
features UserFeature[]
|
||||||
customer UserStripeCustomer?
|
customer UserStripeCustomer?
|
||||||
subscriptions UserSubscription[]
|
subscriptions UserSubscription[]
|
||||||
invoices UserInvoice[]
|
invoices UserInvoice[]
|
||||||
@@ -38,16 +38,16 @@ model User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model ConnectedAccount {
|
model ConnectedAccount {
|
||||||
id String @id @default(uuid()) @db.VarChar(36)
|
id String @id @default(uuid()) @db.VarChar
|
||||||
userId String @map("user_id") @db.VarChar(36)
|
userId String @map("user_id") @db.VarChar
|
||||||
provider String @db.VarChar
|
provider String @db.VarChar
|
||||||
providerAccountId String @map("provider_account_id") @db.VarChar
|
providerAccountId String @map("provider_account_id") @db.VarChar
|
||||||
scope String? @db.Text
|
scope String? @db.Text
|
||||||
accessToken String? @map("access_token") @db.Text
|
accessToken String? @map("access_token") @db.Text
|
||||||
refreshToken String? @map("refresh_token") @db.Text
|
refreshToken String? @map("refresh_token") @db.Text
|
||||||
expiresAt DateTime? @map("expires_at") @db.Timestamptz(6)
|
expiresAt DateTime? @map("expires_at") @db.Timestamp(3)
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(6)
|
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamp(3)
|
||||||
|
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@ -57,9 +57,9 @@ model ConnectedAccount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Session {
|
model Session {
|
||||||
id String @id @default(uuid()) @db.VarChar(36)
|
id String @id @default(uuid()) @db.VarChar
|
||||||
expiresAt DateTime? @map("expires_at") @db.Timestamptz(6)
|
expiresAt DateTime? @map("expires_at") @db.Timestamp(3)
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
|
|
||||||
userSessions UserSession[]
|
userSessions UserSession[]
|
||||||
|
|
||||||
@@ -67,11 +67,11 @@ model Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model UserSession {
|
model UserSession {
|
||||||
id String @id @default(uuid()) @db.VarChar(36)
|
id String @id @default(uuid()) @db.VarChar
|
||||||
sessionId String @map("session_id") @db.VarChar(36)
|
sessionId String @map("session_id") @db.VarChar
|
||||||
userId String @map("user_id") @db.VarChar(36)
|
userId String @map("user_id") @db.VarChar
|
||||||
expiresAt DateTime? @map("expires_at") @db.Timestamptz(6)
|
expiresAt DateTime? @map("expires_at") @db.Timestamp(3)
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
|
|
||||||
session Session @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
session Session @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
@@ -81,10 +81,10 @@ model UserSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model VerificationToken {
|
model VerificationToken {
|
||||||
token String @db.VarChar(36)
|
token String @db.VarChar
|
||||||
type Int @db.SmallInt
|
type Int @db.SmallInt
|
||||||
credential String? @db.Text
|
credential String? @db.Text
|
||||||
expiresAt DateTime @db.Timestamptz(6)
|
expiresAt DateTime @db.Timestamp(3)
|
||||||
|
|
||||||
@@unique([type, token])
|
@@unique([type, token])
|
||||||
@@map("verification_tokens")
|
@@map("verification_tokens")
|
||||||
@@ -93,12 +93,12 @@ model VerificationToken {
|
|||||||
model Workspace {
|
model Workspace {
|
||||||
id String @id @default(uuid()) @db.VarChar
|
id String @id @default(uuid()) @db.VarChar
|
||||||
public Boolean
|
public Boolean
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
|
|
||||||
pages WorkspacePage[]
|
pages WorkspacePage[]
|
||||||
permissions WorkspaceUserPermission[]
|
permissions WorkspaceUserPermission[]
|
||||||
pagePermissions WorkspacePageUserPermission[]
|
pagePermissions WorkspacePageUserPermission[]
|
||||||
features WorkspaceFeatures[]
|
features WorkspaceFeature[]
|
||||||
|
|
||||||
@@map("workspaces")
|
@@map("workspaces")
|
||||||
}
|
}
|
||||||
@@ -109,8 +109,8 @@ model Workspace {
|
|||||||
// Only the ones that have ever changed will have records here,
|
// Only the ones that have ever changed will have records here,
|
||||||
// and for others we will make sure it's has a default value return in our bussiness logic.
|
// and for others we will make sure it's has a default value return in our bussiness logic.
|
||||||
model WorkspacePage {
|
model WorkspacePage {
|
||||||
workspaceId String @map("workspace_id") @db.VarChar(36)
|
workspaceId String @map("workspace_id") @db.VarChar
|
||||||
pageId String @map("page_id") @db.VarChar(36)
|
pageId String @map("page_id") @db.VarChar
|
||||||
public Boolean @default(false)
|
public Boolean @default(false)
|
||||||
// Page/Edgeless
|
// Page/Edgeless
|
||||||
mode Int @default(0) @db.SmallInt
|
mode Int @default(0) @db.SmallInt
|
||||||
@@ -121,31 +121,15 @@ model WorkspacePage {
|
|||||||
@@map("workspace_pages")
|
@@map("workspace_pages")
|
||||||
}
|
}
|
||||||
|
|
||||||
// @deprecated, use WorkspaceUserPermission
|
model WorkspaceUserPermission {
|
||||||
model DeprecatedUserWorkspacePermission {
|
|
||||||
id String @id @default(uuid()) @db.VarChar
|
id String @id @default(uuid()) @db.VarChar
|
||||||
workspaceId String @map("workspace_id") @db.VarChar
|
workspaceId String @map("workspace_id") @db.VarChar
|
||||||
subPageId String? @map("sub_page_id") @db.VarChar
|
userId String @map("user_id") @db.VarChar
|
||||||
userId String? @map("entity_id") @db.VarChar
|
|
||||||
/// Read/Write/Admin/Owner
|
|
||||||
type Int @db.SmallInt
|
|
||||||
/// Whether the permission invitation is accepted by the user
|
|
||||||
accepted Boolean @default(false)
|
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
|
||||||
|
|
||||||
@@unique([workspaceId, subPageId, userId])
|
|
||||||
@@map("user_workspace_permissions")
|
|
||||||
}
|
|
||||||
|
|
||||||
model WorkspaceUserPermission {
|
|
||||||
id String @id @default(uuid()) @db.VarChar(36)
|
|
||||||
workspaceId String @map("workspace_id") @db.VarChar(36)
|
|
||||||
userId String @map("user_id") @db.VarChar(36)
|
|
||||||
// Read/Write
|
// Read/Write
|
||||||
type Int @db.SmallInt
|
type Int @db.SmallInt
|
||||||
/// Whether the permission invitation is accepted by the user
|
/// Whether the permission invitation is accepted by the user
|
||||||
accepted Boolean @default(false)
|
accepted Boolean @default(false)
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
|
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||||
@@ -155,15 +139,15 @@ model WorkspaceUserPermission {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model WorkspacePageUserPermission {
|
model WorkspacePageUserPermission {
|
||||||
id String @id @default(uuid()) @db.VarChar(36)
|
id String @id @default(uuid()) @db.VarChar
|
||||||
workspaceId String @map("workspace_id") @db.VarChar(36)
|
workspaceId String @map("workspace_id") @db.VarChar
|
||||||
pageId String @map("page_id") @db.VarChar(36)
|
pageId String @map("page_id") @db.VarChar
|
||||||
userId String @map("user_id") @db.VarChar(36)
|
userId String @map("user_id") @db.VarChar
|
||||||
// Read/Write
|
// Read/Write
|
||||||
type Int @db.SmallInt
|
type Int @db.SmallInt
|
||||||
/// Whether the permission invitation is accepted by the user
|
/// Whether the permission invitation is accepted by the user
|
||||||
accepted Boolean @default(false)
|
accepted Boolean @default(false)
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
|
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||||
@@ -176,9 +160,9 @@ model WorkspacePageUserPermission {
|
|||||||
// for example:
|
// for example:
|
||||||
// - early access is a feature that allow some users to access the insider version
|
// - early access is a feature that allow some users to access the insider version
|
||||||
// - pro plan is a quota that allow some users access to more resources after they pay
|
// - pro plan is a quota that allow some users access to more resources after they pay
|
||||||
model UserFeatures {
|
model UserFeature {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
userId String @map("user_id") @db.VarChar(36)
|
userId String @map("user_id") @db.VarChar
|
||||||
featureId Int @map("feature_id") @db.Integer
|
featureId Int @map("feature_id") @db.Integer
|
||||||
|
|
||||||
// we will record the reason why the feature is enabled/disabled
|
// we will record the reason why the feature is enabled/disabled
|
||||||
@@ -186,16 +170,16 @@ model UserFeatures {
|
|||||||
// - pro_plan_v1: "user buy the pro plan"
|
// - pro_plan_v1: "user buy the pro plan"
|
||||||
reason String @db.VarChar
|
reason String @db.VarChar
|
||||||
// record the quota enabled time
|
// record the quota enabled time
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
// record the quota expired time, pay plan is a subscription, so it will expired
|
// record the quota expired time, pay plan is a subscription, so it will expired
|
||||||
expiredAt DateTime? @map("expired_at") @db.Timestamptz(6)
|
expiredAt DateTime? @map("expired_at") @db.Timestamp(3)
|
||||||
// whether the feature is activated
|
// whether the feature is activated
|
||||||
// for example:
|
// for example:
|
||||||
// - if we switch the user to another plan, we will set the old plan to deactivated, but dont delete it
|
// - if we switch the user to another plan, we will set the old plan to deactivated, but dont delete it
|
||||||
activated Boolean @default(false)
|
activated Boolean @default(false)
|
||||||
|
|
||||||
feature Features @relation(fields: [featureId], references: [id], onDelete: Cascade)
|
feature Feature @relation(fields: [featureId], references: [id], onDelete: Cascade)
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
@@map("user_features")
|
@@map("user_features")
|
||||||
@@ -204,9 +188,9 @@ model UserFeatures {
|
|||||||
// feature gates is a way to enable/disable features for a workspace
|
// feature gates is a way to enable/disable features for a workspace
|
||||||
// for example:
|
// for example:
|
||||||
// - copilet is a feature that allow some users in a workspace to access the copilet feature
|
// - copilet is a feature that allow some users in a workspace to access the copilet feature
|
||||||
model WorkspaceFeatures {
|
model WorkspaceFeature {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
workspaceId String @map("workspace_id") @db.VarChar(36)
|
workspaceId String @map("workspace_id") @db.VarChar
|
||||||
featureId Int @map("feature_id") @db.Integer
|
featureId Int @map("feature_id") @db.Integer
|
||||||
|
|
||||||
// we will record the reason why the feature is enabled/disabled
|
// we will record the reason why the feature is enabled/disabled
|
||||||
@@ -214,21 +198,21 @@ model WorkspaceFeatures {
|
|||||||
// - copilet_v1: "owner buy the copilet feature package"
|
// - copilet_v1: "owner buy the copilet feature package"
|
||||||
reason String @db.VarChar
|
reason String @db.VarChar
|
||||||
// record the feature enabled time
|
// record the feature enabled time
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
// record the quota expired time, pay plan is a subscription, so it will expired
|
// record the quota expired time, pay plan is a subscription, so it will expired
|
||||||
expiredAt DateTime? @map("expired_at") @db.Timestamptz(6)
|
expiredAt DateTime? @map("expired_at") @db.Timestamp(3)
|
||||||
// whether the feature is activated
|
// whether the feature is activated
|
||||||
// for example:
|
// for example:
|
||||||
// - if owner unsubscribe a feature package, we will set the feature to deactivated, but dont delete it
|
// - if owner unsubscribe a feature package, we will set the feature to deactivated, but dont delete it
|
||||||
activated Boolean @default(false)
|
activated Boolean @default(false)
|
||||||
|
|
||||||
feature Features @relation(fields: [featureId], references: [id], onDelete: Cascade)
|
feature Feature @relation(fields: [featureId], references: [id], onDelete: Cascade)
|
||||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@map("workspace_features")
|
@@map("workspace_features")
|
||||||
}
|
}
|
||||||
|
|
||||||
model Features {
|
model Feature {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
feature String @db.VarChar
|
feature String @db.VarChar
|
||||||
version Int @default(0) @db.Integer
|
version Int @default(0) @db.Integer
|
||||||
@@ -236,82 +220,15 @@ model Features {
|
|||||||
type Int @db.Integer
|
type Int @db.Integer
|
||||||
// configs, define by feature conntroller
|
// configs, define by feature conntroller
|
||||||
configs Json @db.Json
|
configs Json @db.Json
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
|
|
||||||
UserFeatureGates UserFeatures[]
|
UserFeatureGates UserFeature[]
|
||||||
WorkspaceFeatures WorkspaceFeatures[]
|
WorkspaceFeatures WorkspaceFeature[]
|
||||||
|
|
||||||
@@unique([feature, version])
|
@@unique([feature, version])
|
||||||
@@map("features")
|
@@map("features")
|
||||||
}
|
}
|
||||||
|
|
||||||
model DeprecatedNextAuthAccount {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
userId String @map("user_id")
|
|
||||||
type String
|
|
||||||
provider String
|
|
||||||
providerAccountId String @map("provider_account_id")
|
|
||||||
refresh_token String? @db.Text
|
|
||||||
access_token String? @db.Text
|
|
||||||
expires_at Int?
|
|
||||||
token_type String?
|
|
||||||
scope String?
|
|
||||||
id_token String? @db.Text
|
|
||||||
session_state String?
|
|
||||||
|
|
||||||
@@unique([provider, providerAccountId])
|
|
||||||
@@map("accounts")
|
|
||||||
}
|
|
||||||
|
|
||||||
model DeprecatedNextAuthSession {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
sessionToken String @unique @map("session_token")
|
|
||||||
userId String @map("user_id")
|
|
||||||
expires DateTime
|
|
||||||
|
|
||||||
@@map("sessions")
|
|
||||||
}
|
|
||||||
|
|
||||||
model DeprecatedNextAuthVerificationToken {
|
|
||||||
identifier String
|
|
||||||
token String @unique
|
|
||||||
expires DateTime
|
|
||||||
|
|
||||||
@@unique([identifier, token])
|
|
||||||
@@map("verificationtokens")
|
|
||||||
}
|
|
||||||
|
|
||||||
// deprecated, use [ObjectStorage]
|
|
||||||
model Blob {
|
|
||||||
id Int @id @default(autoincrement()) @db.Integer
|
|
||||||
hash String @db.VarChar
|
|
||||||
workspaceId String @map("workspace_id") @db.VarChar
|
|
||||||
blob Bytes @db.ByteA
|
|
||||||
length BigInt
|
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
|
||||||
// not for keeping, but for snapshot history
|
|
||||||
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6)
|
|
||||||
|
|
||||||
@@unique([workspaceId, hash])
|
|
||||||
@@map("blobs")
|
|
||||||
}
|
|
||||||
|
|
||||||
// deprecated, use [ObjectStorage]
|
|
||||||
model OptimizedBlob {
|
|
||||||
id Int @id @default(autoincrement()) @db.Integer
|
|
||||||
hash String @db.VarChar
|
|
||||||
workspaceId String @map("workspace_id") @db.VarChar
|
|
||||||
params String @db.VarChar
|
|
||||||
blob Bytes @db.ByteA
|
|
||||||
length BigInt
|
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
|
||||||
// not for keeping, but for snapshot history
|
|
||||||
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6)
|
|
||||||
|
|
||||||
@@unique([workspaceId, hash, params])
|
|
||||||
@@map("optimized_blobs")
|
|
||||||
}
|
|
||||||
|
|
||||||
// the latest snapshot of each doc that we've seen
|
// the latest snapshot of each doc that we've seen
|
||||||
// Snapshot + Updates are the latest state of the doc
|
// Snapshot + Updates are the latest state of the doc
|
||||||
model Snapshot {
|
model Snapshot {
|
||||||
@@ -320,10 +237,10 @@ model Snapshot {
|
|||||||
blob Bytes @db.ByteA
|
blob Bytes @db.ByteA
|
||||||
seq Int @default(0) @db.Integer
|
seq Int @default(0) @db.Integer
|
||||||
state Bytes? @db.ByteA
|
state Bytes? @db.ByteA
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
// the `updated_at` field will not record the time of record changed,
|
// the `updated_at` field will not record the time of record changed,
|
||||||
// but the created time of last seen update that has been merged into snapshot.
|
// but the created time of last seen update that has been merged into snapshot.
|
||||||
updatedAt DateTime @map("updated_at") @db.Timestamptz(6)
|
updatedAt DateTime @map("updated_at") @db.Timestamp(3)
|
||||||
|
|
||||||
@@id([id, workspaceId])
|
@@id([id, workspaceId])
|
||||||
@@map("snapshots")
|
@@map("snapshots")
|
||||||
@@ -334,37 +251,28 @@ model Update {
|
|||||||
id String @map("guid") @db.VarChar
|
id String @map("guid") @db.VarChar
|
||||||
seq Int @db.Integer
|
seq Int @db.Integer
|
||||||
blob Bytes @db.ByteA
|
blob Bytes @db.ByteA
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
|
|
||||||
@@id([workspaceId, id, seq])
|
@@id([workspaceId, id, seq])
|
||||||
@@map("updates")
|
@@map("updates")
|
||||||
}
|
}
|
||||||
|
|
||||||
model SnapshotHistory {
|
model SnapshotHistory {
|
||||||
workspaceId String @map("workspace_id") @db.VarChar(36)
|
workspaceId String @map("workspace_id") @db.VarChar
|
||||||
id String @map("guid") @db.VarChar(36)
|
id String @map("guid") @db.VarChar
|
||||||
timestamp DateTime @db.Timestamptz(6)
|
timestamp DateTime @db.Timestamp(3)
|
||||||
blob Bytes @db.ByteA
|
blob Bytes @db.ByteA
|
||||||
state Bytes? @db.ByteA
|
state Bytes? @db.ByteA
|
||||||
expiredAt DateTime @map("expired_at") @db.Timestamptz(6)
|
expiredAt DateTime @map("expired_at") @db.Timestamp(3)
|
||||||
|
|
||||||
@@id([workspaceId, id, timestamp])
|
@@id([workspaceId, id, timestamp])
|
||||||
@@map("snapshot_histories")
|
@@map("snapshot_histories")
|
||||||
}
|
}
|
||||||
|
|
||||||
model NewFeaturesWaitingList {
|
|
||||||
id String @id @default(uuid()) @db.VarChar
|
|
||||||
email String @unique
|
|
||||||
type Int @db.SmallInt
|
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
|
||||||
|
|
||||||
@@map("new_features_waiting_list")
|
|
||||||
}
|
|
||||||
|
|
||||||
model UserStripeCustomer {
|
model UserStripeCustomer {
|
||||||
userId String @id @map("user_id") @db.VarChar
|
userId String @id @map("user_id") @db.VarChar
|
||||||
stripeCustomerId String @unique @map("stripe_customer_id") @db.VarChar
|
stripeCustomerId String @unique @map("stripe_customer_id") @db.VarChar
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
|
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@ -373,7 +281,7 @@ model UserStripeCustomer {
|
|||||||
|
|
||||||
model UserSubscription {
|
model UserSubscription {
|
||||||
id Int @id @default(autoincrement()) @db.Integer
|
id Int @id @default(autoincrement()) @db.Integer
|
||||||
userId String @map("user_id") @db.VarChar(36)
|
userId String @map("user_id") @db.VarChar
|
||||||
plan String @db.VarChar(20)
|
plan String @db.VarChar(20)
|
||||||
// yearly/monthly
|
// yearly/monthly
|
||||||
recurring String @db.VarChar(20)
|
recurring String @db.VarChar(20)
|
||||||
@@ -382,21 +290,21 @@ model UserSubscription {
|
|||||||
// subscription.status, active/past_due/canceled/unpaid...
|
// subscription.status, active/past_due/canceled/unpaid...
|
||||||
status String @db.VarChar(20)
|
status String @db.VarChar(20)
|
||||||
// subscription.current_period_start
|
// subscription.current_period_start
|
||||||
start DateTime @map("start") @db.Timestamptz(6)
|
start DateTime @map("start") @db.Timestamp(3)
|
||||||
// subscription.current_period_end, null for lifetime payment
|
// subscription.current_period_end, null for lifetime payment
|
||||||
end DateTime? @map("end") @db.Timestamptz(6)
|
end DateTime? @map("end") @db.Timestamp(3)
|
||||||
// subscription.billing_cycle_anchor
|
// subscription.billing_cycle_anchor
|
||||||
nextBillAt DateTime? @map("next_bill_at") @db.Timestamptz(6)
|
nextBillAt DateTime? @map("next_bill_at") @db.Timestamp(3)
|
||||||
// subscription.canceled_at
|
// subscription.canceled_at
|
||||||
canceledAt DateTime? @map("canceled_at") @db.Timestamptz(6)
|
canceledAt DateTime? @map("canceled_at") @db.Timestamp(3)
|
||||||
// subscription.trial_start
|
// subscription.trial_start
|
||||||
trialStart DateTime? @map("trial_start") @db.Timestamptz(6)
|
trialStart DateTime? @map("trial_start") @db.Timestamp(3)
|
||||||
// subscription.trial_end
|
// subscription.trial_end
|
||||||
trialEnd DateTime? @map("trial_end") @db.Timestamptz(6)
|
trialEnd DateTime? @map("trial_end") @db.Timestamp(3)
|
||||||
stripeScheduleId String? @map("stripe_schedule_id") @db.VarChar
|
stripeScheduleId String? @map("stripe_schedule_id") @db.VarChar
|
||||||
|
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(6)
|
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamp(3)
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@unique([userId, plan])
|
@@unique([userId, plan])
|
||||||
@@ -405,7 +313,7 @@ model UserSubscription {
|
|||||||
|
|
||||||
model UserInvoice {
|
model UserInvoice {
|
||||||
id Int @id @default(autoincrement()) @db.Integer
|
id Int @id @default(autoincrement()) @db.Integer
|
||||||
userId String @map("user_id") @db.VarChar(36)
|
userId String @map("user_id") @db.VarChar
|
||||||
stripeInvoiceId String @unique @map("stripe_invoice_id")
|
stripeInvoiceId String @unique @map("stripe_invoice_id")
|
||||||
currency String @db.VarChar(3)
|
currency String @db.VarChar(3)
|
||||||
// CNY 12.50 stored as 1250
|
// CNY 12.50 stored as 1250
|
||||||
@@ -413,8 +321,8 @@ model UserInvoice {
|
|||||||
status String @db.VarChar(20)
|
status String @db.VarChar(20)
|
||||||
plan String @db.VarChar(20)
|
plan String @db.VarChar(20)
|
||||||
recurring String @db.VarChar(20)
|
recurring String @db.VarChar(20)
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(6)
|
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamp(3)
|
||||||
// billing reason
|
// billing reason
|
||||||
reason String @db.VarChar
|
reason String @db.VarChar
|
||||||
lastPaymentError String? @map("last_payment_error") @db.Text
|
lastPaymentError String? @map("last_payment_error") @db.Text
|
||||||
@@ -442,7 +350,7 @@ model AiPromptMessage {
|
|||||||
content String @db.Text
|
content String @db.Text
|
||||||
attachments Json? @db.Json
|
attachments Json? @db.Json
|
||||||
params Json? @db.Json
|
params Json? @db.Json
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
|
|
||||||
prompt AiPrompt @relation(fields: [promptId], references: [id], onDelete: Cascade)
|
prompt AiPrompt @relation(fields: [promptId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@ -458,7 +366,7 @@ model AiPrompt {
|
|||||||
action String? @db.VarChar
|
action String? @db.VarChar
|
||||||
model String @db.VarChar
|
model String @db.VarChar
|
||||||
config Json? @db.Json
|
config Json? @db.Json
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
|
|
||||||
messages AiPromptMessage[]
|
messages AiPromptMessage[]
|
||||||
sessions AiSession[]
|
sessions AiSession[]
|
||||||
@@ -467,14 +375,14 @@ model AiPrompt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model AiSessionMessage {
|
model AiSessionMessage {
|
||||||
id String @id @default(uuid()) @db.VarChar(36)
|
id String @id @default(uuid()) @db.VarChar
|
||||||
sessionId String @map("session_id") @db.VarChar(36)
|
sessionId String @map("session_id") @db.VarChar
|
||||||
role AiPromptRole
|
role AiPromptRole
|
||||||
content String @db.Text
|
content String @db.Text
|
||||||
attachments Json? @db.Json
|
attachments Json? @db.Json
|
||||||
params Json? @db.Json
|
params Json? @db.Json
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(6)
|
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamp(3)
|
||||||
|
|
||||||
session AiSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
session AiSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@ -482,17 +390,17 @@ model AiSessionMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model AiSession {
|
model AiSession {
|
||||||
id String @id @default(uuid()) @db.VarChar(36)
|
id String @id @default(uuid()) @db.VarChar
|
||||||
userId String @map("user_id") @db.VarChar(36)
|
userId String @map("user_id") @db.VarChar
|
||||||
workspaceId String @map("workspace_id") @db.VarChar(36)
|
workspaceId String @map("workspace_id") @db.VarChar
|
||||||
docId String @map("doc_id") @db.VarChar(36)
|
docId String @map("doc_id") @db.VarChar
|
||||||
promptName String @map("prompt_name") @db.VarChar(32)
|
promptName String @map("prompt_name") @db.VarChar(32)
|
||||||
// the session id of the parent session if this session is a forked session
|
// the session id of the parent session if this session is a forked session
|
||||||
parentSessionId String? @map("parent_session_id") @db.VarChar(36)
|
parentSessionId String? @map("parent_session_id") @db.VarChar
|
||||||
messageCost Int @default(0)
|
messageCost Int @default(0)
|
||||||
tokenCost Int @default(0)
|
tokenCost Int @default(0)
|
||||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
|
||||||
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6)
|
deletedAt DateTime? @map("deleted_at") @db.Timestamp(3)
|
||||||
|
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
prompt AiPrompt @relation(fields: [promptName], references: [name], onDelete: Cascade)
|
prompt AiPrompt @relation(fields: [promptName], references: [name], onDelete: Cascade)
|
||||||
@@ -502,10 +410,10 @@ model AiSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model DataMigration {
|
model DataMigration {
|
||||||
id String @id @default(uuid()) @db.VarChar(36)
|
id String @id @default(uuid()) @db.VarChar
|
||||||
name String @db.VarChar
|
name String @db.VarChar
|
||||||
startedAt DateTime @default(now()) @map("started_at") @db.Timestamptz(6)
|
startedAt DateTime @default(now()) @map("started_at") @db.Timestamp(3)
|
||||||
finishedAt DateTime? @map("finished_at") @db.Timestamptz(6)
|
finishedAt DateTime? @map("finished_at") @db.Timestamp(3)
|
||||||
|
|
||||||
@@map("_data_migrations")
|
@@map("_data_migrations")
|
||||||
}
|
}
|
||||||
@@ -525,9 +433,9 @@ model RuntimeConfig {
|
|||||||
key String @db.VarChar
|
key String @db.VarChar
|
||||||
value Json @db.Json
|
value Json @db.Json
|
||||||
description String @db.Text
|
description String @db.Text
|
||||||
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(6)
|
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamp(3)
|
||||||
deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6)
|
deletedAt DateTime? @map("deleted_at") @db.Timestamp(3)
|
||||||
lastUpdatedBy String? @map("last_updated_by") @db.VarChar(36)
|
lastUpdatedBy String? @map("last_updated_by") @db.VarChar
|
||||||
|
|
||||||
lastUpdatedByUser User? @relation(fields: [lastUpdatedBy], references: [id])
|
lastUpdatedByUser User? @relation(fields: [lastUpdatedBy], references: [id])
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export async function getFeature(prisma: PrismaTransaction, featureId: number) {
|
|||||||
return cachedFeature;
|
return cachedFeature;
|
||||||
}
|
}
|
||||||
|
|
||||||
const feature = await prisma.features.findFirst({
|
const feature = await prisma.feature.findFirst({
|
||||||
where: {
|
where: {
|
||||||
id: featureId,
|
id: featureId,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export class FeatureService {
|
|||||||
constructor(private readonly prisma: PrismaClient) {}
|
constructor(private readonly prisma: PrismaClient) {}
|
||||||
|
|
||||||
async getFeature<F extends FeatureType>(feature: F) {
|
async getFeature<F extends FeatureType>(feature: F) {
|
||||||
const data = await this.prisma.features.findFirst({
|
const data = await this.prisma.feature.findFirst({
|
||||||
where: {
|
where: {
|
||||||
feature,
|
feature,
|
||||||
type: FeatureKind.Feature,
|
type: FeatureKind.Feature,
|
||||||
@@ -36,7 +36,7 @@ export class FeatureService {
|
|||||||
expiredAt?: Date | string
|
expiredAt?: Date | string
|
||||||
) {
|
) {
|
||||||
return this.prisma.$transaction(async tx => {
|
return this.prisma.$transaction(async tx => {
|
||||||
const latestFlag = await tx.userFeatures.findFirst({
|
const latestFlag = await tx.userFeature.findFirst({
|
||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
feature: {
|
feature: {
|
||||||
@@ -53,7 +53,7 @@ export class FeatureService {
|
|||||||
if (latestFlag) {
|
if (latestFlag) {
|
||||||
return latestFlag.id;
|
return latestFlag.id;
|
||||||
} else {
|
} else {
|
||||||
const featureId = await tx.features
|
const featureId = await tx.feature
|
||||||
.findFirst({
|
.findFirst({
|
||||||
where: { feature, type: FeatureKind.Feature },
|
where: { feature, type: FeatureKind.Feature },
|
||||||
orderBy: { version: 'desc' },
|
orderBy: { version: 'desc' },
|
||||||
@@ -65,7 +65,7 @@ export class FeatureService {
|
|||||||
throw new Error(`Feature ${feature} not found`);
|
throw new Error(`Feature ${feature} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx.userFeatures
|
return tx.userFeature
|
||||||
.create({
|
.create({
|
||||||
data: {
|
data: {
|
||||||
reason,
|
reason,
|
||||||
@@ -81,7 +81,7 @@ export class FeatureService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removeUserFeature(userId: string, feature: FeatureType) {
|
async removeUserFeature(userId: string, feature: FeatureType) {
|
||||||
return this.prisma.userFeatures
|
return this.prisma.userFeature
|
||||||
.updateMany({
|
.updateMany({
|
||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
@@ -104,7 +104,7 @@ export class FeatureService {
|
|||||||
* @returns list of features
|
* @returns list of features
|
||||||
*/
|
*/
|
||||||
async getUserFeatures(userId: string) {
|
async getUserFeatures(userId: string) {
|
||||||
const features = await this.prisma.userFeatures.findMany({
|
const features = await this.prisma.userFeature.findMany({
|
||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
feature: { type: FeatureKind.Feature },
|
feature: { type: FeatureKind.Feature },
|
||||||
@@ -129,7 +129,7 @@ export class FeatureService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getActivatedUserFeatures(userId: string) {
|
async getActivatedUserFeatures(userId: string) {
|
||||||
const features = await this.prisma.userFeatures.findMany({
|
const features = await this.prisma.userFeature.findMany({
|
||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
feature: { type: FeatureKind.Feature },
|
feature: { type: FeatureKind.Feature },
|
||||||
@@ -156,7 +156,7 @@ export class FeatureService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async listFeatureUsers(feature: FeatureType) {
|
async listFeatureUsers(feature: FeatureType) {
|
||||||
return this.prisma.userFeatures
|
return this.prisma.userFeature
|
||||||
.findMany({
|
.findMany({
|
||||||
where: {
|
where: {
|
||||||
activated: true,
|
activated: true,
|
||||||
@@ -182,7 +182,7 @@ export class FeatureService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async hasUserFeature(userId: string, feature: FeatureType) {
|
async hasUserFeature(userId: string, feature: FeatureType) {
|
||||||
return this.prisma.userFeatures
|
return this.prisma.userFeature
|
||||||
.count({
|
.count({
|
||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
@@ -206,7 +206,7 @@ export class FeatureService {
|
|||||||
expiredAt?: Date | string
|
expiredAt?: Date | string
|
||||||
) {
|
) {
|
||||||
return this.prisma.$transaction(async tx => {
|
return this.prisma.$transaction(async tx => {
|
||||||
const latestFlag = await tx.workspaceFeatures.findFirst({
|
const latestFlag = await tx.workspaceFeature.findFirst({
|
||||||
where: {
|
where: {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
feature: {
|
feature: {
|
||||||
@@ -223,7 +223,7 @@ export class FeatureService {
|
|||||||
return latestFlag.id;
|
return latestFlag.id;
|
||||||
} else {
|
} else {
|
||||||
// use latest version of feature
|
// use latest version of feature
|
||||||
const featureId = await tx.features
|
const featureId = await tx.feature
|
||||||
.findFirst({
|
.findFirst({
|
||||||
where: { feature, type: FeatureKind.Feature },
|
where: { feature, type: FeatureKind.Feature },
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
@@ -235,7 +235,7 @@ export class FeatureService {
|
|||||||
throw new Error(`Feature ${feature} not found`);
|
throw new Error(`Feature ${feature} not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx.workspaceFeatures
|
return tx.workspaceFeature
|
||||||
.create({
|
.create({
|
||||||
data: {
|
data: {
|
||||||
reason,
|
reason,
|
||||||
@@ -251,7 +251,7 @@ export class FeatureService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removeWorkspaceFeature(workspaceId: string, feature: FeatureType) {
|
async removeWorkspaceFeature(workspaceId: string, feature: FeatureType) {
|
||||||
return this.prisma.workspaceFeatures
|
return this.prisma.workspaceFeature
|
||||||
.updateMany({
|
.updateMany({
|
||||||
where: {
|
where: {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
@@ -274,7 +274,7 @@ export class FeatureService {
|
|||||||
* @returns list of features
|
* @returns list of features
|
||||||
*/
|
*/
|
||||||
async getWorkspaceFeatures(workspaceId: string) {
|
async getWorkspaceFeatures(workspaceId: string) {
|
||||||
const features = await this.prisma.workspaceFeatures.findMany({
|
const features = await this.prisma.workspaceFeature.findMany({
|
||||||
where: {
|
where: {
|
||||||
workspace: { id: workspaceId },
|
workspace: { id: workspaceId },
|
||||||
feature: {
|
feature: {
|
||||||
@@ -301,7 +301,7 @@ export class FeatureService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async listFeatureWorkspaces(feature: FeatureType): Promise<WorkspaceType[]> {
|
async listFeatureWorkspaces(feature: FeatureType): Promise<WorkspaceType[]> {
|
||||||
return this.prisma.workspaceFeatures
|
return this.prisma.workspaceFeature
|
||||||
.findMany({
|
.findMany({
|
||||||
where: {
|
where: {
|
||||||
activated: true,
|
activated: true,
|
||||||
@@ -324,7 +324,7 @@ export class FeatureService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async hasWorkspaceFeature(workspaceId: string, feature: FeatureType) {
|
async hasWorkspaceFeature(workspaceId: string, feature: FeatureType) {
|
||||||
return this.prisma.workspaceFeatures
|
return this.prisma.workspaceFeature
|
||||||
.count({
|
.count({
|
||||||
where: {
|
where: {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export class QuotaConfig {
|
|||||||
return cachedQuota;
|
return cachedQuota;
|
||||||
}
|
}
|
||||||
|
|
||||||
const quota = await tx.features.findFirst({
|
const quota = await tx.feature.findFirst({
|
||||||
where: {
|
where: {
|
||||||
id: featureId,
|
id: featureId,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export class QuotaService {
|
|||||||
|
|
||||||
// get activated user quota
|
// get activated user quota
|
||||||
async getUserQuota(userId: string) {
|
async getUserQuota(userId: string) {
|
||||||
const quota = await this.prisma.userFeatures.findFirst({
|
const quota = await this.prisma.userFeature.findFirst({
|
||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
feature: {
|
feature: {
|
||||||
@@ -44,7 +44,7 @@ export class QuotaService {
|
|||||||
|
|
||||||
// get user all quota records
|
// get user all quota records
|
||||||
async getUserQuotas(userId: string) {
|
async getUserQuotas(userId: string) {
|
||||||
const quotas = await this.prisma.userFeatures.findMany({
|
const quotas = await this.prisma.userFeature.findMany({
|
||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
feature: {
|
feature: {
|
||||||
@@ -58,6 +58,9 @@ export class QuotaService {
|
|||||||
expiredAt: true,
|
expiredAt: true,
|
||||||
featureId: true,
|
featureId: true,
|
||||||
},
|
},
|
||||||
|
orderBy: {
|
||||||
|
id: 'asc',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const configs = await Promise.all(
|
const configs = await Promise.all(
|
||||||
quotas.map(async quota => {
|
quotas.map(async quota => {
|
||||||
@@ -92,7 +95,7 @@ export class QuotaService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const featureId = await tx.features
|
const featureId = await tx.feature
|
||||||
.findFirst({
|
.findFirst({
|
||||||
where: { feature: quota, type: FeatureKind.Quota },
|
where: { feature: quota, type: FeatureKind.Quota },
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
@@ -105,7 +108,7 @@ export class QuotaService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// we will deactivate all exists quota for this user
|
// we will deactivate all exists quota for this user
|
||||||
await tx.userFeatures.updateMany({
|
await tx.userFeature.updateMany({
|
||||||
where: {
|
where: {
|
||||||
id: undefined,
|
id: undefined,
|
||||||
userId,
|
userId,
|
||||||
@@ -118,7 +121,7 @@ export class QuotaService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await tx.userFeatures.create({
|
await tx.userFeature.create({
|
||||||
data: {
|
data: {
|
||||||
userId,
|
userId,
|
||||||
featureId,
|
featureId,
|
||||||
@@ -133,7 +136,7 @@ export class QuotaService {
|
|||||||
async hasQuota(userId: string, quota: QuotaType, tx?: PrismaTransaction) {
|
async hasQuota(userId: string, quota: QuotaType, tx?: PrismaTransaction) {
|
||||||
const executor = tx ?? this.prisma;
|
const executor = tx ?? this.prisma;
|
||||||
|
|
||||||
return executor.userFeatures
|
return executor.userFeature
|
||||||
.count({
|
.count({
|
||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { PrismaClient } from '@prisma/client';
|
|||||||
|
|
||||||
import { Features } from '../../core/features';
|
import { Features } from '../../core/features';
|
||||||
import { Quotas } from '../../core/quota/schema';
|
import { Quotas } from '../../core/quota/schema';
|
||||||
import { migrateNewFeatureTable, upsertFeature } from './utils/user-features';
|
import { upsertFeature } from './utils/user-features';
|
||||||
|
|
||||||
export class UserFeaturesInit1698652531198 {
|
export class UserFeaturesInit1698652531198 {
|
||||||
// do the migration
|
// do the migration
|
||||||
@@ -11,7 +11,6 @@ export class UserFeaturesInit1698652531198 {
|
|||||||
for (const feature of Features) {
|
for (const feature of Features) {
|
||||||
await upsertFeature(db, feature);
|
await upsertFeature(db, feature);
|
||||||
}
|
}
|
||||||
await migrateNewFeatureTable(db);
|
|
||||||
|
|
||||||
for (const quota of Quotas) {
|
for (const quota of Quotas) {
|
||||||
await upsertFeature(db, quota);
|
await upsertFeature(db, quota);
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
import { PrismaClient } from '@prisma/client';
|
|
||||||
export class PagePermission1699005339766 {
|
|
||||||
// do the migration
|
|
||||||
static async up(db: PrismaClient) {
|
|
||||||
let turn = 0;
|
|
||||||
let lastTurnCount = 50;
|
|
||||||
const done = new Set<string>();
|
|
||||||
|
|
||||||
while (lastTurnCount === 50) {
|
|
||||||
const workspaces = await db.workspace.findMany({
|
|
||||||
skip: turn * 50,
|
|
||||||
take: 50,
|
|
||||||
orderBy: {
|
|
||||||
createdAt: 'asc',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
lastTurnCount = workspaces.length;
|
|
||||||
|
|
||||||
for (const workspace of workspaces) {
|
|
||||||
if (done.has(workspace.id)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldPermissions =
|
|
||||||
await db.deprecatedUserWorkspacePermission.findMany({
|
|
||||||
where: {
|
|
||||||
workspaceId: workspace.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const oldPermission of oldPermissions) {
|
|
||||||
// mark subpage public
|
|
||||||
if (oldPermission.subPageId) {
|
|
||||||
const existed = await db.workspacePage.findUnique({
|
|
||||||
where: {
|
|
||||||
workspaceId_pageId: {
|
|
||||||
workspaceId: oldPermission.workspaceId,
|
|
||||||
pageId: oldPermission.subPageId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (!existed) {
|
|
||||||
await db.workspacePage.create({
|
|
||||||
select: null,
|
|
||||||
data: {
|
|
||||||
workspaceId: oldPermission.workspaceId,
|
|
||||||
pageId: oldPermission.subPageId,
|
|
||||||
public: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (oldPermission.userId) {
|
|
||||||
// workspace user permission
|
|
||||||
const existed = await db.workspaceUserPermission.findUnique({
|
|
||||||
where: {
|
|
||||||
id: oldPermission.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existed) {
|
|
||||||
await db.workspaceUserPermission
|
|
||||||
.create({
|
|
||||||
select: null,
|
|
||||||
data: {
|
|
||||||
// this id is used at invite email, should keep
|
|
||||||
id: oldPermission.id,
|
|
||||||
workspaceId: oldPermission.workspaceId,
|
|
||||||
userId: oldPermission.userId,
|
|
||||||
type: oldPermission.type,
|
|
||||||
accepted: oldPermission.accepted,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// duplicated
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// ignore wrong data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
done.add(workspace.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
turn++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// revert the migration
|
|
||||||
static async down(db: PrismaClient) {
|
|
||||||
await db.workspaceUserPermission.deleteMany({});
|
|
||||||
await db.workspacePageUserPermission.deleteMany({});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@ export class OldUserFeature1702620653283 {
|
|||||||
// do the migration
|
// do the migration
|
||||||
static async up(db: PrismaClient) {
|
static async up(db: PrismaClient) {
|
||||||
await db.$transaction(async tx => {
|
await db.$transaction(async tx => {
|
||||||
const latestFreePlan = await tx.features.findFirstOrThrow({
|
const latestFreePlan = await tx.feature.findFirstOrThrow({
|
||||||
where: { feature: QuotaType.FreePlanV1 },
|
where: { feature: QuotaType.FreePlanV1 },
|
||||||
orderBy: { version: 'desc' },
|
orderBy: { version: 'desc' },
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
@@ -17,7 +17,7 @@ export class OldUserFeature1702620653283 {
|
|||||||
select: { id: true },
|
select: { id: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
await tx.userFeatures.createMany({
|
await tx.userFeature.createMany({
|
||||||
data: userIds.map(({ id: userId }) => ({
|
data: userIds.map(({ id: userId }) => ({
|
||||||
userId,
|
userId,
|
||||||
featureId: latestFreePlan.id,
|
featureId: latestFreePlan.id,
|
||||||
@@ -31,6 +31,6 @@ export class OldUserFeature1702620653283 {
|
|||||||
// revert the migration
|
// revert the migration
|
||||||
// WARN: this will drop all user features
|
// WARN: this will drop all user features
|
||||||
static async down(db: PrismaClient) {
|
static async down(db: PrismaClient) {
|
||||||
await db.userFeatures.deleteMany({});
|
await db.userFeature.deleteMany({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
import { ModuleRef } from '@nestjs/core';
|
|
||||||
import { PrismaClient } from '@prisma/client';
|
|
||||||
|
|
||||||
import { WorkspaceBlobStorage } from '../../core/storage';
|
|
||||||
|
|
||||||
export class WorkspaceBlobs1703828796699 {
|
|
||||||
// do the migration
|
|
||||||
static async up(db: PrismaClient, injector: ModuleRef) {
|
|
||||||
const blobStorage = injector.get(WorkspaceBlobStorage, { strict: false });
|
|
||||||
let hasMore = true;
|
|
||||||
let turn = 0;
|
|
||||||
const eachTurnCount = 50;
|
|
||||||
|
|
||||||
while (hasMore) {
|
|
||||||
const blobs = await db.blob.findMany({
|
|
||||||
skip: turn * eachTurnCount,
|
|
||||||
take: eachTurnCount,
|
|
||||||
orderBy: {
|
|
||||||
createdAt: 'asc',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
hasMore = blobs.length === eachTurnCount;
|
|
||||||
turn += 1;
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
blobs.map(async ({ workspaceId, hash, blob }) =>
|
|
||||||
blobStorage.put(workspaceId, hash, blob)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// revert the migration
|
|
||||||
static async down(_db: PrismaClient) {
|
|
||||||
// old data kept, no need to downgrade the migration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import { PrismaClient } from '@prisma/client';
|
|
||||||
|
|
||||||
import { loop } from './utils/loop';
|
|
||||||
|
|
||||||
export class Oauth1710319359062 {
|
|
||||||
// do the migration
|
|
||||||
static async up(db: PrismaClient) {
|
|
||||||
await loop(async (skip, take) => {
|
|
||||||
const oldRecords = await db.deprecatedNextAuthAccount.findMany({
|
|
||||||
skip,
|
|
||||||
take,
|
|
||||||
orderBy: {
|
|
||||||
providerAccountId: 'asc',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await db.connectedAccount.createMany({
|
|
||||||
data: oldRecords.map(record => ({
|
|
||||||
userId: record.userId,
|
|
||||||
provider: record.provider,
|
|
||||||
scope: record.scope,
|
|
||||||
providerAccountId: record.providerAccountId,
|
|
||||||
accessToken: record.access_token,
|
|
||||||
refreshToken: record.refresh_token,
|
|
||||||
expiresAt: record.expires_at
|
|
||||||
? new Date(record.expires_at * 1000)
|
|
||||||
: null,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
|
|
||||||
return oldRecords.length;
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
// revert the migration
|
|
||||||
static async down(db: PrismaClient) {
|
|
||||||
await db.connectedAccount.deleteMany({});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,6 @@
|
|||||||
import { Prisma, PrismaClient } from '@prisma/client';
|
import { Prisma, PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
import {
|
import { CommonFeature, Features, FeatureType } from '../../../core/features';
|
||||||
CommonFeature,
|
|
||||||
FeatureKind,
|
|
||||||
Features,
|
|
||||||
FeatureType,
|
|
||||||
} from '../../../core/features';
|
|
||||||
|
|
||||||
// upgrade features from lower version to higher version
|
// upgrade features from lower version to higher version
|
||||||
export async function upsertFeature(
|
export async function upsertFeature(
|
||||||
@@ -13,7 +8,7 @@ export async function upsertFeature(
|
|||||||
feature: CommonFeature
|
feature: CommonFeature
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const hasEqualOrGreaterVersion =
|
const hasEqualOrGreaterVersion =
|
||||||
(await db.features.count({
|
(await db.feature.count({
|
||||||
where: {
|
where: {
|
||||||
feature: feature.feature,
|
feature: feature.feature,
|
||||||
version: {
|
version: {
|
||||||
@@ -23,7 +18,7 @@ export async function upsertFeature(
|
|||||||
})) > 0;
|
})) > 0;
|
||||||
// will not update exists version
|
// will not update exists version
|
||||||
if (!hasEqualOrGreaterVersion) {
|
if (!hasEqualOrGreaterVersion) {
|
||||||
await db.features.create({
|
await db.feature.create({
|
||||||
data: {
|
data: {
|
||||||
feature: feature.feature,
|
feature: feature.feature,
|
||||||
type: feature.type,
|
type: feature.type,
|
||||||
@@ -43,66 +38,3 @@ export async function upsertLatestFeatureVersion(
|
|||||||
const latestFeature = feature[0];
|
const latestFeature = feature[0];
|
||||||
await upsertFeature(db, latestFeature);
|
await upsertFeature(db, latestFeature);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function migrateNewFeatureTable(prisma: PrismaClient) {
|
|
||||||
const waitingList = await prisma.newFeaturesWaitingList.findMany();
|
|
||||||
const latestEarlyAccessFeatureId = await prisma.features
|
|
||||||
.findFirst({
|
|
||||||
where: { feature: FeatureType.EarlyAccess, type: FeatureKind.Feature },
|
|
||||||
select: { id: true },
|
|
||||||
orderBy: { version: 'desc' },
|
|
||||||
})
|
|
||||||
.then(r => r?.id);
|
|
||||||
if (!latestEarlyAccessFeatureId) {
|
|
||||||
throw new Error('Feature EarlyAccess not found');
|
|
||||||
}
|
|
||||||
for (const oldUser of waitingList) {
|
|
||||||
const user = await prisma.user.findFirst({
|
|
||||||
where: {
|
|
||||||
email: oldUser.email,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (user) {
|
|
||||||
const hasEarlyAccess = await prisma.userFeatures.count({
|
|
||||||
where: {
|
|
||||||
userId: user.id,
|
|
||||||
feature: {
|
|
||||||
feature: FeatureType.EarlyAccess,
|
|
||||||
},
|
|
||||||
activated: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (hasEarlyAccess === 0) {
|
|
||||||
await prisma.$transaction(async tx => {
|
|
||||||
const latestFlag = await tx.userFeatures.findFirst({
|
|
||||||
where: {
|
|
||||||
userId: user.id,
|
|
||||||
feature: {
|
|
||||||
feature: FeatureType.EarlyAccess,
|
|
||||||
type: FeatureKind.Feature,
|
|
||||||
},
|
|
||||||
activated: true,
|
|
||||||
},
|
|
||||||
orderBy: {
|
|
||||||
createdAt: 'desc',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (latestFlag) {
|
|
||||||
return latestFlag.id;
|
|
||||||
} else {
|
|
||||||
return tx.userFeatures
|
|
||||||
.create({
|
|
||||||
data: {
|
|
||||||
reason: 'Early access user',
|
|
||||||
activated: true,
|
|
||||||
userId: user.id,
|
|
||||||
featureId: latestEarlyAccessFeatureId,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then(r => r.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export async function upgradeQuotaVersion(
|
|||||||
// migrate all users that using old quota to new quota
|
// migrate all users that using old quota to new quota
|
||||||
await db.$transaction(
|
await db.$transaction(
|
||||||
async tx => {
|
async tx => {
|
||||||
const latestQuotaVersion = await tx.features.findFirstOrThrow({
|
const latestQuotaVersion = await tx.feature.findFirstOrThrow({
|
||||||
where: { feature: quota.feature },
|
where: { feature: quota.feature },
|
||||||
orderBy: { version: 'desc' },
|
orderBy: { version: 'desc' },
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
@@ -39,7 +39,7 @@ export async function upgradeQuotaVersion(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// deactivate all old quota for the user
|
// deactivate all old quota for the user
|
||||||
await tx.userFeatures.updateMany({
|
await tx.userFeature.updateMany({
|
||||||
where: {
|
where: {
|
||||||
id: undefined,
|
id: undefined,
|
||||||
userId: {
|
userId: {
|
||||||
@@ -55,7 +55,7 @@ export async function upgradeQuotaVersion(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await tx.userFeatures.createMany({
|
await tx.userFeature.createMany({
|
||||||
data: userIds.map(({ id: userId }) => ({
|
data: userIds.map(({ id: userId }) => ({
|
||||||
userId,
|
userId,
|
||||||
featureId: latestQuotaVersion.id,
|
featureId: latestQuotaVersion.id,
|
||||||
|
|||||||
@@ -84,17 +84,17 @@ export class SubscriptionService {
|
|||||||
private readonly db: PrismaClient,
|
private readonly db: PrismaClient,
|
||||||
private readonly scheduleManager: ScheduleManager,
|
private readonly scheduleManager: ScheduleManager,
|
||||||
private readonly event: EventEmitter,
|
private readonly event: EventEmitter,
|
||||||
private readonly features: FeatureManagementService
|
private readonly feature: FeatureManagementService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async listPrices(user?: CurrentUser) {
|
async listPrices(user?: CurrentUser) {
|
||||||
let canHaveEarlyAccessDiscount = false;
|
let canHaveEarlyAccessDiscount = false;
|
||||||
let canHaveAIEarlyAccessDiscount = false;
|
let canHaveAIEarlyAccessDiscount = false;
|
||||||
if (user) {
|
if (user) {
|
||||||
canHaveEarlyAccessDiscount = await this.features.isEarlyAccessUser(
|
canHaveEarlyAccessDiscount = await this.feature.isEarlyAccessUser(
|
||||||
user.id
|
user.id
|
||||||
);
|
);
|
||||||
canHaveAIEarlyAccessDiscount = await this.features.isEarlyAccessUser(
|
canHaveAIEarlyAccessDiscount = await this.feature.isEarlyAccessUser(
|
||||||
user.id,
|
user.id,
|
||||||
EarlyAccessType.AI
|
EarlyAccessType.AI
|
||||||
);
|
);
|
||||||
@@ -181,7 +181,7 @@ export class SubscriptionService {
|
|||||||
if (
|
if (
|
||||||
this.config.deploy &&
|
this.config.deploy &&
|
||||||
this.config.affine.canary &&
|
this.config.affine.canary &&
|
||||||
!this.features.isStaff(user.email)
|
!this.feature.isStaff(user.email)
|
||||||
) {
|
) {
|
||||||
throw new ActionForbidden();
|
throw new ActionForbidden();
|
||||||
}
|
}
|
||||||
@@ -847,7 +847,7 @@ export class SubscriptionService {
|
|||||||
plan: SubscriptionPlan,
|
plan: SubscriptionPlan,
|
||||||
recurring: SubscriptionRecurring
|
recurring: SubscriptionRecurring
|
||||||
): Promise<{ price: string; coupon?: string }> {
|
): Promise<{ price: string; coupon?: string }> {
|
||||||
const isEaUser = await this.features.isEarlyAccessUser(customer.userId);
|
const isEaUser = await this.feature.isEarlyAccessUser(customer.userId);
|
||||||
const oldSubscriptions = await this.stripe.subscriptions.list({
|
const oldSubscriptions = await this.stripe.subscriptions.list({
|
||||||
customer: customer.stripeCustomerId,
|
customer: customer.stripeCustomerId,
|
||||||
status: 'all',
|
status: 'all',
|
||||||
@@ -876,7 +876,7 @@ export class SubscriptionService {
|
|||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const isAIEaUser = await this.features.isEarlyAccessUser(
|
const isAIEaUser = await this.feature.isEarlyAccessUser(
|
||||||
customer.userId,
|
customer.userId,
|
||||||
EarlyAccessType.AI
|
EarlyAccessType.AI
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ export async function createRandomUser(): Promise<{
|
|||||||
password: '123456',
|
password: '123456',
|
||||||
};
|
};
|
||||||
const result = await runPrisma(async client => {
|
const result = await runPrisma(async client => {
|
||||||
const featureId = await client.features
|
const featureId = await client.feature
|
||||||
.findFirst({
|
.findFirst({
|
||||||
where: { feature: 'free_plan_v1' },
|
where: { feature: 'free_plan_v1' },
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
|
|||||||
Reference in New Issue
Block a user