feat: premium workspace usernames

This commit is contained in:
Henry
2025-01-02 20:40:29 +00:00
parent b5dc9433db
commit 67e714b8b0
41 changed files with 1767 additions and 9088 deletions

View File

@@ -10,6 +10,17 @@ if (!postgresUrl) {
}
const migrationClient = postgres(postgresUrl, { max: 1 });
console.log("Starting database migration...");
migrate(drizzle(migrationClient), {
migrationsFolder: "./src/server/db/migrations",
}).catch((e) => console.log(e));
migrationsFolder: "./migrations",
})
.then(() => {
console.log("✅ Database migration completed successfully");
process.exit(0);
})
.catch((error) => {
console.error("❌ Migration failed:");
console.error(error);
process.exit(1);
});

View File

@@ -5,7 +5,7 @@ import type { Database } from "@kan/db/types/database.types";
export const getById = async (db: SupabaseClient<Database>, userId: string) => {
const { data } = await db
.from("user")
.select(`id, name, email`)
.select(`id, name, email, stripeCustomerId`)
.eq("id", userId)
.limit(1)
.single();
@@ -29,11 +29,15 @@ export const getByEmail = async (
export const create = async (
db: SupabaseClient<Database>,
user: { id: string; email: string },
user: { id: string; email: string; stripeCustomerId: string },
) => {
const { data } = await db
.from("user")
.insert({ id: user.id, email: user.email })
.insert({
id: user.id,
email: user.email,
stripeCustomerId: user.stripeCustomerId,
})
.select()
.limit(1)
.single();

View File

@@ -159,3 +159,18 @@ export const hardDelete = async (
return result;
};
export const isWorkspaceSlugAvailable = async (
db: SupabaseClient<Database>,
workspaceSlug: string,
) => {
const { data } = await db
.from("workspace")
.select("id")
.eq("slug", workspaceSlug)
.is("deletedAt", null)
.limit(1)
.single();
return data === null;
};

View File

@@ -0,0 +1,16 @@
import type { SupabaseClient } from "@supabase/supabase-js";
import type { Database } from "@kan/db/types/database.types";
export const getWorkspaceSlug = async (
db: SupabaseClient<Database>,
slug: string,
) => {
const { data } = await db
.from("workspace_slugs")
.select(`slug, type`)
.eq("slug", slug)
.single();
return data;
};

View File

@@ -1,15 +1,15 @@
import { relations } from "drizzle-orm";
import {
integer,
bigint,
bigserial,
uuid,
integer,
pgEnum,
pgTable,
primaryKey,
text,
timestamp,
uuid,
varchar,
bigint,
} from "drizzle-orm/pg-core";
export const importSourceEnum = pgEnum("source", ["trello"]);
@@ -39,6 +39,7 @@ export const activityTypeEnum = pgEnum("card_activity_type", [
"card.updated.comment.deleted",
"card.archived",
]);
export const slugTypeEnum = pgEnum("slug_type", ["reserved", "premium"]);
export const boards = pgTable("board", {
id: bigserial("id", { mode: "number" }).primaryKey(),
@@ -272,6 +273,7 @@ export const users = pgTable("user", {
email: varchar("email", { length: 255 }).notNull().unique(),
emailVerified: timestamp("emailVerified", { mode: "date" }),
image: varchar("image", { length: 255 }),
stripeCustomerId: varchar("stripeCustomerId", { length: 255 }),
});
export const usersRelations = relations(users, ({ many }) => ({
@@ -430,3 +432,8 @@ export const commentsRelations = relations(comments, ({ one }) => ({
references: [users.id],
}),
}));
export const slugs = pgTable("workspace_slugs", {
slug: varchar("slug", { length: 255 }).notNull().unique(),
type: slugTypeEnum("type").notNull(),
});

View File

@@ -8,7 +8,7 @@ export type Json =
| { [key: string]: Json | undefined }
| Json[];
export type Database = {
export interface Database {
public: {
Tables: {
_card_labels: {
@@ -558,6 +558,7 @@ export type Database = {
id: string;
image: string | null;
name: string | null;
stripeCustomerId: string | null;
};
Insert: {
email: string;
@@ -565,6 +566,7 @@ export type Database = {
id: string;
image?: string | null;
name?: string | null;
stripeCustomerId?: string | null;
};
Update: {
email?: string;
@@ -572,6 +574,7 @@ export type Database = {
id?: string;
image?: string | null;
name?: string | null;
stripeCustomerId?: string | null;
};
Relationships: [];
};
@@ -690,10 +693,23 @@ export type Database = {
},
];
};
workspace_slugs: {
Row: {
slug: string;
type: Database["public"]["Enums"]["slug_type"];
};
Insert: {
slug: string;
type: Database["public"]["Enums"]["slug_type"];
};
Update: {
slug?: string;
type?: Database["public"]["Enums"]["slug_type"];
};
Relationships: [];
};
};
Views: {
[_ in never]: never;
};
Views: Record<never, never>;
Functions: {
is_workspace_admin: {
Args: {
@@ -717,7 +733,7 @@ export type Database = {
current_index: number;
new_index: number;
};
Returns: undefined;
Returns: boolean;
};
reorder_lists: {
Args: {
@@ -726,7 +742,7 @@ export type Database = {
current_index: number;
new_index: number;
};
Returns: undefined;
Returns: boolean;
};
shift_card_index: {
Args: {
@@ -760,15 +776,14 @@ export type Database = {
| "card.updated.comment.deleted";
member_status: "invited" | "active" | "removed";
role: "admin" | "member" | "guest";
slug_type: "reserved" | "premium";
source: "trello";
status: "started" | "success" | "failed";
workspace_invite_status: "pending" | "accepted" | "cancelled";
};
CompositeTypes: {
[_ in never]: never;
};
CompositeTypes: Record<never, never>;
};
};
}
type PublicSchema = Database[Extract<keyof Database, "public">];