#### PR Dependency Tree * **PR #14964** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added entitlement resolution to validate licenses and derive plan, quotas, expiry and flags. * Introduced persistent quota/entitlement state for users and workspaces with legacy sync behavior. * Real-time quota-state operations and change events for monitoring usage. * **Chores** * Updated workspace dependencies to add cryptography/hash crates. * **Tests** * Added native entitlement tests covering validation, quantity handling, and signature/expiry cases. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/toeverything/AFFiNE/pull/14964) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
185 lines
8.1 KiB
PL/PgSQL
185 lines
8.1 KiB
PL/PgSQL
-- CreateTable
|
|
CREATE TABLE "entitlements" (
|
|
"id" VARCHAR NOT NULL,
|
|
"target_type" TEXT NOT NULL,
|
|
"target_id" VARCHAR,
|
|
"source" TEXT NOT NULL,
|
|
"plan" TEXT NOT NULL,
|
|
"status" TEXT NOT NULL,
|
|
"subject_id" VARCHAR,
|
|
"issuer" TEXT,
|
|
"quantity" INTEGER,
|
|
"signed_payload" BYTEA,
|
|
"token_hash" TEXT,
|
|
"metadata" JSONB NOT NULL DEFAULT '{}',
|
|
"issued_at" TIMESTAMPTZ(3),
|
|
"starts_at" TIMESTAMPTZ(3),
|
|
"expires_at" TIMESTAMPTZ(3),
|
|
"validated_at" TIMESTAMPTZ(3),
|
|
"grace_until" TIMESTAMPTZ(3),
|
|
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "entitlements_pkey" PRIMARY KEY ("id"),
|
|
CONSTRAINT "entitlements_target_type_check" CHECK ("target_type" IN ('user', 'workspace', 'instance')),
|
|
CONSTRAINT "entitlements_source_check" CHECK ("source" IN ('builtin', 'cloud_subscription', 'selfhost_license', 'admin_grant')),
|
|
CONSTRAINT "entitlements_status_check" CHECK ("status" IN ('active', 'grace', 'expired', 'revoked', 'needs_reupload')),
|
|
CONSTRAINT "entitlements_quantity_check" CHECK ("quantity" IS NULL OR ("quantity" > 0 AND "quantity" <= 100000))
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "effective_user_quota_states" (
|
|
"user_id" VARCHAR NOT NULL,
|
|
"plan" TEXT NOT NULL,
|
|
"source_entitlement_id" VARCHAR,
|
|
"blob_limit" BIGINT NOT NULL,
|
|
"storage_quota" BIGINT NOT NULL,
|
|
"used_storage_quota" BIGINT NOT NULL DEFAULT 0,
|
|
"history_period_seconds" INTEGER NOT NULL,
|
|
"copilot_action_limit" INTEGER,
|
|
"flags" JSONB NOT NULL DEFAULT '{}',
|
|
"known" BOOLEAN NOT NULL DEFAULT false,
|
|
"stale" BOOLEAN NOT NULL DEFAULT false,
|
|
"last_reconciled_at" TIMESTAMPTZ(3),
|
|
"stale_after" TIMESTAMPTZ(3),
|
|
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "effective_user_quota_states_pkey" PRIMARY KEY ("user_id"),
|
|
CONSTRAINT "effective_user_quota_states_blob_limit_check" CHECK ("blob_limit" >= 0),
|
|
CONSTRAINT "effective_user_quota_states_storage_quota_check" CHECK ("storage_quota" >= 0),
|
|
CONSTRAINT "effective_user_quota_states_used_storage_quota_check" CHECK ("used_storage_quota" >= 0),
|
|
CONSTRAINT "effective_user_quota_states_history_period_check" CHECK ("history_period_seconds" >= 0),
|
|
CONSTRAINT "effective_user_quota_states_copilot_limit_check" CHECK ("copilot_action_limit" IS NULL OR "copilot_action_limit" >= 0)
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "effective_workspace_quota_states" (
|
|
"workspace_id" VARCHAR NOT NULL,
|
|
"plan" TEXT NOT NULL,
|
|
"source_entitlement_id" VARCHAR,
|
|
"owner_user_id" VARCHAR,
|
|
"uses_owner_quota" BOOLEAN NOT NULL DEFAULT false,
|
|
"seat_limit" INTEGER NOT NULL,
|
|
"member_count" INTEGER NOT NULL DEFAULT 0,
|
|
"overcapacity_member_count" INTEGER NOT NULL DEFAULT 0,
|
|
"blob_limit" BIGINT NOT NULL,
|
|
"storage_quota" BIGINT NOT NULL,
|
|
"used_storage_quota" BIGINT NOT NULL DEFAULT 0,
|
|
"history_period_seconds" INTEGER NOT NULL,
|
|
"readonly" BOOLEAN NOT NULL DEFAULT false,
|
|
"readonly_reasons" TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[],
|
|
"flags" JSONB NOT NULL DEFAULT '{}',
|
|
"known" BOOLEAN NOT NULL DEFAULT false,
|
|
"stale" BOOLEAN NOT NULL DEFAULT false,
|
|
"last_reconciled_at" TIMESTAMPTZ(3),
|
|
"stale_after" TIMESTAMPTZ(3),
|
|
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "effective_workspace_quota_states_pkey" PRIMARY KEY ("workspace_id"),
|
|
CONSTRAINT "effective_workspace_quota_states_seat_limit_check" CHECK ("seat_limit" >= 0),
|
|
CONSTRAINT "effective_workspace_quota_states_member_count_check" CHECK ("member_count" >= 0),
|
|
CONSTRAINT "effective_workspace_quota_states_overcapacity_check" CHECK ("overcapacity_member_count" >= 0),
|
|
CONSTRAINT "effective_workspace_quota_states_blob_limit_check" CHECK ("blob_limit" >= 0),
|
|
CONSTRAINT "effective_workspace_quota_states_storage_quota_check" CHECK ("storage_quota" >= 0),
|
|
CONSTRAINT "effective_workspace_quota_states_used_storage_quota_check" CHECK ("used_storage_quota" >= 0),
|
|
CONSTRAINT "effective_workspace_quota_states_history_period_check" CHECK ("history_period_seconds" >= 0),
|
|
CONSTRAINT "effective_workspace_quota_states_readonly_reasons_check" CHECK ("readonly_reasons" <@ ARRAY['member_overflow', 'storage_overflow']::TEXT[])
|
|
);
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "entitlements_target_type_target_id_status_idx" ON "entitlements"("target_type", "target_id", "status");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "entitlements_status_expires_at_idx" ON "entitlements"("status", "expires_at");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "entitlements_active_subject_key" ON "entitlements"("source", "subject_id")
|
|
WHERE "subject_id" IS NOT NULL AND "status" IN ('active', 'grace');
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "effective_user_quota_states_known_stale_idx" ON "effective_user_quota_states"("known", "stale");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "effective_user_quota_states_stale_after_idx" ON "effective_user_quota_states"("stale_after");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "effective_workspace_quota_states_owner_user_id_idx" ON "effective_workspace_quota_states"("owner_user_id");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "effective_workspace_quota_states_known_stale_idx" ON "effective_workspace_quota_states"("known", "stale");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "effective_workspace_quota_states_readonly_stale_idx" ON "effective_workspace_quota_states"("readonly", "stale");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "effective_workspace_quota_states_stale_after_idx" ON "effective_workspace_quota_states"("stale_after");
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "effective_user_quota_states" ADD CONSTRAINT "effective_user_quota_states_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "effective_user_quota_states" ADD CONSTRAINT "effective_user_quota_states_source_entitlement_id_fkey" FOREIGN KEY ("source_entitlement_id") REFERENCES "entitlements"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "effective_workspace_quota_states" ADD CONSTRAINT "effective_workspace_quota_states_workspace_id_fkey" FOREIGN KEY ("workspace_id") REFERENCES "workspaces"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "effective_workspace_quota_states" ADD CONSTRAINT "effective_workspace_quota_states_owner_user_id_fkey" FOREIGN KEY ("owner_user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "effective_workspace_quota_states" ADD CONSTRAINT "effective_workspace_quota_states_source_entitlement_id_fkey" FOREIGN KEY ("source_entitlement_id") REFERENCES "entitlements"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
CREATE OR REPLACE FUNCTION "project_legacy_workspace_readonly_feature"()
|
|
RETURNS trigger
|
|
LANGUAGE plpgsql
|
|
AS $$
|
|
BEGIN
|
|
IF TG_OP = 'DELETE' THEN
|
|
DELETE FROM "workspace_features"
|
|
WHERE "workspace_id" = OLD."workspace_id"
|
|
AND "name" = 'quota_exceeded_readonly_workspace_v1';
|
|
RETURN OLD;
|
|
END IF;
|
|
|
|
IF NEW."readonly" THEN
|
|
UPDATE "workspace_features"
|
|
SET "reason" = 'legacy quota state projection trigger',
|
|
"activated" = true
|
|
WHERE "workspace_id" = NEW."workspace_id"
|
|
AND "name" = 'quota_exceeded_readonly_workspace_v1';
|
|
|
|
IF NOT FOUND THEN
|
|
INSERT INTO "workspace_features"(
|
|
"workspace_id",
|
|
"name",
|
|
"type",
|
|
"configs",
|
|
"reason",
|
|
"activated"
|
|
)
|
|
VALUES (
|
|
NEW."workspace_id",
|
|
'quota_exceeded_readonly_workspace_v1',
|
|
0,
|
|
'{}',
|
|
'legacy quota state projection trigger',
|
|
true
|
|
);
|
|
END IF;
|
|
ELSE
|
|
DELETE FROM "workspace_features"
|
|
WHERE "workspace_id" = NEW."workspace_id"
|
|
AND "name" = 'quota_exceeded_readonly_workspace_v1';
|
|
END IF;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$;
|
|
|
|
CREATE TRIGGER "project_legacy_workspace_readonly_feature_trigger"
|
|
AFTER INSERT OR UPDATE OF "readonly" OR DELETE ON "effective_workspace_quota_states"
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION "project_legacy_workspace_readonly_feature"();
|