From 5f91c40e10791c25c59a1e26678b7f8d36d86396 Mon Sep 17 00:00:00 2001 From: Igor Zalutski Date: Thu, 10 Oct 2024 18:03:51 +0100 Subject: [PATCH 01/23] Add Prisma, pull schema, rewrite 1 query --- package.json | 2 + pnpm-lock.yaml | 66 ++ prisma/migrations/0_init/migration.sql | 1330 ++++++++++++++++++++++++ prisma/schema.prisma | 1230 ++++++++++++++++++++++ src/data/user/projects.tsx | 36 +- 5 files changed, 2656 insertions(+), 8 deletions(-) create mode 100644 prisma/migrations/0_init/migration.sql create mode 100644 prisma/schema.prisma diff --git a/package.json b/package.json index fd1ecfb2..f0325298 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@mdx-js/react": "^3.0.0", "@next/bundle-analyzer": "^14.0.0", "@next/mdx": "^14.2.3", + "@prisma/client": "^5.20.0", "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-aspect-ratio": "^1.0.3", @@ -235,6 +236,7 @@ "pg": "^8.11.3", "postcss": "^8.4.20", "prettier": "^3.0.0", + "prisma": "^5.20.0", "react-spring": "^9.7.3", "rehype-autolink-headings": "^7.0.0", "rehype-parse": "^9.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a8e2daa1..1fb9f8cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: '@next/mdx': specifier: ^14.2.3 version: 14.2.5(@mdx-js/loader@3.0.1(webpack@5.93.0))(@mdx-js/react@3.0.1(@types/react@18.3.2)(react@18.3.1)) + '@prisma/client': + specifier: ^5.20.0 + version: 5.20.0(prisma@5.20.0) '@radix-ui/react-accordion': specifier: ^1.1.2 version: 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -639,6 +642,9 @@ importers: prettier: specifier: ^3.0.0 version: 3.3.3 + prisma: + specifier: ^5.20.0 + version: 5.20.0 react-spring: specifier: ^9.7.3 version: 9.7.3(@react-three/fiber@8.16.8(react-dom@18.3.1(react@18.3.1))(react-native@0.74.3(@babel/core@7.24.9)(@babel/preset-env@7.24.8(@babel/core@7.24.9))(@types/react@18.3.2)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)(three@0.166.1))(konva@9.3.13)(react-dom@18.3.1(react@18.3.1))(react-konva@18.2.10(konva@9.3.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-native@0.74.3(@babel/core@7.24.9)(@babel/preset-env@7.24.8(@babel/core@7.24.9))(@types/react@18.3.2)(encoding@0.1.13)(react@18.3.1))(react-zdog@1.2.2)(react@18.3.1)(three@0.166.1)(zdog@1.1.3) @@ -2123,6 +2129,30 @@ packages: '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + '@prisma/client@5.20.0': + resolution: {integrity: sha512-CLv55ZuMuUawMsxoqxGtLT3bEZoa2W8L3Qnp6rDIFWy+ZBrUcOFKdoeGPSnbBqxc3SkdxJrF+D1veN/WNynZYA==} + engines: {node: '>=16.13'} + peerDependencies: + prisma: '*' + peerDependenciesMeta: + prisma: + optional: true + + '@prisma/debug@5.20.0': + resolution: {integrity: sha512-oCx79MJ4HSujokA8S1g0xgZUGybD4SyIOydoHMngFYiwEwYDQ5tBQkK5XoEHuwOYDKUOKRn/J0MEymckc4IgsQ==} + + '@prisma/engines-version@5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284': + resolution: {integrity: sha512-Lg8AS5lpi0auZe2Mn4gjuCg081UZf88k3cn0RCwHgR+6cyHHpttPZBElJTHf83ZGsRNAmVCZCfUGA57WB4u4JA==} + + '@prisma/engines@5.20.0': + resolution: {integrity: sha512-DtqkP+hcZvPEbj8t8dK5df2b7d3B8GNauKqaddRRqQBBlgkbdhJkxhoJTrOowlS3vaRt2iMCkU0+CSNn0KhqAQ==} + + '@prisma/fetch-engine@5.20.0': + resolution: {integrity: sha512-JVcaPXC940wOGpCOwuqQRTz6I9SaBK0c1BAyC1pcz9xBi+dzFgUu3G/p9GV1FhFs9OKpfSpIhQfUJE9y00zhqw==} + + '@prisma/get-platform@5.20.0': + resolution: {integrity: sha512-8/+CehTZZNzJlvuryRgc77hZCWrUDYd/PmlZ7p2yNXtmf2Una4BWnTbak3us6WVdqoz5wmptk6IhsXdG2v5fmA==} + '@radix-ui/number@1.1.0': resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} @@ -7697,6 +7727,11 @@ packages: resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} engines: {node: '>= 0.8'} + prisma@5.20.0: + resolution: {integrity: sha512-6obb3ucKgAnsGS9x9gLOe8qa51XxvJ3vLQtmyf52CTey1Qcez3A6W6ROH5HIz5Q5bW+0VpmZb8WBohieMFGpig==} + engines: {node: '>=16.13'} + hasBin: true + prismjs@1.27.0: resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} engines: {node: '>=6'} @@ -11158,6 +11193,31 @@ snapshots: '@popperjs/core@2.11.8': {} + '@prisma/client@5.20.0(prisma@5.20.0)': + optionalDependencies: + prisma: 5.20.0 + + '@prisma/debug@5.20.0': {} + + '@prisma/engines-version@5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284': {} + + '@prisma/engines@5.20.0': + dependencies: + '@prisma/debug': 5.20.0 + '@prisma/engines-version': 5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284 + '@prisma/fetch-engine': 5.20.0 + '@prisma/get-platform': 5.20.0 + + '@prisma/fetch-engine@5.20.0': + dependencies: + '@prisma/debug': 5.20.0 + '@prisma/engines-version': 5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284 + '@prisma/get-platform': 5.20.0 + + '@prisma/get-platform@5.20.0': + dependencies: + '@prisma/debug': 5.20.0 + '@radix-ui/number@1.1.0': {} '@radix-ui/primitive@1.0.1': @@ -18042,6 +18102,12 @@ snapshots: pretty-hrtime@1.0.3: {} + prisma@5.20.0: + dependencies: + '@prisma/engines': 5.20.0 + optionalDependencies: + fsevents: 2.3.3 + prismjs@1.27.0: {} prismjs@1.29.0: {} diff --git a/prisma/migrations/0_init/migration.sql b/prisma/migrations/0_init/migration.sql new file mode 100644 index 00000000..173835df --- /dev/null +++ b/prisma/migrations/0_init/migration.sql @@ -0,0 +1,1330 @@ +-- CreateSchema +CREATE SCHEMA IF NOT EXISTS "auth"; + +-- CreateSchema +CREATE SCHEMA IF NOT EXISTS "public"; + +-- CreateEnum +CREATE TYPE "auth"."aal_level" AS ENUM ('aal1', 'aal2', 'aal3'); + +-- CreateEnum +CREATE TYPE "auth"."code_challenge_method" AS ENUM ('s256', 'plain'); + +-- CreateEnum +CREATE TYPE "auth"."factor_status" AS ENUM ('unverified', 'verified'); + +-- CreateEnum +CREATE TYPE "auth"."factor_type" AS ENUM ('totp', 'webauthn', 'phone'); + +-- CreateEnum +CREATE TYPE "auth"."one_time_token_type" AS ENUM ('confirmation_token', 'reauthentication_token', 'recovery_token', 'email_change_token_new', 'email_change_token_current', 'phone_change_token'); + +-- CreateEnum +CREATE TYPE "public"."app_admin_role" AS ENUM ('moderator', 'admin', 'super_admin'); + +-- CreateEnum +CREATE TYPE "public"."app_role" AS ENUM ('admin'); + +-- CreateEnum +CREATE TYPE "public"."iac_type_enum" AS ENUM ('terraform', 'terragrunt', 'opentofu'); + +-- CreateEnum +CREATE TYPE "public"."internal_blog_post_status" AS ENUM ('draft', 'published'); + +-- CreateEnum +CREATE TYPE "public"."internal_feedback_thread_priority" AS ENUM ('low', 'medium', 'high'); + +-- CreateEnum +CREATE TYPE "public"."internal_feedback_thread_status" AS ENUM ('open', 'under_review', 'planned', 'closed', 'in_progress', 'completed'); + +-- CreateEnum +CREATE TYPE "public"."internal_feedback_thread_type" AS ENUM ('bug', 'feature_request', 'general'); + +-- CreateEnum +CREATE TYPE "public"."organization_join_invitation_link_status" AS ENUM ('active', 'finished_accepted', 'finished_declined', 'inactive'); + +-- CreateEnum +CREATE TYPE "public"."organization_joining_status" AS ENUM ('invited', 'joinied', 'declined_invitation', 'joined'); + +-- CreateEnum +CREATE TYPE "public"."organization_member_role" AS ENUM ('owner', 'admin', 'member', 'readonly'); + +-- CreateEnum +CREATE TYPE "public"."pricing_plan_interval" AS ENUM ('day', 'week', 'month', 'year'); + +-- CreateEnum +CREATE TYPE "public"."pricing_type" AS ENUM ('one_time', 'recurring'); + +-- CreateEnum +CREATE TYPE "public"."project_status" AS ENUM ('draft', 'pending_approval', 'approved', 'completed'); + +-- CreateEnum +CREATE TYPE "public"."project_team_member_role" AS ENUM ('admin', 'member', 'readonly'); + +-- CreateEnum +CREATE TYPE "public"."subscription_status" AS ENUM ('trialing', 'active', 'canceled', 'incomplete', 'incomplete_expired', 'past_due', 'unpaid', 'paused'); + +-- CreateTable +CREATE TABLE "auth"."audit_log_entries" ( + "instance_id" UUID, + "id" UUID NOT NULL, + "payload" JSON, + "created_at" TIMESTAMPTZ(6), + "ip_address" VARCHAR(64) NOT NULL DEFAULT '', + + CONSTRAINT "audit_log_entries_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "auth"."flow_state" ( + "id" UUID NOT NULL, + "user_id" UUID, + "auth_code" TEXT NOT NULL, + "code_challenge_method" "auth"."code_challenge_method" NOT NULL, + "code_challenge" TEXT NOT NULL, + "provider_type" TEXT NOT NULL, + "provider_access_token" TEXT, + "provider_refresh_token" TEXT, + "created_at" TIMESTAMPTZ(6), + "updated_at" TIMESTAMPTZ(6), + "authentication_method" TEXT NOT NULL, + "auth_code_issued_at" TIMESTAMPTZ(6), + + CONSTRAINT "flow_state_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "auth"."identities" ( + "provider_id" TEXT NOT NULL, + "user_id" UUID NOT NULL, + "identity_data" JSONB NOT NULL, + "provider" TEXT NOT NULL, + "last_sign_in_at" TIMESTAMPTZ(6), + "created_at" TIMESTAMPTZ(6), + "updated_at" TIMESTAMPTZ(6), + "email" TEXT DEFAULT lower((identity_data ->> 'email'::text)), + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + + CONSTRAINT "identities_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "auth"."instances" ( + "id" UUID NOT NULL, + "uuid" UUID, + "raw_base_config" TEXT, + "created_at" TIMESTAMPTZ(6), + "updated_at" TIMESTAMPTZ(6), + + CONSTRAINT "instances_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "auth"."mfa_amr_claims" ( + "session_id" UUID NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL, + "updated_at" TIMESTAMPTZ(6) NOT NULL, + "authentication_method" TEXT NOT NULL, + "id" UUID NOT NULL, + + CONSTRAINT "amr_id_pk" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "auth"."mfa_challenges" ( + "id" UUID NOT NULL, + "factor_id" UUID NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL, + "verified_at" TIMESTAMPTZ(6), + "ip_address" INET NOT NULL, + "otp_code" TEXT, + + CONSTRAINT "mfa_challenges_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "auth"."mfa_factors" ( + "id" UUID NOT NULL, + "user_id" UUID NOT NULL, + "friendly_name" TEXT, + "factor_type" "auth"."factor_type" NOT NULL, + "status" "auth"."factor_status" NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL, + "updated_at" TIMESTAMPTZ(6) NOT NULL, + "secret" TEXT, + "phone" TEXT, + "last_challenged_at" TIMESTAMPTZ(6), + + CONSTRAINT "mfa_factors_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "auth"."one_time_tokens" ( + "id" UUID NOT NULL, + "user_id" UUID NOT NULL, + "token_type" "auth"."one_time_token_type" NOT NULL, + "token_hash" TEXT NOT NULL, + "relates_to" TEXT NOT NULL, + "created_at" TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "one_time_tokens_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "auth"."refresh_tokens" ( + "instance_id" UUID, + "id" BIGSERIAL NOT NULL, + "token" VARCHAR(255), + "user_id" VARCHAR(255), + "revoked" BOOLEAN, + "created_at" TIMESTAMPTZ(6), + "updated_at" TIMESTAMPTZ(6), + "parent" VARCHAR(255), + "session_id" UUID, + + CONSTRAINT "refresh_tokens_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "auth"."saml_providers" ( + "id" UUID NOT NULL, + "sso_provider_id" UUID NOT NULL, + "entity_id" TEXT NOT NULL, + "metadata_xml" TEXT NOT NULL, + "metadata_url" TEXT, + "attribute_mapping" JSONB, + "created_at" TIMESTAMPTZ(6), + "updated_at" TIMESTAMPTZ(6), + "name_id_format" TEXT, + + CONSTRAINT "saml_providers_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "auth"."saml_relay_states" ( + "id" UUID NOT NULL, + "sso_provider_id" UUID NOT NULL, + "request_id" TEXT NOT NULL, + "for_email" TEXT, + "redirect_to" TEXT, + "created_at" TIMESTAMPTZ(6), + "updated_at" TIMESTAMPTZ(6), + "flow_state_id" UUID, + + CONSTRAINT "saml_relay_states_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "auth"."schema_migrations" ( + "version" VARCHAR(255) NOT NULL, + + CONSTRAINT "schema_migrations_pkey" PRIMARY KEY ("version") +); + +-- CreateTable +CREATE TABLE "auth"."sessions" ( + "id" UUID NOT NULL, + "user_id" UUID NOT NULL, + "created_at" TIMESTAMPTZ(6), + "updated_at" TIMESTAMPTZ(6), + "factor_id" UUID, + "aal" "auth"."aal_level", + "not_after" TIMESTAMPTZ(6), + "refreshed_at" TIMESTAMP(6), + "user_agent" TEXT, + "ip" INET, + "tag" TEXT, + + CONSTRAINT "sessions_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "auth"."sso_domains" ( + "id" UUID NOT NULL, + "sso_provider_id" UUID NOT NULL, + "domain" TEXT NOT NULL, + "created_at" TIMESTAMPTZ(6), + "updated_at" TIMESTAMPTZ(6), + + CONSTRAINT "sso_domains_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "auth"."sso_providers" ( + "id" UUID NOT NULL, + "resource_id" TEXT, + "created_at" TIMESTAMPTZ(6), + "updated_at" TIMESTAMPTZ(6), + + CONSTRAINT "sso_providers_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "auth"."users" ( + "instance_id" UUID, + "id" UUID NOT NULL, + "aud" VARCHAR(255), + "role" VARCHAR(255), + "email" VARCHAR(255), + "encrypted_password" VARCHAR(255), + "email_confirmed_at" TIMESTAMPTZ(6), + "invited_at" TIMESTAMPTZ(6), + "confirmation_token" VARCHAR(255), + "confirmation_sent_at" TIMESTAMPTZ(6), + "recovery_token" VARCHAR(255), + "recovery_sent_at" TIMESTAMPTZ(6), + "email_change_token_new" VARCHAR(255), + "email_change" VARCHAR(255), + "email_change_sent_at" TIMESTAMPTZ(6), + "last_sign_in_at" TIMESTAMPTZ(6), + "raw_app_meta_data" JSONB, + "raw_user_meta_data" JSONB, + "is_super_admin" BOOLEAN, + "created_at" TIMESTAMPTZ(6), + "updated_at" TIMESTAMPTZ(6), + "phone" TEXT, + "phone_confirmed_at" TIMESTAMPTZ(6), + "phone_change" TEXT DEFAULT '', + "phone_change_token" VARCHAR(255) DEFAULT '', + "phone_change_sent_at" TIMESTAMPTZ(6), + "confirmed_at" TIMESTAMPTZ(6) DEFAULT LEAST(email_confirmed_at, phone_confirmed_at), + "email_change_token_current" VARCHAR(255) DEFAULT '', + "email_change_confirm_status" SMALLINT DEFAULT 0, + "banned_until" TIMESTAMPTZ(6), + "reauthentication_token" VARCHAR(255) DEFAULT '', + "reauthentication_sent_at" TIMESTAMPTZ(6), + "is_sso_user" BOOLEAN NOT NULL DEFAULT false, + "deleted_at" TIMESTAMPTZ(6), + "is_anonymous" BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT "users_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."account_delete_tokens" ( + "token" UUID NOT NULL DEFAULT uuid_generate_v4(), + "user_id" UUID NOT NULL, + + CONSTRAINT "account_delete_tokens_pkey" PRIMARY KEY ("user_id") +); + +-- CreateTable +CREATE TABLE "public"."billing_bypass_organizations" ( + "id" UUID NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "billing_bypass_organizations_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."chats" ( + "id" TEXT NOT NULL, + "user_id" UUID, + "payload" JSONB, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT timezone('utc'::text, now()), + "project_id" UUID NOT NULL, + + CONSTRAINT "chats_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."customers" ( + "stripe_customer_id" VARCHAR NOT NULL, + "organization_id" UUID NOT NULL, + + CONSTRAINT "customers_pkey" PRIMARY KEY ("stripe_customer_id","organization_id") +); + +-- CreateTable +CREATE TABLE "public"."digger_batches" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "pr_number" BIGINT, + "status" SMALLINT NOT NULL, + "branch_name" TEXT NOT NULL, + "digger_config" TEXT, + "github_installation_id" BIGINT, + "repo_full_name" TEXT NOT NULL, + "repo_owner" TEXT NOT NULL, + "repo_name" TEXT NOT NULL, + "batch_type" TEXT NOT NULL, + "comment_id" BIGINT, + "source_details" BYTEA, + "vcs" TEXT, + "gitlab_project_id" BIGINT, + "organization_id" UUID, + "event_type" TEXT, + + CONSTRAINT "digger_batches_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."digger_job_parent_links" ( + "id" BIGSERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6), + "updated_at" TIMESTAMPTZ(6), + "deleted_at" TIMESTAMPTZ(6), + "digger_job_id" TEXT, + "parent_digger_job_id" TEXT, + + CONSTRAINT "digger_job_parent_links_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."digger_job_summaries" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted_at" TIMESTAMPTZ(6), + "resources_created" BIGINT NOT NULL DEFAULT 0, + "resources_deleted" BIGINT NOT NULL DEFAULT 0, + "resources_updated" BIGINT NOT NULL DEFAULT 0, + + CONSTRAINT "digger_job_summaries_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."digger_job_tokens" ( + "id" BIGSERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6), + "updated_at" TIMESTAMPTZ(6), + "deleted_at" TIMESTAMPTZ(6), + "value" TEXT, + "expiry" TIMESTAMPTZ(6), + "type" TEXT, + "organisation_id" UUID, + + CONSTRAINT "job_tokens_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."digger_jobs" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted_at" TIMESTAMPTZ(6), + "digger_job_id" TEXT NOT NULL, + "status" SMALLINT NOT NULL, + "batch_id" UUID NOT NULL, + "status_updated_at" TIMESTAMPTZ(6), + "digger_job_summary_id" UUID, + "workflow_file" TEXT, + "workflow_run_url" TEXT, + "plan_footprint" BYTEA, + "pr_comment_url" TEXT, + "terraform_output" TEXT, + "job_spec" BYTEA, + "variables_spec" BYTEA, + + CONSTRAINT "digger_jobs_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."digger_locks" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted_at" TIMESTAMPTZ(6), + "resource" TEXT NOT NULL, + "lock_id" BIGINT NOT NULL, + "organization_id" UUID NOT NULL, + + CONSTRAINT "digger_locks_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."digger_run_queue_items" ( + "id" BIGSERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6), + "updated_at" TIMESTAMPTZ(6), + "deleted_at" TIMESTAMPTZ(6), + "digger_run_id" UUID, + "project_id" UUID, + + CONSTRAINT "digger_run_queue_items_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."digger_run_stages" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted_at" TIMESTAMPTZ(6), + "batch_id" UUID NOT NULL, + + CONSTRAINT "digger_run_stages_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."digger_runs" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted_at" TIMESTAMPTZ(6), + "triggertype" TEXT NOT NULL, + "pr_number" BIGINT, + "status" TEXT NOT NULL, + "commit_id" TEXT NOT NULL, + "digger_config" TEXT, + "github_installation_id" BIGINT, + "repo_id" BIGSERIAL NOT NULL, + "run_type" TEXT NOT NULL, + "plan_stage_id" UUID, + "apply_stage_id" UUID, + "project_name" TEXT, + "is_approved" BOOLEAN, + "approval_author" TEXT, + "approval_date" TIMESTAMPTZ(6), + "project_id" UUID NOT NULL, + "terraform_output" TEXT, + "apply_logs" TEXT, + "approver_user_id" UUID, + "triggered_by_user_id" UUID, + + CONSTRAINT "digger_runs_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."env_vars" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "project_id" UUID NOT NULL, + "name" VARCHAR(255) NOT NULL, + "value" TEXT NOT NULL DEFAULT '', + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "is_secret" BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT "encrypted_env_vars_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."github_app_installation_links" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted_at" TIMESTAMPTZ(6), + "github_installation_id" BIGINT NOT NULL, + "organization_id" UUID NOT NULL, + "status" SMALLINT NOT NULL, + + CONSTRAINT "github_app_installation_links_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."github_app_installations" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted_at" TIMESTAMPTZ(6), + "github_installation_id" BIGINT NOT NULL, + "github_app_id" BIGINT NOT NULL, + "account_id" BIGINT NOT NULL, + "login" TEXT NOT NULL, + "repo" TEXT, + "status" BIGINT NOT NULL, + + CONSTRAINT "github_app_installations_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."github_apps" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted_at" TIMESTAMPTZ(6), + "github_id" BIGINT NOT NULL, + "name" TEXT NOT NULL, + "github_app_url" TEXT NOT NULL, + + CONSTRAINT "github_apps_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."internal_blog_author_posts" ( + "author_id" UUID NOT NULL, + "post_id" UUID NOT NULL, + + CONSTRAINT "internal_blog_author_posts_pkey" PRIMARY KEY ("author_id","post_id") +); + +-- CreateTable +CREATE TABLE "public"."internal_blog_author_profiles" ( + "user_id" UUID NOT NULL, + "display_name" VARCHAR(255) NOT NULL, + "bio" TEXT NOT NULL, + "avatar_url" VARCHAR(255) NOT NULL, + "website_url" VARCHAR(255), + "twitter_handle" VARCHAR(255), + "facebook_handle" VARCHAR(255), + "linkedin_handle" VARCHAR(255), + "instagram_handle" VARCHAR(255), + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "internal_blog_author_profiles_pkey" PRIMARY KEY ("user_id") +); + +-- CreateTable +CREATE TABLE "public"."internal_blog_post_tags" ( + "id" SERIAL NOT NULL, + "slug" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + + CONSTRAINT "internal_blog_post_tags_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."internal_blog_post_tags_relationship" ( + "blog_post_id" UUID NOT NULL, + "tag_id" INTEGER NOT NULL, + + CONSTRAINT "internal_blog_post_tags_relationship_pkey" PRIMARY KEY ("blog_post_id","tag_id") +); + +-- CreateTable +CREATE TABLE "public"."internal_blog_posts" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "slug" VARCHAR(255) NOT NULL, + "title" VARCHAR(255) NOT NULL, + "summary" TEXT NOT NULL, + "content" TEXT NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "is_featured" BOOLEAN NOT NULL DEFAULT false, + "status" "public"."internal_blog_post_status" NOT NULL DEFAULT 'draft', + "cover_image" VARCHAR(255), + "seo_data" JSONB, + "json_content" JSONB NOT NULL DEFAULT '{}', + + CONSTRAINT "internal_blog_posts_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."internal_changelog" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "title" VARCHAR(255) NOT NULL, + "changes" TEXT NOT NULL, + "user_id" UUID, + "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, + "cover_image" TEXT, + + CONSTRAINT "internal_changelog_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."internal_feedback_comments" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "user_id" UUID NOT NULL, + "thread_id" UUID NOT NULL, + "content" TEXT NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "internal_feedback_comments_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."internal_feedback_threads" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "title" VARCHAR(255) NOT NULL, + "content" TEXT NOT NULL, + "user_id" UUID NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "priority" "public"."internal_feedback_thread_priority" NOT NULL DEFAULT 'low', + "type" "public"."internal_feedback_thread_type" NOT NULL DEFAULT 'general', + "status" "public"."internal_feedback_thread_status" NOT NULL DEFAULT 'open', + "added_to_roadmap" BOOLEAN NOT NULL DEFAULT false, + "open_for_public_discussion" BOOLEAN NOT NULL DEFAULT false, + "is_publicly_visible" BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT "internal_feedback_threads_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."organization_credits" ( + "organization_id" UUID NOT NULL, + "credits" BIGINT NOT NULL DEFAULT 12, + + CONSTRAINT "organization_credits_pkey" PRIMARY KEY ("organization_id") +); + +-- CreateTable +CREATE TABLE "public"."organization_join_invitations" ( + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "inviter_user_id" UUID NOT NULL, + "status" "public"."organization_join_invitation_link_status" NOT NULL DEFAULT 'active', + "id" UUID NOT NULL DEFAULT uuid_generate_v4(), + "invitee_user_email" VARCHAR NOT NULL, + "organization_id" UUID NOT NULL, + "invitee_organization_role" "public"."organization_member_role" NOT NULL DEFAULT 'member', + "invitee_user_id" UUID, + + CONSTRAINT "organization_invitations_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."organization_members" ( + "id" BIGSERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "member_id" UUID NOT NULL, + "member_role" "public"."organization_member_role" NOT NULL, + "organization_id" UUID NOT NULL, + + CONSTRAINT "organization_members_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."organizations" ( + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "id" UUID NOT NULL DEFAULT uuid_generate_v4(), + "title" VARCHAR NOT NULL DEFAULT 'Test Organization', + "slug" VARCHAR(255) NOT NULL DEFAULT (gen_random_uuid())::text, + "public_key" TEXT, + + CONSTRAINT "organizations_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."organizations_private_info" ( + "id" UUID NOT NULL, + "billing_address" JSON, + "payment_method" JSON, + + CONSTRAINT "projects_private_info_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."prices" ( + "id" VARCHAR NOT NULL, + "product_id" VARCHAR, + "active" BOOLEAN, + "description" VARCHAR, + "unit_amount" BIGINT, + "currency" VARCHAR, + "type" "public"."pricing_type", + "interval" "public"."pricing_plan_interval", + "interval_count" BIGINT, + "trial_period_days" BIGINT, + "metadata" JSONB, + + CONSTRAINT "price_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."products" ( + "id" VARCHAR NOT NULL, + "active" BOOLEAN, + "name" VARCHAR, + "description" VARCHAR, + "image" VARCHAR, + "metadata" JSONB, + "is_visible_in_ui" BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT "product_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."project_comments" ( + "id" BIGSERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, + "text" TEXT NOT NULL, + "user_id" UUID NOT NULL, + "in_reply_to" BIGINT, + "project_id" UUID NOT NULL, + + CONSTRAINT "project_comments_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."project_tfvars" ( + "id" UUID NOT NULL DEFAULT gen_random_uuid(), + "project_id" UUID NOT NULL, + "tfvars" JSONB NOT NULL, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "project_tfvars_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."projects" ( + "id" UUID NOT NULL DEFAULT uuid_generate_v4(), + "name" TEXT NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "organization_id" UUID NOT NULL, + "team_id" BIGINT, + "project_status" "public"."project_status" NOT NULL DEFAULT 'draft', + "slug" VARCHAR(255) NOT NULL DEFAULT (gen_random_uuid())::text, + "latest_action_on" TEXT, + "repo_id" BIGSERIAL NOT NULL, + "configuration_yaml" TEXT, + "status" TEXT, + "is_generated" BOOLEAN, + "is_in_main_branch" BOOLEAN, + "deleted_at" TIMESTAMPTZ(6), + "terraform_working_dir" TEXT, + "is_managing_state" BOOLEAN, + "labels" TEXT[], + "is_drift_detection_enabled" BOOLEAN DEFAULT false, + "drift_crontab" TEXT, + "branch" TEXT, + "latest_drift_output" TEXT, + "iac_type" "public"."iac_type_enum" DEFAULT 'terraform', + "workspace" TEXT, + "workflow_file" TEXT, + "include_patterns" TEXT, + "exclude_patterns" TEXT, + + CONSTRAINT "projects_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."repos" ( + "id" BIGSERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6), + "deleted_at" TIMESTAMPTZ(6), + "name" TEXT NOT NULL, + "organization_id" UUID, + "digger_config" TEXT, + "repo_name" TEXT, + "repo_full_name" TEXT, + "repo_organisation" TEXT, + "repo_url" TEXT, + + CONSTRAINT "repos_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."subscriptions" ( + "id" VARCHAR NOT NULL, + "status" "public"."subscription_status", + "metadata" JSON, + "price_id" VARCHAR, + "quantity" BIGINT, + "cancel_at_period_end" BOOLEAN, + "created" TIMESTAMPTZ(6) NOT NULL, + "current_period_start" TIMESTAMPTZ(6) NOT NULL, + "current_period_end" TIMESTAMPTZ(6) NOT NULL, + "ended_at" TIMESTAMPTZ(6), + "cancel_at" TIMESTAMPTZ(6), + "canceled_at" TIMESTAMPTZ(6), + "trial_start" TIMESTAMPTZ(6), + "trial_end" TIMESTAMPTZ(6), + "organization_id" UUID, + + CONSTRAINT "subscription_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."team_members" ( + "id" BIGSERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, + "user_id" UUID NOT NULL, + "role" "public"."project_team_member_role" NOT NULL DEFAULT 'member', + "team_id" BIGINT NOT NULL, + + CONSTRAINT "team_members_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."teams" ( + "id" BIGSERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, + "organization_id" UUID NOT NULL, + "name" TEXT NOT NULL, + + CONSTRAINT "teams_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."user_api_keys" ( + "key_id" TEXT NOT NULL, + "masked_key" TEXT NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "user_id" UUID NOT NULL, + "expires_at" TIMESTAMPTZ(6), + "is_revoked" BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT "user_api_keys_pkey" PRIMARY KEY ("key_id") +); + +-- CreateTable +CREATE TABLE "public"."user_notifications" ( + "id" UUID NOT NULL DEFAULT uuid_generate_v4(), + "user_id" UUID, + "is_read" BOOLEAN NOT NULL DEFAULT false, + "is_seen" BOOLEAN NOT NULL DEFAULT false, + "payload" JSONB NOT NULL DEFAULT '{}', + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "user_notifications_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."user_onboarding" ( + "user_id" UUID NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "accepted_terms" BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT "user_onboarding_pkey" PRIMARY KEY ("user_id") +); + +-- CreateTable +CREATE TABLE "public"."user_private_info" ( + "id" UUID NOT NULL, + "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, + "default_organization" UUID, + + CONSTRAINT "user_private_info_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."user_profiles" ( + "id" UUID NOT NULL, + "full_name" VARCHAR, + "avatar_url" VARCHAR, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "user_name" VARCHAR, + + CONSTRAINT "user_profiles_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."user_roles" ( + "id" BIGSERIAL NOT NULL, + "user_id" UUID NOT NULL, + "role" "public"."app_role" NOT NULL, + + CONSTRAINT "user_roles_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "audit_logs_instance_id_idx" ON "auth"."audit_log_entries"("instance_id"); + +-- CreateIndex +CREATE INDEX "flow_state_created_at_idx" ON "auth"."flow_state"("created_at" DESC); + +-- CreateIndex +CREATE INDEX "idx_auth_code" ON "auth"."flow_state"("auth_code"); + +-- CreateIndex +CREATE INDEX "idx_user_id_auth_method" ON "auth"."flow_state"("user_id", "authentication_method"); + +-- CreateIndex +CREATE INDEX "identities_email_idx" ON "auth"."identities"("email"); + +-- CreateIndex +CREATE INDEX "identities_user_id_idx" ON "auth"."identities"("user_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "identities_provider_id_provider_unique" ON "auth"."identities"("provider_id", "provider"); + +-- CreateIndex +CREATE UNIQUE INDEX "mfa_amr_claims_session_id_authentication_method_pkey" ON "auth"."mfa_amr_claims"("session_id", "authentication_method"); + +-- CreateIndex +CREATE INDEX "mfa_challenge_created_at_idx" ON "auth"."mfa_challenges"("created_at" DESC); + +-- CreateIndex +CREATE UNIQUE INDEX "mfa_factors_last_challenged_at_key" ON "auth"."mfa_factors"("last_challenged_at"); + +-- CreateIndex +CREATE INDEX "factor_id_created_at_idx" ON "auth"."mfa_factors"("user_id", "created_at"); + +-- CreateIndex +CREATE INDEX "mfa_factors_user_id_idx" ON "auth"."mfa_factors"("user_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "unique_phone_factor_per_user" ON "auth"."mfa_factors"("user_id", "phone"); + +-- CreateIndex +CREATE INDEX "one_time_tokens_relates_to_hash_idx" ON "auth"."one_time_tokens" USING HASH ("relates_to"); + +-- CreateIndex +CREATE INDEX "one_time_tokens_token_hash_hash_idx" ON "auth"."one_time_tokens" USING HASH ("token_hash"); + +-- CreateIndex +CREATE UNIQUE INDEX "one_time_tokens_user_id_token_type_key" ON "auth"."one_time_tokens"("user_id", "token_type"); + +-- CreateIndex +CREATE UNIQUE INDEX "refresh_tokens_token_unique" ON "auth"."refresh_tokens"("token"); + +-- CreateIndex +CREATE INDEX "refresh_tokens_instance_id_idx" ON "auth"."refresh_tokens"("instance_id"); + +-- CreateIndex +CREATE INDEX "refresh_tokens_instance_id_user_id_idx" ON "auth"."refresh_tokens"("instance_id", "user_id"); + +-- CreateIndex +CREATE INDEX "refresh_tokens_parent_idx" ON "auth"."refresh_tokens"("parent"); + +-- CreateIndex +CREATE INDEX "refresh_tokens_session_id_revoked_idx" ON "auth"."refresh_tokens"("session_id", "revoked"); + +-- CreateIndex +CREATE INDEX "refresh_tokens_updated_at_idx" ON "auth"."refresh_tokens"("updated_at" DESC); + +-- CreateIndex +CREATE UNIQUE INDEX "saml_providers_entity_id_key" ON "auth"."saml_providers"("entity_id"); + +-- CreateIndex +CREATE INDEX "saml_providers_sso_provider_id_idx" ON "auth"."saml_providers"("sso_provider_id"); + +-- CreateIndex +CREATE INDEX "saml_relay_states_created_at_idx" ON "auth"."saml_relay_states"("created_at" DESC); + +-- CreateIndex +CREATE INDEX "saml_relay_states_for_email_idx" ON "auth"."saml_relay_states"("for_email"); + +-- CreateIndex +CREATE INDEX "saml_relay_states_sso_provider_id_idx" ON "auth"."saml_relay_states"("sso_provider_id"); + +-- CreateIndex +CREATE INDEX "sessions_not_after_idx" ON "auth"."sessions"("not_after" DESC); + +-- CreateIndex +CREATE INDEX "sessions_user_id_idx" ON "auth"."sessions"("user_id"); + +-- CreateIndex +CREATE INDEX "user_id_created_at_idx" ON "auth"."sessions"("user_id", "created_at"); + +-- CreateIndex +CREATE INDEX "sso_domains_sso_provider_id_idx" ON "auth"."sso_domains"("sso_provider_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "users_phone_key" ON "auth"."users"("phone"); + +-- CreateIndex +CREATE INDEX "users_instance_id_idx" ON "auth"."users"("instance_id"); + +-- CreateIndex +CREATE INDEX "users_is_anonymous_idx" ON "auth"."users"("is_anonymous"); + +-- CreateIndex +CREATE UNIQUE INDEX "customers_stripe_customer_id_key" ON "public"."customers"("stripe_customer_id"); + +-- CreateIndex +CREATE INDEX "customers_organization_id_index" ON "public"."customers"("organization_id"); + +-- CreateIndex +CREATE INDEX "customers_stripe_customer_id_index" ON "public"."customers"("stripe_customer_id"); + +-- CreateIndex +CREATE INDEX "idx_digger_job_parent_links_deleted_at" ON "public"."digger_job_parent_links"("deleted_at"); + +-- CreateIndex +CREATE INDEX "idx_digger_job_summaries_deleted_at" ON "public"."digger_job_summaries"("deleted_at"); + +-- CreateIndex +CREATE INDEX "idx_job_tokens_deleted_at" ON "public"."digger_job_tokens"("deleted_at"); + +-- CreateIndex +CREATE INDEX "digger_jobs_batch_id_idx" ON "public"."digger_jobs"("batch_id"); + +-- CreateIndex +CREATE INDEX "idx_digger_job_id" ON "public"."digger_jobs"("batch_id"); + +-- CreateIndex +CREATE INDEX "idx_digger_jobs_deleted_at" ON "public"."digger_jobs"("deleted_at"); + +-- CreateIndex +CREATE INDEX "idx_digger_locked_resource" ON "public"."digger_locks"("resource"); + +-- CreateIndex +CREATE INDEX "idx_digger_locks_deleted_at" ON "public"."digger_locks"("deleted_at"); + +-- CreateIndex +CREATE INDEX "idx_digger_run_queue_items_deleted_at" ON "public"."digger_run_queue_items"("deleted_at"); + +-- CreateIndex +CREATE INDEX "idx_digger_run_batch_id" ON "public"."digger_run_stages"("batch_id"); + +-- CreateIndex +CREATE INDEX "idx_digger_run_stages_deleted_at" ON "public"."digger_run_stages"("deleted_at"); + +-- CreateIndex +CREATE INDEX "digger_runs_project_id_idx" ON "public"."digger_runs"("project_id"); + +-- CreateIndex +CREATE INDEX "idx_digger_runs_deleted_at" ON "public"."digger_runs"("deleted_at"); + +-- CreateIndex +CREATE INDEX "idx_digger_runs_project_id" ON "public"."digger_runs"("project_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "unique_project_var_name" ON "public"."env_vars"("project_id", "name"); + +-- CreateIndex +CREATE INDEX "idx_github_app_installation_links_deleted_at" ON "public"."github_app_installation_links"("deleted_at"); + +-- CreateIndex +CREATE INDEX "idx_github_installation_org" ON "public"."github_app_installation_links"("github_installation_id", "organization_id"); + +-- CreateIndex +CREATE INDEX "idx_github_app_installations_deleted_at" ON "public"."github_app_installations"("deleted_at"); + +-- CreateIndex +CREATE INDEX "idx_github_apps_deleted_at" ON "public"."github_apps"("deleted_at"); + +-- CreateIndex +CREATE UNIQUE INDEX "internal_blog_posts_slug_key" ON "public"."internal_blog_posts"("slug"); + +-- CreateIndex +CREATE INDEX "organization_join_invitations_invitee_user_email_idx" ON "public"."organization_join_invitations"("invitee_user_email"); + +-- CreateIndex +CREATE INDEX "organization_join_invitations_invitee_user_id_idx" ON "public"."organization_join_invitations"("invitee_user_id"); + +-- CreateIndex +CREATE INDEX "organization_join_invitations_inviter_user_id_idx" ON "public"."organization_join_invitations"("inviter_user_id"); + +-- CreateIndex +CREATE INDEX "organization_join_invitations_organization_id_idx" ON "public"."organization_join_invitations"("organization_id"); + +-- CreateIndex +CREATE INDEX "organization_join_invitations_status_idx" ON "public"."organization_join_invitations"("status"); + +-- CreateIndex +CREATE INDEX "organization_members_member_id_idx" ON "public"."organization_members"("member_id"); + +-- CreateIndex +CREATE INDEX "organization_members_member_role_idx" ON "public"."organization_members"("member_role"); + +-- CreateIndex +CREATE INDEX "organization_members_organization_id_idx" ON "public"."organization_members"("organization_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "organizations_slug_key" ON "public"."organizations"("slug"); + +-- CreateIndex +CREATE INDEX "prices_active_idx" ON "public"."prices"("active"); + +-- CreateIndex +CREATE INDEX "prices_product_id_idx" ON "public"."prices"("product_id"); + +-- CreateIndex +CREATE INDEX "products_active_idx" ON "public"."products"("active"); + +-- CreateIndex +CREATE INDEX "project_comments_project_id_idx" ON "public"."project_comments"("project_id"); + +-- CreateIndex +CREATE INDEX "project_comments_user_id_idx" ON "public"."project_comments"("user_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "project_tfvars_project_id_key" ON "public"."project_tfvars"("project_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "projects_slug_key" ON "public"."projects"("slug"); + +-- CreateIndex +CREATE INDEX "idx_repos_deleted_at" ON "public"."repos"("deleted_at"); + +-- CreateIndex +CREATE UNIQUE INDEX "idx_org_repo" ON "public"."repos"("name", "organization_id"); + +-- CreateIndex +CREATE INDEX "subscriptions_organization_id_idx" ON "public"."subscriptions"("organization_id"); + +-- CreateIndex +CREATE INDEX "subscriptions_price_id_idx" ON "public"."subscriptions"("price_id"); + +-- CreateIndex +CREATE INDEX "subscriptions_status_idx" ON "public"."subscriptions"("status"); + +-- CreateIndex +CREATE INDEX "user_notifications_user_id_idx" ON "public"."user_notifications"("user_id"); + +-- CreateIndex +CREATE INDEX "user_private_info_default_organization_idx" ON "public"."user_private_info"("default_organization"); + +-- CreateIndex +CREATE INDEX "user_roles_user_id_idx" ON "public"."user_roles"("user_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "user_roles_user_id_role_key" ON "public"."user_roles"("user_id", "role"); + +-- AddForeignKey +ALTER TABLE "auth"."identities" ADD CONSTRAINT "identities_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "auth"."mfa_amr_claims" ADD CONSTRAINT "mfa_amr_claims_session_id_fkey" FOREIGN KEY ("session_id") REFERENCES "auth"."sessions"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "auth"."mfa_challenges" ADD CONSTRAINT "mfa_challenges_auth_factor_id_fkey" FOREIGN KEY ("factor_id") REFERENCES "auth"."mfa_factors"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "auth"."mfa_factors" ADD CONSTRAINT "mfa_factors_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "auth"."one_time_tokens" ADD CONSTRAINT "one_time_tokens_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "auth"."refresh_tokens" ADD CONSTRAINT "refresh_tokens_session_id_fkey" FOREIGN KEY ("session_id") REFERENCES "auth"."sessions"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "auth"."saml_providers" ADD CONSTRAINT "saml_providers_sso_provider_id_fkey" FOREIGN KEY ("sso_provider_id") REFERENCES "auth"."sso_providers"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "auth"."saml_relay_states" ADD CONSTRAINT "saml_relay_states_flow_state_id_fkey" FOREIGN KEY ("flow_state_id") REFERENCES "auth"."flow_state"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "auth"."saml_relay_states" ADD CONSTRAINT "saml_relay_states_sso_provider_id_fkey" FOREIGN KEY ("sso_provider_id") REFERENCES "auth"."sso_providers"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "auth"."sessions" ADD CONSTRAINT "sessions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "auth"."sso_domains" ADD CONSTRAINT "sso_domains_sso_provider_id_fkey" FOREIGN KEY ("sso_provider_id") REFERENCES "auth"."sso_providers"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."account_delete_tokens" ADD CONSTRAINT "account_delete_tokens_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."billing_bypass_organizations" ADD CONSTRAINT "billing_bypass_organizations_id_fkey" FOREIGN KEY ("id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."chats" ADD CONSTRAINT "chats_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."chats" ADD CONSTRAINT "public_chats_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."customers" ADD CONSTRAINT "customers_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."digger_job_tokens" ADD CONSTRAINT "fk_jt_organisation_id" FOREIGN KEY ("organisation_id") REFERENCES "public"."organizations"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."digger_jobs" ADD CONSTRAINT "fk_digger_jobs_batch" FOREIGN KEY ("batch_id") REFERENCES "public"."digger_batches"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."digger_jobs" ADD CONSTRAINT "fk_digger_jobs_digger_job_summary" FOREIGN KEY ("digger_job_summary_id") REFERENCES "public"."digger_job_summaries"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."digger_locks" ADD CONSTRAINT "fk_digger_locks_organization" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."digger_run_stages" ADD CONSTRAINT "fk_digger_run_stages_batch" FOREIGN KEY ("batch_id") REFERENCES "public"."digger_batches"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."digger_runs" ADD CONSTRAINT "fk_digger_runs_apply_stage" FOREIGN KEY ("apply_stage_id") REFERENCES "public"."digger_run_stages"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."digger_runs" ADD CONSTRAINT "fk_digger_runs_plan_stage" FOREIGN KEY ("plan_stage_id") REFERENCES "public"."digger_run_stages"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."digger_runs" ADD CONSTRAINT "fk_digger_runs_project" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE SET NULL ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."digger_runs" ADD CONSTRAINT "fk_digger_runs_repo" FOREIGN KEY ("repo_id") REFERENCES "public"."repos"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."digger_runs" ADD CONSTRAINT "fk_triggered_by_user" FOREIGN KEY ("triggered_by_user_id") REFERENCES "public"."user_profiles"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."env_vars" ADD CONSTRAINT "encrypted_env_vars_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."github_app_installation_links" ADD CONSTRAINT "fk_github_app_installation_links_organization" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."internal_blog_author_posts" ADD CONSTRAINT "internal_blog_author_posts_author_id_fkey" FOREIGN KEY ("author_id") REFERENCES "public"."internal_blog_author_profiles"("user_id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."internal_blog_author_posts" ADD CONSTRAINT "internal_blog_author_posts_post_id_fkey" FOREIGN KEY ("post_id") REFERENCES "public"."internal_blog_posts"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."internal_blog_author_profiles" ADD CONSTRAINT "internal_blog_author_profiles_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."internal_blog_post_tags_relationship" ADD CONSTRAINT "internal_blog_post_tags_relationship_blog_post_id_fkey" FOREIGN KEY ("blog_post_id") REFERENCES "public"."internal_blog_posts"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."internal_blog_post_tags_relationship" ADD CONSTRAINT "internal_blog_post_tags_relationship_tag_id_fkey" FOREIGN KEY ("tag_id") REFERENCES "public"."internal_blog_post_tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."internal_changelog" ADD CONSTRAINT "internal_changelog_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."internal_feedback_comments" ADD CONSTRAINT "internal_feedback_comments_thread_id_fkey" FOREIGN KEY ("thread_id") REFERENCES "public"."internal_feedback_threads"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."internal_feedback_comments" ADD CONSTRAINT "internal_feedback_comments_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."internal_feedback_threads" ADD CONSTRAINT "internal_feedback_threads_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."organization_credits" ADD CONSTRAINT "organization_credits_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."organization_join_invitations" ADD CONSTRAINT "organization_join_invitations_invitee_user_id_fkey" FOREIGN KEY ("invitee_user_id") REFERENCES "public"."user_profiles"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."organization_join_invitations" ADD CONSTRAINT "organization_join_invitations_inviter_user_id_fkey" FOREIGN KEY ("inviter_user_id") REFERENCES "public"."user_profiles"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."organization_join_invitations" ADD CONSTRAINT "organization_join_invitations_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."organization_members" ADD CONSTRAINT "organization_members_member_id_fkey" FOREIGN KEY ("member_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."organization_members" ADD CONSTRAINT "organization_members_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."organizations_private_info" ADD CONSTRAINT "organizations_private_info_id_fkey" FOREIGN KEY ("id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."prices" ADD CONSTRAINT "prices_product_id_fkey" FOREIGN KEY ("product_id") REFERENCES "public"."products"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."project_comments" ADD CONSTRAINT "project_comments_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."projects" ADD CONSTRAINT "fk_projects_organization" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."projects" ADD CONSTRAINT "fk_projects_repo" FOREIGN KEY ("repo_id") REFERENCES "public"."repos"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."projects" ADD CONSTRAINT "projects_team_id_fkey" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."repos" ADD CONSTRAINT "fk_repos_organization" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."subscriptions" ADD CONSTRAINT "subscriptions_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."subscriptions" ADD CONSTRAINT "subscriptions_price_id_fkey" FOREIGN KEY ("price_id") REFERENCES "public"."prices"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."team_members" ADD CONSTRAINT "team_members_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."teams" ADD CONSTRAINT "teams_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."user_notifications" ADD CONSTRAINT "user_notifications_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."user_onboarding" ADD CONSTRAINT "user_onboarding_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."user_private_info" ADD CONSTRAINT "user_private_info_default_organization_fkey" FOREIGN KEY ("default_organization") REFERENCES "public"."organizations"("id") ON DELETE SET NULL ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."user_private_info" ADD CONSTRAINT "user_private_info_id_fkey" FOREIGN KEY ("id") REFERENCES "auth"."users"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."user_profiles" ADD CONSTRAINT "user_profiles_id_fkey" FOREIGN KEY ("id") REFERENCES "auth"."users"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "public"."user_roles" ADD CONSTRAINT "user_roles_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 00000000..34146113 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,1230 @@ +generator client { + provider = "prisma-client-js" + previewFeatures = ["multiSchema"] +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + schemas = ["auth", "public"] +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model audit_log_entries { + instance_id String? @db.Uuid + id String @id @db.Uuid + payload Json? @db.Json + created_at DateTime? @db.Timestamptz(6) + ip_address String @default("") @db.VarChar(64) + + @@index([instance_id], map: "audit_logs_instance_id_idx") + @@schema("auth") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model flow_state { + id String @id @db.Uuid + user_id String? @db.Uuid + auth_code String + code_challenge_method code_challenge_method + code_challenge String + provider_type String + provider_access_token String? + provider_refresh_token String? + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + authentication_method String + auth_code_issued_at DateTime? @db.Timestamptz(6) + saml_relay_states saml_relay_states[] + + @@index([created_at(sort: Desc)]) + @@index([auth_code], map: "idx_auth_code") + @@index([user_id, authentication_method], map: "idx_user_id_auth_method") + @@schema("auth") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model identities { + provider_id String + user_id String @db.Uuid + identity_data Json + provider String + last_sign_in_at DateTime? @db.Timestamptz(6) + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + email String? @default(dbgenerated("lower((identity_data ->> 'email'::text))")) + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@unique([provider_id, provider], map: "identities_provider_id_provider_unique") + @@index([email]) + @@index([user_id]) + @@schema("auth") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model instances { + id String @id @db.Uuid + uuid String? @db.Uuid + raw_base_config String? + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + + @@schema("auth") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model mfa_amr_claims { + session_id String @db.Uuid + created_at DateTime @db.Timestamptz(6) + updated_at DateTime @db.Timestamptz(6) + authentication_method String + id String @id(map: "amr_id_pk") @db.Uuid + sessions sessions @relation(fields: [session_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@unique([session_id, authentication_method], map: "mfa_amr_claims_session_id_authentication_method_pkey") + @@schema("auth") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model mfa_challenges { + id String @id @db.Uuid + factor_id String @db.Uuid + created_at DateTime @db.Timestamptz(6) + verified_at DateTime? @db.Timestamptz(6) + ip_address String @db.Inet + otp_code String? + mfa_factors mfa_factors @relation(fields: [factor_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "mfa_challenges_auth_factor_id_fkey") + + @@index([created_at(sort: Desc)], map: "mfa_challenge_created_at_idx") + @@schema("auth") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model mfa_factors { + id String @id @db.Uuid + user_id String @db.Uuid + friendly_name String? + factor_type factor_type + status factor_status + created_at DateTime @db.Timestamptz(6) + updated_at DateTime @db.Timestamptz(6) + secret String? + phone String? + last_challenged_at DateTime? @unique @db.Timestamptz(6) + mfa_challenges mfa_challenges[] + users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@unique([user_id, phone], map: "unique_phone_factor_per_user") + @@index([user_id, created_at], map: "factor_id_created_at_idx") + @@index([user_id]) + @@schema("auth") +} + +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model one_time_tokens { + id String @id @db.Uuid + user_id String @db.Uuid + token_type one_time_token_type + token_hash String + relates_to String + created_at DateTime @default(now()) @db.Timestamp(6) + updated_at DateTime @default(now()) @db.Timestamp(6) + users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@unique([user_id, token_type]) + @@index([relates_to], map: "one_time_tokens_relates_to_hash_idx", type: Hash) + @@index([token_hash], map: "one_time_tokens_token_hash_hash_idx", type: Hash) + @@schema("auth") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model refresh_tokens { + instance_id String? @db.Uuid + id BigInt @id @default(autoincrement()) + token String? @unique(map: "refresh_tokens_token_unique") @db.VarChar(255) + user_id String? @db.VarChar(255) + revoked Boolean? + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + parent String? @db.VarChar(255) + session_id String? @db.Uuid + sessions sessions? @relation(fields: [session_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@index([instance_id]) + @@index([instance_id, user_id]) + @@index([parent]) + @@index([session_id, revoked]) + @@index([updated_at(sort: Desc)]) + @@schema("auth") +} + +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model saml_providers { + id String @id @db.Uuid + sso_provider_id String @db.Uuid + entity_id String @unique + metadata_xml String + metadata_url String? + attribute_mapping Json? + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + name_id_format String? + sso_providers sso_providers @relation(fields: [sso_provider_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@index([sso_provider_id]) + @@schema("auth") +} + +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model saml_relay_states { + id String @id @db.Uuid + sso_provider_id String @db.Uuid + request_id String + for_email String? + redirect_to String? + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + flow_state_id String? @db.Uuid + flow_state flow_state? @relation(fields: [flow_state_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + sso_providers sso_providers @relation(fields: [sso_provider_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@index([created_at(sort: Desc)]) + @@index([for_email]) + @@index([sso_provider_id]) + @@schema("auth") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model schema_migrations { + version String @id @db.VarChar(255) + + @@schema("auth") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model sessions { + id String @id @db.Uuid + user_id String @db.Uuid + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + factor_id String? @db.Uuid + aal aal_level? + not_after DateTime? @db.Timestamptz(6) + refreshed_at DateTime? @db.Timestamp(6) + user_agent String? + ip String? @db.Inet + tag String? + mfa_amr_claims mfa_amr_claims[] + refresh_tokens refresh_tokens[] + users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@index([not_after(sort: Desc)]) + @@index([user_id]) + @@index([user_id, created_at], map: "user_id_created_at_idx") + @@schema("auth") +} + +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info. +model sso_domains { + id String @id @db.Uuid + sso_provider_id String @db.Uuid + domain String + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + sso_providers sso_providers @relation(fields: [sso_provider_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@index([sso_provider_id]) + @@schema("auth") +} + +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info. +model sso_providers { + id String @id @db.Uuid + resource_id String? + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + saml_providers saml_providers[] + saml_relay_states saml_relay_states[] + sso_domains sso_domains[] + + @@schema("auth") +} + +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info. +model users { + instance_id String? @db.Uuid + id String @id @db.Uuid + aud String? @db.VarChar(255) + role String? @db.VarChar(255) + email String? @db.VarChar(255) + encrypted_password String? @db.VarChar(255) + email_confirmed_at DateTime? @db.Timestamptz(6) + invited_at DateTime? @db.Timestamptz(6) + confirmation_token String? @db.VarChar(255) + confirmation_sent_at DateTime? @db.Timestamptz(6) + recovery_token String? @db.VarChar(255) + recovery_sent_at DateTime? @db.Timestamptz(6) + email_change_token_new String? @db.VarChar(255) + email_change String? @db.VarChar(255) + email_change_sent_at DateTime? @db.Timestamptz(6) + last_sign_in_at DateTime? @db.Timestamptz(6) + raw_app_meta_data Json? + raw_user_meta_data Json? + is_super_admin Boolean? + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + phone String? @unique + phone_confirmed_at DateTime? @db.Timestamptz(6) + phone_change String? @default("") + phone_change_token String? @default("") @db.VarChar(255) + phone_change_sent_at DateTime? @db.Timestamptz(6) + confirmed_at DateTime? @default(dbgenerated("LEAST(email_confirmed_at, phone_confirmed_at)")) @db.Timestamptz(6) + email_change_token_current String? @default("") @db.VarChar(255) + email_change_confirm_status Int? @default(0) @db.SmallInt + banned_until DateTime? @db.Timestamptz(6) + reauthentication_token String? @default("") @db.VarChar(255) + reauthentication_sent_at DateTime? @db.Timestamptz(6) + is_sso_user Boolean @default(false) + deleted_at DateTime? @db.Timestamptz(6) + is_anonymous Boolean @default(false) + identities identities[] + mfa_factors mfa_factors[] + one_time_tokens one_time_tokens[] + sessions sessions[] + chats chats[] + user_private_info user_private_info? + user_profiles user_profiles? + + @@index([instance_id]) + @@index([is_anonymous]) + @@schema("auth") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model account_delete_tokens { + token String @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + user_id String @id @db.Uuid + user_profiles user_profiles @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model billing_bypass_organizations { + id String @id @db.Uuid + created_at DateTime @default(now()) @db.Timestamptz(6) + organizations organizations @relation(fields: [id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model chats { + id String @id + user_id String? @db.Uuid + payload Json? + created_at DateTime @default(dbgenerated("timezone('utc'::text, now())")) @db.Timestamptz(6) + project_id String @db.Uuid + users users? @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + projects projects @relation(fields: [project_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "public_chats_project_id_fkey") + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model customers { + stripe_customer_id String @unique @db.VarChar + organization_id String @db.Uuid + organizations organizations @relation(fields: [organization_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@id([stripe_customer_id, organization_id]) + @@index([organization_id], map: "customers_organization_id_index") + @@index([stripe_customer_id], map: "customers_stripe_customer_id_index") + @@schema("public") +} + +model digger_batches { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + pr_number BigInt? + status Int @db.SmallInt + branch_name String + digger_config String? + github_installation_id BigInt? + repo_full_name String + repo_owner String + repo_name String + batch_type String + comment_id BigInt? + source_details Bytes? + vcs String? + gitlab_project_id BigInt? + organization_id String? @db.Uuid + event_type String? + digger_jobs digger_jobs[] + digger_run_stages digger_run_stages[] + + @@schema("public") +} + +model digger_job_parent_links { + id BigInt @id @default(autoincrement()) + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + deleted_at DateTime? @db.Timestamptz(6) + digger_job_id String? + parent_digger_job_id String? + + @@index([deleted_at], map: "idx_digger_job_parent_links_deleted_at") + @@schema("public") +} + +model digger_job_summaries { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + deleted_at DateTime? @db.Timestamptz(6) + resources_created BigInt @default(0) + resources_deleted BigInt @default(0) + resources_updated BigInt @default(0) + digger_jobs digger_jobs[] + + @@index([deleted_at], map: "idx_digger_job_summaries_deleted_at") + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model digger_job_tokens { + id BigInt @id(map: "job_tokens_pkey") @default(autoincrement()) + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + deleted_at DateTime? @db.Timestamptz(6) + value String? + expiry DateTime? @db.Timestamptz(6) + type String? + organisation_id String? @db.Uuid + organizations organizations? @relation(fields: [organisation_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_jt_organisation_id") + + @@index([deleted_at], map: "idx_job_tokens_deleted_at") + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model digger_jobs { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + deleted_at DateTime? @db.Timestamptz(6) + digger_job_id String + status Int @db.SmallInt + batch_id String @db.Uuid + status_updated_at DateTime? @db.Timestamptz(6) + digger_job_summary_id String? @db.Uuid + workflow_file String? + workflow_run_url String? + plan_footprint Bytes? + pr_comment_url String? + terraform_output String? + job_spec Bytes? + variables_spec Bytes? + digger_batches digger_batches @relation(fields: [batch_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_digger_jobs_batch") + digger_job_summaries digger_job_summaries? @relation(fields: [digger_job_summary_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_digger_jobs_digger_job_summary") + + @@index([batch_id]) + @@index([batch_id], map: "idx_digger_job_id") + @@index([deleted_at], map: "idx_digger_jobs_deleted_at") + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model digger_locks { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + deleted_at DateTime? @db.Timestamptz(6) + resource String + lock_id BigInt + organization_id String @db.Uuid + organizations organizations @relation(fields: [organization_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_digger_locks_organization") + + @@index([resource], map: "idx_digger_locked_resource") + @@index([deleted_at], map: "idx_digger_locks_deleted_at") + @@schema("public") +} + +model digger_run_queue_items { + id BigInt @id @default(autoincrement()) + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + deleted_at DateTime? @db.Timestamptz(6) + digger_run_id String? @db.Uuid + project_id String? @db.Uuid + + @@index([deleted_at], map: "idx_digger_run_queue_items_deleted_at") + @@schema("public") +} + +model digger_run_stages { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + deleted_at DateTime? @db.Timestamptz(6) + batch_id String @db.Uuid + digger_batches digger_batches @relation(fields: [batch_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_digger_run_stages_batch") + digger_runs_digger_runs_apply_stage_idTodigger_run_stages digger_runs[] @relation("digger_runs_apply_stage_idTodigger_run_stages") + digger_runs_digger_runs_plan_stage_idTodigger_run_stages digger_runs[] @relation("digger_runs_plan_stage_idTodigger_run_stages") + + @@index([batch_id], map: "idx_digger_run_batch_id") + @@index([deleted_at], map: "idx_digger_run_stages_deleted_at") + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model digger_runs { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + deleted_at DateTime? @db.Timestamptz(6) + triggertype String + pr_number BigInt? + status String + commit_id String + digger_config String? + github_installation_id BigInt? + repo_id BigInt @default(autoincrement()) + run_type String + plan_stage_id String? @db.Uuid + apply_stage_id String? @db.Uuid + project_name String? + is_approved Boolean? + approval_author String? + approval_date DateTime? @db.Timestamptz(6) + project_id String @db.Uuid + terraform_output String? + apply_logs String? + approver_user_id String? @db.Uuid + triggered_by_user_id String? @db.Uuid + digger_run_stages_digger_runs_apply_stage_idTodigger_run_stages digger_run_stages? @relation("digger_runs_apply_stage_idTodigger_run_stages", fields: [apply_stage_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_digger_runs_apply_stage") + digger_run_stages_digger_runs_plan_stage_idTodigger_run_stages digger_run_stages? @relation("digger_runs_plan_stage_idTodigger_run_stages", fields: [plan_stage_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_digger_runs_plan_stage") + projects projects @relation(fields: [project_id], references: [id], onDelete: SetNull, onUpdate: NoAction, map: "fk_digger_runs_project") + repos repos @relation(fields: [repo_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_digger_runs_repo") + user_profiles user_profiles? @relation(fields: [triggered_by_user_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_triggered_by_user") + + @@index([project_id]) + @@index([deleted_at], map: "idx_digger_runs_deleted_at") + @@index([project_id], map: "idx_digger_runs_project_id") + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model env_vars { + id String @id(map: "encrypted_env_vars_pkey") @default(dbgenerated("gen_random_uuid()")) @db.Uuid + project_id String @db.Uuid + name String @db.VarChar(255) + value String @default("") + updated_at DateTime @default(now()) @db.Timestamptz(6) + is_secret Boolean @default(false) + projects projects @relation(fields: [project_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "encrypted_env_vars_project_id_fkey") + + @@unique([project_id, name], map: "unique_project_var_name") + @@schema("public") +} + +model github_app_installation_links { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + deleted_at DateTime? @db.Timestamptz(6) + github_installation_id BigInt + organization_id String @db.Uuid + status Int @db.SmallInt + organizations organizations @relation(fields: [organization_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_github_app_installation_links_organization") + + @@index([deleted_at], map: "idx_github_app_installation_links_deleted_at") + @@index([github_installation_id, organization_id], map: "idx_github_installation_org") + @@schema("public") +} + +model github_app_installations { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + deleted_at DateTime? @db.Timestamptz(6) + github_installation_id BigInt + github_app_id BigInt + account_id BigInt + login String + repo String? + status BigInt + + @@index([deleted_at], map: "idx_github_app_installations_deleted_at") + @@schema("public") +} + +model github_apps { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + deleted_at DateTime? @db.Timestamptz(6) + github_id BigInt + name String + github_app_url String + + @@index([deleted_at], map: "idx_github_apps_deleted_at") + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model internal_blog_author_posts { + author_id String @db.Uuid + post_id String @db.Uuid + internal_blog_author_profiles internal_blog_author_profiles @relation(fields: [author_id], references: [user_id], onDelete: Cascade, onUpdate: NoAction) + internal_blog_posts internal_blog_posts @relation(fields: [post_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@id([author_id, post_id]) + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model internal_blog_author_profiles { + user_id String @id @db.Uuid + display_name String @db.VarChar(255) + bio String + avatar_url String @db.VarChar(255) + website_url String? @db.VarChar(255) + twitter_handle String? @db.VarChar(255) + facebook_handle String? @db.VarChar(255) + linkedin_handle String? @db.VarChar(255) + instagram_handle String? @db.VarChar(255) + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + internal_blog_author_posts internal_blog_author_posts[] + user_profiles user_profiles @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model internal_blog_post_tags { + id Int @id @default(autoincrement()) + slug String + name String + description String? + internal_blog_post_tags_relationship internal_blog_post_tags_relationship[] + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model internal_blog_post_tags_relationship { + blog_post_id String @db.Uuid + tag_id Int + internal_blog_posts internal_blog_posts @relation(fields: [blog_post_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + internal_blog_post_tags internal_blog_post_tags @relation(fields: [tag_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@id([blog_post_id, tag_id]) + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model internal_blog_posts { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + slug String @unique @db.VarChar(255) + title String @db.VarChar(255) + summary String + content String + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + is_featured Boolean @default(false) + status internal_blog_post_status @default(draft) + cover_image String? @db.VarChar(255) + seo_data Json? + json_content Json @default("{}") + internal_blog_author_posts internal_blog_author_posts[] + internal_blog_post_tags_relationship internal_blog_post_tags_relationship[] + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model internal_changelog { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + title String @db.VarChar(255) + changes String + user_id String? @db.Uuid + created_at DateTime? @default(now()) @db.Timestamptz(6) + updated_at DateTime? @default(now()) @db.Timestamptz(6) + cover_image String? + user_profiles user_profiles? @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model internal_feedback_comments { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + user_id String @db.Uuid + thread_id String @db.Uuid + content String + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + internal_feedback_threads internal_feedback_threads @relation(fields: [thread_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + user_profiles user_profiles @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model internal_feedback_threads { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + title String @db.VarChar(255) + content String + user_id String @db.Uuid + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + priority internal_feedback_thread_priority @default(low) + type internal_feedback_thread_type @default(general) + status internal_feedback_thread_status @default(open) + added_to_roadmap Boolean @default(false) + open_for_public_discussion Boolean @default(false) + is_publicly_visible Boolean @default(false) + internal_feedback_comments internal_feedback_comments[] + user_profiles user_profiles @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model organization_credits { + organization_id String @id @db.Uuid + credits BigInt @default(12) + organizations organizations @relation(fields: [organization_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model organization_join_invitations { + created_at DateTime @default(now()) @db.Timestamptz(6) + inviter_user_id String @db.Uuid + status organization_join_invitation_link_status @default(active) + id String @id(map: "organization_invitations_pkey") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + invitee_user_email String @db.VarChar + organization_id String @db.Uuid + invitee_organization_role organization_member_role @default(member) + invitee_user_id String? @db.Uuid + user_profiles_organization_join_invitations_invitee_user_idTouser_profiles user_profiles? @relation("organization_join_invitations_invitee_user_idTouser_profiles", fields: [invitee_user_id], references: [id], onDelete: NoAction, onUpdate: NoAction) + user_profiles_organization_join_invitations_inviter_user_idTouser_profiles user_profiles @relation("organization_join_invitations_inviter_user_idTouser_profiles", fields: [inviter_user_id], references: [id], onDelete: NoAction, onUpdate: NoAction) + organizations organizations @relation(fields: [organization_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@index([invitee_user_email]) + @@index([invitee_user_id]) + @@index([inviter_user_id]) + @@index([organization_id]) + @@index([status]) + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model organization_members { + id BigInt @id @default(autoincrement()) + created_at DateTime @default(now()) @db.Timestamptz(6) + member_id String @db.Uuid + member_role organization_member_role + organization_id String @db.Uuid + user_profiles user_profiles @relation(fields: [member_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + organizations organizations @relation(fields: [organization_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@index([member_id]) + @@index([member_role]) + @@index([organization_id]) + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model organizations { + created_at DateTime @default(now()) @db.Timestamptz(6) + id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + title String @default("Test Organization") @db.VarChar + slug String @unique @default(dbgenerated("(gen_random_uuid())::text")) @db.VarChar(255) + public_key String? + billing_bypass_organizations billing_bypass_organizations? + customers customers[] + digger_job_tokens digger_job_tokens[] + digger_locks digger_locks[] + github_app_installation_links github_app_installation_links[] + organization_credits organization_credits? + organization_join_invitations organization_join_invitations[] + organization_members organization_members[] + organizations_private_info organizations_private_info? + projects projects[] + repos repos[] + subscriptions subscriptions[] + teams teams[] + user_private_info user_private_info[] + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model organizations_private_info { + id String @id(map: "projects_private_info_pkey") @db.Uuid + billing_address Json? @db.Json + payment_method Json? @db.Json + organizations organizations @relation(fields: [id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model prices { + id String @id(map: "price_pkey") @db.VarChar + product_id String? @db.VarChar + active Boolean? + description String? @db.VarChar + unit_amount BigInt? + currency String? @db.VarChar + type pricing_type? + interval pricing_plan_interval? + interval_count BigInt? + trial_period_days BigInt? + metadata Json? + products products? @relation(fields: [product_id], references: [id], onDelete: NoAction, onUpdate: NoAction) + subscriptions subscriptions[] + + @@index([active]) + @@index([product_id]) + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model products { + id String @id(map: "product_pkey") @db.VarChar + active Boolean? + name String? @db.VarChar + description String? @db.VarChar + image String? @db.VarChar + metadata Json? + is_visible_in_ui Boolean @default(false) + prices prices[] + + @@index([active]) + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model project_comments { + id BigInt @id @default(autoincrement()) + created_at DateTime? @default(now()) @db.Timestamptz(6) + text String + user_id String @db.Uuid + in_reply_to BigInt? + project_id String @db.Uuid + user_profiles user_profiles @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@index([project_id]) + @@index([user_id]) + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model project_tfvars { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + project_id String @unique @db.Uuid + tfvars Json + updated_at DateTime @default(now()) @db.Timestamptz(6) + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model projects { + id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + name String + created_at DateTime @default(now()) + updated_at DateTime @default(now()) + organization_id String @db.Uuid + team_id BigInt? + project_status project_status @default(draft) + slug String @unique @default(dbgenerated("(gen_random_uuid())::text")) @db.VarChar(255) + latest_action_on String? + repo_id BigInt @default(autoincrement()) + configuration_yaml String? + status String? + is_generated Boolean? + is_in_main_branch Boolean? + deleted_at DateTime? @db.Timestamptz(6) + terraform_working_dir String? + is_managing_state Boolean? + labels String[] + is_drift_detection_enabled Boolean? @default(false) + drift_crontab String? + branch String? + latest_drift_output String? + iac_type iac_type_enum? @default(terraform) + workspace String? + workflow_file String? + include_patterns String? + exclude_patterns String? + chats chats[] + digger_runs digger_runs[] + env_vars env_vars[] + organizations organizations @relation(fields: [organization_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_projects_organization") + repos repos @relation(fields: [repo_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_projects_repo") + teams teams? @relation(fields: [team_id], references: [id], onDelete: Cascade) + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model repos { + id BigInt @id @default(autoincrement()) + created_at DateTime? @default(now()) @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + deleted_at DateTime? @db.Timestamptz(6) + name String + organization_id String? @db.Uuid + digger_config String? + repo_name String? + repo_full_name String? + repo_organisation String? + repo_url String? + digger_runs digger_runs[] + projects projects[] + organizations organizations? @relation(fields: [organization_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_repos_organization") + + @@unique([name, organization_id], map: "idx_org_repo") + @@index([deleted_at], map: "idx_repos_deleted_at") + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model subscriptions { + id String @id(map: "subscription_pkey") @db.VarChar + status subscription_status? + metadata Json? @db.Json + price_id String? @db.VarChar + quantity BigInt? + cancel_at_period_end Boolean? + created DateTime @db.Timestamptz(6) + current_period_start DateTime @db.Timestamptz(6) + current_period_end DateTime @db.Timestamptz(6) + ended_at DateTime? @db.Timestamptz(6) + cancel_at DateTime? @db.Timestamptz(6) + canceled_at DateTime? @db.Timestamptz(6) + trial_start DateTime? @db.Timestamptz(6) + trial_end DateTime? @db.Timestamptz(6) + organization_id String? @db.Uuid + organizations organizations? @relation(fields: [organization_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + prices prices? @relation(fields: [price_id], references: [id], onDelete: NoAction, onUpdate: NoAction) + + @@index([organization_id]) + @@index([price_id]) + @@index([status]) + @@schema("public") +} + +model team_members { + id BigInt @id @default(autoincrement()) + created_at DateTime? @default(now()) @db.Timestamptz(6) + user_id String @db.Uuid + role project_team_member_role @default(member) + team_id BigInt + user_profiles user_profiles @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model teams { + id BigInt @id @default(autoincrement()) + created_at DateTime? @default(now()) @db.Timestamptz(6) + organization_id String @db.Uuid + name String + projects projects[] + organizations organizations @relation(fields: [organization_id], references: [id], onDelete: Cascade) + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model user_api_keys { + key_id String @id + masked_key String + created_at DateTime @default(now()) @db.Timestamptz(6) + user_id String @db.Uuid + expires_at DateTime? @db.Timestamptz(6) + is_revoked Boolean @default(false) + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model user_notifications { + id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid + user_id String? @db.Uuid + is_read Boolean @default(false) + is_seen Boolean @default(false) + payload Json @default("{}") + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + user_profiles user_profiles? @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@index([user_id]) + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model user_onboarding { + user_id String @id @db.Uuid + created_at DateTime @default(now()) @db.Timestamptz(6) + accepted_terms Boolean @default(false) + user_profiles user_profiles @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model user_private_info { + id String @id @db.Uuid + created_at DateTime? @default(now()) @db.Timestamptz(6) + default_organization String? @db.Uuid + organizations organizations? @relation(fields: [default_organization], references: [id], onUpdate: NoAction) + users users @relation(fields: [id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@index([default_organization]) + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model user_profiles { + id String @id @db.Uuid + full_name String? @db.VarChar + avatar_url String? @db.VarChar + created_at DateTime @default(now()) @db.Timestamptz(6) + user_name String? @db.VarChar + account_delete_tokens account_delete_tokens? + digger_runs digger_runs[] + internal_blog_author_profiles internal_blog_author_profiles? + internal_changelog internal_changelog[] + internal_feedback_comments internal_feedback_comments[] + internal_feedback_threads internal_feedback_threads[] + organization_join_invitations_organization_join_invitations_invitee_user_idTouser_profiles organization_join_invitations[] @relation("organization_join_invitations_invitee_user_idTouser_profiles") + organization_join_invitations_organization_join_invitations_inviter_user_idTouser_profiles organization_join_invitations[] @relation("organization_join_invitations_inviter_user_idTouser_profiles") + organization_members organization_members[] + project_comments project_comments[] + team_members team_members[] + user_notifications user_notifications[] + user_onboarding user_onboarding? + users users @relation(fields: [id], references: [id], onDelete: Cascade, onUpdate: NoAction) + user_roles user_roles[] + + @@schema("public") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model user_roles { + id BigInt @id @default(autoincrement()) + user_id String @db.Uuid + role app_role + user_profiles user_profiles @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@unique([user_id, role]) + @@index([user_id]) + @@schema("public") +} + +enum aal_level { + aal1 + aal2 + aal3 + + @@schema("auth") +} + +enum code_challenge_method { + s256 + plain + + @@schema("auth") +} + +enum factor_status { + unverified + verified + + @@schema("auth") +} + +enum factor_type { + totp + webauthn + phone + + @@schema("auth") +} + +enum one_time_token_type { + confirmation_token + reauthentication_token + recovery_token + email_change_token_new + email_change_token_current + phone_change_token + + @@schema("auth") +} + +enum app_admin_role { + moderator + admin + super_admin + + @@schema("public") +} + +enum app_role { + admin + + @@schema("public") +} + +enum iac_type_enum { + terraform + terragrunt + opentofu + + @@schema("public") +} + +enum internal_blog_post_status { + draft + published + + @@schema("public") +} + +enum internal_feedback_thread_priority { + low + medium + high + + @@schema("public") +} + +enum internal_feedback_thread_status { + open + under_review + planned + closed + in_progress + completed + + @@schema("public") +} + +enum internal_feedback_thread_type { + bug + feature_request + general + + @@schema("public") +} + +enum organization_join_invitation_link_status { + active + finished_accepted + finished_declined + inactive + + @@schema("public") +} + +enum organization_joining_status { + invited + joinied + declined_invitation + joined + + @@schema("public") +} + +enum organization_member_role { + owner + admin + member + readonly + + @@schema("public") +} + +enum pricing_plan_interval { + day + week + month + year + + @@schema("public") +} + +enum pricing_type { + one_time + recurring + + @@schema("public") +} + +enum project_status { + draft + pending_approval + approved + completed + + @@schema("public") +} + +enum project_team_member_role { + admin + member + readonly + + @@schema("public") +} + +enum subscription_status { + trialing + active + canceled + incomplete + incomplete_expired + past_due + unpaid + paused + + @@schema("public") +} diff --git a/src/data/user/projects.tsx b/src/data/user/projects.tsx index bbfb7b93..07c1f0d6 100644 --- a/src/data/user/projects.tsx +++ b/src/data/user/projects.tsx @@ -8,21 +8,41 @@ import { createSupabaseUserServerComponentClient } from "@/supabase-clients/user import type { CommentWithUser, Enum, SAPayload } from "@/types"; import { normalizeComment } from "@/utils/comments"; import { serverGetLoggedInUser } from "@/utils/server/serverGetLoggedInUser"; +import { PrismaClient } from '@prisma/client'; import { revalidatePath } from "next/cache"; import { Suspense } from "react"; import { getRepoDetails } from "./repos"; + export async function getSlimProjectById(projectId: string) { - const supabaseClient = createSupabaseUserServerComponentClient(); - const { data, error } = await supabaseClient - .from("projects") - .select("id,name,project_status,organization_id,team_id,slug, repo_id") - .eq("id", projectId) - .single(); - if (error) { + const prisma = new PrismaClient(); + + try { + const project = await prisma.projects.findUnique({ + where: { + id: projectId, + }, + select: { + id: true, + name: true, + project_status: true, + organization_id: true, + team_id: true, + slug: true, + repo_id: true, + }, + }); + + if (!project) { + throw new Error('Project not found'); + } + + return project; + } catch (error) { throw error; + } finally { + await prisma.$disconnect(); } - return data; } export const getSlimProjectBySlug = async (projectSlug: string) => { From a38e0c1dd801f46be7852edab81ddf01715bfe9c Mon Sep 17 00:00:00 2001 From: Igor Zalutski Date: Fri, 11 Oct 2024 21:50:25 +0100 Subject: [PATCH 02/23] WIP auth with Auth.js + Auth0 --- package.json | 1 + pnpm-lock.yaml | 99 +++++++++++++++++++ src/app/api/auth/[...nextauth]/route.ts | 3 + src/app/route.ts | 12 ++- src/auth.ts | 13 +++ src/data/user/organizations.ts | 5 +- src/data/user/projects.tsx | 2 +- src/data/user/session.ts | 9 +- src/middleware.ts | 33 +++++-- src/utils/server/serverGetLoggedInUser.ts | 21 ++-- src/utils/server/serverGetLoggedInUserRole.ts | 5 +- src/utils/server/serverGetUserType.ts | 9 +- src/utils/server/verifySession.ts | 39 +++++--- 13 files changed, 205 insertions(+), 46 deletions(-) create mode 100644 src/app/api/auth/[...nextauth]/route.ts create mode 100644 src/auth.ts diff --git a/package.json b/package.json index f0325298..06001f0a 100644 --- a/package.json +++ b/package.json @@ -130,6 +130,7 @@ "moment": "^2.29.4", "nanoid": "^5.0.7", "next": "^14.2.5", + "next-auth": "5.0.0-beta.22", "next-mdx-remote": "^4.4.1", "next-nprogress-bar": "^2.1.2", "next-themes": "^0.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1fb9f8cd..9b2e7d5d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -377,6 +377,9 @@ importers: next: specifier: ^14.2.5 version: 14.2.5(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(@playwright/test@1.45.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next-auth: + specifier: 5.0.0-beta.22 + version: 5.0.0-beta.22(next@14.2.5(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(@playwright/test@1.45.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.14)(react@18.3.1) next-mdx-remote: specifier: ^4.4.1 version: 4.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -792,6 +795,20 @@ packages: resolution: {integrity: sha512-2aDL3WUv8hMJb2L3r/PIQWsTLyq7RQr3v9xD16fiz6O8ys1xEyLhhTOv8gxtZvJiTzjTF5pHoArvRdesGL1DMQ==} hasBin: true + '@auth/core@0.35.3': + resolution: {integrity: sha512-g6qfiqU4OtyvIEZ8J7UoIwAxEnNnLJV0/f/DW41U+4G5nhBlaCrnKhawJIJpU0D3uavXLeDT3B0BkjtiimvMDA==} + peerDependencies: + '@simplewebauthn/browser': ^9.0.1 + '@simplewebauthn/server': ^9.0.2 + nodemailer: ^6.8.0 + peerDependenciesMeta: + '@simplewebauthn/browser': + optional: true + '@simplewebauthn/server': + optional: true + nodemailer: + optional: true + '@babel/code-frame@7.24.7': resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} @@ -2110,6 +2127,9 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} + '@panva/hkdf@1.2.1': + resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -3714,6 +3734,9 @@ packages: '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/d3-array@3.2.1': resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} @@ -4765,6 +4788,10 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + copy-to-clipboard@3.3.3: resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} @@ -6289,6 +6316,9 @@ packages: jose@4.15.9: resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} + jose@5.9.4: + resolution: {integrity: sha512-WBBl6au1qg6OHj67yCffCgFR3BADJBXN8MdRvCgJDuMv3driV2nHr7jdGvaKX9IolosAsn+M0XRArqLXUhyJHQ==} + js-beautify@1.15.1: resolution: {integrity: sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==} engines: {node: '>=14'} @@ -7164,6 +7194,22 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + next-auth@5.0.0-beta.22: + resolution: {integrity: sha512-QGBo9HGOjmnJBHGXvtFztl0tM5tL0porDlk74HVoCCzXd986ApOlIW3EmiCuho7YzEopgkFiwwmcXpoCrHAtYw==} + peerDependencies: + '@simplewebauthn/browser': ^9.0.1 + '@simplewebauthn/server': ^9.0.2 + next: ^14.0.0-0 || ^15.0.0-0 + nodemailer: ^6.6.5 + react: ^18.2.0 || ^19.0.0-0 + peerDependenciesMeta: + '@simplewebauthn/browser': + optional: true + '@simplewebauthn/server': + optional: true + nodemailer: + optional: true + next-mdx-remote@4.4.1: resolution: {integrity: sha512-1BvyXaIou6xy3XoNF4yaMZUCb6vD2GTAa5ciOa6WoO+gAUTYsb1K4rI/HSC2ogAWLrb/7VSV52skz07vOzmqIQ==} engines: {node: '>=14', npm: '>=7'} @@ -7301,6 +7347,9 @@ packages: oauth-sign@0.9.0: resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} + oauth4webapi@2.17.0: + resolution: {integrity: sha512-lbC0Z7uzAFNFyzEYRIC+pkSVvDHJTbEW+dYlSBAlCYDe6RxUkJ26bClhk8ocBZip1wfI9uKTe0fm4Ib4RHn6uQ==} + ob1@0.80.9: resolution: {integrity: sha512-v9yOxowkZbxWhKOaaTyLjIm1aLy4ebMNcSn4NYJKOAI/Qv+SkfEfszpLr2GIxsccmb2Y2HA9qtsqiIJ80ucpVA==} engines: {node: '>=18'} @@ -7695,6 +7744,14 @@ packages: resolution: {integrity: sha512-rtqm2h22QxLGBrW2bLYzbRhliIrqgZ0k+gF0LkQ1SNdeD06YE5eilV0MxZppFSxC8TfH0+B0cWCuebEnreIDgQ==} engines: {node: '>=15.0.0'} + preact-render-to-string@5.2.3: + resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==} + peerDependencies: + preact: '>=10' + + preact@10.11.3: + resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==} + preact@10.22.1: resolution: {integrity: sha512-jRYbDDgMpIb5LHq3hkI0bbl+l/TQ9UnkdQ0ww+lp+4MMOdqaUYdFc5qeyP+IV8FAd/2Em7drVPeKdQxsiWCf/A==} @@ -7723,6 +7780,9 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + pretty-format@3.8.0: + resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + pretty-hrtime@1.0.3: resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==} engines: {node: '>= 0.8'} @@ -9685,6 +9745,18 @@ snapshots: '@antfu/ni@0.21.12': {} + '@auth/core@0.35.3(nodemailer@6.9.14)': + dependencies: + '@panva/hkdf': 1.2.1 + '@types/cookie': 0.6.0 + cookie: 0.6.0 + jose: 5.9.4 + oauth4webapi: 2.17.0 + preact: 10.11.3 + preact-render-to-string: 5.2.3(preact@10.11.3) + optionalDependencies: + nodemailer: 6.9.14 + '@babel/code-frame@7.24.7': dependencies: '@babel/highlight': 7.24.7 @@ -11180,6 +11252,8 @@ snapshots: '@opentelemetry/api@1.9.0': {} + '@panva/hkdf@1.2.1': {} + '@pkgjs/parseargs@0.11.0': optional: true @@ -13084,6 +13158,8 @@ snapshots: dependencies: '@babel/types': 7.24.9 + '@types/cookie@0.6.0': {} + '@types/d3-array@3.2.1': {} '@types/d3-color@3.1.3': {} @@ -14291,6 +14367,8 @@ snapshots: convert-source-map@2.0.0: {} + cookie@0.6.0: {} + copy-to-clipboard@3.3.3: dependencies: toggle-selection: 1.0.6 @@ -16031,6 +16109,8 @@ snapshots: jose@4.15.9: {} + jose@5.9.4: {} + js-beautify@1.15.1: dependencies: config-chain: 1.1.13 @@ -17552,6 +17632,14 @@ snapshots: neo-async@2.6.2: {} + next-auth@5.0.0-beta.22(next@14.2.5(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(@playwright/test@1.45.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.14)(react@18.3.1): + dependencies: + '@auth/core': 0.35.3(nodemailer@6.9.14) + next: 14.2.5(@babel/core@7.24.9)(@opentelemetry/api@1.9.0)(@playwright/test@1.45.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + optionalDependencies: + nodemailer: 6.9.14 + next-mdx-remote@4.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@mdx-js/mdx': 2.3.0 @@ -17678,6 +17766,8 @@ snapshots: oauth-sign@0.9.0: {} + oauth4webapi@2.17.0: {} + ob1@0.80.9: {} object-assign@4.1.1: {} @@ -18071,6 +18161,13 @@ snapshots: transitivePeerDependencies: - debug + preact-render-to-string@5.2.3(preact@10.11.3): + dependencies: + preact: 10.11.3 + pretty-format: 3.8.0 + + preact@10.11.3: {} + preact@10.22.1: {} prelude-ls@1.2.1: {} @@ -18100,6 +18197,8 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 + pretty-format@3.8.0: {} + pretty-hrtime@1.0.3: {} prisma@5.20.0: diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 00000000..0a98352f --- /dev/null +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,3 @@ +import { handlers } from '@/auth'; + +export const { GET, POST } = handlers; diff --git a/src/app/route.ts b/src/app/route.ts index 778f1a41..b28cd2f8 100644 --- a/src/app/route.ts +++ b/src/app/route.ts @@ -1,8 +1,15 @@ -import { createSupabaseUserRouteHandlerClient } from '@/supabase-clients/user/createSupabaseUserRouteHandlerClient'; -import { NextResponse } from 'next/server'; +//import { createSupabaseUserRouteHandlerClient } from '@/supabase-clients/user/createSupabaseUserRouteHandlerClient'; + +import { redirect } from 'next/navigation'; export const dynamic = 'force-dynamic'; +export async function GET() { + redirect('/dashboard'); +} + +// TODO remove when move to authjs is complete +/* export async function GET() { const supabase = createSupabaseUserRouteHandlerClient(); @@ -31,3 +38,4 @@ export async function GET() { ); } } +*/ diff --git a/src/auth.ts b/src/auth.ts new file mode 100644 index 00000000..2bae1518 --- /dev/null +++ b/src/auth.ts @@ -0,0 +1,13 @@ +import NextAuth from 'next-auth'; +import Auth0 from 'next-auth/providers/auth0'; + +export const { handlers, signIn, signOut, auth } = NextAuth({ + //providers: [WorkOS({ connection: 'conn_01HVH5N4RFQVD9DH5QWGYT844V' })], + providers: [Auth0], + callbacks: { + authorized: async ({ auth }) => { + // Logged in users are authenticated, otherwise redirect to login page + return !!auth; + }, + }, +}); diff --git a/src/data/user/organizations.ts b/src/data/user/organizations.ts index 24f48ad0..1ff011ee 100644 --- a/src/data/user/organizations.ts +++ b/src/data/user/organizations.ts @@ -103,6 +103,7 @@ export const createOrganization = async ( return { status: 'error', message: orgMemberErrors.message }; } + // Why are we checking for onboarding deep in the data layer? Bad code. if (isOnboardingFlow) { const { error: updateError } = await supabaseClient .from('user_private_info') @@ -621,7 +622,9 @@ export async function getInitialOrganizationToRedirectTo(): Promise< }; } -export async function getMaybeInitialOrganizationToRedirectTo(): Promise> { +export async function getMaybeInitialOrganizationToRedirectTo(): Promise< + SAPayload +> { const initialOrganization = await getInitialOrganizationToRedirectTo(); if (initialOrganization.status === 'error') { return { diff --git a/src/data/user/projects.tsx b/src/data/user/projects.tsx index 07c1f0d6..4e826d00 100644 --- a/src/data/user/projects.tsx +++ b/src/data/user/projects.tsx @@ -196,7 +196,7 @@ export const createProjectCommentAction = async ( const user = await serverGetLoggedInUser(); const { data, error } = await supabaseClient .from("project_comments") - .insert({ project_id: projectId, text, user_id: user.id }) + .insert({ project_id: projectId, text, user_id: user.id! }) //TODO remove assertion or resolve .select("*, user_profiles(*)") .single(); if (error) { diff --git a/src/data/user/session.ts b/src/data/user/session.ts index 7852ae71..a641e6c7 100644 --- a/src/data/user/session.ts +++ b/src/data/user/session.ts @@ -1,9 +1,9 @@ -"use server" +'use server'; -import { createSupabaseUserServerActionClient } from "@/supabase-clients/user/createSupabaseUserServerActionClient"; -import { SAPayload } from "@/types"; +import { SAPayload } from '@/types'; export async function refreshSessionAction(): Promise { + /* const supabaseClient = createSupabaseUserServerActionClient(); const refreshSessionResponse = await supabaseClient.auth.refreshSession(); @@ -13,6 +13,9 @@ export async function refreshSessionAction(): Promise { message: refreshSessionResponse.error.message, }; } + */ + + //TODO re-implement with Auth.js or remove return { status: 'success', diff --git a/src/middleware.ts b/src/middleware.ts index 9f420f02..58cacbac 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,14 +1,9 @@ -import { - createMiddlewareClient, - type User, -} from '@supabase/auth-helpers-nextjs'; -import type { NextRequest } from 'next/server'; -import { NextResponse } from 'next/server'; // const matchAppAdmin = match('/app_admin_preview/(.*)?'); +import { auth } from '@/auth'; +import { NextResponse } from 'next/server'; import { match } from 'path-to-regexp'; -import type { Database } from './lib/database.types'; import { toSiteURL } from './utils/helpers'; -import { authUserMetadataSchema } from './utils/zod-schemas/authUserMetadata'; +import { serverGetLoggedInUser } from './utils/server/serverGetLoggedInUser'; const onboardingPaths = `/onboarding/(.*)?`; // Using a middleware to protect pages from unauthorized access @@ -48,7 +43,8 @@ function isUnprotectedPage(pathname: string) { }); } -function shouldOnboardUser(pathname: string, user: User | undefined) { +function shouldOnboardUser(pathname: string, userId: string) { + /* const matchOnboarding = match(onboardingPaths); const isOnboardingRoute = matchOnboarding(pathname); if (!isUnprotectedPage(pathname) && user && !isOnboardingRoute) { @@ -69,11 +65,17 @@ function shouldOnboardUser(pathname: string, user: User | undefined) { } console.log('user is onboarded'); return false; + */ + return true; + //TODO figure way to store user metadata (extend user_profile table?) } // this middleware refreshes the user's session and must be run // for any Server Component route that uses `createServerComponentSupabaseClient` -export async function middleware(req: NextRequest) { +// renamed while moving to auth.js + +/* +export async function middleware_NEXTBASE_LEGACY(req: NextRequest) { const res = NextResponse.next(); const supabase = createMiddlewareClient({ req, res }); const sessionResponse = await supabase.auth.getSession(); @@ -121,6 +123,17 @@ export async function middleware(req: NextRequest) { return res; } +*/ + +export default auth(async (req) => { + const user = await serverGetLoggedInUser(); + if (shouldOnboardUser(req.nextUrl.pathname, user.id)) { + return NextResponse.redirect(toSiteURL('/onboarding')); + } else { + return NextResponse.next(); + } +}); + export const config = { matcher: [ /* diff --git a/src/utils/server/serverGetLoggedInUser.ts b/src/utils/server/serverGetLoggedInUser.ts index e3c9c00f..50a8dccb 100644 --- a/src/utils/server/serverGetLoggedInUser.ts +++ b/src/utils/server/serverGetLoggedInUser.ts @@ -1,20 +1,19 @@ 'use server'; -import { cache } from 'react'; -import { getSession } from './verifySession'; -export const serverGetLoggedInUser = cache(async () => { - const { - data: { session }, - error: sessionError, - } = await getSession() +import { AuthUser, getSession } from './verifySession'; - if (sessionError) { - throw sessionError; +//TODO reintroduce cache; used to be cache from react +// removed because it was failing: "(0 , react__WEBPACK_IMPORTED_MODULE_2__.cache) is not a function" +export const serverGetLoggedInUser = async () => { + const session = await getSession(); + + if (!session) { + throw new Error('serverGetLoggedInUser: No session'); } if (!session?.user) { throw new Error('serverGetLoggedInUser: Not logged in'); } - return session.user; -}); + return session.user as AuthUser; +}; diff --git a/src/utils/server/serverGetLoggedInUserRole.ts b/src/utils/server/serverGetLoggedInUserRole.ts index 705e8dea..f2dbcbd1 100644 --- a/src/utils/server/serverGetLoggedInUserRole.ts +++ b/src/utils/server/serverGetLoggedInUserRole.ts @@ -1,5 +1,4 @@ 'use server'; -import { cache } from 'react'; import { serverGetLoggedInUser } from './serverGetLoggedInUser'; type UserRole = 'admin' | 'user'; @@ -9,11 +8,11 @@ type UserRole = 'admin' | 'user'; * You can use this to determine if the user is an admin or a regular user. * Based on this value you can show or hide certain UI elements. */ -export const serverGetLoggedInUserRole = cache(async () => { +export const serverGetLoggedInUserRole = async () => { const user = await serverGetLoggedInUser(); if ('user_role' in user) { return user.user_role as UserRole; } else { return 'user' as UserRole; } -}); +}; diff --git a/src/utils/server/serverGetUserType.ts b/src/utils/server/serverGetUserType.ts index 550e4028..31c89470 100644 --- a/src/utils/server/serverGetUserType.ts +++ b/src/utils/server/serverGetUserType.ts @@ -1,10 +1,12 @@ 'use server'; import { createSupabaseUserServerComponentClient } from '@/supabase-clients/user/createSupabaseUserServerComponentClient'; import { userRoles } from '@/utils/userTypes'; -import { cache } from 'react'; // make sure to return one of UserRoles -export const serverGetUserType = cache(async () => { + +//TODO reintroduce cache; used to be cache from react +// removed because it was failing: "(0 , react__WEBPACK_IMPORTED_MODULE_2__.cache) is not a function" +export const serverGetUserType = async () => { const supabase = createSupabaseUserServerComponentClient(); const { data: { session }, @@ -27,5 +29,4 @@ export const serverGetUserType = cache(async () => { } return userRoles.USER; -} -) +}; diff --git a/src/utils/server/verifySession.ts b/src/utils/server/verifySession.ts index 4cd2bf13..44a9f5c5 100644 --- a/src/utils/server/verifySession.ts +++ b/src/utils/server/verifySession.ts @@ -1,18 +1,29 @@ 'use server'; -import { createSupabaseUserServerComponentClient } from '@/supabase-clients/user/createSupabaseUserServerComponentClient'; + +import { auth } from '@/auth'; import { redirect } from 'next/navigation'; -import { cache } from 'react'; -export const getSession = cache(async () => { - const supabase = createSupabaseUserServerComponentClient(); - return await supabase.auth.getSession(); -}); +export interface AuthUser { + id: string; + email: string; + name: string; +} + +//TODO reintroduce cache; used to be cache from react +// removed because it was failing: "(0 , react__WEBPACK_IMPORTED_MODULE_2__.cache) is not a function" +export const getSession = async () => { + //const supabase = createSupabaseUserServerComponentClient(); + //return await supabase.auth.getSession(); + const session = await auth(); + return session; +}; -// This is a server-side function that verifies the session of the user. -// and runs in server components. -export const verifySession = cache(async () => { +//TODO reintroduce cache; used to be cache from react +// removed because it was failing: "(0 , react__WEBPACK_IMPORTED_MODULE_2__.cache) is not a function" +export const verifySession = async () => { + /* const { - data: { session }, + user: { session }, error: sessionError, } = await getSession(); @@ -25,4 +36,10 @@ export const verifySession = cache(async () => { } return session.user; -}); + */ + const session = await auth(); + if (!session?.user) { + redirect('/login'); //TODO update route - there's no longer embedded login pager. preserving as-is for now + } + return session?.user as AuthUser; +}; From f71c57cb2af82e42c8e3dd38979440461323533c Mon Sep 17 00:00:00 2001 From: Igor Zalutski Date: Wed, 16 Oct 2024 15:16:39 +0100 Subject: [PATCH 03/23] Fix middleware redirect loop --- src/middleware.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/middleware.ts b/src/middleware.ts index 58cacbac..3fad6cae 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -126,11 +126,18 @@ export async function middleware_NEXTBASE_LEGACY(req: NextRequest) { */ export default auth(async (req) => { - const user = await serverGetLoggedInUser(); - if (shouldOnboardUser(req.nextUrl.pathname, user.id)) { - return NextResponse.redirect(toSiteURL('/onboarding')); + if (!req.auth) { + //not authenticated - redirect to login URL defined by Auth.js + return NextResponse.redirect(toSiteURL('/api/auth/signin')); } else { - return NextResponse.next(); + const user = await serverGetLoggedInUser(); + if (shouldOnboardUser(req.nextUrl.pathname, user.id)) { + // authenticated but not onboarded + return NextResponse.redirect(toSiteURL('/onboarding')); + } else { + // authenticated and onboarded - no changes to URL + return NextResponse.next(); + } } }); From 22333530810c085e0356370bcca431da932c81c5 Mon Sep 17 00:00:00 2001 From: Igor Zalutski Date: Wed, 16 Oct 2024 15:21:13 +0100 Subject: [PATCH 04/23] Fix onboarding redirect loop --- src/middleware.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/middleware.ts b/src/middleware.ts index 3fad6cae..eb55c060 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -131,7 +131,10 @@ export default auth(async (req) => { return NextResponse.redirect(toSiteURL('/api/auth/signin')); } else { const user = await serverGetLoggedInUser(); - if (shouldOnboardUser(req.nextUrl.pathname, user.id)) { + if ( + shouldOnboardUser(req.nextUrl.pathname, user.id) && + req.nextUrl.pathname !== '/onboarding' + ) { // authenticated but not onboarded return NextResponse.redirect(toSiteURL('/onboarding')); } else { From aa51f66cb3f19e2b199a331fa345185f9f7bdcba Mon Sep 17 00:00:00 2001 From: Igor Zalutski Date: Wed, 16 Oct 2024 23:19:15 +0100 Subject: [PATCH 05/23] Create user profile on onboarding --- .../(authenticated-pages)/onboarding/page.tsx | 29 +++++++------ src/data/user/user.tsx | 42 +++++++++++++++++++ src/lib/database.types.ts | 42 +++++++++---------- .../20241016213106_user_profile_email.sql | 7 ++++ .../20241016215947_user_profile_drop_rls.sql | 1 + ...1016220301_user_profile_drop_auth_fkey.sql | 2 + 6 files changed, 87 insertions(+), 36 deletions(-) create mode 100644 supabase/migrations/20241016213106_user_profile_email.sql create mode 100644 supabase/migrations/20241016215947_user_profile_drop_rls.sql create mode 100644 supabase/migrations/20241016220301_user_profile_drop_auth_fkey.sql diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx index 6dea51d9..7cecdb18 100644 --- a/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx +++ b/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx @@ -1,6 +1,6 @@ import { Skeleton } from "@/components/ui/skeleton"; import { fetchSlimOrganizations, getDefaultOrganization, setDefaultOrganization } from "@/data/user/organizations"; -import { getUserProfile } from "@/data/user/user"; +import { getUserProfileByEmailOrCreate } from "@/data/user/user"; import { serverGetLoggedInUser } from "@/utils/server/serverGetLoggedInUser"; import { authUserMetadataSchema } from "@/utils/zod-schemas/authUserMetadata"; import { Suspense } from 'react'; @@ -34,9 +34,10 @@ async function getDefaultOrganizationOrSet(): Promise { return firstOrganization.id; } -async function getOnboardingConditions(userId: string) { +//TODO rename / refactor: it also creates profile. Temporary workaround. +async function getOnboardingConditions(email: string) { const [userProfile, defaultOrganizationId] = await Promise.all([ - getUserProfile(userId), + getUserProfileByEmailOrCreate(email), getDefaultOrganizationOrSet(), ]); @@ -48,14 +49,17 @@ async function getOnboardingConditions(userId: string) { }; } -async function OnboardingFlowWrapper({ userId, userEmail }: { userId: string; userEmail: string | undefined }) { - const [onboardingConditions, user] = await Promise.all([ - getOnboardingConditions(userId), - serverGetLoggedInUser(), - ]); - const { userProfile } = onboardingConditions; - console.log("userProfile", userProfile); - const onboardingStatus = authUserMetadataSchema.parse(user.user_metadata); +async function OnboardingFlowWrapper({ userEmail }: { userEmail: string }) { + const { userProfile } = await getOnboardingConditions(userEmail) + console.log("Conditions got - profile", userProfile); + //TODO use actual user metadata instead of dummy + const dummyMetadata = authUserMetadataSchema.parse({ + onboardingHasAcceptedTerms: true, + onboardingHasCompletedProfile: false, + onboardingHasCreatedOrganization: false, + isUserCreatedThroughOrgInvitation: false, + }) + const onboardingStatus = dummyMetadata console.log(onboardingStatus); console.log(userEmail); @@ -70,11 +74,10 @@ async function OnboardingFlowWrapper({ userId, userEmail }: { userId: string; us export default async function OnboardingPage() { const user = await serverGetLoggedInUser(); - return (
}> - +
); diff --git a/src/data/user/user.tsx b/src/data/user/user.tsx index 1f47c576..27ea1cc6 100644 --- a/src/data/user/user.tsx +++ b/src/data/user/user.tsx @@ -13,6 +13,7 @@ import ConfirmAccountDeletionEmail from "emails/account-deletion-request"; import { revalidatePath } from "next/cache"; import slugify from "slugify"; import urlJoin from "url-join"; +import { v4 as uuidv4 } from 'uuid'; import { acceptInvitationAction } from "./invitation"; import { createOrganization, setDefaultOrganization } from "./organizations"; import { refreshSessionAction } from "./session"; @@ -39,6 +40,47 @@ export const getUserProfile = async (userId: string) => { return data; }; + +export const getUserProfileByEmail = async (email: string) => { + const supabase = createSupabaseUserServerComponentClient(); + const { data, error } = await supabase + .from("user_profiles") + .select("*") + .eq("email", email) + .single(); + + if (error) { + throw error; + } + + return data; +}; + +export const getUserProfileByEmailOrCreate = async (email: string) => { + const supabase = createSupabaseUserServerComponentClient(); + try { + // Try to get the existing user profile + const existingProfile = await getUserProfileByEmail(email); + return existingProfile; + } catch (error) { + // If the profile doesn't exist, create a new one + // TODO filter error by code PGRST116 or details "the result contains 0 rows" + const { data, error: createError } = await supabase + .from("user_profiles") + .insert({ id: uuidv4(), email }) + .select() + .single(); + + if (createError) { + console.log('creating profil error', createError); + throw createError; + } + + return data; + + } +}; + export const getUserFullName = async (userId: string) => { const supabase = createSupabaseUserServerComponentClient(); const { data, error } = await supabase diff --git a/src/lib/database.types.ts b/src/lib/database.types.ts index 5c1352db..2eb1827a 100644 --- a/src/lib/database.types.ts +++ b/src/lib/database.types.ts @@ -78,13 +78,6 @@ export type Database = { user_id?: string | null } Relationships: [ - { - foreignKeyName: "chats_user_id_fkey" - columns: ["user_id"] - isOneToOne: false - referencedRelation: "users" - referencedColumns: ["id"] - }, { foreignKeyName: "public_chats_project_id_fkey" columns: ["project_id"] @@ -1719,19 +1712,13 @@ export type Database = { referencedRelation: "organizations" referencedColumns: ["id"] }, - { - foreignKeyName: "user_private_info_id_fkey" - columns: ["id"] - isOneToOne: true - referencedRelation: "users" - referencedColumns: ["id"] - }, ] } user_profiles: { Row: { avatar_url: string | null created_at: string + email: string | null full_name: string | null id: string user_name: string | null @@ -1739,6 +1726,7 @@ export type Database = { Insert: { avatar_url?: string | null created_at?: string + email?: string | null full_name?: string | null id: string user_name?: string | null @@ -1746,19 +1734,12 @@ export type Database = { Update: { avatar_url?: string | null created_at?: string + email?: string | null full_name?: string | null id?: string user_name?: string | null } - Relationships: [ - { - foreignKeyName: "user_profiles_id_fkey" - columns: ["id"] - isOneToOne: true - referencedRelation: "users" - referencedColumns: ["id"] - }, - ] + Relationships: [] } user_roles: { Row: { @@ -2132,3 +2113,18 @@ export type Enums< : PublicEnumNameOrOptions extends keyof PublicSchema["Enums"] ? PublicSchema["Enums"][PublicEnumNameOrOptions] : never + +export type CompositeTypes< + PublicCompositeTypeNameOrOptions extends + | keyof PublicSchema["CompositeTypes"] + | { schema: keyof Database }, + CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { + schema: keyof Database + } + ? keyof Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] + : never = never, +> = PublicCompositeTypeNameOrOptions extends { schema: keyof Database } + ? Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] + : PublicCompositeTypeNameOrOptions extends keyof PublicSchema["CompositeTypes"] + ? PublicSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] + : never diff --git a/supabase/migrations/20241016213106_user_profile_email.sql b/supabase/migrations/20241016213106_user_profile_email.sql new file mode 100644 index 00000000..145407b1 --- /dev/null +++ b/supabase/migrations/20241016213106_user_profile_email.sql @@ -0,0 +1,7 @@ +-- Add email column to user_profiles table +ALTER TABLE user_profiles +ADD COLUMN email VARCHAR(255); + +-- Optionally, you can add constraints or indexes +ALTER TABLE user_profiles +ADD CONSTRAINT email_unique UNIQUE (email); \ No newline at end of file diff --git a/supabase/migrations/20241016215947_user_profile_drop_rls.sql b/supabase/migrations/20241016215947_user_profile_drop_rls.sql new file mode 100644 index 00000000..2e6a9215 --- /dev/null +++ b/supabase/migrations/20241016215947_user_profile_drop_rls.sql @@ -0,0 +1 @@ +ALTER TABLE user_profiles DISABLE ROW LEVEL SECURITY; \ No newline at end of file diff --git a/supabase/migrations/20241016220301_user_profile_drop_auth_fkey.sql b/supabase/migrations/20241016220301_user_profile_drop_auth_fkey.sql new file mode 100644 index 00000000..e88b9289 --- /dev/null +++ b/supabase/migrations/20241016220301_user_profile_drop_auth_fkey.sql @@ -0,0 +1,2 @@ +ALTER TABLE user_profiles +DROP CONSTRAINT IF EXISTS user_profiles_id_fkey; \ No newline at end of file From 459b7e1b92358f7976b262fe23b1ca858646164a Mon Sep 17 00:00:00 2001 From: Igor Zalutski Date: Thu, 17 Oct 2024 02:10:52 +0100 Subject: [PATCH 06/23] Use user_profile table in serverGetLoggedInUser --- package.json | 2 +- pnpm-lock.yaml | 6 ++--- .../ApprovalControls.tsx | 4 ++-- src/contexts/LoggedInUserContext.tsx | 8 +++---- src/contexts/PostHogProvider.tsx | 2 +- src/hooks/useLoggedInUser.ts | 4 ++-- src/utils/server/serverGetLoggedInUser.ts | 22 +++++++++++++++++-- 7 files changed, 33 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 06001f0a..1eb8fbcf 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "@mdx-js/react": "^3.0.0", "@next/bundle-analyzer": "^14.0.0", "@next/mdx": "^14.2.3", - "@prisma/client": "^5.20.0", "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-aspect-ratio": "^1.0.3", @@ -204,6 +203,7 @@ "@microflash/rehype-slugify": "^1.0.0", "@next/eslint-plugin-next": "^14.0.0", "@playwright/test": "^1.42.1", + "@prisma/client": "^5.20.0", "@testing-library/react": "^14.0.0", "@types/events": "^3.0.1", "@types/formidable": "^3.4.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9b2e7d5d..77a83aa6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,9 +41,6 @@ importers: '@next/mdx': specifier: ^14.2.3 version: 14.2.5(@mdx-js/loader@3.0.1(webpack@5.93.0))(@mdx-js/react@3.0.1(@types/react@18.3.2)(react@18.3.1)) - '@prisma/client': - specifier: ^5.20.0 - version: 5.20.0(prisma@5.20.0) '@radix-ui/react-accordion': specifier: ^1.1.2 version: 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -546,6 +543,9 @@ importers: '@playwright/test': specifier: ^1.42.1 version: 1.45.1 + '@prisma/client': + specifier: ^5.20.0 + version: 5.20.0(prisma@5.20.0) '@testing-library/react': specifier: ^14.0.0 version: 14.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/ApprovalControls.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/ApprovalControls.tsx index feb1742f..f2979049 100644 --- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/ApprovalControls.tsx +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/ApprovalControls.tsx @@ -13,10 +13,10 @@ async function fetchData(projectId: string) { const [organizationData, maybeTeamData, organizationRole, teamRole] = await Promise.all([ getSlimOrganizationById(projectByIdData.organization_id), - projectByIdData.team_id ? getSlimTeamById(projectByIdData.team_id) : null, + projectByIdData.team_id ? getSlimTeamById(Number(projectByIdData.team_id)) : null, getLoggedInUserOrganizationRole(projectByIdData.organization_id), projectByIdData.team_id - ? getLoggedInUserTeamRole(projectByIdData.team_id) + ? getLoggedInUserTeamRole(Number(projectByIdData.team_id)) : null, ]); diff --git a/src/contexts/LoggedInUserContext.tsx b/src/contexts/LoggedInUserContext.tsx index 1c0ae96b..319e4783 100644 --- a/src/contexts/LoggedInUserContext.tsx +++ b/src/contexts/LoggedInUserContext.tsx @@ -1,20 +1,20 @@ 'use client'; -import { User } from '@supabase/supabase-js'; +import { AuthUser } from '@/utils/server/verifySession'; import { createContext } from 'react'; type LoggedInUserContextType = { - user: User; + user: AuthUser; }; export const LoggedInUserContext = createContext({ - user: null as unknown as User, + user: null as unknown as AuthUser, }); export const LoggedInUserProvider = ({ user, children, }: { - user: User; + user: AuthUser; children: React.ReactNode; }) => { return ( diff --git a/src/contexts/PostHogProvider.tsx b/src/contexts/PostHogProvider.tsx index 596734ca..62c2a604 100644 --- a/src/contexts/PostHogProvider.tsx +++ b/src/contexts/PostHogProvider.tsx @@ -26,7 +26,7 @@ const useInitPostHog = () => { if (user && hasInit) { posthog.identify(user.id, { email: user.email, - name: user.user_metadata.full_name, + name: user.name, }); } }, [user, hasInit]); diff --git a/src/hooks/useLoggedInUser.ts b/src/hooks/useLoggedInUser.ts index 5fdca9dd..a619524b 100644 --- a/src/hooks/useLoggedInUser.ts +++ b/src/hooks/useLoggedInUser.ts @@ -1,12 +1,12 @@ import { LoggedInUserContext } from '@/contexts/LoggedInUserContext'; -import { User } from '@supabase/supabase-js'; +import { AuthUser } from '@/utils/server/verifySession'; import { useContext } from 'react'; /** * This is useful for pages that require the user to be logged in. * @returns The logged in user or throws an error if the user is not logged in */ -export const useLoggedInUser = (): User => { +export const useLoggedInUser = (): AuthUser => { const { user } = useContext(LoggedInUserContext); if (!user) throw new Error('User is not logged in'); return user; diff --git a/src/utils/server/serverGetLoggedInUser.ts b/src/utils/server/serverGetLoggedInUser.ts index 50a8dccb..2c511b8f 100644 --- a/src/utils/server/serverGetLoggedInUser.ts +++ b/src/utils/server/serverGetLoggedInUser.ts @@ -1,9 +1,11 @@ 'use server'; +import { getUserProfileByEmail } from '@/data/user/user'; import { AuthUser, getSession } from './verifySession'; //TODO reintroduce cache; used to be cache from react // removed because it was failing: "(0 , react__WEBPACK_IMPORTED_MODULE_2__.cache) is not a function" +//TODO don't check DB here it will likely make it slow export const serverGetLoggedInUser = async () => { const session = await getSession(); @@ -14,6 +16,22 @@ export const serverGetLoggedInUser = async () => { if (!session?.user) { throw new Error('serverGetLoggedInUser: Not logged in'); } - - return session.user as AuthUser; + try { + //TODO this is a dirty workaround to avoid migrating from auth.user.id to email as id + // user object from auth.js only has email, but no ID unlike Subabase's auth + // but we have user_profiles table which used to be FK for auth.user.id + // so using that id as a substitute for actual user id for auth + // PS if the app feels slow this might be the reason why :) hope it's not too bad thx to cache + const profile = await getUserProfileByEmail(session.user.email!); + return { + ...session.user, + id: profile.id, + } as AuthUser; + } catch (e) { + console.error( + 'serverGetLoggedInUser: Failed to get user profile by email', + e, + ); + throw e; + } }; From 3de12d7639c42fcbd7955581bb685d435286e186 Mon Sep 17 00:00:00 2001 From: Igor Zalutski Date: Thu, 17 Oct 2024 10:44:08 +0100 Subject: [PATCH 07/23] Move auth to route handler --- src/app/route.ts | 49 ++++++++++++++++++++++++++++++++++++++++++++--- src/middleware.ts | 12 ++++++++---- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/app/route.ts b/src/app/route.ts index b28cd2f8..fded9f75 100644 --- a/src/app/route.ts +++ b/src/app/route.ts @@ -1,11 +1,54 @@ //import { createSupabaseUserRouteHandlerClient } from '@/supabase-clients/user/createSupabaseUserRouteHandlerClient'; -import { redirect } from 'next/navigation'; +import { toSiteURL } from '@/utils/helpers'; +import { serverGetLoggedInUser } from '@/utils/server/serverGetLoggedInUser'; +import { NextRequest, NextResponse } from 'next/server'; export const dynamic = 'force-dynamic'; -export async function GET() { - redirect('/dashboard'); +function shouldOnboardUser(pathname: string, userId: string) { + /* + const matchOnboarding = match(onboardingPaths); + const isOnboardingRoute = matchOnboarding(pathname); + if (!isUnprotectedPage(pathname) && user && !isOnboardingRoute) { + const userMetadata = authUserMetadataSchema.parse(user.user_metadata); + console.log('user metadata:', userMetadata); + const { + onboardingHasAcceptedTerms, + onboardingHasCompletedProfile, + onboardingHasCreatedOrganization, + } = userMetadata; + if ( + !onboardingHasAcceptedTerms || + !onboardingHasCompletedProfile || + !onboardingHasCreatedOrganization + ) { + return true; + } + } + console.log('user is onboarded'); + return false; + */ + return true; + //TODO figure way to store user metadata (extend user_profile table?) +} + +export async function GET(request: NextRequest) { + try { + const user = await serverGetLoggedInUser(); + if ( + shouldOnboardUser(request.nextUrl.pathname, user.id) && + request.nextUrl.pathname !== '/onboarding' + ) { + // Authenticated but not onboarded + return NextResponse.redirect(toSiteURL('/onboarding')); + } else { + return NextResponse.next(); + } + } catch (error) { + console.log('User not signed in, redirecting to login', error); + return NextResponse.redirect(toSiteURL('/api/auth/signin')); + } } // TODO remove when move to authjs is complete diff --git a/src/middleware.ts b/src/middleware.ts index eb55c060..5a715204 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,9 +1,6 @@ // const matchAppAdmin = match('/app_admin_preview/(.*)?'); -import { auth } from '@/auth'; -import { NextResponse } from 'next/server'; +import { NextRequest, NextResponse } from 'next/server'; import { match } from 'path-to-regexp'; -import { toSiteURL } from './utils/helpers'; -import { serverGetLoggedInUser } from './utils/server/serverGetLoggedInUser'; const onboardingPaths = `/onboarding/(.*)?`; // Using a middleware to protect pages from unauthorized access @@ -32,6 +29,10 @@ const unprotectedPagePrefixes = [ `/waitlist(/.*)?`, ]; +export function middleware(request: NextRequest) { + return NextResponse.next(); +} + function isLandingPage(pathname: string) { return pathname === '/'; } @@ -125,6 +126,8 @@ export async function middleware_NEXTBASE_LEGACY(req: NextRequest) { */ +/* +Middleware for Auth.js - but it doesn't work, edge runtime error. Back to route level export default auth(async (req) => { if (!req.auth) { //not authenticated - redirect to login URL defined by Auth.js @@ -143,6 +146,7 @@ export default auth(async (req) => { } } }); +*/ export const config = { matcher: [ From c48d70e2643a2c209c8153c9ba4fd1f706281b16 Mon Sep 17 00:00:00 2001 From: Igor Zalutski Date: Thu, 17 Oct 2024 15:16:20 +0100 Subject: [PATCH 08/23] Handle first and default organisations in onboarding --- .../(authenticated-pages)/onboarding/page.tsx | 47 ++++++++------- src/auth.ts | 6 +- src/data/user/organizations.ts | 60 ++++++++++++++++++- src/utils/server/serverGetLoggedInUser.ts | 17 +++--- .../20241017125938_orgs_drop_rls.sql | 1 + .../20241017130111_org_members_drop_rls.sql | 1 + 6 files changed, 100 insertions(+), 32 deletions(-) create mode 100644 supabase/migrations/20241017125938_orgs_drop_rls.sql create mode 100644 supabase/migrations/20241017130111_org_members_drop_rls.sql diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx index 0dcb9293..3d5beeeb 100644 --- a/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx +++ b/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx @@ -8,31 +8,38 @@ import { UserOnboardingFlow } from "./OnboardingFlow"; async function getDefaultOrganizationOrSet(): Promise { console.log(`get default organisation`) - const [slimOrganizations, defaultOrganizationId] = await Promise.all([ - fetchSlimOrganizations(), - getDefaultOrganization(), - ]); - console.log('slimOrganizations on onboarding', slimOrganizations); - const firstOrganization = slimOrganizations[0]; + try { + const [slimOrganizations, defaultOrganizationId] = await Promise.all([ + fetchSlimOrganizations(), + getDefaultOrganization(), + ]); + console.log('slimOrganizations on onboarding', slimOrganizations); + const firstOrganization = slimOrganizations[0]; - console.log('firstOrganization on onboarding', firstOrganization); - if (defaultOrganizationId) { - console.log('defaultOrganizationId on onboarding', defaultOrganizationId); - return defaultOrganizationId; - } + console.log('firstOrganization on onboarding', firstOrganization); + if (defaultOrganizationId) { + console.log('defaultOrganizationId on onboarding', defaultOrganizationId); + return defaultOrganizationId; + } + + if (!firstOrganization) { + console.log('no firstOrganization on onboarding'); + return null; + } - if (!firstOrganization) { - console.log('no firstOrganization on onboarding'); + // if the user has an organization already for some + // reason, because of an invite or for some other reason, + // make sure that the default organization is set to the first + await setDefaultOrganization(firstOrganization.id); + console.log('Set default organization to firstOrganization on onboarding', firstOrganization.id); + + return firstOrganization.id; + } catch (error) { + // this means that getDefaultOrganization() threw an error (bc fetchSlimOrganizations() always returns) + console.log('No default organization on onboarding'); return null; } - // if the user has an organization already for some - // reason, because of an invite or for some other reason, - // make sure that the default organization is set to the first - await setDefaultOrganization(firstOrganization.id); - console.log('set default organization to firstOrganization on onboarding', firstOrganization.id); - - return firstOrganization.id; } //TODO rename / refactor: it also creates profile. Temporary workaround. diff --git a/src/auth.ts b/src/auth.ts index 2bae1518..5ea60ef9 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -3,7 +3,11 @@ import Auth0 from 'next-auth/providers/auth0'; export const { handlers, signIn, signOut, auth } = NextAuth({ //providers: [WorkOS({ connection: 'conn_01HVH5N4RFQVD9DH5QWGYT844V' })], - providers: [Auth0], + providers: [ + Auth0({ + authorization: { params: { prompt: 'login' } }, + }), + ], callbacks: { authorized: async ({ auth }) => { // Logged in users are authenticated, otherwise redirect to login page diff --git a/src/data/user/organizations.ts b/src/data/user/organizations.ts index 1ff011ee..43a3432d 100644 --- a/src/data/user/organizations.ts +++ b/src/data/user/organizations.ts @@ -13,7 +13,7 @@ import type { import { serverGetLoggedInUser } from '@/utils/server/serverGetLoggedInUser'; import type { AuthUserMetadata } from '@/utils/zod-schemas/authUserMetadata'; import { revalidatePath } from 'next/cache'; -import { v4 as uuid } from 'uuid'; +import { v4 as uuidv4 } from 'uuid'; import { refreshSessionAction } from './session'; export const getOrganizationIdBySlug = async (slug: string) => { @@ -62,7 +62,7 @@ export const createOrganization = async ( const supabaseClient = createSupabaseUserServerActionClient(); const user = await serverGetLoggedInUser(); - const organizationId = uuid(); + const organizationId = uuidv4(); if (RESTRICTED_SLUG_NAMES.includes(slug)) { return { status: 'error', message: 'Slug is restricted' }; @@ -175,7 +175,6 @@ export async function fetchSlimOrganizations() { if (error) { throw error; } - return data || []; } @@ -500,6 +499,61 @@ export const getDefaultOrganization = async () => { return data.default_organization; }; +//TODO remove, likely not needed +export const getDefaultOrganizationOrCreate = async () => { + const supabaseClient = createSupabaseUserServerComponentClient(); + const user = await serverGetLoggedInUser(); + + try { + const defaultOrganizationId = await getDefaultOrganization(); + if (defaultOrganizationId) { + return defaultOrganizationId; + } + } catch (error) { + if (error.code !== 'PGRST116') { + throw error; + } + } + // create org + const { data: newOrg, error: createError } = await supabaseClient + .from('organizations') + .insert({ id: uuidv4() }) + .select() + .single(); + + if (createError) { + console.error('Failed to create default organization', createError); + throw createError; + } + // update private info + const { error: privateInfoUpdateError } = await supabaseClient + .from('user_private_info') + .update({ default_organization: newOrg.id }) + .eq('id', user.id); + + if (privateInfoUpdateError) { + console.error('Failed to update user private info', privateInfoUpdateError); + throw privateInfoUpdateError; + } + // create org membership + const { error: membershipError } = await supabaseClient + .from('organization_members') + .insert({ + member_id: user.id, + organization_id: newOrg.id, + member_role: 'owner', + }) + .select() + .single(); + + if (membershipError) { + console.error('Failed to create default organization', membershipError); + throw membershipError; + } + + return newOrg.id; +}; + export const getDefaultOrganizationId = async () => { const supabaseClient = createSupabaseUserServerComponentClient(); const { data, error } = await supabaseClient diff --git a/src/utils/server/serverGetLoggedInUser.ts b/src/utils/server/serverGetLoggedInUser.ts index 2c511b8f..4d6924ad 100644 --- a/src/utils/server/serverGetLoggedInUser.ts +++ b/src/utils/server/serverGetLoggedInUser.ts @@ -1,6 +1,6 @@ 'use server'; -import { getUserProfileByEmail } from '@/data/user/user'; +import { getUserProfileByEmailOrCreate } from '@/data/user/user'; import { AuthUser, getSession } from './verifySession'; //TODO reintroduce cache; used to be cache from react @@ -16,22 +16,23 @@ export const serverGetLoggedInUser = async () => { if (!session?.user) { throw new Error('serverGetLoggedInUser: Not logged in'); } + + if (!session.user.email) { + throw new Error('User has no email'); + } try { //TODO this is a dirty workaround to avoid migrating from auth.user.id to email as id // user object from auth.js only has email, but no ID unlike Subabase's auth // but we have user_profiles table which used to be FK for auth.user.id // so using that id as a substitute for actual user id for auth // PS if the app feels slow this might be the reason why :) hope it's not too bad thx to cache - const profile = await getUserProfileByEmail(session.user.email!); + const profile = await getUserProfileByEmailOrCreate(session.user.email!); return { ...session.user, id: profile.id, } as AuthUser; - } catch (e) { - console.error( - 'serverGetLoggedInUser: Failed to get user profile by email', - e, - ); - throw e; + } catch (error) { + console.error('Failed to get or create user profile by email', error); + throw error; } }; diff --git a/supabase/migrations/20241017125938_orgs_drop_rls.sql b/supabase/migrations/20241017125938_orgs_drop_rls.sql new file mode 100644 index 00000000..94fb7675 --- /dev/null +++ b/supabase/migrations/20241017125938_orgs_drop_rls.sql @@ -0,0 +1 @@ +ALTER TABLE organizations DISABLE ROW LEVEL SECURITY; \ No newline at end of file diff --git a/supabase/migrations/20241017130111_org_members_drop_rls.sql b/supabase/migrations/20241017130111_org_members_drop_rls.sql new file mode 100644 index 00000000..29d804b5 --- /dev/null +++ b/supabase/migrations/20241017130111_org_members_drop_rls.sql @@ -0,0 +1 @@ +ALTER TABLE organization_members DISABLE ROW LEVEL SECURITY; \ No newline at end of file From cdbc9abcd5b298eca9007e794ea51d29ae8c72c7 Mon Sep 17 00:00:00 2001 From: Igor Zalutski Date: Thu, 17 Oct 2024 17:07:53 +0100 Subject: [PATCH 09/23] Move user metadata to the db --- .../(authenticated-pages)/onboarding/page.tsx | 16 ++++---- src/app/route.ts | 38 ++++++------------- src/data/user/organizations.ts | 23 ++++------- src/data/user/user.tsx | 35 ++++++++++++----- src/lib/database.types.ts | 12 ++++++ .../20241017150518_user_profile_metadata.sql | 5 +++ .../20241017151343_user_profile_camelcase.sql | 5 +++ .../20241017151749_user_profile_naming.sql | 13 +++++++ 8 files changed, 88 insertions(+), 59 deletions(-) create mode 100644 supabase/migrations/20241017150518_user_profile_metadata.sql create mode 100644 supabase/migrations/20241017151343_user_profile_camelcase.sql create mode 100644 supabase/migrations/20241017151749_user_profile_naming.sql diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx index 3d5beeeb..634140c8 100644 --- a/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx +++ b/src/app/(dynamic-pages)/(authenticated-pages)/onboarding/page.tsx @@ -2,7 +2,6 @@ import { Skeleton } from "@/components/ui/skeleton"; import { fetchSlimOrganizations, getDefaultOrganization, setDefaultOrganization } from "@/data/user/organizations"; import { getUserProfileByEmailOrCreate } from "@/data/user/user"; import { serverGetLoggedInUser } from "@/utils/server/serverGetLoggedInUser"; -import { authUserMetadataSchema } from "@/utils/zod-schemas/authUserMetadata"; import { Suspense } from 'react'; import { UserOnboardingFlow } from "./OnboardingFlow"; @@ -61,14 +60,13 @@ async function getOnboardingConditions(email: string) { async function OnboardingFlowWrapper({ userEmail }: { userEmail: string }) { const { userProfile } = await getOnboardingConditions(userEmail) console.log("Conditions got - profile", userProfile); - //TODO use actual user metadata instead of dummy - const dummyMetadata = authUserMetadataSchema.parse({ - onboardingHasAcceptedTerms: true, - onboardingHasCompletedProfile: false, - onboardingHasCreatedOrganization: false, - isUserCreatedThroughOrgInvitation: false, - }) - const onboardingStatus = dummyMetadata + + const onboardingStatus = { + onboardingHasAcceptedTerms: Boolean(userProfile.has_accepted_terms), + onboardingHasCompletedProfile: Boolean(userProfile.has_completed_profile), + onboardingHasCreatedOrganization: Boolean(userProfile.has_created_organization), + isUserCreatedThroughOrgInvitation: Boolean(userProfile.is_created_through_org_invitation), + } console.log(onboardingStatus); console.log(userEmail); diff --git a/src/app/route.ts b/src/app/route.ts index e0a2c4dc..fbe55eff 100644 --- a/src/app/route.ts +++ b/src/app/route.ts @@ -1,36 +1,17 @@ //import { createSupabaseUserRouteHandlerClient } from '@/supabase-clients/user/createSupabaseUserRouteHandlerClient'; +import { getUserProfileByEmail } from '@/data/user/user'; import { toSiteURL } from '@/utils/helpers'; import { serverGetLoggedInUser } from '@/utils/server/serverGetLoggedInUser'; import { NextRequest, NextResponse } from 'next/server'; export const dynamic = 'force-dynamic'; -function shouldOnboardUser(pathname: string, userId: string) { - /* - const matchOnboarding = match(onboardingPaths); - const isOnboardingRoute = matchOnboarding(pathname); - if (!isUnprotectedPage(pathname) && user && !isOnboardingRoute) { - const userMetadata = authUserMetadataSchema.parse(user.user_metadata); - console.log('user metadata:', userMetadata); - const { - onboardingHasAcceptedTerms, - onboardingHasCompletedProfile, - onboardingHasCreatedOrganization, - } = userMetadata; - if ( - !onboardingHasAcceptedTerms || - !onboardingHasCompletedProfile || - !onboardingHasCreatedOrganization - ) { - return true; - } - } - console.log('user is onboarded'); - return false; - */ - return true; - //TODO figure way to store user metadata (extend user_profile table?) +async function shouldOnboardUser(pathname: string, email: string) { + const userProfile = await getUserProfileByEmail(email); + return ( + !userProfile.has_completed_profile || !userProfile.has_created_organization + ); } export async function GET(request: NextRequest) { @@ -42,7 +23,7 @@ export async function GET(request: NextRequest) { try { const user = await serverGetLoggedInUser(); if ( - shouldOnboardUser(request.nextUrl.pathname, user.id) && + (await shouldOnboardUser(request.nextUrl.pathname, user.email)) && request.nextUrl.pathname !== '/onboarding' ) { // Authenticated but not onboarded @@ -56,6 +37,11 @@ export async function GET(request: NextRequest) { } } +function getOnboardingConditions( + email: string, +): { userProfile: any } | PromiseLike<{ userProfile: any }> { + throw new Error('Function not implemented.'); +} // TODO remove when move to authjs is complete /* export async function GET() { diff --git a/src/data/user/organizations.ts b/src/data/user/organizations.ts index 43a3432d..a49477b9 100644 --- a/src/data/user/organizations.ts +++ b/src/data/user/organizations.ts @@ -11,10 +11,10 @@ import type { UnwrapPromise, } from '@/types'; import { serverGetLoggedInUser } from '@/utils/server/serverGetLoggedInUser'; -import type { AuthUserMetadata } from '@/utils/zod-schemas/authUserMetadata'; import { revalidatePath } from 'next/cache'; import { v4 as uuidv4 } from 'uuid'; import { refreshSessionAction } from './session'; +import { updateUserProfileMetadata } from './user'; export const getOrganizationIdBySlug = async (slug: string) => { const supabaseClient = createSupabaseUserServerComponentClient(); @@ -115,22 +115,15 @@ export const createOrganization = async ( return { status: 'error', message: updateError.message }; } - const updateUserMetadataPayload: Partial = { - onboardingHasCreatedOrganization: true, - }; - - const updateUserMetadataResponse = await supabaseClient.auth.updateUser({ - data: updateUserMetadataPayload, - }); - - if (updateUserMetadataResponse.error) { - console.error( - 'Error updating user metadata:', - updateUserMetadataResponse.error, - ); + try { + await updateUserProfileMetadata(user.id, { + has_created_organization: true, + }); + } catch (e) { + console.error('Error updating user metadata:', e.message); return { status: 'error', - message: updateUserMetadataResponse.error.message, + message: e.message, }; } diff --git a/src/data/user/user.tsx b/src/data/user/user.tsx index 700aecf4..461af041 100644 --- a/src/data/user/user.tsx +++ b/src/data/user/user.tsx @@ -186,6 +186,28 @@ export const uploadPublicUserAvatar = async ( return { status: "success", data: supabaseFileUrl }; }; +export async function updateUserProfileMetadata(userId: string, metadata: { + has_accepted_terms?: boolean, + has_completed_profile?: boolean, + has_created_organization?: boolean, + is_created_through_org_invitation?: boolean, +}) { + const supabaseClient = createSupabaseUserServerActionClient(); + + const { data, error } = await supabaseClient + .from("user_profiles") + .update(metadata) + .eq("id", userId) + .select() + .single(); + + if (error) { + throw error; + } + + return data; +} + export const updateUserProfileNameAndAvatar = async ( { fullName, @@ -224,18 +246,13 @@ export const updateUserProfileNameAndAvatar = async ( } if (isOnboardingFlow) { - const updateUserMetadataPayload: Partial = { - onboardingHasCompletedProfile: true, - }; - - const updateUserMetadataResponse = await supabaseClient.auth.updateUser({ - data: updateUserMetadataPayload, - }); - if (updateUserMetadataResponse.error) { + try { + await updateUserProfileMetadata(user.id, { has_completed_profile: true }); + } catch (e) { return { status: "error", - message: updateUserMetadataResponse.error.message, + message: e.message, }; } diff --git a/src/lib/database.types.ts b/src/lib/database.types.ts index 2eb1827a..48c63c74 100644 --- a/src/lib/database.types.ts +++ b/src/lib/database.types.ts @@ -1720,7 +1720,11 @@ export type Database = { created_at: string email: string | null full_name: string | null + has_accepted_terms: boolean | null + has_completed_profile: boolean | null + has_created_organization: boolean | null id: string + is_created_through_org_invitation: boolean | null user_name: string | null } Insert: { @@ -1728,7 +1732,11 @@ export type Database = { created_at?: string email?: string | null full_name?: string | null + has_accepted_terms?: boolean | null + has_completed_profile?: boolean | null + has_created_organization?: boolean | null id: string + is_created_through_org_invitation?: boolean | null user_name?: string | null } Update: { @@ -1736,7 +1744,11 @@ export type Database = { created_at?: string email?: string | null full_name?: string | null + has_accepted_terms?: boolean | null + has_completed_profile?: boolean | null + has_created_organization?: boolean | null id?: string + is_created_through_org_invitation?: boolean | null user_name?: string | null } Relationships: [] diff --git a/supabase/migrations/20241017150518_user_profile_metadata.sql b/supabase/migrations/20241017150518_user_profile_metadata.sql new file mode 100644 index 00000000..0a3d4b45 --- /dev/null +++ b/supabase/migrations/20241017150518_user_profile_metadata.sql @@ -0,0 +1,5 @@ +ALTER TABLE user_profiles +ADD COLUMN hasAcceptedTerms BOOLEAN DEFAULT TRUE, +ADD COLUMN hasCompletedProfile BOOLEAN DEFAULT FALSE, +ADD COLUMN hasCreatedOrganization BOOLEAN DEFAULT FALSE, +ADD COLUMN isCreatedThroughOrgInvitation BOOLEAN DEFAULT FALSE; \ No newline at end of file diff --git a/supabase/migrations/20241017151343_user_profile_camelcase.sql b/supabase/migrations/20241017151343_user_profile_camelcase.sql new file mode 100644 index 00000000..c23db01f --- /dev/null +++ b/supabase/migrations/20241017151343_user_profile_camelcase.sql @@ -0,0 +1,5 @@ +ALTER TABLE user_profiles +RENAME COLUMN hasAcceptedTerms TO has_accepted_terms, +RENAME COLUMN hasCompletedProfile TO has_completed_profile, +RENAME COLUMN hasCreatedOrganization TO has_created_organization, +RENAME COLUMN isCreatedThroughOrgInvitation TO is_created_through_org_invitation; \ No newline at end of file diff --git a/supabase/migrations/20241017151749_user_profile_naming.sql b/supabase/migrations/20241017151749_user_profile_naming.sql new file mode 100644 index 00000000..53d90a49 --- /dev/null +++ b/supabase/migrations/20241017151749_user_profile_naming.sql @@ -0,0 +1,13 @@ +-- Drop existing columns +ALTER TABLE user_profiles +DROP COLUMN IF EXISTS hasAcceptedTerms, +DROP COLUMN IF EXISTS hasCompletedProfile, +DROP COLUMN IF EXISTS hasCreatedOrganization, +DROP COLUMN IF EXISTS isCreatedThroughOrgInvitation; + +-- Recreate columns with underscore notation +ALTER TABLE user_profiles +ADD COLUMN has_accepted_terms BOOLEAN DEFAULT TRUE, +ADD COLUMN has_completed_profile BOOLEAN DEFAULT FALSE, +ADD COLUMN has_created_organization BOOLEAN DEFAULT FALSE, +ADD COLUMN is_created_through_org_invitation BOOLEAN DEFAULT FALSE; \ No newline at end of file From 0f66243a5218b28c4e9a776157fc449c457c507e Mon Sep 17 00:00:00 2001 From: Igor Zalutski Date: Thu, 17 Oct 2024 17:30:34 +0100 Subject: [PATCH 10/23] Fix root route; add session provider --- src/app/AppProviders.tsx | 27 +++++++++++++++------------ src/app/route.ts | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/app/AppProviders.tsx b/src/app/AppProviders.tsx index 095bacea..a3eafbd4 100644 --- a/src/app/AppProviders.tsx +++ b/src/app/AppProviders.tsx @@ -1,5 +1,6 @@ "use client"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { SessionProvider } from "next-auth/react"; import { AppProgressBar as ProgressBar } from "next-nprogress-bar"; import type React from "react"; import { Suspense } from "react"; @@ -27,18 +28,20 @@ export function AppProviders({ return ( <> - - {children} - - - - - + + + {children} + + + + + + ); diff --git a/src/app/route.ts b/src/app/route.ts index fbe55eff..d0e697c5 100644 --- a/src/app/route.ts +++ b/src/app/route.ts @@ -29,7 +29,7 @@ export async function GET(request: NextRequest) { // Authenticated but not onboarded return NextResponse.redirect(toSiteURL('/onboarding')); } else { - return NextResponse.next(); + return NextResponse.redirect(toSiteURL('/dashboard')); } } catch (error) { console.log('User not signed in, redirecting to login', error); From ea3d6e3a0d7852c1bd4c902c2b237918a3ffc1e2 Mon Sep 17 00:00:00 2001 From: Igor Zalutski Date: Thu, 17 Oct 2024 19:17:32 +0100 Subject: [PATCH 11/23] Refresh prisma schema; delete app/page.tsx (supabase sso) --- prisma/migrations/0_init/migration.sql | 1330 ----------------- prisma/schema.prisma | 468 +----- src/app/page.tsx | 58 - .../20241017174059_auth_constraints.sql | 6 + 4 files changed, 13 insertions(+), 1849 deletions(-) delete mode 100644 prisma/migrations/0_init/migration.sql delete mode 100644 src/app/page.tsx create mode 100644 supabase/migrations/20241017174059_auth_constraints.sql diff --git a/prisma/migrations/0_init/migration.sql b/prisma/migrations/0_init/migration.sql deleted file mode 100644 index 173835df..00000000 --- a/prisma/migrations/0_init/migration.sql +++ /dev/null @@ -1,1330 +0,0 @@ --- CreateSchema -CREATE SCHEMA IF NOT EXISTS "auth"; - --- CreateSchema -CREATE SCHEMA IF NOT EXISTS "public"; - --- CreateEnum -CREATE TYPE "auth"."aal_level" AS ENUM ('aal1', 'aal2', 'aal3'); - --- CreateEnum -CREATE TYPE "auth"."code_challenge_method" AS ENUM ('s256', 'plain'); - --- CreateEnum -CREATE TYPE "auth"."factor_status" AS ENUM ('unverified', 'verified'); - --- CreateEnum -CREATE TYPE "auth"."factor_type" AS ENUM ('totp', 'webauthn', 'phone'); - --- CreateEnum -CREATE TYPE "auth"."one_time_token_type" AS ENUM ('confirmation_token', 'reauthentication_token', 'recovery_token', 'email_change_token_new', 'email_change_token_current', 'phone_change_token'); - --- CreateEnum -CREATE TYPE "public"."app_admin_role" AS ENUM ('moderator', 'admin', 'super_admin'); - --- CreateEnum -CREATE TYPE "public"."app_role" AS ENUM ('admin'); - --- CreateEnum -CREATE TYPE "public"."iac_type_enum" AS ENUM ('terraform', 'terragrunt', 'opentofu'); - --- CreateEnum -CREATE TYPE "public"."internal_blog_post_status" AS ENUM ('draft', 'published'); - --- CreateEnum -CREATE TYPE "public"."internal_feedback_thread_priority" AS ENUM ('low', 'medium', 'high'); - --- CreateEnum -CREATE TYPE "public"."internal_feedback_thread_status" AS ENUM ('open', 'under_review', 'planned', 'closed', 'in_progress', 'completed'); - --- CreateEnum -CREATE TYPE "public"."internal_feedback_thread_type" AS ENUM ('bug', 'feature_request', 'general'); - --- CreateEnum -CREATE TYPE "public"."organization_join_invitation_link_status" AS ENUM ('active', 'finished_accepted', 'finished_declined', 'inactive'); - --- CreateEnum -CREATE TYPE "public"."organization_joining_status" AS ENUM ('invited', 'joinied', 'declined_invitation', 'joined'); - --- CreateEnum -CREATE TYPE "public"."organization_member_role" AS ENUM ('owner', 'admin', 'member', 'readonly'); - --- CreateEnum -CREATE TYPE "public"."pricing_plan_interval" AS ENUM ('day', 'week', 'month', 'year'); - --- CreateEnum -CREATE TYPE "public"."pricing_type" AS ENUM ('one_time', 'recurring'); - --- CreateEnum -CREATE TYPE "public"."project_status" AS ENUM ('draft', 'pending_approval', 'approved', 'completed'); - --- CreateEnum -CREATE TYPE "public"."project_team_member_role" AS ENUM ('admin', 'member', 'readonly'); - --- CreateEnum -CREATE TYPE "public"."subscription_status" AS ENUM ('trialing', 'active', 'canceled', 'incomplete', 'incomplete_expired', 'past_due', 'unpaid', 'paused'); - --- CreateTable -CREATE TABLE "auth"."audit_log_entries" ( - "instance_id" UUID, - "id" UUID NOT NULL, - "payload" JSON, - "created_at" TIMESTAMPTZ(6), - "ip_address" VARCHAR(64) NOT NULL DEFAULT '', - - CONSTRAINT "audit_log_entries_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "auth"."flow_state" ( - "id" UUID NOT NULL, - "user_id" UUID, - "auth_code" TEXT NOT NULL, - "code_challenge_method" "auth"."code_challenge_method" NOT NULL, - "code_challenge" TEXT NOT NULL, - "provider_type" TEXT NOT NULL, - "provider_access_token" TEXT, - "provider_refresh_token" TEXT, - "created_at" TIMESTAMPTZ(6), - "updated_at" TIMESTAMPTZ(6), - "authentication_method" TEXT NOT NULL, - "auth_code_issued_at" TIMESTAMPTZ(6), - - CONSTRAINT "flow_state_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "auth"."identities" ( - "provider_id" TEXT NOT NULL, - "user_id" UUID NOT NULL, - "identity_data" JSONB NOT NULL, - "provider" TEXT NOT NULL, - "last_sign_in_at" TIMESTAMPTZ(6), - "created_at" TIMESTAMPTZ(6), - "updated_at" TIMESTAMPTZ(6), - "email" TEXT DEFAULT lower((identity_data ->> 'email'::text)), - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - - CONSTRAINT "identities_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "auth"."instances" ( - "id" UUID NOT NULL, - "uuid" UUID, - "raw_base_config" TEXT, - "created_at" TIMESTAMPTZ(6), - "updated_at" TIMESTAMPTZ(6), - - CONSTRAINT "instances_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "auth"."mfa_amr_claims" ( - "session_id" UUID NOT NULL, - "created_at" TIMESTAMPTZ(6) NOT NULL, - "updated_at" TIMESTAMPTZ(6) NOT NULL, - "authentication_method" TEXT NOT NULL, - "id" UUID NOT NULL, - - CONSTRAINT "amr_id_pk" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "auth"."mfa_challenges" ( - "id" UUID NOT NULL, - "factor_id" UUID NOT NULL, - "created_at" TIMESTAMPTZ(6) NOT NULL, - "verified_at" TIMESTAMPTZ(6), - "ip_address" INET NOT NULL, - "otp_code" TEXT, - - CONSTRAINT "mfa_challenges_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "auth"."mfa_factors" ( - "id" UUID NOT NULL, - "user_id" UUID NOT NULL, - "friendly_name" TEXT, - "factor_type" "auth"."factor_type" NOT NULL, - "status" "auth"."factor_status" NOT NULL, - "created_at" TIMESTAMPTZ(6) NOT NULL, - "updated_at" TIMESTAMPTZ(6) NOT NULL, - "secret" TEXT, - "phone" TEXT, - "last_challenged_at" TIMESTAMPTZ(6), - - CONSTRAINT "mfa_factors_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "auth"."one_time_tokens" ( - "id" UUID NOT NULL, - "user_id" UUID NOT NULL, - "token_type" "auth"."one_time_token_type" NOT NULL, - "token_hash" TEXT NOT NULL, - "relates_to" TEXT NOT NULL, - "created_at" TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "one_time_tokens_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "auth"."refresh_tokens" ( - "instance_id" UUID, - "id" BIGSERIAL NOT NULL, - "token" VARCHAR(255), - "user_id" VARCHAR(255), - "revoked" BOOLEAN, - "created_at" TIMESTAMPTZ(6), - "updated_at" TIMESTAMPTZ(6), - "parent" VARCHAR(255), - "session_id" UUID, - - CONSTRAINT "refresh_tokens_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "auth"."saml_providers" ( - "id" UUID NOT NULL, - "sso_provider_id" UUID NOT NULL, - "entity_id" TEXT NOT NULL, - "metadata_xml" TEXT NOT NULL, - "metadata_url" TEXT, - "attribute_mapping" JSONB, - "created_at" TIMESTAMPTZ(6), - "updated_at" TIMESTAMPTZ(6), - "name_id_format" TEXT, - - CONSTRAINT "saml_providers_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "auth"."saml_relay_states" ( - "id" UUID NOT NULL, - "sso_provider_id" UUID NOT NULL, - "request_id" TEXT NOT NULL, - "for_email" TEXT, - "redirect_to" TEXT, - "created_at" TIMESTAMPTZ(6), - "updated_at" TIMESTAMPTZ(6), - "flow_state_id" UUID, - - CONSTRAINT "saml_relay_states_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "auth"."schema_migrations" ( - "version" VARCHAR(255) NOT NULL, - - CONSTRAINT "schema_migrations_pkey" PRIMARY KEY ("version") -); - --- CreateTable -CREATE TABLE "auth"."sessions" ( - "id" UUID NOT NULL, - "user_id" UUID NOT NULL, - "created_at" TIMESTAMPTZ(6), - "updated_at" TIMESTAMPTZ(6), - "factor_id" UUID, - "aal" "auth"."aal_level", - "not_after" TIMESTAMPTZ(6), - "refreshed_at" TIMESTAMP(6), - "user_agent" TEXT, - "ip" INET, - "tag" TEXT, - - CONSTRAINT "sessions_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "auth"."sso_domains" ( - "id" UUID NOT NULL, - "sso_provider_id" UUID NOT NULL, - "domain" TEXT NOT NULL, - "created_at" TIMESTAMPTZ(6), - "updated_at" TIMESTAMPTZ(6), - - CONSTRAINT "sso_domains_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "auth"."sso_providers" ( - "id" UUID NOT NULL, - "resource_id" TEXT, - "created_at" TIMESTAMPTZ(6), - "updated_at" TIMESTAMPTZ(6), - - CONSTRAINT "sso_providers_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "auth"."users" ( - "instance_id" UUID, - "id" UUID NOT NULL, - "aud" VARCHAR(255), - "role" VARCHAR(255), - "email" VARCHAR(255), - "encrypted_password" VARCHAR(255), - "email_confirmed_at" TIMESTAMPTZ(6), - "invited_at" TIMESTAMPTZ(6), - "confirmation_token" VARCHAR(255), - "confirmation_sent_at" TIMESTAMPTZ(6), - "recovery_token" VARCHAR(255), - "recovery_sent_at" TIMESTAMPTZ(6), - "email_change_token_new" VARCHAR(255), - "email_change" VARCHAR(255), - "email_change_sent_at" TIMESTAMPTZ(6), - "last_sign_in_at" TIMESTAMPTZ(6), - "raw_app_meta_data" JSONB, - "raw_user_meta_data" JSONB, - "is_super_admin" BOOLEAN, - "created_at" TIMESTAMPTZ(6), - "updated_at" TIMESTAMPTZ(6), - "phone" TEXT, - "phone_confirmed_at" TIMESTAMPTZ(6), - "phone_change" TEXT DEFAULT '', - "phone_change_token" VARCHAR(255) DEFAULT '', - "phone_change_sent_at" TIMESTAMPTZ(6), - "confirmed_at" TIMESTAMPTZ(6) DEFAULT LEAST(email_confirmed_at, phone_confirmed_at), - "email_change_token_current" VARCHAR(255) DEFAULT '', - "email_change_confirm_status" SMALLINT DEFAULT 0, - "banned_until" TIMESTAMPTZ(6), - "reauthentication_token" VARCHAR(255) DEFAULT '', - "reauthentication_sent_at" TIMESTAMPTZ(6), - "is_sso_user" BOOLEAN NOT NULL DEFAULT false, - "deleted_at" TIMESTAMPTZ(6), - "is_anonymous" BOOLEAN NOT NULL DEFAULT false, - - CONSTRAINT "users_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."account_delete_tokens" ( - "token" UUID NOT NULL DEFAULT uuid_generate_v4(), - "user_id" UUID NOT NULL, - - CONSTRAINT "account_delete_tokens_pkey" PRIMARY KEY ("user_id") -); - --- CreateTable -CREATE TABLE "public"."billing_bypass_organizations" ( - "id" UUID NOT NULL, - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "billing_bypass_organizations_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."chats" ( - "id" TEXT NOT NULL, - "user_id" UUID, - "payload" JSONB, - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT timezone('utc'::text, now()), - "project_id" UUID NOT NULL, - - CONSTRAINT "chats_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."customers" ( - "stripe_customer_id" VARCHAR NOT NULL, - "organization_id" UUID NOT NULL, - - CONSTRAINT "customers_pkey" PRIMARY KEY ("stripe_customer_id","organization_id") -); - --- CreateTable -CREATE TABLE "public"."digger_batches" ( - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - "pr_number" BIGINT, - "status" SMALLINT NOT NULL, - "branch_name" TEXT NOT NULL, - "digger_config" TEXT, - "github_installation_id" BIGINT, - "repo_full_name" TEXT NOT NULL, - "repo_owner" TEXT NOT NULL, - "repo_name" TEXT NOT NULL, - "batch_type" TEXT NOT NULL, - "comment_id" BIGINT, - "source_details" BYTEA, - "vcs" TEXT, - "gitlab_project_id" BIGINT, - "organization_id" UUID, - "event_type" TEXT, - - CONSTRAINT "digger_batches_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."digger_job_parent_links" ( - "id" BIGSERIAL NOT NULL, - "created_at" TIMESTAMPTZ(6), - "updated_at" TIMESTAMPTZ(6), - "deleted_at" TIMESTAMPTZ(6), - "digger_job_id" TEXT, - "parent_digger_job_id" TEXT, - - CONSTRAINT "digger_job_parent_links_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."digger_job_summaries" ( - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted_at" TIMESTAMPTZ(6), - "resources_created" BIGINT NOT NULL DEFAULT 0, - "resources_deleted" BIGINT NOT NULL DEFAULT 0, - "resources_updated" BIGINT NOT NULL DEFAULT 0, - - CONSTRAINT "digger_job_summaries_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."digger_job_tokens" ( - "id" BIGSERIAL NOT NULL, - "created_at" TIMESTAMPTZ(6), - "updated_at" TIMESTAMPTZ(6), - "deleted_at" TIMESTAMPTZ(6), - "value" TEXT, - "expiry" TIMESTAMPTZ(6), - "type" TEXT, - "organisation_id" UUID, - - CONSTRAINT "job_tokens_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."digger_jobs" ( - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted_at" TIMESTAMPTZ(6), - "digger_job_id" TEXT NOT NULL, - "status" SMALLINT NOT NULL, - "batch_id" UUID NOT NULL, - "status_updated_at" TIMESTAMPTZ(6), - "digger_job_summary_id" UUID, - "workflow_file" TEXT, - "workflow_run_url" TEXT, - "plan_footprint" BYTEA, - "pr_comment_url" TEXT, - "terraform_output" TEXT, - "job_spec" BYTEA, - "variables_spec" BYTEA, - - CONSTRAINT "digger_jobs_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."digger_locks" ( - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted_at" TIMESTAMPTZ(6), - "resource" TEXT NOT NULL, - "lock_id" BIGINT NOT NULL, - "organization_id" UUID NOT NULL, - - CONSTRAINT "digger_locks_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."digger_run_queue_items" ( - "id" BIGSERIAL NOT NULL, - "created_at" TIMESTAMPTZ(6), - "updated_at" TIMESTAMPTZ(6), - "deleted_at" TIMESTAMPTZ(6), - "digger_run_id" UUID, - "project_id" UUID, - - CONSTRAINT "digger_run_queue_items_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."digger_run_stages" ( - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted_at" TIMESTAMPTZ(6), - "batch_id" UUID NOT NULL, - - CONSTRAINT "digger_run_stages_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."digger_runs" ( - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted_at" TIMESTAMPTZ(6), - "triggertype" TEXT NOT NULL, - "pr_number" BIGINT, - "status" TEXT NOT NULL, - "commit_id" TEXT NOT NULL, - "digger_config" TEXT, - "github_installation_id" BIGINT, - "repo_id" BIGSERIAL NOT NULL, - "run_type" TEXT NOT NULL, - "plan_stage_id" UUID, - "apply_stage_id" UUID, - "project_name" TEXT, - "is_approved" BOOLEAN, - "approval_author" TEXT, - "approval_date" TIMESTAMPTZ(6), - "project_id" UUID NOT NULL, - "terraform_output" TEXT, - "apply_logs" TEXT, - "approver_user_id" UUID, - "triggered_by_user_id" UUID, - - CONSTRAINT "digger_runs_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."env_vars" ( - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - "project_id" UUID NOT NULL, - "name" VARCHAR(255) NOT NULL, - "value" TEXT NOT NULL DEFAULT '', - "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "is_secret" BOOLEAN NOT NULL DEFAULT false, - - CONSTRAINT "encrypted_env_vars_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."github_app_installation_links" ( - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted_at" TIMESTAMPTZ(6), - "github_installation_id" BIGINT NOT NULL, - "organization_id" UUID NOT NULL, - "status" SMALLINT NOT NULL, - - CONSTRAINT "github_app_installation_links_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."github_app_installations" ( - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted_at" TIMESTAMPTZ(6), - "github_installation_id" BIGINT NOT NULL, - "github_app_id" BIGINT NOT NULL, - "account_id" BIGINT NOT NULL, - "login" TEXT NOT NULL, - "repo" TEXT, - "status" BIGINT NOT NULL, - - CONSTRAINT "github_app_installations_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."github_apps" ( - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted_at" TIMESTAMPTZ(6), - "github_id" BIGINT NOT NULL, - "name" TEXT NOT NULL, - "github_app_url" TEXT NOT NULL, - - CONSTRAINT "github_apps_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."internal_blog_author_posts" ( - "author_id" UUID NOT NULL, - "post_id" UUID NOT NULL, - - CONSTRAINT "internal_blog_author_posts_pkey" PRIMARY KEY ("author_id","post_id") -); - --- CreateTable -CREATE TABLE "public"."internal_blog_author_profiles" ( - "user_id" UUID NOT NULL, - "display_name" VARCHAR(255) NOT NULL, - "bio" TEXT NOT NULL, - "avatar_url" VARCHAR(255) NOT NULL, - "website_url" VARCHAR(255), - "twitter_handle" VARCHAR(255), - "facebook_handle" VARCHAR(255), - "linkedin_handle" VARCHAR(255), - "instagram_handle" VARCHAR(255), - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "internal_blog_author_profiles_pkey" PRIMARY KEY ("user_id") -); - --- CreateTable -CREATE TABLE "public"."internal_blog_post_tags" ( - "id" SERIAL NOT NULL, - "slug" TEXT NOT NULL, - "name" TEXT NOT NULL, - "description" TEXT, - - CONSTRAINT "internal_blog_post_tags_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."internal_blog_post_tags_relationship" ( - "blog_post_id" UUID NOT NULL, - "tag_id" INTEGER NOT NULL, - - CONSTRAINT "internal_blog_post_tags_relationship_pkey" PRIMARY KEY ("blog_post_id","tag_id") -); - --- CreateTable -CREATE TABLE "public"."internal_blog_posts" ( - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - "slug" VARCHAR(255) NOT NULL, - "title" VARCHAR(255) NOT NULL, - "summary" TEXT NOT NULL, - "content" TEXT NOT NULL, - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "is_featured" BOOLEAN NOT NULL DEFAULT false, - "status" "public"."internal_blog_post_status" NOT NULL DEFAULT 'draft', - "cover_image" VARCHAR(255), - "seo_data" JSONB, - "json_content" JSONB NOT NULL DEFAULT '{}', - - CONSTRAINT "internal_blog_posts_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."internal_changelog" ( - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - "title" VARCHAR(255) NOT NULL, - "changes" TEXT NOT NULL, - "user_id" UUID, - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - "cover_image" TEXT, - - CONSTRAINT "internal_changelog_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."internal_feedback_comments" ( - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - "user_id" UUID NOT NULL, - "thread_id" UUID NOT NULL, - "content" TEXT NOT NULL, - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "internal_feedback_comments_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."internal_feedback_threads" ( - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - "title" VARCHAR(255) NOT NULL, - "content" TEXT NOT NULL, - "user_id" UUID NOT NULL, - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "priority" "public"."internal_feedback_thread_priority" NOT NULL DEFAULT 'low', - "type" "public"."internal_feedback_thread_type" NOT NULL DEFAULT 'general', - "status" "public"."internal_feedback_thread_status" NOT NULL DEFAULT 'open', - "added_to_roadmap" BOOLEAN NOT NULL DEFAULT false, - "open_for_public_discussion" BOOLEAN NOT NULL DEFAULT false, - "is_publicly_visible" BOOLEAN NOT NULL DEFAULT false, - - CONSTRAINT "internal_feedback_threads_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."organization_credits" ( - "organization_id" UUID NOT NULL, - "credits" BIGINT NOT NULL DEFAULT 12, - - CONSTRAINT "organization_credits_pkey" PRIMARY KEY ("organization_id") -); - --- CreateTable -CREATE TABLE "public"."organization_join_invitations" ( - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "inviter_user_id" UUID NOT NULL, - "status" "public"."organization_join_invitation_link_status" NOT NULL DEFAULT 'active', - "id" UUID NOT NULL DEFAULT uuid_generate_v4(), - "invitee_user_email" VARCHAR NOT NULL, - "organization_id" UUID NOT NULL, - "invitee_organization_role" "public"."organization_member_role" NOT NULL DEFAULT 'member', - "invitee_user_id" UUID, - - CONSTRAINT "organization_invitations_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."organization_members" ( - "id" BIGSERIAL NOT NULL, - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "member_id" UUID NOT NULL, - "member_role" "public"."organization_member_role" NOT NULL, - "organization_id" UUID NOT NULL, - - CONSTRAINT "organization_members_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."organizations" ( - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "id" UUID NOT NULL DEFAULT uuid_generate_v4(), - "title" VARCHAR NOT NULL DEFAULT 'Test Organization', - "slug" VARCHAR(255) NOT NULL DEFAULT (gen_random_uuid())::text, - "public_key" TEXT, - - CONSTRAINT "organizations_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."organizations_private_info" ( - "id" UUID NOT NULL, - "billing_address" JSON, - "payment_method" JSON, - - CONSTRAINT "projects_private_info_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."prices" ( - "id" VARCHAR NOT NULL, - "product_id" VARCHAR, - "active" BOOLEAN, - "description" VARCHAR, - "unit_amount" BIGINT, - "currency" VARCHAR, - "type" "public"."pricing_type", - "interval" "public"."pricing_plan_interval", - "interval_count" BIGINT, - "trial_period_days" BIGINT, - "metadata" JSONB, - - CONSTRAINT "price_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."products" ( - "id" VARCHAR NOT NULL, - "active" BOOLEAN, - "name" VARCHAR, - "description" VARCHAR, - "image" VARCHAR, - "metadata" JSONB, - "is_visible_in_ui" BOOLEAN NOT NULL DEFAULT false, - - CONSTRAINT "product_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."project_comments" ( - "id" BIGSERIAL NOT NULL, - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - "text" TEXT NOT NULL, - "user_id" UUID NOT NULL, - "in_reply_to" BIGINT, - "project_id" UUID NOT NULL, - - CONSTRAINT "project_comments_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."project_tfvars" ( - "id" UUID NOT NULL DEFAULT gen_random_uuid(), - "project_id" UUID NOT NULL, - "tfvars" JSONB NOT NULL, - "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "project_tfvars_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."projects" ( - "id" UUID NOT NULL DEFAULT uuid_generate_v4(), - "name" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "organization_id" UUID NOT NULL, - "team_id" BIGINT, - "project_status" "public"."project_status" NOT NULL DEFAULT 'draft', - "slug" VARCHAR(255) NOT NULL DEFAULT (gen_random_uuid())::text, - "latest_action_on" TEXT, - "repo_id" BIGSERIAL NOT NULL, - "configuration_yaml" TEXT, - "status" TEXT, - "is_generated" BOOLEAN, - "is_in_main_branch" BOOLEAN, - "deleted_at" TIMESTAMPTZ(6), - "terraform_working_dir" TEXT, - "is_managing_state" BOOLEAN, - "labels" TEXT[], - "is_drift_detection_enabled" BOOLEAN DEFAULT false, - "drift_crontab" TEXT, - "branch" TEXT, - "latest_drift_output" TEXT, - "iac_type" "public"."iac_type_enum" DEFAULT 'terraform', - "workspace" TEXT, - "workflow_file" TEXT, - "include_patterns" TEXT, - "exclude_patterns" TEXT, - - CONSTRAINT "projects_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."repos" ( - "id" BIGSERIAL NOT NULL, - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6), - "deleted_at" TIMESTAMPTZ(6), - "name" TEXT NOT NULL, - "organization_id" UUID, - "digger_config" TEXT, - "repo_name" TEXT, - "repo_full_name" TEXT, - "repo_organisation" TEXT, - "repo_url" TEXT, - - CONSTRAINT "repos_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."subscriptions" ( - "id" VARCHAR NOT NULL, - "status" "public"."subscription_status", - "metadata" JSON, - "price_id" VARCHAR, - "quantity" BIGINT, - "cancel_at_period_end" BOOLEAN, - "created" TIMESTAMPTZ(6) NOT NULL, - "current_period_start" TIMESTAMPTZ(6) NOT NULL, - "current_period_end" TIMESTAMPTZ(6) NOT NULL, - "ended_at" TIMESTAMPTZ(6), - "cancel_at" TIMESTAMPTZ(6), - "canceled_at" TIMESTAMPTZ(6), - "trial_start" TIMESTAMPTZ(6), - "trial_end" TIMESTAMPTZ(6), - "organization_id" UUID, - - CONSTRAINT "subscription_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."team_members" ( - "id" BIGSERIAL NOT NULL, - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - "user_id" UUID NOT NULL, - "role" "public"."project_team_member_role" NOT NULL DEFAULT 'member', - "team_id" BIGINT NOT NULL, - - CONSTRAINT "team_members_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."teams" ( - "id" BIGSERIAL NOT NULL, - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - "organization_id" UUID NOT NULL, - "name" TEXT NOT NULL, - - CONSTRAINT "teams_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."user_api_keys" ( - "key_id" TEXT NOT NULL, - "masked_key" TEXT NOT NULL, - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "user_id" UUID NOT NULL, - "expires_at" TIMESTAMPTZ(6), - "is_revoked" BOOLEAN NOT NULL DEFAULT false, - - CONSTRAINT "user_api_keys_pkey" PRIMARY KEY ("key_id") -); - --- CreateTable -CREATE TABLE "public"."user_notifications" ( - "id" UUID NOT NULL DEFAULT uuid_generate_v4(), - "user_id" UUID, - "is_read" BOOLEAN NOT NULL DEFAULT false, - "is_seen" BOOLEAN NOT NULL DEFAULT false, - "payload" JSONB NOT NULL DEFAULT '{}', - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "user_notifications_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."user_onboarding" ( - "user_id" UUID NOT NULL, - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "accepted_terms" BOOLEAN NOT NULL DEFAULT false, - - CONSTRAINT "user_onboarding_pkey" PRIMARY KEY ("user_id") -); - --- CreateTable -CREATE TABLE "public"."user_private_info" ( - "id" UUID NOT NULL, - "created_at" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP, - "default_organization" UUID, - - CONSTRAINT "user_private_info_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."user_profiles" ( - "id" UUID NOT NULL, - "full_name" VARCHAR, - "avatar_url" VARCHAR, - "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "user_name" VARCHAR, - - CONSTRAINT "user_profiles_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "public"."user_roles" ( - "id" BIGSERIAL NOT NULL, - "user_id" UUID NOT NULL, - "role" "public"."app_role" NOT NULL, - - CONSTRAINT "user_roles_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "audit_logs_instance_id_idx" ON "auth"."audit_log_entries"("instance_id"); - --- CreateIndex -CREATE INDEX "flow_state_created_at_idx" ON "auth"."flow_state"("created_at" DESC); - --- CreateIndex -CREATE INDEX "idx_auth_code" ON "auth"."flow_state"("auth_code"); - --- CreateIndex -CREATE INDEX "idx_user_id_auth_method" ON "auth"."flow_state"("user_id", "authentication_method"); - --- CreateIndex -CREATE INDEX "identities_email_idx" ON "auth"."identities"("email"); - --- CreateIndex -CREATE INDEX "identities_user_id_idx" ON "auth"."identities"("user_id"); - --- CreateIndex -CREATE UNIQUE INDEX "identities_provider_id_provider_unique" ON "auth"."identities"("provider_id", "provider"); - --- CreateIndex -CREATE UNIQUE INDEX "mfa_amr_claims_session_id_authentication_method_pkey" ON "auth"."mfa_amr_claims"("session_id", "authentication_method"); - --- CreateIndex -CREATE INDEX "mfa_challenge_created_at_idx" ON "auth"."mfa_challenges"("created_at" DESC); - --- CreateIndex -CREATE UNIQUE INDEX "mfa_factors_last_challenged_at_key" ON "auth"."mfa_factors"("last_challenged_at"); - --- CreateIndex -CREATE INDEX "factor_id_created_at_idx" ON "auth"."mfa_factors"("user_id", "created_at"); - --- CreateIndex -CREATE INDEX "mfa_factors_user_id_idx" ON "auth"."mfa_factors"("user_id"); - --- CreateIndex -CREATE UNIQUE INDEX "unique_phone_factor_per_user" ON "auth"."mfa_factors"("user_id", "phone"); - --- CreateIndex -CREATE INDEX "one_time_tokens_relates_to_hash_idx" ON "auth"."one_time_tokens" USING HASH ("relates_to"); - --- CreateIndex -CREATE INDEX "one_time_tokens_token_hash_hash_idx" ON "auth"."one_time_tokens" USING HASH ("token_hash"); - --- CreateIndex -CREATE UNIQUE INDEX "one_time_tokens_user_id_token_type_key" ON "auth"."one_time_tokens"("user_id", "token_type"); - --- CreateIndex -CREATE UNIQUE INDEX "refresh_tokens_token_unique" ON "auth"."refresh_tokens"("token"); - --- CreateIndex -CREATE INDEX "refresh_tokens_instance_id_idx" ON "auth"."refresh_tokens"("instance_id"); - --- CreateIndex -CREATE INDEX "refresh_tokens_instance_id_user_id_idx" ON "auth"."refresh_tokens"("instance_id", "user_id"); - --- CreateIndex -CREATE INDEX "refresh_tokens_parent_idx" ON "auth"."refresh_tokens"("parent"); - --- CreateIndex -CREATE INDEX "refresh_tokens_session_id_revoked_idx" ON "auth"."refresh_tokens"("session_id", "revoked"); - --- CreateIndex -CREATE INDEX "refresh_tokens_updated_at_idx" ON "auth"."refresh_tokens"("updated_at" DESC); - --- CreateIndex -CREATE UNIQUE INDEX "saml_providers_entity_id_key" ON "auth"."saml_providers"("entity_id"); - --- CreateIndex -CREATE INDEX "saml_providers_sso_provider_id_idx" ON "auth"."saml_providers"("sso_provider_id"); - --- CreateIndex -CREATE INDEX "saml_relay_states_created_at_idx" ON "auth"."saml_relay_states"("created_at" DESC); - --- CreateIndex -CREATE INDEX "saml_relay_states_for_email_idx" ON "auth"."saml_relay_states"("for_email"); - --- CreateIndex -CREATE INDEX "saml_relay_states_sso_provider_id_idx" ON "auth"."saml_relay_states"("sso_provider_id"); - --- CreateIndex -CREATE INDEX "sessions_not_after_idx" ON "auth"."sessions"("not_after" DESC); - --- CreateIndex -CREATE INDEX "sessions_user_id_idx" ON "auth"."sessions"("user_id"); - --- CreateIndex -CREATE INDEX "user_id_created_at_idx" ON "auth"."sessions"("user_id", "created_at"); - --- CreateIndex -CREATE INDEX "sso_domains_sso_provider_id_idx" ON "auth"."sso_domains"("sso_provider_id"); - --- CreateIndex -CREATE UNIQUE INDEX "users_phone_key" ON "auth"."users"("phone"); - --- CreateIndex -CREATE INDEX "users_instance_id_idx" ON "auth"."users"("instance_id"); - --- CreateIndex -CREATE INDEX "users_is_anonymous_idx" ON "auth"."users"("is_anonymous"); - --- CreateIndex -CREATE UNIQUE INDEX "customers_stripe_customer_id_key" ON "public"."customers"("stripe_customer_id"); - --- CreateIndex -CREATE INDEX "customers_organization_id_index" ON "public"."customers"("organization_id"); - --- CreateIndex -CREATE INDEX "customers_stripe_customer_id_index" ON "public"."customers"("stripe_customer_id"); - --- CreateIndex -CREATE INDEX "idx_digger_job_parent_links_deleted_at" ON "public"."digger_job_parent_links"("deleted_at"); - --- CreateIndex -CREATE INDEX "idx_digger_job_summaries_deleted_at" ON "public"."digger_job_summaries"("deleted_at"); - --- CreateIndex -CREATE INDEX "idx_job_tokens_deleted_at" ON "public"."digger_job_tokens"("deleted_at"); - --- CreateIndex -CREATE INDEX "digger_jobs_batch_id_idx" ON "public"."digger_jobs"("batch_id"); - --- CreateIndex -CREATE INDEX "idx_digger_job_id" ON "public"."digger_jobs"("batch_id"); - --- CreateIndex -CREATE INDEX "idx_digger_jobs_deleted_at" ON "public"."digger_jobs"("deleted_at"); - --- CreateIndex -CREATE INDEX "idx_digger_locked_resource" ON "public"."digger_locks"("resource"); - --- CreateIndex -CREATE INDEX "idx_digger_locks_deleted_at" ON "public"."digger_locks"("deleted_at"); - --- CreateIndex -CREATE INDEX "idx_digger_run_queue_items_deleted_at" ON "public"."digger_run_queue_items"("deleted_at"); - --- CreateIndex -CREATE INDEX "idx_digger_run_batch_id" ON "public"."digger_run_stages"("batch_id"); - --- CreateIndex -CREATE INDEX "idx_digger_run_stages_deleted_at" ON "public"."digger_run_stages"("deleted_at"); - --- CreateIndex -CREATE INDEX "digger_runs_project_id_idx" ON "public"."digger_runs"("project_id"); - --- CreateIndex -CREATE INDEX "idx_digger_runs_deleted_at" ON "public"."digger_runs"("deleted_at"); - --- CreateIndex -CREATE INDEX "idx_digger_runs_project_id" ON "public"."digger_runs"("project_id"); - --- CreateIndex -CREATE UNIQUE INDEX "unique_project_var_name" ON "public"."env_vars"("project_id", "name"); - --- CreateIndex -CREATE INDEX "idx_github_app_installation_links_deleted_at" ON "public"."github_app_installation_links"("deleted_at"); - --- CreateIndex -CREATE INDEX "idx_github_installation_org" ON "public"."github_app_installation_links"("github_installation_id", "organization_id"); - --- CreateIndex -CREATE INDEX "idx_github_app_installations_deleted_at" ON "public"."github_app_installations"("deleted_at"); - --- CreateIndex -CREATE INDEX "idx_github_apps_deleted_at" ON "public"."github_apps"("deleted_at"); - --- CreateIndex -CREATE UNIQUE INDEX "internal_blog_posts_slug_key" ON "public"."internal_blog_posts"("slug"); - --- CreateIndex -CREATE INDEX "organization_join_invitations_invitee_user_email_idx" ON "public"."organization_join_invitations"("invitee_user_email"); - --- CreateIndex -CREATE INDEX "organization_join_invitations_invitee_user_id_idx" ON "public"."organization_join_invitations"("invitee_user_id"); - --- CreateIndex -CREATE INDEX "organization_join_invitations_inviter_user_id_idx" ON "public"."organization_join_invitations"("inviter_user_id"); - --- CreateIndex -CREATE INDEX "organization_join_invitations_organization_id_idx" ON "public"."organization_join_invitations"("organization_id"); - --- CreateIndex -CREATE INDEX "organization_join_invitations_status_idx" ON "public"."organization_join_invitations"("status"); - --- CreateIndex -CREATE INDEX "organization_members_member_id_idx" ON "public"."organization_members"("member_id"); - --- CreateIndex -CREATE INDEX "organization_members_member_role_idx" ON "public"."organization_members"("member_role"); - --- CreateIndex -CREATE INDEX "organization_members_organization_id_idx" ON "public"."organization_members"("organization_id"); - --- CreateIndex -CREATE UNIQUE INDEX "organizations_slug_key" ON "public"."organizations"("slug"); - --- CreateIndex -CREATE INDEX "prices_active_idx" ON "public"."prices"("active"); - --- CreateIndex -CREATE INDEX "prices_product_id_idx" ON "public"."prices"("product_id"); - --- CreateIndex -CREATE INDEX "products_active_idx" ON "public"."products"("active"); - --- CreateIndex -CREATE INDEX "project_comments_project_id_idx" ON "public"."project_comments"("project_id"); - --- CreateIndex -CREATE INDEX "project_comments_user_id_idx" ON "public"."project_comments"("user_id"); - --- CreateIndex -CREATE UNIQUE INDEX "project_tfvars_project_id_key" ON "public"."project_tfvars"("project_id"); - --- CreateIndex -CREATE UNIQUE INDEX "projects_slug_key" ON "public"."projects"("slug"); - --- CreateIndex -CREATE INDEX "idx_repos_deleted_at" ON "public"."repos"("deleted_at"); - --- CreateIndex -CREATE UNIQUE INDEX "idx_org_repo" ON "public"."repos"("name", "organization_id"); - --- CreateIndex -CREATE INDEX "subscriptions_organization_id_idx" ON "public"."subscriptions"("organization_id"); - --- CreateIndex -CREATE INDEX "subscriptions_price_id_idx" ON "public"."subscriptions"("price_id"); - --- CreateIndex -CREATE INDEX "subscriptions_status_idx" ON "public"."subscriptions"("status"); - --- CreateIndex -CREATE INDEX "user_notifications_user_id_idx" ON "public"."user_notifications"("user_id"); - --- CreateIndex -CREATE INDEX "user_private_info_default_organization_idx" ON "public"."user_private_info"("default_organization"); - --- CreateIndex -CREATE INDEX "user_roles_user_id_idx" ON "public"."user_roles"("user_id"); - --- CreateIndex -CREATE UNIQUE INDEX "user_roles_user_id_role_key" ON "public"."user_roles"("user_id", "role"); - --- AddForeignKey -ALTER TABLE "auth"."identities" ADD CONSTRAINT "identities_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "auth"."mfa_amr_claims" ADD CONSTRAINT "mfa_amr_claims_session_id_fkey" FOREIGN KEY ("session_id") REFERENCES "auth"."sessions"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "auth"."mfa_challenges" ADD CONSTRAINT "mfa_challenges_auth_factor_id_fkey" FOREIGN KEY ("factor_id") REFERENCES "auth"."mfa_factors"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "auth"."mfa_factors" ADD CONSTRAINT "mfa_factors_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "auth"."one_time_tokens" ADD CONSTRAINT "one_time_tokens_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "auth"."refresh_tokens" ADD CONSTRAINT "refresh_tokens_session_id_fkey" FOREIGN KEY ("session_id") REFERENCES "auth"."sessions"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "auth"."saml_providers" ADD CONSTRAINT "saml_providers_sso_provider_id_fkey" FOREIGN KEY ("sso_provider_id") REFERENCES "auth"."sso_providers"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "auth"."saml_relay_states" ADD CONSTRAINT "saml_relay_states_flow_state_id_fkey" FOREIGN KEY ("flow_state_id") REFERENCES "auth"."flow_state"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "auth"."saml_relay_states" ADD CONSTRAINT "saml_relay_states_sso_provider_id_fkey" FOREIGN KEY ("sso_provider_id") REFERENCES "auth"."sso_providers"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "auth"."sessions" ADD CONSTRAINT "sessions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "auth"."sso_domains" ADD CONSTRAINT "sso_domains_sso_provider_id_fkey" FOREIGN KEY ("sso_provider_id") REFERENCES "auth"."sso_providers"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."account_delete_tokens" ADD CONSTRAINT "account_delete_tokens_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."billing_bypass_organizations" ADD CONSTRAINT "billing_bypass_organizations_id_fkey" FOREIGN KEY ("id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."chats" ADD CONSTRAINT "chats_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."chats" ADD CONSTRAINT "public_chats_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."customers" ADD CONSTRAINT "customers_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."digger_job_tokens" ADD CONSTRAINT "fk_jt_organisation_id" FOREIGN KEY ("organisation_id") REFERENCES "public"."organizations"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."digger_jobs" ADD CONSTRAINT "fk_digger_jobs_batch" FOREIGN KEY ("batch_id") REFERENCES "public"."digger_batches"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."digger_jobs" ADD CONSTRAINT "fk_digger_jobs_digger_job_summary" FOREIGN KEY ("digger_job_summary_id") REFERENCES "public"."digger_job_summaries"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."digger_locks" ADD CONSTRAINT "fk_digger_locks_organization" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."digger_run_stages" ADD CONSTRAINT "fk_digger_run_stages_batch" FOREIGN KEY ("batch_id") REFERENCES "public"."digger_batches"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."digger_runs" ADD CONSTRAINT "fk_digger_runs_apply_stage" FOREIGN KEY ("apply_stage_id") REFERENCES "public"."digger_run_stages"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."digger_runs" ADD CONSTRAINT "fk_digger_runs_plan_stage" FOREIGN KEY ("plan_stage_id") REFERENCES "public"."digger_run_stages"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."digger_runs" ADD CONSTRAINT "fk_digger_runs_project" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE SET NULL ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."digger_runs" ADD CONSTRAINT "fk_digger_runs_repo" FOREIGN KEY ("repo_id") REFERENCES "public"."repos"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."digger_runs" ADD CONSTRAINT "fk_triggered_by_user" FOREIGN KEY ("triggered_by_user_id") REFERENCES "public"."user_profiles"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."env_vars" ADD CONSTRAINT "encrypted_env_vars_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."github_app_installation_links" ADD CONSTRAINT "fk_github_app_installation_links_organization" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."internal_blog_author_posts" ADD CONSTRAINT "internal_blog_author_posts_author_id_fkey" FOREIGN KEY ("author_id") REFERENCES "public"."internal_blog_author_profiles"("user_id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."internal_blog_author_posts" ADD CONSTRAINT "internal_blog_author_posts_post_id_fkey" FOREIGN KEY ("post_id") REFERENCES "public"."internal_blog_posts"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."internal_blog_author_profiles" ADD CONSTRAINT "internal_blog_author_profiles_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."internal_blog_post_tags_relationship" ADD CONSTRAINT "internal_blog_post_tags_relationship_blog_post_id_fkey" FOREIGN KEY ("blog_post_id") REFERENCES "public"."internal_blog_posts"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."internal_blog_post_tags_relationship" ADD CONSTRAINT "internal_blog_post_tags_relationship_tag_id_fkey" FOREIGN KEY ("tag_id") REFERENCES "public"."internal_blog_post_tags"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."internal_changelog" ADD CONSTRAINT "internal_changelog_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."internal_feedback_comments" ADD CONSTRAINT "internal_feedback_comments_thread_id_fkey" FOREIGN KEY ("thread_id") REFERENCES "public"."internal_feedback_threads"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."internal_feedback_comments" ADD CONSTRAINT "internal_feedback_comments_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."internal_feedback_threads" ADD CONSTRAINT "internal_feedback_threads_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."organization_credits" ADD CONSTRAINT "organization_credits_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."organization_join_invitations" ADD CONSTRAINT "organization_join_invitations_invitee_user_id_fkey" FOREIGN KEY ("invitee_user_id") REFERENCES "public"."user_profiles"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."organization_join_invitations" ADD CONSTRAINT "organization_join_invitations_inviter_user_id_fkey" FOREIGN KEY ("inviter_user_id") REFERENCES "public"."user_profiles"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."organization_join_invitations" ADD CONSTRAINT "organization_join_invitations_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."organization_members" ADD CONSTRAINT "organization_members_member_id_fkey" FOREIGN KEY ("member_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."organization_members" ADD CONSTRAINT "organization_members_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."organizations_private_info" ADD CONSTRAINT "organizations_private_info_id_fkey" FOREIGN KEY ("id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."prices" ADD CONSTRAINT "prices_product_id_fkey" FOREIGN KEY ("product_id") REFERENCES "public"."products"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."project_comments" ADD CONSTRAINT "project_comments_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."projects" ADD CONSTRAINT "fk_projects_organization" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."projects" ADD CONSTRAINT "fk_projects_repo" FOREIGN KEY ("repo_id") REFERENCES "public"."repos"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."projects" ADD CONSTRAINT "projects_team_id_fkey" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "public"."repos" ADD CONSTRAINT "fk_repos_organization" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."subscriptions" ADD CONSTRAINT "subscriptions_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."subscriptions" ADD CONSTRAINT "subscriptions_price_id_fkey" FOREIGN KEY ("price_id") REFERENCES "public"."prices"("id") ON DELETE NO ACTION ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."team_members" ADD CONSTRAINT "team_members_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."teams" ADD CONSTRAINT "teams_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "public"."user_notifications" ADD CONSTRAINT "user_notifications_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."user_onboarding" ADD CONSTRAINT "user_onboarding_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."user_private_info" ADD CONSTRAINT "user_private_info_default_organization_fkey" FOREIGN KEY ("default_organization") REFERENCES "public"."organizations"("id") ON DELETE SET NULL ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."user_private_info" ADD CONSTRAINT "user_private_info_id_fkey" FOREIGN KEY ("id") REFERENCES "auth"."users"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."user_profiles" ADD CONSTRAINT "user_profiles_id_fkey" FOREIGN KEY ("id") REFERENCES "auth"."users"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - --- AddForeignKey -ALTER TABLE "public"."user_roles" ADD CONSTRAINT "user_roles_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user_profiles"("id") ON DELETE CASCADE ON UPDATE NO ACTION; - diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 34146113..a9e09b7a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,328 +1,10 @@ generator client { - provider = "prisma-client-js" - previewFeatures = ["multiSchema"] + provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") - schemas = ["auth", "public"] -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -model audit_log_entries { - instance_id String? @db.Uuid - id String @id @db.Uuid - payload Json? @db.Json - created_at DateTime? @db.Timestamptz(6) - ip_address String @default("") @db.VarChar(64) - - @@index([instance_id], map: "audit_logs_instance_id_idx") - @@schema("auth") -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -model flow_state { - id String @id @db.Uuid - user_id String? @db.Uuid - auth_code String - code_challenge_method code_challenge_method - code_challenge String - provider_type String - provider_access_token String? - provider_refresh_token String? - created_at DateTime? @db.Timestamptz(6) - updated_at DateTime? @db.Timestamptz(6) - authentication_method String - auth_code_issued_at DateTime? @db.Timestamptz(6) - saml_relay_states saml_relay_states[] - - @@index([created_at(sort: Desc)]) - @@index([auth_code], map: "idx_auth_code") - @@index([user_id, authentication_method], map: "idx_user_id_auth_method") - @@schema("auth") -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -model identities { - provider_id String - user_id String @db.Uuid - identity_data Json - provider String - last_sign_in_at DateTime? @db.Timestamptz(6) - created_at DateTime? @db.Timestamptz(6) - updated_at DateTime? @db.Timestamptz(6) - email String? @default(dbgenerated("lower((identity_data ->> 'email'::text))")) - id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid - users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@unique([provider_id, provider], map: "identities_provider_id_provider_unique") - @@index([email]) - @@index([user_id]) - @@schema("auth") -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -model instances { - id String @id @db.Uuid - uuid String? @db.Uuid - raw_base_config String? - created_at DateTime? @db.Timestamptz(6) - updated_at DateTime? @db.Timestamptz(6) - - @@schema("auth") -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -model mfa_amr_claims { - session_id String @db.Uuid - created_at DateTime @db.Timestamptz(6) - updated_at DateTime @db.Timestamptz(6) - authentication_method String - id String @id(map: "amr_id_pk") @db.Uuid - sessions sessions @relation(fields: [session_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@unique([session_id, authentication_method], map: "mfa_amr_claims_session_id_authentication_method_pkey") - @@schema("auth") -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -model mfa_challenges { - id String @id @db.Uuid - factor_id String @db.Uuid - created_at DateTime @db.Timestamptz(6) - verified_at DateTime? @db.Timestamptz(6) - ip_address String @db.Inet - otp_code String? - mfa_factors mfa_factors @relation(fields: [factor_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "mfa_challenges_auth_factor_id_fkey") - - @@index([created_at(sort: Desc)], map: "mfa_challenge_created_at_idx") - @@schema("auth") -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -model mfa_factors { - id String @id @db.Uuid - user_id String @db.Uuid - friendly_name String? - factor_type factor_type - status factor_status - created_at DateTime @db.Timestamptz(6) - updated_at DateTime @db.Timestamptz(6) - secret String? - phone String? - last_challenged_at DateTime? @unique @db.Timestamptz(6) - mfa_challenges mfa_challenges[] - users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@unique([user_id, phone], map: "unique_phone_factor_per_user") - @@index([user_id, created_at], map: "factor_id_created_at_idx") - @@index([user_id]) - @@schema("auth") -} - -/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -model one_time_tokens { - id String @id @db.Uuid - user_id String @db.Uuid - token_type one_time_token_type - token_hash String - relates_to String - created_at DateTime @default(now()) @db.Timestamp(6) - updated_at DateTime @default(now()) @db.Timestamp(6) - users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@unique([user_id, token_type]) - @@index([relates_to], map: "one_time_tokens_relates_to_hash_idx", type: Hash) - @@index([token_hash], map: "one_time_tokens_token_hash_hash_idx", type: Hash) - @@schema("auth") -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -model refresh_tokens { - instance_id String? @db.Uuid - id BigInt @id @default(autoincrement()) - token String? @unique(map: "refresh_tokens_token_unique") @db.VarChar(255) - user_id String? @db.VarChar(255) - revoked Boolean? - created_at DateTime? @db.Timestamptz(6) - updated_at DateTime? @db.Timestamptz(6) - parent String? @db.VarChar(255) - session_id String? @db.Uuid - sessions sessions? @relation(fields: [session_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@index([instance_id]) - @@index([instance_id, user_id]) - @@index([parent]) - @@index([session_id, revoked]) - @@index([updated_at(sort: Desc)]) - @@schema("auth") -} - -/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -model saml_providers { - id String @id @db.Uuid - sso_provider_id String @db.Uuid - entity_id String @unique - metadata_xml String - metadata_url String? - attribute_mapping Json? - created_at DateTime? @db.Timestamptz(6) - updated_at DateTime? @db.Timestamptz(6) - name_id_format String? - sso_providers sso_providers @relation(fields: [sso_provider_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@index([sso_provider_id]) - @@schema("auth") -} - -/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -model saml_relay_states { - id String @id @db.Uuid - sso_provider_id String @db.Uuid - request_id String - for_email String? - redirect_to String? - created_at DateTime? @db.Timestamptz(6) - updated_at DateTime? @db.Timestamptz(6) - flow_state_id String? @db.Uuid - flow_state flow_state? @relation(fields: [flow_state_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - sso_providers sso_providers @relation(fields: [sso_provider_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@index([created_at(sort: Desc)]) - @@index([for_email]) - @@index([sso_provider_id]) - @@schema("auth") -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -model schema_migrations { - version String @id @db.VarChar(255) - - @@schema("auth") -} - -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -model sessions { - id String @id @db.Uuid - user_id String @db.Uuid - created_at DateTime? @db.Timestamptz(6) - updated_at DateTime? @db.Timestamptz(6) - factor_id String? @db.Uuid - aal aal_level? - not_after DateTime? @db.Timestamptz(6) - refreshed_at DateTime? @db.Timestamp(6) - user_agent String? - ip String? @db.Inet - tag String? - mfa_amr_claims mfa_amr_claims[] - refresh_tokens refresh_tokens[] - users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@index([not_after(sort: Desc)]) - @@index([user_id]) - @@index([user_id, created_at], map: "user_id_created_at_idx") - @@schema("auth") -} - -/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info. -model sso_domains { - id String @id @db.Uuid - sso_provider_id String @db.Uuid - domain String - created_at DateTime? @db.Timestamptz(6) - updated_at DateTime? @db.Timestamptz(6) - sso_providers sso_providers @relation(fields: [sso_provider_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@index([sso_provider_id]) - @@schema("auth") -} - -/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info. -model sso_providers { - id String @id @db.Uuid - resource_id String? - created_at DateTime? @db.Timestamptz(6) - updated_at DateTime? @db.Timestamptz(6) - saml_providers saml_providers[] - saml_relay_states saml_relay_states[] - sso_domains sso_domains[] - - @@schema("auth") -} - -/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. -/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. -/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info. -model users { - instance_id String? @db.Uuid - id String @id @db.Uuid - aud String? @db.VarChar(255) - role String? @db.VarChar(255) - email String? @db.VarChar(255) - encrypted_password String? @db.VarChar(255) - email_confirmed_at DateTime? @db.Timestamptz(6) - invited_at DateTime? @db.Timestamptz(6) - confirmation_token String? @db.VarChar(255) - confirmation_sent_at DateTime? @db.Timestamptz(6) - recovery_token String? @db.VarChar(255) - recovery_sent_at DateTime? @db.Timestamptz(6) - email_change_token_new String? @db.VarChar(255) - email_change String? @db.VarChar(255) - email_change_sent_at DateTime? @db.Timestamptz(6) - last_sign_in_at DateTime? @db.Timestamptz(6) - raw_app_meta_data Json? - raw_user_meta_data Json? - is_super_admin Boolean? - created_at DateTime? @db.Timestamptz(6) - updated_at DateTime? @db.Timestamptz(6) - phone String? @unique - phone_confirmed_at DateTime? @db.Timestamptz(6) - phone_change String? @default("") - phone_change_token String? @default("") @db.VarChar(255) - phone_change_sent_at DateTime? @db.Timestamptz(6) - confirmed_at DateTime? @default(dbgenerated("LEAST(email_confirmed_at, phone_confirmed_at)")) @db.Timestamptz(6) - email_change_token_current String? @default("") @db.VarChar(255) - email_change_confirm_status Int? @default(0) @db.SmallInt - banned_until DateTime? @db.Timestamptz(6) - reauthentication_token String? @default("") @db.VarChar(255) - reauthentication_sent_at DateTime? @db.Timestamptz(6) - is_sso_user Boolean @default(false) - deleted_at DateTime? @db.Timestamptz(6) - is_anonymous Boolean @default(false) - identities identities[] - mfa_factors mfa_factors[] - one_time_tokens one_time_tokens[] - sessions sessions[] - chats chats[] - user_private_info user_private_info? - user_profiles user_profiles? - - @@index([instance_id]) - @@index([is_anonymous]) - @@schema("auth") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -330,8 +12,6 @@ model account_delete_tokens { token String @default(dbgenerated("uuid_generate_v4()")) @db.Uuid user_id String @id @db.Uuid user_profiles user_profiles @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -339,8 +19,6 @@ model billing_bypass_organizations { id String @id @db.Uuid created_at DateTime @default(now()) @db.Timestamptz(6) organizations organizations @relation(fields: [id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -350,10 +28,7 @@ model chats { payload Json? created_at DateTime @default(dbgenerated("timezone('utc'::text, now())")) @db.Timestamptz(6) project_id String @db.Uuid - users users? @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) projects projects @relation(fields: [project_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "public_chats_project_id_fkey") - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -365,7 +40,6 @@ model customers { @@id([stripe_customer_id, organization_id]) @@index([organization_id], map: "customers_organization_id_index") @@index([stripe_customer_id], map: "customers_stripe_customer_id_index") - @@schema("public") } model digger_batches { @@ -387,8 +61,6 @@ model digger_batches { event_type String? digger_jobs digger_jobs[] digger_run_stages digger_run_stages[] - - @@schema("public") } model digger_job_parent_links { @@ -400,7 +72,6 @@ model digger_job_parent_links { parent_digger_job_id String? @@index([deleted_at], map: "idx_digger_job_parent_links_deleted_at") - @@schema("public") } model digger_job_summaries { @@ -414,7 +85,6 @@ model digger_job_summaries { digger_jobs digger_jobs[] @@index([deleted_at], map: "idx_digger_job_summaries_deleted_at") - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -430,7 +100,6 @@ model digger_job_tokens { organizations organizations? @relation(fields: [organisation_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_jt_organisation_id") @@index([deleted_at], map: "idx_job_tokens_deleted_at") - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -457,7 +126,6 @@ model digger_jobs { @@index([batch_id]) @@index([batch_id], map: "idx_digger_job_id") @@index([deleted_at], map: "idx_digger_jobs_deleted_at") - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -473,7 +141,6 @@ model digger_locks { @@index([resource], map: "idx_digger_locked_resource") @@index([deleted_at], map: "idx_digger_locks_deleted_at") - @@schema("public") } model digger_run_queue_items { @@ -485,7 +152,6 @@ model digger_run_queue_items { project_id String? @db.Uuid @@index([deleted_at], map: "idx_digger_run_queue_items_deleted_at") - @@schema("public") } model digger_run_stages { @@ -500,7 +166,6 @@ model digger_run_stages { @@index([batch_id], map: "idx_digger_run_batch_id") @@index([deleted_at], map: "idx_digger_run_stages_deleted_at") - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -528,6 +193,7 @@ model digger_runs { apply_logs String? approver_user_id String? @db.Uuid triggered_by_user_id String? @db.Uuid + failure_reason String? digger_run_stages_digger_runs_apply_stage_idTodigger_run_stages digger_run_stages? @relation("digger_runs_apply_stage_idTodigger_run_stages", fields: [apply_stage_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_digger_runs_apply_stage") digger_run_stages_digger_runs_plan_stage_idTodigger_run_stages digger_run_stages? @relation("digger_runs_plan_stage_idTodigger_run_stages", fields: [plan_stage_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_digger_runs_plan_stage") projects projects @relation(fields: [project_id], references: [id], onDelete: SetNull, onUpdate: NoAction, map: "fk_digger_runs_project") @@ -537,7 +203,6 @@ model digger_runs { @@index([project_id]) @@index([deleted_at], map: "idx_digger_runs_deleted_at") @@index([project_id], map: "idx_digger_runs_project_id") - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -551,7 +216,6 @@ model env_vars { projects projects @relation(fields: [project_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "encrypted_env_vars_project_id_fkey") @@unique([project_id, name], map: "unique_project_var_name") - @@schema("public") } model github_app_installation_links { @@ -566,7 +230,6 @@ model github_app_installation_links { @@index([deleted_at], map: "idx_github_app_installation_links_deleted_at") @@index([github_installation_id, organization_id], map: "idx_github_installation_org") - @@schema("public") } model github_app_installations { @@ -582,7 +245,6 @@ model github_app_installations { status BigInt @@index([deleted_at], map: "idx_github_app_installations_deleted_at") - @@schema("public") } model github_apps { @@ -595,7 +257,6 @@ model github_apps { github_app_url String @@index([deleted_at], map: "idx_github_apps_deleted_at") - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -606,7 +267,6 @@ model internal_blog_author_posts { internal_blog_posts internal_blog_posts @relation(fields: [post_id], references: [id], onDelete: Cascade, onUpdate: NoAction) @@id([author_id, post_id]) - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -624,8 +284,6 @@ model internal_blog_author_profiles { updated_at DateTime @default(now()) @db.Timestamptz(6) internal_blog_author_posts internal_blog_author_posts[] user_profiles user_profiles @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -635,8 +293,6 @@ model internal_blog_post_tags { name String description String? internal_blog_post_tags_relationship internal_blog_post_tags_relationship[] - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -647,7 +303,6 @@ model internal_blog_post_tags_relationship { internal_blog_post_tags internal_blog_post_tags @relation(fields: [tag_id], references: [id], onDelete: Cascade, onUpdate: NoAction) @@id([blog_post_id, tag_id]) - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -666,8 +321,6 @@ model internal_blog_posts { json_content Json @default("{}") internal_blog_author_posts internal_blog_author_posts[] internal_blog_post_tags_relationship internal_blog_post_tags_relationship[] - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -680,8 +333,6 @@ model internal_changelog { updated_at DateTime? @default(now()) @db.Timestamptz(6) cover_image String? user_profiles user_profiles? @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -694,8 +345,6 @@ model internal_feedback_comments { updated_at DateTime @default(now()) @db.Timestamptz(6) internal_feedback_threads internal_feedback_threads @relation(fields: [thread_id], references: [id], onDelete: Cascade, onUpdate: NoAction) user_profiles user_profiles @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -714,8 +363,6 @@ model internal_feedback_threads { is_publicly_visible Boolean @default(false) internal_feedback_comments internal_feedback_comments[] user_profiles user_profiles @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -723,8 +370,6 @@ model organization_credits { organization_id String @id @db.Uuid credits BigInt @default(12) organizations organizations @relation(fields: [organization_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -746,10 +391,8 @@ model organization_join_invitations { @@index([inviter_user_id]) @@index([organization_id]) @@index([status]) - @@schema("public") } -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. model organization_members { id BigInt @id @default(autoincrement()) created_at DateTime @default(now()) @db.Timestamptz(6) @@ -762,10 +405,8 @@ model organization_members { @@index([member_id]) @@index([member_role]) @@index([organization_id]) - @@schema("public") } -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. model organizations { created_at DateTime @default(now()) @db.Timestamptz(6) id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid @@ -786,8 +427,6 @@ model organizations { subscriptions subscriptions[] teams teams[] user_private_info user_private_info[] - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -796,8 +435,6 @@ model organizations_private_info { billing_address Json? @db.Json payment_method Json? @db.Json organizations organizations @relation(fields: [id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -818,7 +455,6 @@ model prices { @@index([active]) @@index([product_id]) - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -833,7 +469,6 @@ model products { prices prices[] @@index([active]) - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -848,7 +483,6 @@ model project_comments { @@index([project_id]) @@index([user_id]) - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -857,8 +491,6 @@ model project_tfvars { project_id String @unique @db.Uuid tfvars Json updated_at DateTime @default(now()) @db.Timestamptz(6) - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -896,8 +528,6 @@ model projects { organizations organizations @relation(fields: [organization_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_projects_organization") repos repos @relation(fields: [repo_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fk_projects_repo") teams teams? @relation(fields: [team_id], references: [id], onDelete: Cascade) - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -919,7 +549,6 @@ model repos { @@unique([name, organization_id], map: "idx_org_repo") @@index([deleted_at], map: "idx_repos_deleted_at") - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -945,7 +574,6 @@ model subscriptions { @@index([organization_id]) @@index([price_id]) @@index([status]) - @@schema("public") } model team_members { @@ -955,8 +583,6 @@ model team_members { role project_team_member_role @default(member) team_id BigInt user_profiles user_profiles @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -967,8 +593,6 @@ model teams { name String projects projects[] organizations organizations @relation(fields: [organization_id], references: [id], onDelete: Cascade) - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -979,8 +603,6 @@ model user_api_keys { user_id String @db.Uuid expires_at DateTime? @db.Timestamptz(6) is_revoked Boolean @default(false) - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -995,7 +617,6 @@ model user_notifications { user_profiles user_profiles? @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) @@index([user_id]) - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -1004,8 +625,6 @@ model user_onboarding { created_at DateTime @default(now()) @db.Timestamptz(6) accepted_terms Boolean @default(false) user_profiles user_profiles @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@schema("public") } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. @@ -1014,19 +633,21 @@ model user_private_info { created_at DateTime? @default(now()) @db.Timestamptz(6) default_organization String? @db.Uuid organizations organizations? @relation(fields: [default_organization], references: [id], onUpdate: NoAction) - users users @relation(fields: [id], references: [id], onDelete: Cascade, onUpdate: NoAction) @@index([default_organization]) - @@schema("public") } -/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. model user_profiles { id String @id @db.Uuid full_name String? @db.VarChar avatar_url String? @db.VarChar created_at DateTime @default(now()) @db.Timestamptz(6) user_name String? @db.VarChar + email String? @unique(map: "email_unique") @db.VarChar(255) + has_accepted_terms Boolean? @default(true) + has_completed_profile Boolean? @default(false) + has_created_organization Boolean? @default(false) + is_created_through_org_invitation Boolean? @default(false) account_delete_tokens account_delete_tokens? digger_runs digger_runs[] internal_blog_author_profiles internal_blog_author_profiles? @@ -1040,10 +661,7 @@ model user_profiles { team_members team_members[] user_notifications user_notifications[] user_onboarding user_onboarding? - users users @relation(fields: [id], references: [id], onDelete: Cascade, onUpdate: NoAction) user_roles user_roles[] - - @@schema("public") } /// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments @@ -1056,85 +674,33 @@ model user_roles { @@unique([user_id, role]) @@index([user_id]) - @@schema("public") -} - -enum aal_level { - aal1 - aal2 - aal3 - - @@schema("auth") -} - -enum code_challenge_method { - s256 - plain - - @@schema("auth") -} - -enum factor_status { - unverified - verified - - @@schema("auth") -} - -enum factor_type { - totp - webauthn - phone - - @@schema("auth") -} - -enum one_time_token_type { - confirmation_token - reauthentication_token - recovery_token - email_change_token_new - email_change_token_current - phone_change_token - - @@schema("auth") } enum app_admin_role { moderator admin super_admin - - @@schema("public") } enum app_role { admin - - @@schema("public") } enum iac_type_enum { terraform terragrunt opentofu - - @@schema("public") } enum internal_blog_post_status { draft published - - @@schema("public") } enum internal_feedback_thread_priority { low medium high - - @@schema("public") } enum internal_feedback_thread_status { @@ -1144,16 +710,12 @@ enum internal_feedback_thread_status { closed in_progress completed - - @@schema("public") } enum internal_feedback_thread_type { bug feature_request general - - @@schema("public") } enum organization_join_invitation_link_status { @@ -1161,8 +723,6 @@ enum organization_join_invitation_link_status { finished_accepted finished_declined inactive - - @@schema("public") } enum organization_joining_status { @@ -1170,8 +730,6 @@ enum organization_joining_status { joinied declined_invitation joined - - @@schema("public") } enum organization_member_role { @@ -1179,8 +737,6 @@ enum organization_member_role { admin member readonly - - @@schema("public") } enum pricing_plan_interval { @@ -1188,15 +744,11 @@ enum pricing_plan_interval { week month year - - @@schema("public") } enum pricing_type { one_time recurring - - @@schema("public") } enum project_status { @@ -1204,16 +756,12 @@ enum project_status { pending_approval approved completed - - @@schema("public") } enum project_team_member_role { admin member readonly - - @@schema("public") } enum subscription_status { @@ -1225,6 +773,4 @@ enum subscription_status { past_due unpaid paused - - @@schema("public") } diff --git a/src/app/page.tsx b/src/app/page.tsx deleted file mode 100644 index a659b465..00000000 --- a/src/app/page.tsx +++ /dev/null @@ -1,58 +0,0 @@ - -'use client' - -import { supabaseAnonClient } from '@/supabase-clients/anon/supabaseAnonClient'; -import { AuthChangeEvent, Session } from "@supabase/supabase-js"; -import { useEffect, useState } from "react"; -import { Login } from './(dynamic-pages)/(login-pages)/login/Login'; - - -export default function Default() { - const supabase = supabaseAnonClient; - - // console.log(window.location.hash); - - // const hash = window.location.hash - // // Remove the '#' character if present - // const cleanHash = hash.startsWith('#') ? hash.slice(1) : hash; - - // // You can now use the hash value as needed - // if (cleanHash) { - // console.log({ message: `Hash found: ${cleanHash}` }); - // } else { - // console.log({ message: 'No hash found in the URL' }); - // } - - const [authState, setAuthState] = useState(""); - const [session, setSession] = useState(null); - - useEffect(() => { - supabase.auth.onAuthStateChange((event, sessionValue) => { - console.log('state change', event, sessionValue) - if (event === "INITIAL_SESSION") { - - setSession(sessionValue); - console.log("got initial session value") - window.location.href = `/auth/callback-tokens?access_token=${sessionValue?.access_token}&refresh_token=${sessionValue?.refresh_token}` - } else { - setAuthState(event); - } - }); - }, []); - - return ( - <> -

SSO Login Demo

-

Auth state: {JSON.stringify(authState)}

-

Session

-
{JSON.stringify(session, null, 2)}
- {authState === "SIGNED_IN" ? ( -

- you are signed in -

- ) : ( - - )} - - ); -} diff --git a/supabase/migrations/20241017174059_auth_constraints.sql b/supabase/migrations/20241017174059_auth_constraints.sql new file mode 100644 index 00000000..5227867f --- /dev/null +++ b/supabase/migrations/20241017174059_auth_constraints.sql @@ -0,0 +1,6 @@ +ALTER TABLE public.chats +DROP CONSTRAINT IF EXISTS chats_user_id_fkey; + +ALTER TABLE public.user_private_info +DROP CONSTRAINT IF EXISTS user_private_info_id_fkey; + From 203f202e5b78f482a72b02c52df5670e0115b70e Mon Sep 17 00:00:00 2001 From: Igor Zalutski Date: Thu, 17 Oct 2024 19:51:03 +0100 Subject: [PATCH 12/23] Rewrite getUserProfile() --- src/data/user/user.tsx | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/data/user/user.tsx b/src/data/user/user.tsx index 461af041..f16c7073 100644 --- a/src/data/user/user.tsx +++ b/src/data/user/user.tsx @@ -8,6 +8,7 @@ import { sendEmail } from "@/utils/api-routes/utils"; import { toSiteURL } from "@/utils/helpers"; import { serverGetLoggedInUser } from "@/utils/server/serverGetLoggedInUser"; import type { AuthUserMetadata } from "@/utils/zod-schemas/authUserMetadata"; +import { PrismaClient } from '@prisma/client'; import { renderAsync } from "@react-email/render"; import ConfirmAccountDeletionEmail from "emails/account-deletion-request"; import { revalidatePath } from "next/cache"; @@ -28,19 +29,25 @@ export async function getIsAppAdmin(): Promise { export const getUserProfile = async (userId: string) => { console.log(`get user profile ${userId}`) - const supabase = createSupabaseUserServerComponentClient(); - const { data, error } = await supabase - .from("user_profiles") - .select("*") - .eq("id", userId) - .single(); + const prisma = new PrismaClient(); + try { + const data = await prisma.user_profiles.findUnique({ + where: { + id: userId + } + }) - if (error) { - throw error; - } + if (!data) { + throw new Error('User profile not found') + } - return data; -}; + return data + } catch (error) { + throw error + } finally { + await prisma.$disconnect() + } +} export const getUserProfileByEmail = async (email: string) => { const supabase = createSupabaseUserServerComponentClient(); From 376e953697fc442e6a65a96a601f8fb1f2233d57 Mon Sep 17 00:00:00 2001 From: Igor Zalutski Date: Fri, 18 Oct 2024 13:28:38 +0100 Subject: [PATCH 13/23] Migrate user_profiles to Prisma; delete blog bloatware --- .../runs/[runId]/ProjectRunDetails.tsx | 3 +- .../[projectSlug]/runs/[runId]/page.tsx | 4 +- .../(user-pages)/settings/AccountSettings.tsx | 4 +- .../(blog-list)/AddAuthorProfileDialog.tsx | 250 -------- .../blog/(blog-list)/AddBlogTagDialog.tsx | 147 ----- .../blog/(blog-list)/BlogFilters.tsx | 95 --- .../blog/(blog-list)/BlogListPreview.tsx | 150 ----- .../blog/(blog-list)/DeleteBlogPost.tsx | 42 -- .../(blog-list)/EditAuthorProfileDialog.tsx | 239 ------- .../blog/(blog-list)/EditBlogTagDialog.tsx | 151 ----- .../blog/(blog-list)/ManageAuthorsDialog.tsx | 138 ----- .../blog/(blog-list)/ManageBlogTagsDialog.tsx | 104 ---- .../blog/(blog-list)/RecentlyList.tsx | 32 - .../(admin-pages)/blog/(blog-list)/page.tsx | 123 ---- .../(admin-pages)/blog/(blog-list)/schema.ts | 14 - .../app_admin/(admin-pages)/blog/BlogForm.tsx | 513 --------------- .../blog/post/[postId]/edit/page.tsx | 96 --- .../(admin-pages)/blog/post/create/page.tsx | 44 -- .../(admin-pages)/blog/post/upload/route.ts | 31 - .../(admin-preview-pages)/blog/page.tsx | 29 - .../onboarding/OnboardingFlow.tsx | 4 +- .../onboarding/ProfileUpdate.tsx | 4 +- src/components/blog/BlogViews.tsx | 174 ------ src/components/blog/ConfigureDomainsForm.tsx | 96 --- src/data/admin/internal-blog.ts | 583 ------------------ src/data/admin/user.tsx | 35 +- src/data/user/user.tsx | 164 ++--- 27 files changed, 117 insertions(+), 3152 deletions(-) delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/(blog-list)/AddAuthorProfileDialog.tsx delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/(blog-list)/AddBlogTagDialog.tsx delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/(blog-list)/BlogFilters.tsx delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/(blog-list)/BlogListPreview.tsx delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/(blog-list)/DeleteBlogPost.tsx delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/(blog-list)/EditAuthorProfileDialog.tsx delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/(blog-list)/EditBlogTagDialog.tsx delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/(blog-list)/ManageAuthorsDialog.tsx delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/(blog-list)/ManageBlogTagsDialog.tsx delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/(blog-list)/RecentlyList.tsx delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/(blog-list)/page.tsx delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/(blog-list)/schema.ts delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/BlogForm.tsx delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/post/[postId]/edit/page.tsx delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/post/create/page.tsx delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/post/upload/route.ts delete mode 100644 src/app/(dynamic-pages)/(authenticated-pages)/app_admin_preview/(admin-preview-pages)/blog/page.tsx delete mode 100644 src/components/blog/BlogViews.tsx delete mode 100644 src/components/blog/ConfigureDomainsForm.tsx delete mode 100644 src/data/admin/internal-blog.ts diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/[runId]/ProjectRunDetails.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/[runId]/ProjectRunDetails.tsx index 97f54388..7c604aff 100644 --- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/[runId]/ProjectRunDetails.tsx +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/[runId]/ProjectRunDetails.tsx @@ -11,6 +11,7 @@ import { useSAToastMutation } from "@/hooks/useSAToastMutation"; import { ToSnakeCase, ToTitleCase } from "@/lib/utils"; import { supabaseUserClientComponentClient } from "@/supabase-clients/user/supabaseUserClientComponentClient"; import { Table } from "@/types"; +import { user_profiles } from "@prisma/client"; import { DotFilledIcon } from "@radix-ui/react-icons"; import { AnimatePresence, motion } from 'framer-motion'; import { CheckCircle2, GitPullRequest, LinkIcon, Loader2, Play, XCircle } from 'lucide-react'; @@ -118,7 +119,7 @@ function RenderContent({ export const ProjectRunDetails: React.FC<{ run: Table<'digger_runs'>, - loggedInUser: Table<'user_profiles'>, + loggedInUser: user_profiles, approverUser: Table<'user_profiles'> | null, triggeredByUser: Table<'user_profiles'> | null, isUserOrgAdmin: boolean, diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/[runId]/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/[runId]/page.tsx index 227d84a4..67ac9204 100644 --- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/[runId]/page.tsx +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/runs/[runId]/page.tsx @@ -11,6 +11,7 @@ import { serverGetLoggedInUser } from "@/utils/server/serverGetLoggedInUser"; import { runIdParamSchema } from "@/utils/zod-schemas/params"; +import { user_profiles } from "@prisma/client"; import type { Metadata } from "next"; import dynamic from 'next/dynamic'; import { ComponentType, Suspense } from "react"; @@ -28,7 +29,7 @@ type RunDetailPageProps = { type ProjectRunDetailsProps = { run: Table<'digger_runs'>, - loggedInUser: Table<'user_profiles'>, + loggedInUser: user_profiles, isUserOrgAdmin: boolean tfOutput: string | null workflowRunUrl: string | null @@ -42,7 +43,6 @@ type ProjectRunDetailsProps = { } - const DynamicProjectRunDetails = dynamic(() => import('./ProjectRunDetails').then((mod) => mod.ProjectRunDetails as ComponentType), { ssr: false } diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(user-pages)/settings/AccountSettings.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(user-pages)/settings/AccountSettings.tsx index f260339d..a4c0d473 100644 --- a/src/app/(dynamic-pages)/(authenticated-pages)/(user-pages)/settings/AccountSettings.tsx +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(user-pages)/settings/AccountSettings.tsx @@ -6,7 +6,7 @@ import { uploadPublicUserAvatar, } from '@/data/user/user'; import { useSAToastMutation } from '@/hooks/useSAToastMutation'; -import type { Table } from '@/types'; +import { user_profiles } from '@prisma/client'; import { useRouter } from 'next/navigation'; import { useState } from 'react'; import { ConfirmDeleteAccountDialog } from './ConfirmDeleteAccountDialog'; @@ -15,7 +15,7 @@ export function AccountSettings({ userProfile, userEmail }: { - userProfile: Table<'user_profiles'>; + userProfile: user_profiles; userEmail: string | undefined; }) { const router = useRouter(); diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/(blog-list)/AddAuthorProfileDialog.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/(blog-list)/AddAuthorProfileDialog.tsx deleted file mode 100644 index 9c052916..00000000 --- a/src/app/(dynamic-pages)/(authenticated-pages)/app_admin/(admin-pages)/blog/(blog-list)/AddAuthorProfileDialog.tsx +++ /dev/null @@ -1,250 +0,0 @@ -"use client"; -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { Textarea } from "@/components/ui/textarea"; -import { createAuthorProfile } from "@/data/admin/internal-blog"; -import { useSAToastMutation } from "@/hooks/useSAToastMutation"; -import type { Table } from "@/types"; -import { authorProfileSchema } from "@/utils/zod-schemas/internalBlog"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { UserPlus } from "lucide-react"; -import { useRouter } from "next/navigation"; -import { useState } from "react"; -import { Controller, useForm } from "react-hook-form"; -import { toast } from "sonner"; -import type { z } from "zod"; - -type AuthorProfileFormType = z.infer; -export type CreateAuthorPayload = Omit< - Table<"internal_blog_author_profiles">, - "created_at" | "updated_at" ->; -export const AddAuthorProfileDialog = ({ - appAdmins, - authorProfiles, -}: { - appAdmins: Array>; - authorProfiles: Array>; -}) => { - const [isOpen, setIsOpen] = useState(false); - const router = useRouter(); - const { control, handleSubmit, formState, reset, setValue } = - useForm({ - resolver: zodResolver(authorProfileSchema), - }); - - const { - mutate: createAuthorProfileMutation, - isLoading: isCreatingAuthorProfile, - } = useSAToastMutation( - async (payload: CreateAuthorPayload) => { - return createAuthorProfile(payload); - }, - { - onSuccess: () => { - router.refresh(); - toast.success("Successfully created author profile"); - setIsOpen(false); - reset(); - }, - errorMessage(error) { - try { - if (error instanceof Error) { - return String(error.message); - } - return `Failed to create author profile ${String(error)}`; - } catch (_err) { - console.warn(_err); - return 'Failed to create author profile'; - } - }, - }, - ); - - const { isValid, isLoading } = formState; - - const onSubmit = (data: AuthorProfileFormType) => { - void createAuthorProfileMutation({ - user_id: data.user_id, - display_name: data.display_name, - bio: data.bio, - avatar_url: data.avatar_url, - website_url: data.website_url ?? null, - twitter_handle: data.twitter_handle ?? null, - facebook_handle: data.facebook_handle ?? null, - linkedin_handle: data.linkedin_handle ?? null, - instagram_handle: data.instagram_handle ?? null, - }); - }; - - return ( - setIsOpen(newIsOpen)}> - - - - - - -
- -
-
- Add Author Profile - - Fill in the details for the new author profile. - -
-
-
-
-
- - ( - - )} - /> -
-
- - ( - - )} - /> -
-
- - ( -