feat: setup drizzle (#594)

* chore: migration to SvelteKit 2

* stage 1: migrate to svelte 5 and fix rerendering issues

* stage 2: fix more $: reactivity across dashboard

* fix merge conflicts

* chore: migrate code in main to svelte 5

* chore: update preventDefault

* chore: simplify tsconfig and remove unused mobileMenuRef in MobileMenu.svelte

* chore: enforce .ts across dashboard

* rename from classroomio-com to website

* stage-3: Replace carbon icon svelte with lucide icon

* update lock file

* chore: finish icon replacement

* fix: settings and setup page multiple rerender

* stage 3: More icon and rerendering fixes

* feat: setup drizzle and better auth

* fix: onboarding validation error showing translation key #586 (#593)

* fix: onboarding validation error showing translation key #586

* fix: remove debug log for profile update errors

* chore: add a clean command

* fix: remove message from email verification response
This commit is contained in:
Rotimi Best
2025-11-01 15:49:29 +01:00
committed by GitHub
parent 97fb446df5
commit b96d91cd7c
801 changed files with 22279 additions and 10834 deletions
+12
View File
@@ -0,0 +1,12 @@
import 'dotenv/config';
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/schema.ts',
out: './src/migrations',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!
}
});
+1
View File
@@ -0,0 +1 @@
[]
+24
View File
@@ -0,0 +1,24 @@
{
"name": "@cio/db",
"version": "1.0.0",
"type": "module",
"scripts": {
"db": "drizzle-kit",
"move-users": "tsx src/scripts/supabase-to-betterauth.ts",
"pull": "drizzle-kit pull --config drizzle.config.ts"
},
"dependencies": {
"@cio/tsconfig": "workspace:*",
"better-auth": "^1.3.28",
"drizzle-kit": "^0.31.5",
"drizzle-orm": "^0.44.6",
"pg": "^8.16.3"
},
"devDependencies": {
"@types/node": "^24.8.1",
"@types/pg": "^8.15.5",
"dotenv": "^17.2.3",
"tsx": "^4.20.3",
"typescript": "^5.9.3"
}
}
+67
View File
@@ -0,0 +1,67 @@
import { pgTable, text, timestamp, boolean } from "drizzle-orm/pg-core";
export const user = pgTable("user", {
id: text("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
emailVerified: boolean("email_verified").default(false).notNull(),
image: text("image"),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at")
.defaultNow()
.$onUpdate(() => /* @__PURE__ */ new Date())
.notNull(),
role: text("role"),
banned: boolean("banned").default(false),
banReason: text("ban_reason"),
banExpires: timestamp("ban_expires"),
isAnonymous: boolean("is_anonymous"),
});
export const session = pgTable("session", {
id: text("id").primaryKey(),
expiresAt: timestamp("expires_at").notNull(),
token: text("token").notNull().unique(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at")
.$onUpdate(() => /* @__PURE__ */ new Date())
.notNull(),
ipAddress: text("ip_address"),
userAgent: text("user_agent"),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
impersonatedBy: text("impersonated_by"),
});
export const account = pgTable("account", {
id: text("id").primaryKey(),
accountId: text("account_id").notNull(),
providerId: text("provider_id").notNull(),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
accessToken: text("access_token"),
refreshToken: text("refresh_token"),
idToken: text("id_token"),
accessTokenExpiresAt: timestamp("access_token_expires_at"),
refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
scope: text("scope"),
password: text("password"),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at")
.$onUpdate(() => /* @__PURE__ */ new Date())
.notNull(),
});
export const verification = pgTable("verification", {
id: text("id").primaryKey(),
identifier: text("identifier").notNull(),
value: text("value").notNull(),
expiresAt: timestamp("expires_at").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at")
.defaultNow()
.$onUpdate(() => /* @__PURE__ */ new Date())
.notNull(),
});
+25
View File
@@ -0,0 +1,25 @@
import * as schema from '@db/schema';
import { admin, anonymous } from 'better-auth/plugins';
import { betterAuth } from 'better-auth';
import { db } from '@db/drizzle';
import { drizzleAdapter } from 'better-auth/adapters/drizzle';
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: 'pg',
schema
}),
emailVerification: {},
emailAndPassword: {
enabled: true
},
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!
}
},
plugins: [admin(), anonymous()]
});
+60
View File
@@ -0,0 +1,60 @@
import 'dotenv/config';
import * as schema from './schema';
import { SQL, sql } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/node-postgres';
const connectionString = process.env.DATABASE_URL ?? process.env.PRIVATE_DATABASE_URL ?? '';
export const db = drizzle(connectionString, {
schema
});
export * from 'drizzle-orm';
export * from './schema';
/**
* Creates a SQL expression that returns the length of a JSON array column
*
* @param column The column containing the JSON array
* @returns A SQL expression that can be used in where clauses
*/
function jsonArrayLength(column: any) {
// Create a SQL expression using Drizzle's sql template literal
const sqlExpression = sql`jsonb_array_length(${column})`;
// Return an object with utility methods for comparison operations
return {
// Check if array length equals a specific value
equals: (length: number): SQL<unknown> => {
return sql`${sqlExpression} = ${length}`;
},
// Check if array length is greater than a specific value
gt: (length: number): SQL<unknown> => {
return sql`${sqlExpression} > ${length}`;
},
// Check if array length is less than a specific value
lt: (length: number): SQL<unknown> => {
return sql`${sqlExpression} < ${length}`;
},
// Check if array length is greater than or equal to a specific value
gte: (length: number): SQL<unknown> => {
return sql`${sqlExpression} >= ${length}`;
},
// Check if array length is less than or equal to a specific value
lte: (length: number): SQL<unknown> => {
return sql`${sqlExpression} <= ${length}`;
}
};
}
const explainAnalyze = async (d: typeof db, query: any) => {
console.log(query.sql);
const debugResult = await d.execute(sql`EXPLAIN ANALYZE ${query}`);
console.debug(debugResult);
return query;
};
export { explainAnalyze, jsonArrayLength };
@@ -0,0 +1,644 @@
-- CREATE TYPE "public"."COURSE_TYPE" AS ENUM('SELF_PACED', 'LIVE_CLASS');--> statement-breakpoint
-- CREATE TYPE "public"."COURSE_VERSION" AS ENUM('V1', 'V2');--> statement-breakpoint
-- CREATE TYPE "public"."LOCALE" AS ENUM('en', 'hi', 'fr', 'pt', 'de', 'vi', 'ru', 'es', 'pl', 'da');--> statement-breakpoint
-- CREATE TYPE "public"."PLAN" AS ENUM('EARLY_ADOPTER', 'ENTERPRISE', 'BASIC');--> statement-breakpoint
-- CREATE TABLE "analytics_login_events" (
-- "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
-- "user_id" uuid NOT NULL,
-- "logged_in_at" timestamp with time zone DEFAULT now(),
-- CONSTRAINT "analytics_login_events_user_id_unique" UNIQUE("user_id")
-- );
-- --> statement-breakpoint
-- ALTER TABLE "analytics_login_events" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "apps_poll" (
-- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
-- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "question" text,
-- "authorId" uuid,
-- "isPublic" boolean,
-- "status" varchar DEFAULT 'draft',
-- "expiration" timestamp with time zone,
-- "courseId" uuid
-- );
-- --> statement-breakpoint
-- ALTER TABLE "apps_poll" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "apps_poll_option" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "apps_poll_option_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
-- "updated_at" timestamp with time zone,
-- "poll_id" uuid,
-- "label" varchar
-- );
-- --> statement-breakpoint
-- ALTER TABLE "apps_poll_option" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "apps_poll_submission" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "apps_poll_submision_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
-- "poll_option_id" bigint,
-- "selected_by_id" uuid,
-- "poll_id" uuid
-- );
-- --> statement-breakpoint
-- ALTER TABLE "apps_poll_submission" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "community_answer" (
-- "id" uuid PRIMARY KEY DEFAULT extensions.gen_random_uuid() NOT NULL,
-- "created_at" timestamp with time zone DEFAULT now(),
-- "question_id" bigint,
-- "body" varchar,
-- "author_id" bigint,
-- "votes" bigint,
-- "author_profile_id" uuid
-- );
-- --> statement-breakpoint
-- ALTER TABLE "community_answer" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "community_question" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "community_question_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "created_at" timestamp with time zone DEFAULT now(),
-- "title" varchar,
-- "body" text,
-- "author_id" bigint,
-- "votes" bigint DEFAULT '0',
-- "organization_id" uuid,
-- "slug" text,
-- "author_profile_id" uuid,
-- "course_id" uuid NOT NULL
-- );
-- --> statement-breakpoint
-- ALTER TABLE "community_question" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "course" (
-- "title" varchar NOT NULL,
-- "description" varchar NOT NULL,
-- "overview" varchar DEFAULT 'Welcome to this amazing course 🚀 ',
-- "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
-- "created_at" timestamp with time zone DEFAULT now(),
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "group_id" uuid,
-- "is_template" boolean DEFAULT true,
-- "logo" text DEFAULT '' NOT NULL,
-- "slug" varchar,
-- "metadata" jsonb DEFAULT '{"goals":"","description":"","requirements":""}'::jsonb NOT NULL,
-- "cost" bigint DEFAULT '0',
-- "currency" varchar DEFAULT 'USD' NOT NULL,
-- "banner_image" text,
-- "is_published" boolean DEFAULT false,
-- "is_certificate_downloadable" boolean DEFAULT false,
-- "certificate_theme" text,
-- "status" text DEFAULT 'ACTIVE' NOT NULL,
-- "type" "COURSE_TYPE" DEFAULT 'LIVE_CLASS',
-- "version" "COURSE_VERSION" DEFAULT 'V1' NOT NULL,
-- CONSTRAINT "course_slug_key" UNIQUE("slug")
-- );
-- --> statement-breakpoint
-- ALTER TABLE "course" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "course_newsfeed" (
-- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
-- "author_id" uuid,
-- "content" text,
-- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
-- "course_id" uuid,
-- "reaction" jsonb DEFAULT '{"clap":[],"smile":[],"thumbsup":[],"thumbsdown":[]}'::jsonb,
-- "is_pinned" boolean DEFAULT false NOT NULL
-- );
-- --> statement-breakpoint
-- ALTER TABLE "course_newsfeed" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "course_newsfeed_comment" (
-- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
-- "author_id" uuid,
-- "content" text,
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "course_newsfeed_comment_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "course_newsfeed_id" uuid,
-- CONSTRAINT "course_newsfeed_comment_id_key" UNIQUE("id")
-- );
-- --> statement-breakpoint
-- ALTER TABLE "course_newsfeed_comment" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "currency" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "currency_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "created_at" timestamp with time zone DEFAULT now(),
-- "name" varchar
-- );
-- --> statement-breakpoint
-- CREATE TABLE "exercise" (
-- "title" varchar NOT NULL,
-- "description" varchar,
-- "lesson_id" uuid,
-- "created_at" timestamp with time zone DEFAULT now(),
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
-- "due_by" timestamp
-- );
-- --> statement-breakpoint
-- ALTER TABLE "exercise" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "group" (
-- "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
-- "name" varchar NOT NULL,
-- "description" text,
-- "created_at" timestamp with time zone DEFAULT now(),
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "organization_id" uuid
-- );
-- --> statement-breakpoint
-- ALTER TABLE "group" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "group_attendance" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "group_attendance_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "created_at" timestamp with time zone DEFAULT now(),
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "course_id" uuid,
-- "student_id" uuid,
-- "is_present" boolean DEFAULT false,
-- "lesson_id" uuid NOT NULL
-- );
-- --> statement-breakpoint
-- ALTER TABLE "group_attendance" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "groupmember" (
-- "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
-- "group_id" uuid NOT NULL,
-- "role_id" bigint NOT NULL,
-- "profile_id" uuid,
-- "email" varchar,
-- "created_at" timestamp with time zone DEFAULT now(),
-- "assigned_student_id" varchar,
-- CONSTRAINT "unique_entries" UNIQUE("group_id","profile_id","email"),
-- CONSTRAINT "unique_group_email" UNIQUE("group_id","email"),
-- CONSTRAINT "unique_group_profile" UNIQUE("group_id","profile_id")
-- );
-- --> statement-breakpoint
-- ALTER TABLE "groupmember" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "lesson" (
-- "note" varchar,
-- "video_url" varchar,
-- "slide_url" varchar,
-- "course_id" uuid NOT NULL,
-- "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
-- "created_at" timestamp with time zone DEFAULT now(),
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "title" varchar NOT NULL,
-- "public" boolean DEFAULT false,
-- "lesson_at" timestamp with time zone DEFAULT now(),
-- "teacher_id" uuid,
-- "is_complete" boolean DEFAULT false,
-- "call_url" text,
-- "order" bigint,
-- "is_unlocked" boolean DEFAULT false,
-- "videos" jsonb DEFAULT '[]'::jsonb,
-- "section_id" uuid,
-- "documents" jsonb DEFAULT '[]'::jsonb
-- );
-- --> statement-breakpoint
-- ALTER TABLE "lesson" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "lesson_comment" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "lesson_comment_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "lesson_id" uuid,
-- "groupmember_id" uuid,
-- "comment" text
-- );
-- --> statement-breakpoint
-- ALTER TABLE "lesson_comment" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "lesson_completion" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "lesson_completion_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
-- "lesson_id" uuid,
-- "profile_id" uuid,
-- "is_complete" boolean DEFAULT false,
-- "updated_at" timestamp with time zone DEFAULT now(),
-- CONSTRAINT "unique_lesson_profile" UNIQUE("lesson_id","profile_id")
-- );
-- --> statement-breakpoint
-- ALTER TABLE "lesson_completion" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "lesson_language" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "lesson_language_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "content" text,
-- "lesson_id" uuid DEFAULT gen_random_uuid(),
-- "locale" "LOCALE" DEFAULT 'en'
-- );
-- --> statement-breakpoint
-- ALTER TABLE "lesson_language" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "lesson_language_history" (
-- "id" serial PRIMARY KEY NOT NULL,
-- "lesson_language_id" integer,
-- "old_content" text,
-- "new_content" text,
-- "timestamp" timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL
-- );
-- --> statement-breakpoint
-- ALTER TABLE "lesson_language_history" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "lesson_section" (
-- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
-- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "title" varchar,
-- "order" bigint DEFAULT '0',
-- "course_id" uuid
-- );
-- --> statement-breakpoint
-- ALTER TABLE "lesson_section" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "option" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "option_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "label" varchar NOT NULL,
-- "is_correct" boolean DEFAULT false NOT NULL,
-- "question_id" bigint NOT NULL,
-- "value" uuid DEFAULT extensions.gen_random_uuid(),
-- "created_at" timestamp with time zone DEFAULT now(),
-- "updated_at" timestamp with time zone DEFAULT now()
-- );
-- --> statement-breakpoint
-- ALTER TABLE "option" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "organization" (
-- "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
-- "name" varchar NOT NULL,
-- "siteName" text,
-- "avatar_url" text,
-- "settings" jsonb DEFAULT '{}'::jsonb,
-- "landingpage" jsonb DEFAULT '{}'::jsonb,
-- "theme" text,
-- "created_at" timestamp with time zone DEFAULT timezone('utc'::text, now()) NOT NULL,
-- "customization" json DEFAULT '{"apps":{"poll":true,"comments":true},"course":{"grading":true,"newsfeed":true},"dashboard":{"exercise":true,"community":true,"bannerText":"","bannerImage":""}}'::json NOT NULL,
-- "is_restricted" boolean DEFAULT false NOT NULL,
-- "customCode" text,
-- "customDomain" text,
-- "favicon" text,
-- "isCustomDomainVerified" boolean DEFAULT false,
-- CONSTRAINT "organization_siteName_key" UNIQUE("siteName"),
-- CONSTRAINT "organization_customDomain_key" UNIQUE("customDomain")
-- );
-- --> statement-breakpoint
-- ALTER TABLE "organization" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "organization_contacts" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "organization_contacts_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
-- "email" text,
-- "phone" text,
-- "name" text,
-- "message" text,
-- "organization_id" uuid
-- );
-- --> statement-breakpoint
-- ALTER TABLE "organization_contacts" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "organization_emaillist" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "organization_emaillist_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
-- "email" text,
-- "organization_id" uuid
-- );
-- --> statement-breakpoint
-- ALTER TABLE "organization_emaillist" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "organization_plan" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "organization_plan_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "activated_at" timestamp with time zone DEFAULT now() NOT NULL,
-- "org_id" uuid,
-- "plan_name" "PLAN",
-- "is_active" boolean,
-- "deactivated_at" timestamp with time zone,
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "payload" jsonb,
-- "triggered_by" bigint,
-- "provider" text DEFAULT 'lmz',
-- "subscription_id" text,
-- CONSTRAINT "organization_plan_subscription_id_key" UNIQUE("subscription_id")
-- );
-- --> statement-breakpoint
-- ALTER TABLE "organization_plan" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "organizationmember" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "organizationmember_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "organization_id" uuid NOT NULL,
-- "role_id" bigint NOT NULL,
-- "profile_id" uuid,
-- "email" text,
-- "verified" boolean DEFAULT false,
-- "created_at" timestamp with time zone DEFAULT timezone('utc'::text, now()) NOT NULL
-- );
-- --> statement-breakpoint
-- ALTER TABLE "organizationmember" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "profile" (
-- "id" uuid PRIMARY KEY NOT NULL,
-- "fullname" text NOT NULL,
-- "username" text NOT NULL,
-- "avatar_url" text DEFAULT 'https://tapaozmyjsuykgerrfkt.supabase.co/storage/v1/object/public/avatars/avatar.png',
-- "created_at" timestamp with time zone DEFAULT now(),
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "email" varchar,
-- "can_add_course" boolean DEFAULT true,
-- "role" varchar,
-- "goal" varchar,
-- "source" varchar,
-- "metadata" json,
-- "telegram_chat_id" bigint,
-- "is_email_verified" boolean DEFAULT false,
-- "verified_at" timestamp with time zone,
-- "locale" "LOCALE" DEFAULT 'en',
-- "is_restricted" boolean DEFAULT false NOT NULL,
-- CONSTRAINT "profile_username_key" UNIQUE("username"),
-- CONSTRAINT "profile_email_key" UNIQUE("email")
-- );
-- --> statement-breakpoint
-- ALTER TABLE "profile" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "question" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "question_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "question_type_id" bigint NOT NULL,
-- "title" varchar NOT NULL,
-- "created_at" timestamp with time zone DEFAULT now(),
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "exercise_id" uuid NOT NULL,
-- "name" uuid DEFAULT extensions.gen_random_uuid(),
-- "points" double precision,
-- "order" bigint
-- );
-- --> statement-breakpoint
-- ALTER TABLE "question" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "question_answer" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "question_answer_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "answers" varchar[],
-- "question_id" bigint NOT NULL,
-- "open_answer" text,
-- "group_member_id" uuid NOT NULL,
-- "submission_id" uuid,
-- "point" bigint DEFAULT '0'
-- );
-- --> statement-breakpoint
-- ALTER TABLE "question_answer" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "question_type" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "question_type_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "label" varchar NOT NULL,
-- "created_at" timestamp with time zone DEFAULT now(),
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "typename" varchar
-- );
-- --> statement-breakpoint
-- ALTER TABLE "question_type" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "quiz" (
-- "id" uuid PRIMARY KEY DEFAULT extensions.gen_random_uuid() NOT NULL,
-- "created_at" timestamp with time zone DEFAULT now(),
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "title" text,
-- "questions" json,
-- "timelimit" varchar DEFAULT '10s',
-- "theme" varchar DEFAULT 'standard',
-- "organization_id" uuid NOT NULL
-- );
-- --> statement-breakpoint
-- ALTER TABLE "quiz" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "quiz_play" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "play_quiz_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "created_at" timestamp with time zone DEFAULT now(),
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "quiz_id" uuid,
-- "players" json DEFAULT '[]'::json,
-- "started" boolean DEFAULT false,
-- "currentQuestionId" bigint DEFAULT '0',
-- "showCurrentQuestionAnswer" boolean DEFAULT false,
-- "isLastQuestion" boolean,
-- "step" text DEFAULT 'CONNECT_TO_PLAY',
-- "studentStep" text DEFAULT 'PIN_SETUP',
-- "pin" text,
-- CONSTRAINT "quiz_play_pin_key" UNIQUE("pin")
-- );
-- --> statement-breakpoint
-- ALTER TABLE "quiz_play" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "role" (
-- "type" varchar NOT NULL,
-- "description" varchar,
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "role_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "created_at" timestamp with time zone DEFAULT now()
-- );
-- --> statement-breakpoint
-- ALTER TABLE "role" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "submission" (
-- "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
-- "reviewer_id" bigint,
-- "status_id" bigint DEFAULT '1',
-- "total" bigint DEFAULT '0',
-- "created_at" timestamp with time zone DEFAULT now(),
-- "updated_at" timestamp with time zone DEFAULT now(),
-- "exercise_id" uuid NOT NULL,
-- "submitted_by" uuid,
-- "course_id" uuid,
-- "feedback" text
-- );
-- --> statement-breakpoint
-- ALTER TABLE "submission" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "submissionstatus" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "submission_status_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "label" varchar NOT NULL,
-- "updated_at" timestamp with time zone DEFAULT now()
-- );
-- --> statement-breakpoint
-- ALTER TABLE "submissionstatus" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint
-- CREATE TABLE "test_tenant" (
-- "id" serial PRIMARY KEY NOT NULL,
-- "details" text
-- );
-- --> statement-breakpoint
-- CREATE TABLE "video_transcripts" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "video_transcripts_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
-- "muse_svid" text,
-- "transcript" text,
-- "downloaded" boolean DEFAULT false,
-- "link" text
-- );
-- --> statement-breakpoint
-- CREATE TABLE "waitinglist" (
-- "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (sequence name "waitinglist_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854776000 START WITH 1 CACHE 1),
-- "email" varchar NOT NULL,
-- "created_at" timestamp with time zone DEFAULT now(),
-- CONSTRAINT "constraint_name" UNIQUE("email")
-- );
-- --> statement-breakpoint
-- ALTER TABLE "analytics_login_events" ADD CONSTRAINT "analytics_login_events_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "apps_poll" ADD CONSTRAINT "apps_poll_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "public"."groupmember"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "apps_poll" ADD CONSTRAINT "apps_poll_courseId_fkey" FOREIGN KEY ("courseId") REFERENCES "public"."course"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "apps_poll_option" ADD CONSTRAINT "apps_poll_option_poll_id_fkey" FOREIGN KEY ("poll_id") REFERENCES "public"."apps_poll"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "apps_poll_submission" ADD CONSTRAINT "apps_poll_submission_poll_id_fkey" FOREIGN KEY ("poll_id") REFERENCES "public"."apps_poll"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "apps_poll_submission" ADD CONSTRAINT "apps_poll_submission_poll_option_id_fkey" FOREIGN KEY ("poll_option_id") REFERENCES "public"."apps_poll_option"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "apps_poll_submission" ADD CONSTRAINT "apps_poll_submission_selected_by_id_fkey" FOREIGN KEY ("selected_by_id") REFERENCES "public"."groupmember"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "community_answer" ADD CONSTRAINT "community_answer_author_id_fkey" FOREIGN KEY ("author_id") REFERENCES "public"."organizationmember"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "community_answer" ADD CONSTRAINT "community_answer_author_profile_id_fkey" FOREIGN KEY ("author_profile_id") REFERENCES "public"."profile"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "community_answer" ADD CONSTRAINT "community_answer_question_id_fkey" FOREIGN KEY ("question_id") REFERENCES "public"."community_question"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "community_question" ADD CONSTRAINT "community_question_author_id_fkey" FOREIGN KEY ("author_id") REFERENCES "public"."organizationmember"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "community_question" ADD CONSTRAINT "community_question_author_profile_id_fkey" FOREIGN KEY ("author_profile_id") REFERENCES "public"."profile"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "community_question" ADD CONSTRAINT "community_question_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "public"."course"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "community_question" ADD CONSTRAINT "community_question_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "course" ADD CONSTRAINT "course_group_id_fkey" FOREIGN KEY ("group_id") REFERENCES "public"."group"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "course_newsfeed" ADD CONSTRAINT "course_newsfeed_author_id_fkey" FOREIGN KEY ("author_id") REFERENCES "public"."groupmember"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "course_newsfeed" ADD CONSTRAINT "course_newsfeed_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "public"."course"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "course_newsfeed_comment" ADD CONSTRAINT "course_newsfeed_comment_author_id_fkey" FOREIGN KEY ("author_id") REFERENCES "public"."groupmember"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "course_newsfeed_comment" ADD CONSTRAINT "course_newsfeed_comment_course_newsfeed_id_fkey" FOREIGN KEY ("course_newsfeed_id") REFERENCES "public"."course_newsfeed"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "exercise" ADD CONSTRAINT "exercise_lesson_id_fkey" FOREIGN KEY ("lesson_id") REFERENCES "public"."lesson"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "group" ADD CONSTRAINT "group_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "group_attendance" ADD CONSTRAINT "group_attendance_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "public"."course"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "group_attendance" ADD CONSTRAINT "group_attendance_student_id_fkey" FOREIGN KEY ("student_id") REFERENCES "public"."groupmember"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "groupmember" ADD CONSTRAINT "groupmember_group_id_fkey" FOREIGN KEY ("group_id") REFERENCES "public"."group"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "groupmember" ADD CONSTRAINT "groupmember_profile_id_fkey" FOREIGN KEY ("profile_id") REFERENCES "public"."profile"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "groupmember" ADD CONSTRAINT "groupmember_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "public"."role"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "lesson" ADD CONSTRAINT "lesson_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "public"."course"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "lesson" ADD CONSTRAINT "lesson_teacher_id_fkey" FOREIGN KEY ("teacher_id") REFERENCES "public"."profile"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "lesson" ADD CONSTRAINT "public_lesson_section_id_fkey" FOREIGN KEY ("section_id") REFERENCES "public"."lesson_section"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint
-- ALTER TABLE "lesson_comment" ADD CONSTRAINT "lesson_comment_groupmember_id_fkey" FOREIGN KEY ("groupmember_id") REFERENCES "public"."groupmember"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "lesson_comment" ADD CONSTRAINT "lesson_comment_lesson_id_fkey" FOREIGN KEY ("lesson_id") REFERENCES "public"."lesson"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "lesson_completion" ADD CONSTRAINT "lesson_completion_lesson_id_fkey" FOREIGN KEY ("lesson_id") REFERENCES "public"."lesson"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "lesson_completion" ADD CONSTRAINT "lesson_completion_profile_id_fkey" FOREIGN KEY ("profile_id") REFERENCES "public"."profile"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "lesson_language" ADD CONSTRAINT "public_lesson_language_lesson_id_fkey" FOREIGN KEY ("lesson_id") REFERENCES "public"."lesson"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint
-- ALTER TABLE "lesson_language_history" ADD CONSTRAINT "public_lesson_language_history_lesson_language_id_fkey" FOREIGN KEY ("lesson_language_id") REFERENCES "public"."lesson_language"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint
-- ALTER TABLE "lesson_section" ADD CONSTRAINT "public_lesson_section_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "public"."course"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint
-- ALTER TABLE "option" ADD CONSTRAINT "option_question_id_fkey" FOREIGN KEY ("question_id") REFERENCES "public"."question"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "organization_contacts" ADD CONSTRAINT "organization_contacts_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "organization_emaillist" ADD CONSTRAINT "organization_emaillist_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "organization_plan" ADD CONSTRAINT "organization_plan_org_id_fkey" FOREIGN KEY ("org_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "organization_plan" ADD CONSTRAINT "organization_plan_triggered_by_fkey" FOREIGN KEY ("triggered_by") REFERENCES "public"."organizationmember"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "organizationmember" ADD CONSTRAINT "organizationmember_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "organizationmember" ADD CONSTRAINT "organizationmember_profile_id_fkey" FOREIGN KEY ("profile_id") REFERENCES "public"."profile"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "organizationmember" ADD CONSTRAINT "organizationmember_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "public"."role"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "profile" ADD CONSTRAINT "profile_id_fkey" FOREIGN KEY ("id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "question" ADD CONSTRAINT "question_exercise_id_fkey" FOREIGN KEY ("exercise_id") REFERENCES "public"."exercise"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "question" ADD CONSTRAINT "question_question_type_id_fkey" FOREIGN KEY ("question_type_id") REFERENCES "public"."question_type"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "question_answer" ADD CONSTRAINT "question_answer_group_member_id_fkey" FOREIGN KEY ("group_member_id") REFERENCES "public"."groupmember"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "question_answer" ADD CONSTRAINT "question_answer_question_id_fkey" FOREIGN KEY ("question_id") REFERENCES "public"."question"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "question_answer" ADD CONSTRAINT "question_answer_submission_id_fkey" FOREIGN KEY ("submission_id") REFERENCES "public"."submission"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "quiz" ADD CONSTRAINT "quiz_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "quiz_play" ADD CONSTRAINT "quiz_play_quiz_id_fkey" FOREIGN KEY ("quiz_id") REFERENCES "public"."quiz"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "submission" ADD CONSTRAINT "submission_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "public"."course"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "submission" ADD CONSTRAINT "submission_exercise_id_fkey" FOREIGN KEY ("exercise_id") REFERENCES "public"."exercise"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "submission" ADD CONSTRAINT "submission_status_id_fkey" FOREIGN KEY ("status_id") REFERENCES "public"."submissionstatus"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- ALTER TABLE "submission" ADD CONSTRAINT "submission_submitted_by_fkey" FOREIGN KEY ("submitted_by") REFERENCES "public"."groupmember"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-- CREATE INDEX "idx_analytics_login_events_logged_in_at" ON "analytics_login_events" USING btree ("logged_in_at" timestamptz_ops);--> statement-breakpoint
-- CREATE INDEX "idx_analytics_login_events_user_id" ON "analytics_login_events" USING btree ("user_id" uuid_ops);--> statement-breakpoint
-- CREATE VIEW "public"."dash_org_stats" AS (SELECT gp.organization_id AS org_id, count(DISTINCT course.id) AS no_of_courses, count(DISTINCT gm.profile_id) AS enrolled_students FROM course JOIN "group" gp ON gp.id = course.group_id LEFT JOIN groupmember gm ON gm.group_id = gp.id AND gm.role_id = 3 WHERE course.status = 'ACTIVE'::text GROUP BY gp.organization_id);--> statement-breakpoint
-- CREATE VIEW "public"."lesson_versions" AS (SELECT llh.old_content, llh.new_content, llh."timestamp", ll.locale, ll.lesson_id FROM lesson_language_history llh JOIN lesson_language ll ON ll.id = llh.lesson_language_id);--> statement-breakpoint
-- CREATE POLICY "Enable delete for users based on user_id" ON "analytics_login_events" AS PERMISSIVE FOR DELETE TO public USING ((auth.uid() = user_id));--> statement-breakpoint
-- CREATE POLICY "Users can insert their own login events" ON "analytics_login_events" AS PERMISSIVE FOR INSERT TO "authenticated";--> statement-breakpoint
-- CREATE POLICY "Delete only your own poll" ON "apps_poll" AS PERMISSIVE FOR DELETE TO public USING ((auth.uid() = ( SELECT groupmember.profile_id
-- FROM groupmember
-- WHERE (groupmember.id = apps_poll."authorId"))));--> statement-breakpoint
-- CREATE POLICY "Update only your own" ON "apps_poll" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be a course member to INSERT" ON "apps_poll" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be course member to SELECT" ON "apps_poll" AS PERMISSIVE FOR SELECT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be a course member to INSERT" ON "apps_poll_option" AS PERMISSIVE FOR INSERT TO public WITH CHECK (is_user_in_course_group_or_admin(( SELECT groupmember.group_id
-- FROM groupmember
-- WHERE (groupmember.id = ( SELECT apps_poll."authorId"
-- FROM apps_poll
-- WHERE (apps_poll.id = apps_poll_option.poll_id)
-- LIMIT 1))
-- LIMIT 1)));--> statement-breakpoint
-- CREATE POLICY "User must be a course member to UPDATE" ON "apps_poll_option" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be a teacher to DELETE" ON "apps_poll_option" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be course member to SELECT" ON "apps_poll_option" AS PERMISSIVE FOR SELECT TO public;--> statement-breakpoint
-- CREATE POLICY "Authenticated users can read" ON "apps_poll_submission" AS PERMISSIVE FOR SELECT TO public USING ((auth.uid() IS NOT NULL));--> statement-breakpoint
-- CREATE POLICY "Delete your own submission" ON "apps_poll_submission" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "Enable insert for authenticated users only" ON "apps_poll_submission" AS PERMISSIVE FOR INSERT TO "authenticated";--> statement-breakpoint
-- CREATE POLICY "Update your own submission" ON "apps_poll_submission" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Authenticated users can SELECT" ON "community_answer" AS PERMISSIVE FOR SELECT TO public USING ((auth.uid() IS NOT NULL));--> statement-breakpoint
-- CREATE POLICY "Delete your own answer" ON "community_answer" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "Enable insert for authenticated users only" ON "community_answer" AS PERMISSIVE FOR INSERT TO "authenticated";--> statement-breakpoint
-- CREATE POLICY "Update your own answer" ON "community_answer" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Authenticated users can SELECT" ON "community_question" AS PERMISSIVE FOR SELECT TO public USING ((auth.uid() IS NOT NULL));--> statement-breakpoint
-- CREATE POLICY "Delete your own question" ON "community_question" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "Enable insert for authenticated users only" ON "community_question" AS PERMISSIVE FOR INSERT TO "authenticated";--> statement-breakpoint
-- CREATE POLICY "Update your own question" ON "community_question" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Enable access to all users if PUBLIC or to course members when " ON "course" AS PERMISSIVE FOR SELECT TO public USING ((is_published OR is_user_in_course_group_or_admin(group_id)));--> statement-breakpoint
-- CREATE POLICY "User must be an org member to DELETE" ON "course" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to INSERT" ON "course" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to UPDATE" ON "course" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Delete your own comment" ON "course_newsfeed" AS PERMISSIVE FOR DELETE TO public USING ((auth.uid() = ( SELECT groupmember.profile_id
-- FROM groupmember
-- WHERE (groupmember.id = course_newsfeed.author_id))));--> statement-breakpoint
-- CREATE POLICY "Update only your own" ON "course_newsfeed" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be a course member to INSERT" ON "course_newsfeed" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be a course member to SELECT" ON "course_newsfeed" AS PERMISSIVE FOR SELECT TO public;--> statement-breakpoint
-- CREATE POLICY "Delete your own" ON "course_newsfeed_comment" AS PERMISSIVE FOR DELETE TO public USING ((auth.uid() = ( SELECT groupmember.profile_id
-- FROM groupmember
-- WHERE (groupmember.id = course_newsfeed_comment.author_id))));--> statement-breakpoint
-- CREATE POLICY "Update only your own" ON "course_newsfeed_comment" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be a course member to INSERT" ON "course_newsfeed_comment" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be a course member to SELECT" ON "course_newsfeed_comment" AS PERMISSIVE FOR SELECT TO public;--> statement-breakpoint
-- CREATE POLICY "Enable read access for all users" ON "exercise" AS PERMISSIVE FOR SELECT TO public USING (true);--> statement-breakpoint
-- CREATE POLICY "User must be an org member to DELETE" ON "exercise" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to INSERT" ON "exercise" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to UPDATE" ON "exercise" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Enable insert for authenticated users only" ON "group" AS PERMISSIVE FOR INSERT TO "authenticated" WITH CHECK (true);--> statement-breakpoint
-- CREATE POLICY "Enable read access for all users" ON "group" AS PERMISSIVE FOR SELECT TO public;--> statement-breakpoint
-- CREATE POLICY "Only org admins can delete" ON "group" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "Only org admins can update" ON "group" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be a course member to INSERT" ON "group_attendance" AS PERMISSIVE FOR INSERT TO public WITH CHECK (is_user_in_course_group_or_admin(( SELECT course.group_id
-- FROM course
-- WHERE (course.id = group_attendance.course_id)
-- LIMIT 1)));--> statement-breakpoint
-- CREATE POLICY "User must be a course member to SELECT" ON "group_attendance" AS PERMISSIVE FOR SELECT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be a course member to UPDATE" ON "group_attendance" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to DELETE" ON "group_attendance" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "Enable insert for authenticated users only" ON "groupmember" AS PERMISSIVE FOR INSERT TO "authenticated" WITH CHECK (true);--> statement-breakpoint
-- CREATE POLICY "Enable read access for all users" ON "groupmember" AS PERMISSIVE FOR SELECT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to DELETE" ON "groupmember" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to UPDATE" ON "groupmember" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Enable read access for all users" ON "lesson" AS PERMISSIVE FOR SELECT TO public USING (true);--> statement-breakpoint
-- CREATE POLICY "User must be an org member to DELETE" ON "lesson" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to INSERT" ON "lesson" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to UPDATE" ON "lesson" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Delete only your own comment" ON "lesson_comment" AS PERMISSIVE FOR DELETE TO public USING ((auth.uid() = ( SELECT groupmember.profile_id
-- FROM groupmember
-- WHERE (groupmember.id = lesson_comment.groupmember_id))));--> statement-breakpoint
-- CREATE POLICY "Enable read access for all users" ON "lesson_comment" AS PERMISSIVE FOR SELECT TO public;--> statement-breakpoint
-- CREATE POLICY "Update only your own" ON "lesson_comment" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be in course group to INSERT" ON "lesson_comment" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "Enable read access for all users" ON "lesson_completion" AS PERMISSIVE FOR SELECT TO public USING (true);--> statement-breakpoint
-- CREATE POLICY "User must be an course member or Admin to perform ALL operation" ON "lesson_completion" AS PERMISSIVE FOR ALL TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to DELETE" ON "lesson_completion" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to UPDATE" ON "lesson_completion" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Enable read access for all users" ON "lesson_language" AS PERMISSIVE FOR SELECT TO public USING (true);--> statement-breakpoint
-- CREATE POLICY "User must be an org member to DELETE" ON "lesson_language" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to INSERT" ON "lesson_language" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to UPDATE" ON "lesson_language" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Authenticated users can SELECT" ON "lesson_language_history" AS PERMISSIVE FOR SELECT TO public USING ((auth.uid() IS NOT NULL));--> statement-breakpoint
-- CREATE POLICY "User must be an org member to DELETE" ON "lesson_language_history" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to INSERT" ON "lesson_language_history" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to UPDATE" ON "lesson_language_history" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Enable read access for all users" ON "lesson_section" AS PERMISSIVE FOR SELECT TO public USING (true);--> statement-breakpoint
-- CREATE POLICY "User must be an org member to DELETE" ON "lesson_section" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to INSERT" ON "lesson_section" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to UPDATE" ON "lesson_section" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Allow authenticated users to SELECT" ON "option" AS PERMISSIVE FOR SELECT TO public USING ((auth.uid() IS NOT NULL));--> statement-breakpoint
-- CREATE POLICY "User must be an org member to DELETE" ON "option" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to INSERT" ON "option" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to UPDATE" ON "option" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Authenticated users can delete" ON "organization" AS PERMISSIVE FOR DELETE TO public USING ((auth.uid() IS NOT NULL));--> statement-breakpoint
-- CREATE POLICY "Enable insert for authenticated users only" ON "organization" AS PERMISSIVE FOR INSERT TO "authenticated";--> statement-breakpoint
-- CREATE POLICY "Enable read access for all users" ON "organization" AS PERMISSIVE FOR SELECT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be admin to UPDATE" ON "organization" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Enable read access for all users" ON "organization_contacts" AS PERMISSIVE FOR INSERT TO public WITH CHECK (true);--> statement-breakpoint
-- CREATE POLICY "Enable read access for all users" ON "organization_emaillist" AS PERMISSIVE FOR INSERT TO public WITH CHECK (true);--> statement-breakpoint
-- CREATE POLICY "Enable read access for all users" ON "organization_plan" AS PERMISSIVE FOR SELECT TO public USING (true);--> statement-breakpoint
-- CREATE POLICY "User must be an org member to DELETE" ON "organization_plan" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to INSERT" ON "organization_plan" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to UPDATE" ON "organization_plan" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Allow authenticated users to read." ON "organizationmember" AS PERMISSIVE FOR SELECT TO public USING ((auth.uid() IS NOT NULL));--> statement-breakpoint
-- CREATE POLICY "Enable insert for authenticated users only" ON "organizationmember" AS PERMISSIVE FOR INSERT TO "authenticated";--> statement-breakpoint
-- CREATE POLICY "Only admin can delete" ON "organizationmember" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "Only admin can update" ON "organizationmember" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Only user can update their account via email" ON "organizationmember" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an admin to INSERT or allow if no existing member" ON "organizationmember" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "Only auth users can read profile" ON "profile" AS PERMISSIVE FOR SELECT TO "anon", "authenticated" USING (true);--> statement-breakpoint
-- CREATE POLICY "Public profiles are viewable by everyone." ON "profile" AS PERMISSIVE FOR SELECT TO public;--> statement-breakpoint
-- CREATE POLICY "User can only delete their profiles" ON "profile" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "Users can insert their own profile." ON "profile" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "Users can update own profile." ON "profile" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Allow authenticated users to SELECT" ON "question" AS PERMISSIVE FOR SELECT TO public USING ((auth.uid() IS NOT NULL));--> statement-breakpoint
-- CREATE POLICY "User must be an org member to DELETE" ON "question" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to INSERT" ON "question" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an org member to UPDATE" ON "question" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Only authenticated users can select." ON "question_answer" AS PERMISSIVE FOR SELECT TO public USING ((auth.uid() IS NOT NULL));--> statement-breakpoint
-- CREATE POLICY "User must be an course member to DELETE" ON "question_answer" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an course member to INSERT" ON "question_answer" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be an course member to UPDATE" ON "question_answer" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Enable read access for all users" ON "question_type" AS PERMISSIVE FOR SELECT TO public USING (true);--> statement-breakpoint
-- CREATE POLICY "Authenticated users can SELECT" ON "quiz" AS PERMISSIVE FOR SELECT TO "authenticated" USING (true);--> statement-breakpoint
-- CREATE POLICY "Authenticated users can SELECT" ON "quiz_play" AS PERMISSIVE FOR SELECT TO "authenticated" USING (true);--> statement-breakpoint
-- CREATE POLICY "Enable read access for all users" ON "role" AS PERMISSIVE FOR SELECT TO public USING (true);--> statement-breakpoint
-- CREATE POLICY "Only authenticated users can SELECT" ON "submission" AS PERMISSIVE FOR SELECT TO "authenticated" USING (true);--> statement-breakpoint
-- CREATE POLICY "User must be a course member to DELETE" ON "submission" AS PERMISSIVE FOR DELETE TO public;--> statement-breakpoint
-- CREATE POLICY "User must be a course member to INSERT" ON "submission" AS PERMISSIVE FOR INSERT TO public;--> statement-breakpoint
-- CREATE POLICY "User must be a course member to UPDATE" ON "submission" AS PERMISSIVE FOR UPDATE TO public;--> statement-breakpoint
-- CREATE POLICY "Authenticated users can SELECT" ON "submissionstatus" AS PERMISSIVE FOR SELECT TO "authenticated" USING (true);
select id from public.profile limit 1;
@@ -0,0 +1,47 @@
CREATE TABLE "account" (
"id" text PRIMARY KEY NOT NULL,
"account_id" text NOT NULL,
"provider_id" text NOT NULL,
"user_id" text NOT NULL,
"access_token" text,
"refresh_token" text,
"id_token" text,
"access_token_expires_at" timestamp,
"refresh_token_expires_at" timestamp,
"scope" text,
"password" text,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp NOT NULL
);
--> statement-breakpoint
CREATE TABLE "session" (
"id" text PRIMARY KEY NOT NULL,
"expires_at" timestamp NOT NULL,
"token" text NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp NOT NULL,
"ip_address" text,
"user_agent" text,
"user_id" text NOT NULL,
"impersonated_by" text,
CONSTRAINT "session_token_unique" UNIQUE("token")
);
--> statement-breakpoint
CREATE TABLE "user" (
"id" text PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"email" text NOT NULL,
"email_verified" boolean DEFAULT false NOT NULL,
"image" text,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL,
"role" text,
"banned" boolean DEFAULT false,
"ban_reason" text,
"ban_expires" timestamp,
"is_anonymous" boolean,
CONSTRAINT "user_email_unique" UNIQUE("email")
);
--> statement-breakpoint
ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,20 @@
{
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1760910273722,
"tag": "0000_stale_storm",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1760912065001,
"tag": "0001_tranquil_thunderbolts",
"breakpoints": true
}
]
}
+424
View File
@@ -0,0 +1,424 @@
import {
analyticsLoginEvents,
appsPoll,
appsPollOption,
appsPollSubmission,
communityAnswer,
communityQuestion,
course,
courseNewsfeed,
courseNewsfeedComment,
exercise,
group,
groupAttendance,
groupmember,
lesson,
lessonComment,
lessonCompletion,
lessonLanguage,
lessonLanguageHistory,
lessonSection,
option,
organization,
organizationContacts,
organizationEmaillist,
organizationPlan,
organizationmember,
profile,
question,
questionAnswer,
questionType,
quiz,
quizPlay,
role,
submission,
submissionstatus
} from './schema';
import { relations } from 'drizzle-orm/relations';
import { user } from './auth-schema';
export const analyticsLoginEventsRelations = relations(analyticsLoginEvents, ({ one }) => ({
usersInAuth: one(user, {
fields: [analyticsLoginEvents.userId],
references: [user.id]
})
}));
export const usersInAuthRelations = relations(user, ({ many }) => ({
analyticsLoginEvents: many(analyticsLoginEvents),
profiles: many(profile)
}));
export const lessonSectionRelations = relations(lessonSection, ({ one, many }) => ({
course: one(course, {
fields: [lessonSection.courseId],
references: [course.id]
}),
lessons: many(lesson)
}));
export const courseRelations = relations(course, ({ one, many }) => ({
lessonSections: many(lessonSection),
groupAttendances: many(groupAttendance),
submissions: many(submission),
group: one(group, {
fields: [course.groupId],
references: [group.id]
}),
appsPolls: many(appsPoll),
lessons: many(lesson),
communityQuestions: many(communityQuestion),
courseNewsfeeds: many(courseNewsfeed)
}));
export const groupRelations = relations(group, ({ one, many }) => ({
organization: one(organization, {
fields: [group.organizationId],
references: [organization.id]
}),
courses: many(course),
groupmembers: many(groupmember)
}));
export const organizationRelations = relations(organization, ({ many }) => ({
groups: many(group),
organizationContacts: many(organizationContacts),
quizzes: many(quiz),
organizationEmaillists: many(organizationEmaillist),
communityQuestions: many(communityQuestion),
organizationPlans: many(organizationPlan),
organizationmembers: many(organizationmember)
}));
export const groupAttendanceRelations = relations(groupAttendance, ({ one }) => ({
course: one(course, {
fields: [groupAttendance.courseId],
references: [course.id]
}),
groupmember: one(groupmember, {
fields: [groupAttendance.studentId],
references: [groupmember.id]
})
}));
export const groupmemberRelations = relations(groupmember, ({ one, many }) => ({
groupAttendances: many(groupAttendance),
lessonComments: many(lessonComment),
submissions: many(submission),
appsPolls: many(appsPoll),
appsPollSubmissions: many(appsPollSubmission),
group: one(group, {
fields: [groupmember.groupId],
references: [group.id]
}),
profile: one(profile, {
fields: [groupmember.profileId],
references: [profile.id]
}),
role: one(role, {
fields: [groupmember.roleId],
references: [role.id]
}),
courseNewsfeeds: many(courseNewsfeed),
courseNewsfeedComments: many(courseNewsfeedComment),
questionAnswers: many(questionAnswer)
}));
export const appsPollOptionRelations = relations(appsPollOption, ({ one, many }) => ({
appsPoll: one(appsPoll, {
fields: [appsPollOption.pollId],
references: [appsPoll.id]
}),
appsPollSubmissions: many(appsPollSubmission)
}));
export const appsPollRelations = relations(appsPoll, ({ one, many }) => ({
appsPollOptions: many(appsPollOption),
groupmember: one(groupmember, {
fields: [appsPoll.authorId],
references: [groupmember.id]
}),
course: one(course, {
fields: [appsPoll.courseId],
references: [course.id]
}),
appsPollSubmissions: many(appsPollSubmission)
}));
export const profileRelations = relations(profile, ({ one, many }) => ({
usersInAuth: one(user, {
fields: [profile.id],
references: [user.id]
}),
lessons: many(lesson),
groupmembers: many(groupmember),
lessonCompletions: many(lessonCompletion),
communityAnswers: many(communityAnswer),
communityQuestions: many(communityQuestion),
organizationmembers: many(organizationmember)
}));
export const optionRelations = relations(option, ({ one }) => ({
question: one(question, {
fields: [option.questionId],
references: [question.id]
})
}));
export const questionRelations = relations(question, ({ one, many }) => ({
options: many(option),
exercise: one(exercise, {
fields: [question.exerciseId],
references: [exercise.id]
}),
questionType: one(questionType, {
fields: [question.questionTypeId],
references: [questionType.id]
}),
questionAnswers: many(questionAnswer)
}));
export const quizPlayRelations = relations(quizPlay, ({ one }) => ({
quiz: one(quiz, {
fields: [quizPlay.quizId],
references: [quiz.id]
})
}));
export const quizRelations = relations(quiz, ({ one, many }) => ({
quizPlays: many(quizPlay),
organization: one(organization, {
fields: [quiz.organizationId],
references: [organization.id]
})
}));
export const organizationContactsRelations = relations(organizationContacts, ({ one }) => ({
organization: one(organization, {
fields: [organizationContacts.organizationId],
references: [organization.id]
})
}));
export const lessonCommentRelations = relations(lessonComment, ({ one }) => ({
groupmember: one(groupmember, {
fields: [lessonComment.groupmemberId],
references: [groupmember.id]
}),
lesson: one(lesson, {
fields: [lessonComment.lessonId],
references: [lesson.id]
})
}));
export const lessonRelations = relations(lesson, ({ one, many }) => ({
lessonComments: many(lessonComment),
course: one(course, {
fields: [lesson.courseId],
references: [course.id]
}),
profile: one(profile, {
fields: [lesson.teacherId],
references: [profile.id]
}),
lessonSection: one(lessonSection, {
fields: [lesson.sectionId],
references: [lessonSection.id]
}),
exercises: many(exercise),
lessonCompletions: many(lessonCompletion),
lessonLanguages: many(lessonLanguage)
}));
export const submissionRelations = relations(submission, ({ one, many }) => ({
course: one(course, {
fields: [submission.courseId],
references: [course.id]
}),
exercise: one(exercise, {
fields: [submission.exerciseId],
references: [exercise.id]
}),
submissionstatus: one(submissionstatus, {
fields: [submission.statusId],
references: [submissionstatus.id]
}),
groupmember: one(groupmember, {
fields: [submission.submittedBy],
references: [groupmember.id]
}),
questionAnswers: many(questionAnswer)
}));
export const exerciseRelations = relations(exercise, ({ one, many }) => ({
submissions: many(submission),
lesson: one(lesson, {
fields: [exercise.lessonId],
references: [lesson.id]
}),
questions: many(question)
}));
export const submissionstatusRelations = relations(submissionstatus, ({ many }) => ({
submissions: many(submission)
}));
export const organizationEmaillistRelations = relations(organizationEmaillist, ({ one }) => ({
organization: one(organization, {
fields: [organizationEmaillist.organizationId],
references: [organization.id]
})
}));
export const appsPollSubmissionRelations = relations(appsPollSubmission, ({ one }) => ({
appsPoll: one(appsPoll, {
fields: [appsPollSubmission.pollId],
references: [appsPoll.id]
}),
appsPollOption: one(appsPollOption, {
fields: [appsPollSubmission.pollOptionId],
references: [appsPollOption.id]
}),
groupmember: one(groupmember, {
fields: [appsPollSubmission.selectedById],
references: [groupmember.id]
})
}));
export const roleRelations = relations(role, ({ many }) => ({
groupmembers: many(groupmember),
organizationmembers: many(organizationmember)
}));
export const lessonCompletionRelations = relations(lessonCompletion, ({ one }) => ({
lesson: one(lesson, {
fields: [lessonCompletion.lessonId],
references: [lesson.id]
}),
profile: one(profile, {
fields: [lessonCompletion.profileId],
references: [profile.id]
})
}));
export const communityAnswerRelations = relations(communityAnswer, ({ one }) => ({
organizationmember: one(organizationmember, {
fields: [communityAnswer.authorId],
references: [organizationmember.id]
}),
profile: one(profile, {
fields: [communityAnswer.authorProfileId],
references: [profile.id]
}),
communityQuestion: one(communityQuestion, {
fields: [communityAnswer.questionId],
references: [communityQuestion.id]
})
}));
export const organizationmemberRelations = relations(organizationmember, ({ one, many }) => ({
communityAnswers: many(communityAnswer),
communityQuestions: many(communityQuestion),
organizationPlans: many(organizationPlan),
organization: one(organization, {
fields: [organizationmember.organizationId],
references: [organization.id]
}),
profile: one(profile, {
fields: [organizationmember.profileId],
references: [profile.id]
}),
role: one(role, {
fields: [organizationmember.roleId],
references: [role.id]
})
}));
export const communityQuestionRelations = relations(communityQuestion, ({ one, many }) => ({
communityAnswers: many(communityAnswer),
organizationmember: one(organizationmember, {
fields: [communityQuestion.authorId],
references: [organizationmember.id]
}),
profile: one(profile, {
fields: [communityQuestion.authorProfileId],
references: [profile.id]
}),
course: one(course, {
fields: [communityQuestion.courseId],
references: [course.id]
}),
organization: one(organization, {
fields: [communityQuestion.organizationId],
references: [organization.id]
})
}));
export const courseNewsfeedRelations = relations(courseNewsfeed, ({ one, many }) => ({
groupmember: one(groupmember, {
fields: [courseNewsfeed.authorId],
references: [groupmember.id]
}),
course: one(course, {
fields: [courseNewsfeed.courseId],
references: [course.id]
}),
courseNewsfeedComments: many(courseNewsfeedComment)
}));
export const courseNewsfeedCommentRelations = relations(courseNewsfeedComment, ({ one }) => ({
groupmember: one(groupmember, {
fields: [courseNewsfeedComment.authorId],
references: [groupmember.id]
}),
courseNewsfeed: one(courseNewsfeed, {
fields: [courseNewsfeedComment.courseNewsfeedId],
references: [courseNewsfeed.id]
})
}));
export const organizationPlanRelations = relations(organizationPlan, ({ one }) => ({
organization: one(organization, {
fields: [organizationPlan.orgId],
references: [organization.id]
}),
organizationmember: one(organizationmember, {
fields: [organizationPlan.triggeredBy],
references: [organizationmember.id]
})
}));
export const lessonLanguageRelations = relations(lessonLanguage, ({ one, many }) => ({
lesson: one(lesson, {
fields: [lessonLanguage.lessonId],
references: [lesson.id]
}),
lessonLanguageHistories: many(lessonLanguageHistory)
}));
export const lessonLanguageHistoryRelations = relations(lessonLanguageHistory, ({ one }) => ({
lessonLanguage: one(lessonLanguage, {
fields: [lessonLanguageHistory.lessonLanguageId],
references: [lessonLanguage.id]
})
}));
export const questionTypeRelations = relations(questionType, ({ many }) => ({
questions: many(question)
}));
export const questionAnswerRelations = relations(questionAnswer, ({ one }) => ({
groupmember: one(groupmember, {
fields: [questionAnswer.groupMemberId],
references: [groupmember.id]
}),
question: one(question, {
fields: [questionAnswer.questionId],
references: [question.id]
}),
submission: one(submission, {
fields: [questionAnswer.submissionId],
references: [submission.id]
})
}));
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,173 @@
import 'dotenv/config';
import { admin, anonymous } from 'better-auth/plugins';
import { Pool } from 'pg';
import { User as SupabaseUser } from '@supabase/supabase-js';
import { betterAuth } from 'better-auth';
// We create a custom betterAuth here because this code works well with pg
const auth = betterAuth({
database: new Pool({
connectionString: process.env.DATABASE_URL
}),
emailVerification: {},
emailAndPassword: {
enabled: true
},
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!
}
},
plugins: [admin(), anonymous()],
user: {
modelName: 'user',
fields: {
emailVerified: 'email_verified',
createdAt: 'created_at',
updatedAt: 'updated_at'
}
},
account: {
modelName: 'account',
fields: {
userId: 'user_id',
createdAt: 'created_at',
updatedAt: 'updated_at',
accountId: 'account_id',
providerId: 'provider_id'
}
}
});
type User = SupabaseUser & {
is_super_admin: boolean;
raw_user_meta_data: {
avatar_url: string;
};
encrypted_password: string;
email_confirmed_at: string;
created_at: string;
updated_at: string;
is_anonymous: boolean;
profile_fullname: string;
identities: {
provider: string;
identity_data: {
sub: string;
email: string;
};
created_at: string;
updated_at: string;
};
};
const migrateFromSupabase = async () => {
const ctx = await auth.$context;
const db = ctx.options.database as Pool;
const users = await db
.query(
`
SELECT
u.*,
COALESCE(
json_agg(
i.* ORDER BY i.id
) FILTER (WHERE i.id IS NOT NULL),
'[]'::json
) as identities,
p.fullname as profile_fullname -- Select fullname from public.profile
FROM auth.users u
LEFT JOIN auth.identities i ON u.id = i.user_id
LEFT JOIN public.profile p ON u.id = p.id -- LEFT JOIN with public.profile
GROUP BY u.id, p.fullname
`
)
.then((res) => res.rows as User[]);
console.log('users length', users.length);
for (const user of users) {
if (!user.email) {
continue;
}
const userName = user.profile_fullname || user.email;
console.log('inserting user', user.email);
let shouldContinue = false;
await ctx.adapter
.create({
model: 'user',
forceAllowId: true,
data: {
id: user.id,
email: user.email,
name: userName,
role: user.is_super_admin ? 'admin' : user.role,
emailVerified: !!user.email_confirmed_at,
image: user.raw_user_meta_data?.avatar_url || '',
createdAt: new Date(user.created_at),
updatedAt: new Date(user.updated_at)
}
})
.catch((e) => {
console.log('catch');
shouldContinue = true;
});
if (shouldContinue) {
console.log('Continue');
continue;
}
for (const identity of user.identities) {
const existingAccounts = await ctx.internalAdapter.findAccounts(user.id);
if (identity.provider === 'email') {
console.log('email provider', identity);
const hasCredential = existingAccounts.find((account) => account.providerId === 'credential');
console.log('hasCredential', hasCredential);
if (!hasCredential) {
console.log('doesnt have credentials');
await ctx.adapter
.create({
model: 'account',
data: {
userId: user.id,
providerId: 'credential',
accountId: user.id,
password: user.encrypted_password,
createdAt: new Date(user.created_at),
updatedAt: new Date(user.updated_at)
}
})
.catch((e) => {
console.log('catch error', e);
});
}
}
const supportedProviders = Object.keys(ctx.options.socialProviders || {});
if (supportedProviders.includes(identity.provider)) {
const hasAccount = existingAccounts.find((account) => account.providerId === identity.provider);
if (!hasAccount) {
await ctx.adapter.create({
model: 'account',
data: {
userId: user.id,
providerId: identity.provider,
accountId: identity.identity_data?.sub,
createdAt: new Date(identity.created_at ?? user.created_at),
updatedAt: new Date(identity.updated_at ?? user.updated_at)
}
});
}
}
}
}
};
migrateFromSupabase();
+11
View File
@@ -0,0 +1,11 @@
{
"extends": "@cio/tsconfig/base.json",
"include": ["src"],
"exclude": ["node_modules"],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@db/*": ["./src/*"]
}
}
}
+155
View File
@@ -0,0 +1,155 @@
[
{
"instance_id": "00000000-0000-0000-0000-000000000000",
"id": "01676a50-bb56-4c5e-8a61-fb9e9190fb10",
"aud": "authenticated",
"role": "authenticated",
"email": "[email protected]",
"encrypted_password": "$2a$10$WmfPO84h.8SbkZWSzXAygead77QQQXdeO5XIMd8iXk8SFQKB3Udy2",
"email_confirmed_at": "2023-11-22T10:09:32.250Z",
"invited_at": null,
"confirmation_token": "",
"confirmation_sent_at": null,
"recovery_token": "",
"recovery_sent_at": null,
"email_change_token_new": "",
"email_change": "",
"email_change_sent_at": null,
"last_sign_in_at": "2023-12-04T12:57:34.337Z",
"raw_app_meta_data": { "provider": "email", "providers": ["email"] },
"raw_user_meta_data": {},
"is_super_admin": null,
"created_at": "2023-11-22T10:09:32.171Z",
"updated_at": "2023-12-16T14:00:35.986Z",
"phone": null,
"phone_confirmed_at": null,
"phone_change": "",
"phone_change_token": "",
"phone_change_sent_at": null,
"confirmed_at": "2023-11-22T10:09:32.250Z",
"email_change_token_current": "",
"email_change_confirm_status": 0,
"banned_until": null,
"reauthentication_token": "",
"reauthentication_sent_at": null,
"is_sso_user": false,
"deleted_at": null,
"is_anonymous": false,
"identities": [
{
"provider_id": "01676a50-bb56-4c5e-8a61-fb9e9190fb10",
"user_id": "01676a50-bb56-4c5e-8a61-fb9e9190fb10",
"identity_data": { "sub": "01676a50-bb56-4c5e-8a61-fb9e9190fb10", "email": "[email protected]" },
"provider": "email",
"last_sign_in_at": "2023-11-22T10:09:32.225444+00:00",
"created_at": "2023-11-22T10:09:32.225521+00:00",
"updated_at": "2023-11-22T10:09:32.225521+00:00",
"email": "[email protected]",
"id": "a8961fe8-d51f-4b58-b8bb-a77d6a3c0cbd"
}
],
"profile_fullname": "Alice"
},
{
"instance_id": "00000000-0000-0000-0000-000000000000",
"id": "0c256e75-aa40-4f62-8d30-0217ca1c60d9",
"aud": "authenticated",
"role": "authenticated",
"email": "[email protected]",
"encrypted_password": "$2a$10$dgxySj.k12gDKhLx7X4x6./J.Nzhz7WQrwh5lkjLKwIwWW4o5GJcW",
"email_confirmed_at": "2023-12-18T17:08:57.413Z",
"invited_at": null,
"confirmation_token": "",
"confirmation_sent_at": null,
"recovery_token": "",
"recovery_sent_at": null,
"email_change_token_new": "",
"email_change": "",
"email_change_sent_at": null,
"last_sign_in_at": "2023-12-19T18:22:55.359Z",
"raw_app_meta_data": { "provider": "email", "providers": ["email"] },
"raw_user_meta_data": {},
"is_super_admin": null,
"created_at": "2023-12-18T17:08:57.399Z",
"updated_at": "2024-01-22T19:48:40.922Z",
"phone": null,
"phone_confirmed_at": null,
"phone_change": "",
"phone_change_token": "",
"phone_change_sent_at": null,
"confirmed_at": "2023-12-18T17:08:57.413Z",
"email_change_token_current": "",
"email_change_confirm_status": 0,
"banned_until": null,
"reauthentication_token": "",
"reauthentication_sent_at": null,
"is_sso_user": false,
"deleted_at": null,
"is_anonymous": false,
"identities": [
{
"provider_id": "0c256e75-aa40-4f62-8d30-0217ca1c60d9",
"user_id": "0c256e75-aa40-4f62-8d30-0217ca1c60d9",
"identity_data": { "sub": "0c256e75-aa40-4f62-8d30-0217ca1c60d9", "email": "[email protected]" },
"provider": "email",
"last_sign_in_at": "2023-12-18T17:08:57.410285+00:00",
"created_at": "2023-12-18T17:08:57.410344+00:00",
"updated_at": "2023-12-18T17:08:57.410344+00:00",
"email": "[email protected]",
"id": "65fa8bad-ca49-4676-8b17-ead4cd37fed6"
}
],
"profile_fullname": "John Doe"
},
{
"instance_id": "00000000-0000-0000-0000-000000000000",
"id": "7ac00503-8519-43c8-a5ea-b79aeca900b1",
"aud": "authenticated",
"role": "authenticated",
"email": "[email protected]",
"encrypted_password": "$2a$10$n8vBI6.pyE0W/RO9DcJDseLKF/CRwsU4X4Lc2MaQogt8pQgnJavTa",
"email_confirmed_at": "2023-12-16T14:04:38.311Z",
"invited_at": null,
"confirmation_token": "",
"confirmation_sent_at": null,
"recovery_token": "",
"recovery_sent_at": null,
"email_change_token_new": "",
"email_change": "",
"email_change_sent_at": null,
"last_sign_in_at": "2025-10-12T19:35:13.256Z",
"raw_app_meta_data": { "provider": "email", "providers": ["email"] },
"raw_user_meta_data": {},
"is_super_admin": null,
"created_at": "2023-12-16T14:04:38.281Z",
"updated_at": "2025-10-15T09:45:29.267Z",
"phone": null,
"phone_confirmed_at": null,
"phone_change": "",
"phone_change_token": "",
"phone_change_sent_at": null,
"confirmed_at": "2023-12-16T14:04:38.311Z",
"email_change_token_current": "",
"email_change_confirm_status": 0,
"banned_until": null,
"reauthentication_token": "",
"reauthentication_sent_at": null,
"is_sso_user": false,
"deleted_at": null,
"is_anonymous": false,
"identities": [
{
"provider_id": "7ac00503-8519-43c8-a5ea-b79aeca900b1",
"user_id": "7ac00503-8519-43c8-a5ea-b79aeca900b1",
"identity_data": { "sub": "7ac00503-8519-43c8-a5ea-b79aeca900b1", "email": "[email protected]" },
"provider": "email",
"last_sign_in_at": "2023-12-16T14:04:38.302264+00:00",
"created_at": "2023-12-16T14:04:38.302326+00:00",
"updated_at": "2023-12-16T14:04:38.302326+00:00",
"email": "[email protected]",
"id": "e790ab09-9c5c-4fb4-aa6b-78a5abe8cd56"
}
],
"profile_fullname": "Elon Gates"
}
]
-7
View File
@@ -1,7 +0,0 @@
{
"name": "shared",
"version": "1.0.0",
"dependencies": {
"tsconfig": "workspace:*"
}
}
-3
View File
@@ -1,3 +0,0 @@
{
"extends": "tsconfig/base.json"
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "tsconfig",
"name": "@cio/tsconfig",
"version": "0.0.0",
"private": true,
"license": "MIT",
+11
View File
@@ -0,0 +1,11 @@
{
"name": "@cio/utils",
"version": "1.0.0",
"exports": {
"./plans": "./src/plans/index.ts",
"./senja": "./src/senja/index.ts"
},
"dependencies": {
"@cio/tsconfig": "workspace:*"
}
}
+6
View File
@@ -0,0 +1,6 @@
export * from './constants';
export * from './utils';
import * as PLANS from './data.json';
export { PLANS };
+6
View File
@@ -0,0 +1,6 @@
{
"extends": "tsconfig/base.json",
"compilerOptions": {
"resolveJsonModule": true
}
}