From e2adab7805d4da670c35dcc3072f8d0dc1b217f5 Mon Sep 17 00:00:00 2001 From: DarkSky <25152247+darkskygit@users.noreply.github.com> Date: Tue, 30 Dec 2025 12:01:27 +0800 Subject: [PATCH] fix: table compatibility (#14185) --- .../migration.sql | 69 ++++++--- .../src/plugins/payment/manager/user.ts | 9 +- packages/common/env/package.json | 1 - .../env/src/workspace/legacy-cloud/index.ts | 132 ------------------ 4 files changed, 52 insertions(+), 159 deletions(-) delete mode 100644 packages/common/env/src/workspace/legacy-cloud/index.ts diff --git a/packages/backend/server/migrations/20251229174000_update_feature/migration.sql b/packages/backend/server/migrations/20251229174000_update_feature/migration.sql index b57055cbb..a00d66136 100644 --- a/packages/backend/server/migrations/20251229174000_update_feature/migration.sql +++ b/packages/backend/server/migrations/20251229174000_update_feature/migration.sql @@ -1,28 +1,57 @@ /* - Warnings: - - - You are about to drop the column `feature_id` on the `user_features` table. All the data in the column will be lost. - - You are about to drop the column `feature_id` on the `workspace_features` table. All the data in the column will be lost. - - You are about to drop the `features` table. If the table is not empty, all the data it contains will be lost. - + Preserve backward compatibility between beta and stable deployments that share a database. + Newer code no longer writes feature_id, so we keep the legacy columns/table and backfill + them via triggers instead of dropping them. */ --- DropForeignKey -ALTER TABLE "user_features" DROP CONSTRAINT "user_features_feature_id_fkey"; --- DropForeignKey -ALTER TABLE "workspace_features" DROP CONSTRAINT "workspace_features_feature_id_fkey"; +-- Ensure a feature row exists and return its id +CREATE OR REPLACE FUNCTION ensure_feature_exists(feature_name TEXT) RETURNS INTEGER AS $$ +DECLARE + feature_record INTEGER; +BEGIN + SELECT id INTO feature_record FROM "features" WHERE "feature" = feature_name ORDER BY "version" DESC LIMIT 1; --- DropIndex -DROP INDEX "user_features_feature_id_idx"; + IF feature_record IS NULL THEN + INSERT INTO "features" ("feature", "configs") + VALUES (feature_name, '{}') + ON CONFLICT ("feature", "version") DO NOTHING + RETURNING id INTO feature_record; --- DropIndex -DROP INDEX "workspace_features_feature_id_idx"; + IF feature_record IS NULL THEN + SELECT id INTO feature_record FROM "features" WHERE "feature" = feature_name ORDER BY "version" DESC LIMIT 1; + END IF; + END IF; --- AlterTable -ALTER TABLE "user_features" DROP COLUMN "feature_id"; + RETURN feature_record; +END; +$$ LANGUAGE plpgsql; --- AlterTable -ALTER TABLE "workspace_features" DROP COLUMN "feature_id"; +-- Fill user_features.feature_id when omitted by newer code +CREATE OR REPLACE FUNCTION set_user_feature_id_from_name() RETURNS trigger AS $$ +BEGIN + IF NEW.feature_id IS NULL THEN + NEW.feature_id := ensure_feature_exists(NEW.name); + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; --- DropTable -DROP TABLE "features"; +DROP TRIGGER IF EXISTS user_features_set_feature_id ON "user_features"; +CREATE TRIGGER user_features_set_feature_id +BEFORE INSERT OR UPDATE ON "user_features" +FOR EACH ROW EXECUTE FUNCTION set_user_feature_id_from_name(); + +-- Fill workspace_features.feature_id when omitted by newer code +CREATE OR REPLACE FUNCTION set_workspace_feature_id_from_name() RETURNS trigger AS $$ +BEGIN + IF NEW.feature_id IS NULL THEN + NEW.feature_id := ensure_feature_exists(NEW.name); + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +DROP TRIGGER IF EXISTS workspace_features_set_feature_id ON "workspace_features"; +CREATE TRIGGER workspace_features_set_feature_id +BEFORE INSERT OR UPDATE ON "workspace_features" +FOR EACH ROW EXECUTE FUNCTION set_workspace_feature_id_from_name(); diff --git a/packages/backend/server/src/plugins/payment/manager/user.ts b/packages/backend/server/src/plugins/payment/manager/user.ts index fbec833fa..9743beadd 100644 --- a/packages/backend/server/src/plugins/payment/manager/user.ts +++ b/packages/backend/server/src/plugins/payment/manager/user.ts @@ -499,13 +499,10 @@ export class UserSubscriptionManager extends SubscriptionManager { }, }); - // TODO(@forehalo): time helper const subscriptionTime = - (lookupKey.recurring === SubscriptionRecurring.Monthly ? 30 : 365) * - 24 * - 60 * - 60 * - 1000; + lookupKey.recurring === SubscriptionRecurring.Monthly + ? OneMonth + : OneYear; let subscription: Subscription; diff --git a/packages/common/env/package.json b/packages/common/env/package.json index acdfbb824..4c84f9338 100644 --- a/packages/common/env/package.json +++ b/packages/common/env/package.json @@ -10,7 +10,6 @@ "./global": "./src/global.ts", "./constant": "./src/constant.ts", "./workspace": "./src/workspace.ts", - "./workspace/legacy-cloud": "./src/workspace/legacy-cloud/index.ts", "./filter": "./src/filter.ts", "./blocksuite": "./src/blocksuite/index.ts", "./worker": "./src/worker.ts" diff --git a/packages/common/env/src/workspace/legacy-cloud/index.ts b/packages/common/env/src/workspace/legacy-cloud/index.ts deleted file mode 100644 index fae7c9381..000000000 --- a/packages/common/env/src/workspace/legacy-cloud/index.ts +++ /dev/null @@ -1,132 +0,0 @@ -/** - * @deprecated Remove this file after we migrate to the new cloud. - */ -import { z } from 'zod'; - -export interface User { - id: string; - name: string; - email: string; - avatar_url: string; - create_at: string; -} - -export interface GetUserByEmailParams { - email: string; - workspace_id: string; -} - -export const usageResponseSchema = z.object({ - blob_usage: z.object({ - usage: z.number(), - max_usage: z.number(), - }), -}); - -export type UsageResponse = z.infer; - -export interface GetWorkspaceDetailParams { - id: string; -} - -export enum WorkspaceType { - Private = 0, - Normal = 1, -} - -export enum PermissionType { - Read = 0, - Write = 1, - Admin = 10, - Owner = 99, -} - -export const userSchema = z.object({ - id: z.string(), - name: z.string(), - email: z.string(), - avatar_url: z.string(), - created_at: z.number(), -}); - -export const workspaceSchema = z.object({ - id: z.string(), - type: z.nativeEnum(WorkspaceType), - public: z.boolean(), - permission: z.nativeEnum(PermissionType), -}); - -export type Workspace = z.infer; - -export const workspaceDetailSchema = z.object({ - ...workspaceSchema.shape, - permission: z.undefined(), - owner: userSchema, - member_count: z.number(), -}); - -export type WorkspaceDetail = z.infer; - -export interface Permission { - id: string; - type: PermissionType; - workspace_id: string; - user_id: string; - user_email: string; - accepted: boolean; - create_at: number; -} - -export interface RegisteredUser extends User { - type: 'Registered'; -} - -export interface UnregisteredUser { - type: 'Unregistered'; - email: string; -} - -export interface Member extends Permission { - user: RegisteredUser | UnregisteredUser; -} - -export interface GetWorkspaceMembersParams { - id: string; -} - -export interface CreateWorkspaceParams { - name: string; -} - -export interface UpdateWorkspaceParams { - id: string; - public: boolean; -} - -export interface DeleteWorkspaceParams { - id: string; -} - -export interface InviteMemberParams { - id: string; - email: string; -} - -export interface RemoveMemberParams { - permissionId: number; -} - -export interface AcceptInvitingParams { - invitingCode: string; -} - -export interface LeaveWorkspaceParams { - id: number | string; -} - -export const createWorkspaceResponseSchema = z.object({ - id: z.string(), - public: z.boolean(), - type: z.nativeEnum(WorkspaceType), - created_at: z.number(), -});